PURPOSE
The purpose of this project is to create a remote-controlled car with a parking sensor controlled by an Arduino. The parking sensor will detect its distance away from an object or wall using echolocation. It will creating "beeping" sounds and light up different amounts of LEDs based on its distance away from the wall.
PROCEDURE
1] Program an Arduino in TinkerCAD to ensure the code works successfully
2] Solder all of the components required for the project to the Arduino
3] Upload the code to the Arduino
4] Assemble the car
5] Test all parts of the project to ensure it functions as intended
DATA/GRAPH
This is a video of the data that the Arduino outputs to the serial monitor (port 9600) as it is pushed towards a wall. The output details the distance from the wall in cm and in as well as the red, green, and blue values that are being sent to the RGB-LED.
ANALYSIS
This car senses its distance away from a wall using a sensor, turns a motor to stop the car before hitting the wall, outputs sounds from a speaker to alert users of a potential collision, and lights up an RGB-LED with different colors corresponding to the car's position. This project made use of my 3D-spatial reasoning (lego building), programming (Arduino), wiring (wiring components/Arduino), and prototyping/testing skills.
DISCUSSION
Challenges:
The first major challenge I encountered was aligning the motor/break. The car sensor turns a servo motor between its 0 degree and 90 degree positions, but when I attached a lego piece to the motor, I had to ensure that it was at its 0 degree position. This took several tries before I was successful.
Next, when debugging the buzzer, I realized that it had extremely low volume but was working. Figuring this out took a long time.
Afterwards, I had to mount the servo motor to the bottom of the car to serve as a break. I tested several ways of accomplishing this and eventually decided to use tape, which worked successfully.
Also, to attach the lego piece to the servo motor, I put a dab of hot glue in the lego piece, let it dry, then stuck it onto the tip of the motor. This ensured that the glue would not enter the motor and prevent it from functioning correctly.
Lastly, the sensor in TinkerCAD differed from the real-life verison, so I needed to look up the documentation and modify the code to adapt to the real sensor. As I mentioned before, I posted my solution on Google Classroom and detailed it above.
DAILY LOG
2-23-22
Today, I created, wired, and programmed a full simulation of my parking sensor using TinkerCAD. This allowed me to both create a proof-of-concept and create a wiring diagram before assembling the project in real life. Below is a copy of the code as well as an image of the wiring.
/*
Ping))) Sensor
This sketch reads a PING))) ultrasonic rangefinder and returns the distance
to the closest object in range. To do this, it sends a pulse to the sensor to
initiate a reading, then listens for a pulse to return. The length of the
returning pulse is proportional to the distance of the object from the sensor.
The circuit:
- +V connection of the PING))) attached to +5V
- GND connection of the PING))) attached to ground
- SIG connection of the PING))) attached to digital pin 7
created 3 Nov 2008
by David A. Mellis
modified 30 Aug 2011
by Tom Igoe
This example code is in the public domain.
https://www.arduino.cc/en/Tutorial/BuiltInExamples/Ping
*/
// this constant won't change. It's the pin number of the sensor's output:
#include <Servo.h>
Servo servo;
const int servoPin = 12;
const int pingPin = 13;
void setup() {
// initialize serial communication:
Serial.begin(9600);
for (int i = 2; i <= 10; ++i)
pinMode(i, OUTPUT);
servo.attach(servoPin);
}
void onThrough(int a, int b) {
for (int i = a; i <= b; ++i)
digitalWrite(i, HIGH);
}
void offThrough(int a, int b) {
for (int i = a; i <= b; ++i)
digitalWrite(i, LOW);
}
void t(int pitch, int _delay) {
tone(11, pitch);
delay(_delay);
noTone(11);
delay(1000);
}
void loop() {
// establish variables for duration of the ping, and the distance result
// in inches and centimeters:
long duration, inches, cm;
// The PING))) is triggered by a HIGH pulse of 2 or more microseconds.
// Give a short LOW pulse beforehand to ensure a clean HIGH pulse:
pinMode(pingPin, OUTPUT);
digitalWrite(pingPin, LOW);
delayMicroseconds(2);
digitalWrite(pingPin, HIGH);
delayMicroseconds(5);
digitalWrite(pingPin, LOW);
// The same pin is used to read the signal from the PING))): a HIGH pulse
// whose duration is the time (in microseconds) from the sending of the ping
// to the reception of its echo off of an object.
pinMode(pingPin, INPUT);
duration = pulseIn(pingPin, HIGH);
// convert the time into a distance
inches = microsecondsToInches(duration);
cm = microsecondsToCentimeters(duration);
Serial.print(inches);
Serial.print("in, ");
Serial.print(cm);
Serial.print("cm");
Serial.println();
delay(100);
int dis[9] = {80, 70, 60, 50, 40, 30, 20, 10, 5};
bool broke = false;
for (int i = 0; i < 9; ++i) {
if (cm <= dis[3])
{
Serial.println("here");
servo.write(90);
}
else
servo.write(0);
if (cm > dis[i]) {
if (i > 0)
onThrough(2, 2+(i-1));
offThrough(2+i, 10);
t(300 + (25*i), 100-(10*i));
broke=true;
break;
}
}
if (!broke){
onThrough(2, 10);
tone(11,600);
}
}
long microsecondsToInches(long microseconds) {
// According to Parallax's datasheet for the PING))), there are 73.746
// microseconds per inch (i.e. sound travels at 1130 feet per second).
// This gives the distance travelled by the ping, outbound and return,
// so we divide by 2 to get the distance of the obstacle.
// See: https://www.parallax.com/package/ping-ultrasonic-distance-sensor-downloads/
return microseconds / 74 / 2;
}
long microsecondsToCentimeters(long microseconds) {
// The speed of sound is 340 m/s or 29 microseconds per centimeter.
// The ping travels out and back, so to find the distance of the object we
// take half of the distance travelled.
return microseconds / 29 / 2;
}
To begin this program, I imported the "ping" example from the Arduino example programs library. This program asks a sensor to detect the distance it is away from an object. It then reports the data back and write it onto the "serial monitor."
This section includes the "Servo.h" library which is a prerequisite to controlling servo motors from the Arduino. It also declares an unassigned variable of type Servo called servo. Lastly, it declares to constant, global variables, "sevoPin" which is of type int and has a value of 12, and pingPin which is also of type int and has a value of 13.
The "setup" function is called at the very start of a program, and in the body of this function, the "serial monitor" is initialized, and it loops through every number from 2 to 10 (inclusive) and temporarily stores it in the mutable variable of type int, "i." Then, for each of these numbers, it calls the "pinMode" function with arguments "i" and "OUTPUT," meaning that all of the pins 2-10 are initialized as output pins that the Arduino can send a signal out from. Also, in C++ and other C++-style languages like Arduino, if there is only one line in the body of an if statment, you can indent it an remove the curly braces.
Next, I take the unassigned, global variable servo and attach it to servoPin (a variable storing 12) meaning that the Arduino can now interact with the servo variable and it will send signals to pin 12.
Next, the program declares three functions: onThrough, offThrough, and t. the onThrough function takes two parameters of type int, a and b. It then cycles through them and outputs a signal to every pin from a to b, inclusive. offThrough does the same as onThrough except it turns all of the signals to pins a through b off. Next, the function, t, takes two parameters also of type int, pitch and _delay. (_delay because delay is a built-in function in the Arduino programming language, and it is common practice in programming to include an underscore of this sort to avoid accidental overlaps and confusion between global functions and lower-scope variables/functions. This function calls the "tone" function and passes it arguments "11" and "pitch" (the argument passed into "t." The tone function then tells the speaker connected to pin 11 to start playing a sounds at (pitch) hertz. Then, the t function calls the delay function and passes it in the argument _delay, causing this speaker tone to be played for (_delay) seconds where _delay is another argument passed into t. Next, t calls the noTone function and passes 11 as an argument. This turns off the sound on the speaker attached to pin 11. Lastly, the program calls the delay function one more time with the parameter 1000, causing the program to delay for 1 second (1000 milliseconds).
The loop function is called repeatedly (in an infinite loop) after the setup function is called.
Here, three unassigned variables of type "long" (similar to double or decimal) are declared (duration, inches, and cm).
This part of the program requests the sensor on pin pingPin to check its distance away from an object. The sensor sends out an ultrasonic signal which is then received back after a short delay. This delay is stored (in microseconds) in the duration variable.
Next, the program calls the microsecondsToInches and microsecondsToCentimeters functions (which is OK because they are defined later, but because C++-style languages like Arduino are compiled and not interpreted/transpiled, you can declare functions lower-scope functions after they are called). These function convert the duration variable recorded previously into inches and centimeters the sensor is away from an object, in the variables inches and cm, respectively.
Next, the program outputs these measurements to the serial monitor for debugging purposes.
Next, the program waits 0.1s (100 milliseconds).
This next portion of the program is slightly complicated. First, an array of type int[9] (a list of integers 9 items long) is declared and immediately assigned the value 80, 70, 60, 50, 40, 30, 20, 10, 5. Next, a boolean (true or false) named broke is assigned the value false. Now, they program cycles through each integer from 0 to 9 (inclusive, exclusive) and temporarily assigns them to the mutable, integer variable "i" (it is OK to use the variable i again because last time it was declared in a different scope, and thus doesn't conflict). Now, within the "for loop," the program checks whether the cm variable (assigned above) it less than or equal to the fourth item (index 3 because C++ is zero-indexed) of the array dis. If it is, the program outputs "here" to the serial monitor (for debugging purposes) and tells the servo (attached to servoPin previously) to rotate 90 degrees. If the condition is false, the servo is instructed to return to its 0 degree position (one again, curly braces are unnecessary because the body of the else statement is only one line long. After this first if statments, the program checks whether the cm variable is greater than the (i+1)th element of the array dis (once again, index i because C++ is a zero-indexed programming language). If this is true, the program also checks whether i is greater than zero. If it is, is calls the onThrough function with parameters 2 and 2+(i-1), turning on all pins with LEDs from 2 to 2+(i-1) (inclusive). Next, whether or not the previous condition was met, the program turns off all pins 2+i through 10 (inclusive). Then, it calls the t function with arguments 300 + (25 * i) and 100-(10*i) (see the above explanation of this function (t()) to understand its purpose). Next, it sets the boolean broke to true and breaks the loop. After the loop, it checks whether the variable, broke, is false, it if it is false, the condition evaluate to true (the bang operator (!) inverts the value of the boolean expression following it). In other programming languages, like Python, you can simply add an else statement after a loop to achieve the same result, but because that syntactical sugar is not found in C++-style languages, this method is requires. So if the loop has not been broken out of, the onThrough function it called where a=2 and b=10, turning on all pins 2-10 (inclusive). It also calls the tone function with arguments 11 and 600, emitting a constant 600hz tone from the speaker connected to pin 11.
Also, these two functions convert the microseconds it takes for the sensor to receive a signal back from an object into the distance it is away from said object, in both inches and centimeters. You can read more about this process on the sensor's documentation online.
2-24-22
/*
Ping))) Sensor
This sketch reads a PING))) ultrasonic rangefinder and returns the distance
to the closest object in range. To do this, it sends a pulse to the sensor to
initiate a reading, then listens for a pulse to return. The length of the
returning pulse is proportional to the distance of the object from the sensor.
The circuit:
- +V connection of the PING))) attached to +5V
- GND connection of the PING))) attached to ground
- SIG connection of the PING))) attached to digital pin 7
created 3 Nov 2008
by David A. Mellis
modified 30 Aug 2011
by Tom Igoe
This example code is in the public domain.
https://www.arduino.cc/en/Tutorial/BuiltInExamples/Ping
*/
// this constant won't change. It's the pin number of the sensor's output:
#include <Servo.h>
Servo servo;
const int servoPin = 12;
const int pingPin = 13;
const int redPin = 2;
const int bluePin = 3;
const int greenPin = 4;
void setup() {
// initialize serial communication:
Serial.begin(9600);
for (int i = 2; i <= 4; ++i)
pinMode(i, OUTPUT);
servo.attach(servoPin);
}
void RGB_color(int red_light_value, int green_light_value, int blue_light_value)
{
analogWrite(redPin, red_light_value);
analogWrite(greenPin, green_light_value);
analogWrite(bluePin, blue_light_value);
}
void onThrough(int a, int b) {
for (int i = a; i <= b; ++i)
digitalWrite(i, HIGH);
}
void offThrough(int a, int b) {
for (int i = a; i <= b; ++i)
digitalWrite(i, LOW);
}
void t(int pitch, int _delay) {
tone(11, pitch);
delay(_delay);
noTone(11);
delay(1000);
}
void loop() {
// establish variables for duration of the ping, and the distance result
// in inches and centimeters:
long duration, inches, cm;
// The PING))) is triggered by a HIGH pulse of 2 or more microseconds.
// Give a short LOW pulse beforehand to ensure a clean HIGH pulse:
pinMode(pingPin, OUTPUT);
digitalWrite(pingPin, LOW);
delayMicroseconds(2);
digitalWrite(pingPin, HIGH);
delayMicroseconds(5);
digitalWrite(pingPin, LOW);
// The same pin is used to read the signal from the PING))): a HIGH pulse
// whose duration is the time (in microseconds) from the sending of the ping
// to the reception of its echo off of an object.
pinMode(pingPin, INPUT);
duration = pulseIn(pingPin, HIGH);
// convert the time into a distance
inches = microsecondsToInches(duration);
cm = microsecondsToCentimeters(duration);
Serial.print(inches);
Serial.print("in, ");
Serial.print(cm);
Serial.print("cm");
Serial.println();
delay(100);
int dis[9] = {80, 70, 60, 50, 40, 30, 20, 10, 5};
bool broke = false;
for (int i = 0; i < 9; ++i) {
if (cm <= dis[3])
servo.write(90);
else
servo.write(0);
if (cm > dis[i]) {
RGB_color((255/9)*i, 255-((255/9)*i), 0);
Serial.print("Red: ");
Serial.println((255/9)*i);
Serial.println("Green: ");
Serial.println(255-((255/9)*i));
Serial.println("Blue: 0\n\n");
t(300 + (25*i), 100-(10*i));
broke=true;
break;
}
}
if (!broke){
onThrough(2, 10);
tone(11,600);
}
}
long microsecondsToInches(long microseconds) {
// According to Parallax's datasheet for the PING))), there are 73.746
// microseconds per inch (i.e. sound travels at 1130 feet per second).
// This gives the distance travelled by the ping, outbound and return,
// so we divide by 2 to get the distance of the obstacle.
// See: https://www.parallax.com/package/ping-ultrasonic-distance-sensor-downloads/
return microseconds / 74 / 2;
}
long microsecondsToCentimeters(long microseconds) {
// The speed of sound is 340 m/s or 29 microseconds per centimeter.
// The ping travels out and back, so to find the distance of the object we
// take half of the distance travelled.
return microseconds / 29 / 2;
}
Next, I revised the program with slight modifications to use and RGB-LED to free up more IO ports (instead of nine LEDs occupying nine ports). You can see this change in the loop function below.
Lastly, in real life, we were using a slightly different sensor than that in the simulation, so I modifying the "ping" example's code to adapt to the documentation for this other sensor that I found online. I detailed the changes on Google Classroom.
Fixing the Sensor
1] Plug a wire from the trig pin to pin 13 of the Arduino
2] Plug a write from the echo pin to pin 10 of the Arduino
3] Add "const int pingPin2 = 10;" beloew "const int pingPin = 13;" in the code
4] Change "
pinMode(pingPin, INPUT);
duration = pulseIn(pingPin, HIGH);
"
to
"
pinMode(pingPin2, INPUT);duration = pulseIn(pingPin2, HIGH);
"
Upload your code
/*
Ping))) Sensor
This sketch reads a PING))) ultrasonic rangefinder and returns the distance
to the closest object in range. To do this, it sends a pulse to the sensor to
initiate a reading, then listens for a pulse to return. The length of the
returning pulse is proportional to the distance of the object from the sensor.
The circuit:
- +V connection of the PING))) attached to +5V
- GND connection of the PING))) attached to ground
- SIG connection of the PING))) attached to digital pin 7
created 3 Nov 2008
by David A. Mellis
modified 30 Aug 2011
by Tom Igoe
This example code is in the public domain.
https://www.arduino.cc/en/Tutorial/BuiltInExamples/Ping
*/
// this constant won't change. It's the pin number of the sensor's output:
#include <Servo.h>
Servo servo;
const int servoPin = 12;
const int pingPin = 13;
const int pingPin2 = 10;
const int redPin = 2;
const int bluePin = 3;
const int greenPin = 4;
void setup() {
// initialize serial communication:
Serial.begin(9600);
for (int i = 2; i <= 4; ++i)
pinMode(i, OUTPUT);
servo.attach(servoPin);
}
void RGB_color(int red_light_value, int green_light_value, int blue_light_value)
{
analogWrite(redPin, red_light_value);
analogWrite(greenPin, green_light_value);
analogWrite(bluePin, blue_light_value);
}
void onThrough(int a, int b) {
for (int i = a; i <= b; ++i)
digitalWrite(i, HIGH);
}
void offThrough(int a, int b) {
for (int i = a; i <= b; ++i)
digitalWrite(i, LOW);
}
void t(int pitch, int _delay) {
tone(11, pitch);
delay(_delay);
noTone(11);
delay(500);
}
void loop() {
// establish variables for duration of the ping, and the distance result
// in inches and centimeters:
long duration, inches, cm;
// The PING))) is triggered by a HIGH pulse of 2 or more microseconds.
// Give a short LOW pulse beforehand to ensure a clean HIGH pulse:
pinMode(pingPin, OUTPUT);
digitalWrite(pingPin, LOW);
delayMicroseconds(2);
digitalWrite(pingPin, HIGH);
delayMicroseconds(5);
digitalWrite(pingPin, LOW);
// The same pin is used to read the signal from the PING))): a HIGH pulse
// whose duration is the time (in microseconds) from the sending of the ping
// to the reception of its echo off of an object.
pinMode(pingPin2, INPUT);
duration = pulseIn(pingPin2, HIGH);
// convert the time into a distance
inches = microsecondsToInches(duration);
cm = microsecondsToCentimeters(duration);
Serial.print(inches);
Serial.print("in, ");
Serial.print(cm);
Serial.print("cm");
Serial.println();
delay(100);
int dis[9] = {80, 70, 60, 50, 40, 30, 20, 10, 5};
bool broke = false;
for (int i = 0; i < 9; ++i) {
if (cm <= dis[5])
servo.write(90);
else
servo.write(0);
if (cm > dis[i]) {
RGB_color((255/9)*i, 255-((255/9)*i), 0);
Serial.print("Red: ");
Serial.println((255/9)*i);
Serial.println("Green: ");
Serial.println(255-((255/9)*i));
Serial.println("Blue: 0\n\n");
t(300 + (25*i), 100-(10*i));
broke=true;
break;
}
}
if (!broke){
onThrough(2, 10);
tone(11,600);
}
}
long microsecondsToInches(long microseconds) {
// According to Parallax's datasheet for the PING))), there are 73.746
// microseconds per inch (i.e. sound travels at 1130 feet per second).
// This gives the distance travelled by the ping, outbound and return,
// so we divide by 2 to get the distance of the obstacle.
// See: https://www.parallax.com/package/ping-ultrasonic-distance-sensor-downloads/
return microseconds / 74 / 2;
}
long microsecondsToCentimeters(long microseconds) {
// The speed of sound is 340 m/s or 29 microseconds per centimeter.
// The ping travels out and back, so to find the distance of the object we
// take half of the distance travelled.
return microseconds / 29 / 2;
}
I also recreated the circuit I designed in real life using an Arduino and breadboard, and I mounted the circuit, along with the sensor, on a lego car.
2-28-22
Today, I finished building the circuit in real life, debugged several issues surrounding the buzzer and motor, and tested the final product.
First, I used found a long cable that connects the Arduino to the computer, both allowing me to upload code in real-time and provide a power source.
Then, I pushed the car towards and object to test the sensor, the LED changing colors, and the motor breaking the car. Below are two videos: one of the car stopping itself when being rolled to a wall, and one of the serial monitor when the car is slowly pushed towards a wall, outputting the red, green, and blue values sent to the RGB-LED as well as the distance the sensor detects from the wall.
4-in-1 Electric Game
In this project, I soldered many different, electrical components onto a circuit board which allows you to play fun minigames like Tetris, space invaders, and many more.
First, I looked at the instructions that came with the kit and made sure that I had all of the materials to complete the project.
Then, the first step was to solder an MCU chip onto the circuit. This was tricky because there are no pins on the chip, but I managed to solder it anyhow while avoiding solder-bridges (when solder connects to different wires together, unintentionally). Throughout most of the components in this project, I used tape to hold the component in place and used a "helping hand" to flip the circuit upside-down and solder it in place.
Afterwards, I attached the USB socket to the circuit. This step was difficult because the instructions explained that three pins must not be soldered because "the USB cable is only used for power supply."
Next, I soldered six "keys" (buttons) to the circuit. These buttons talk to the processing chip and allow the user to interact with the game.
Also, I soldered a capacitor, which acts like a temporary battery and can smooth out varying electrical signals. I also used a cutting tool to cut the ends off of the anode and cathode from the component. Because the capacitor is polarized, I made sure that the anode (longer leg) was connected to the positive and vice versa for the cathode.
Next up, I implemented the buzzer into the circuit. This provides sound effects during the game. Because this component is a passive buzzer, it is not polarized and can be connected in any orientation.
Shorty thereafter, I connected the power switch which lets users toggle the game on and off.
Then, I connected the "digital tube" (numbers display) which the MCU chip uses to display the users score during various gamed.
The "dot matrices" (LED panels/pixels) were soldered next which is the screen used during the games.
After, I started to assemble the battery box. I took the protecting veneer off of the acrylic and screwed the battery box to the piece.
Then, I soldered the positive and negative wires from the battery back to the circuit, providing a power source for the game.
Then, I used "hex copper pillars" and different types of screws to finish assembling the box around the game.
Next, I put key caps on all of the buttons in the game.
Finally, I finished the case around the game and the project is completed! Below is a video of the finished product.
There were three major challenges I encountered throughout this project:
1] Peeling off the battery box-acrylic protector was very challenging and time consuming, but after figuring out the correct technique, I was able to remove it more efficiently and finish the project on time.
2] After I assembled the entire box, I realized that there is one pin that did not solder correctly in the LED panel that prevents one row of pixels from turning on. I can go back and fix this, but it will be extremely unpleasant to disassemble the entire project, re-solder the pin, and risk messing-up other parts. This taught me the importance of testing throughout the entire duration of a project to ensure a similar situation will not occur in the future.
3] Solder the MCU chip was extremely difficult because there were no pins to solder, but I decided to place my solder in between the two metal parts that needed to be connected that create a "glob" of solder that stretched across the gap. This strategy was very effective and allowed me to successfully complete the project.