1.
Part Description |
Amazon.com |
---|---|
Arduino Uno | Arduino Uno R3 with Atmega 328P |
HC-SR04 Ultrasonic Sensor | DHT11 Digital Temp Sensor 4 Pack |
Jumper Wires | 60 Piece Jumper Wire Kit |
Bread Board | Tektrum 2200 Tie-Point Breadboard |
Servo | Hitec 33322S HS-322HD Servo |
Tool Description |
Download Link |
---|---|
Arduino IDE | Download from Arduino.cc |
Servo.h Library | Included with the Arduino IDE |
Download the code for this tutorial from Github:
This weeks tutorial revisits some of our past experiences with the HC-SR04 Ultrasonic sensor, and adds in a new element, the Servo! I decided to write up this tutorial as a resource for the students in my Young Makers class to follow in a project they are currently working on.
Much like my previous Ultrasonic Sensor tutorial, the Young Makers need to be able to trigger an event when someone walks past a display. In their case, they need to make an element on a poster move when someone walks within three feet of the ultrasonic sensor. The class decided to utilize HC-SR04 Ultrasonic Sensor, Small Servo, and an Arduino clone from some kits that SainSmart generously donated to the class about two years ago.
Before we get started, I want to take a moment and talk about The Young Makers of Greater Augusta, or “Young Makers” for short. Our Young Makers class is about three years old and is comprised of youth aged from about 11 years old up to 17. Myself as well as another instructor, Ed Elser, hold these classes free of charge every second and fourth Thursday of each month at the main branch of the Augusta Library in Augusta, GA.
The class is open to all, but we do try and limit the class sizes to something two instructors can easily manage. Since we hold the class for free, we are always looking for sponsors to help us with funding the program in both monetary and hardware donation forms. If you are interested in getting involved with the class, or donating please use the contact us page here at The Makers Workbench and reach out.
(Click to enlarge)
Using the image above as a visual guide, wire up the circuit as listed below.
With all of the components connected, and checked twice lets take a moment and talk about the theory of how these components work together to achieve our goal of having the servo move when the HC-SR04 Ultrasonic Distance Sensor senses something within the specified range.
We are using an HC-SR04 Ultrasonic Distance sensor aka a Ping Sensor. This sensor basically works like depth finders on boats, and radar works. The HC-SR04 sensor detects objects by emitting a very short ultrasonic burst and then "listens" for the echo.
Using a host microcontroller such as an Arduino, the sensor emits a short 40 kHz (ultrasonic) burst. This burst travels through the air at the speed of sound which is 340 m/s or 29 microseconds per centimeter. If it hits an object, it will bounce back to the sensor. The sensor provides an output pulse to the host that will terminate when the echo is detected, hence the width of this pulse corresponds to the distance to the target.
Since the ping travels out and back, to find the distance of the object we take the distance the ping traveled and divide it by 2. In the code we using the following formula to determine the distance is from our sensor. (Microseconds / 29 / 2). The Arduino will take this information and by using “If” and “else” statements, will tell the servo whether to “sweep” or to do nothing.
(Click to enlarge)
Using the Servo.h Arduino Library and a subroutine, we can have the servo perform movement to any position you would like. Ok with all of the boring stuff out of the way, lets get the code setup and uploaded to the Arduino.
Im going to break the code down in pieces here so that it is easier for some to understand. Lets get started with the Variable Declaration Section. This is where we tell the Arduino IDE to include the libraries we will be using as well as declaring the variables we will be using in our program. This code is only read once during the upload process.
First we need to tell the Arduino IDE to include any Libraries we might need. In this instance we will be including the Servo.h Library.
#include <Servo.h>
Now we need to set up the variables for the HC-SR04 sensor. We need to tell the Arduino that the Trigger Pin is connected to Digital Pin 8 and the Echo Pin is connected to Digital Pin 7.
const int trigPin = 6; const int echoPin = 7;
Up next we need to tell the Arduino to create a servo object to control a servo, and then create a variable to store the servo position.
Servo myservo; int pos = 0;
The setup section,once when the program begins, follows the variable declaration section. Statements that lay the foundation for actions that happen later on in the program are put in the setup section. This section always begins with void setup() with its contents wrapped in brackets { }. The setup section only runs once and will not run again until the Arduino is reset or power cycled.
The first thing we need to do in our setup is initialize the serial port, and tell it what baudrate to communicate at. Then we need to set up the variables for the Servo. We need to tell the Arduino to attach a “servo” at digital pin 9.
void setup() { Serial.begin(9600); myservo.attach(9); }
Our final section is the Loop. This section contains code that is ran after the setup and loops over and over until the Arduino is either reset or powered off. Just like the setup section, statements in the loop section are placed between open and closed brackets { }. Additionally certain statements within the loop with also contain sets of brackets { }, and if a bracket is not where it should be, the sketch will not compile.
To get the loop started we first need to establish variables for the duration of the ping, and its distance results in inches and centimeters.
long duration, inches, cm;
Now we need to trigger the sensor with a high pulse of at least 10 microseconds, but first we need to trigger a very short low pulse to ensure our high pulses are clean and crisp.
pinMode(trigPin, OUTPUT); digitalWrite(trigPin, LOW); delayMicroseconds(2); digitalWrite(trigPin, HIGH); delayMicroseconds(10); digitalWrite(trigPin, LOW);
Now we need to tell the Arduino to listen for the return pulse and read the signal from the sensor: It needs to listen for 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(echoPin, INPUT); duration = pulseIn(echoPin, HIGH);
Now we need to tell the Arduino to convert the time it just read into a distance in both Inches and Centimeters, and then tell it to print the results to the serial console.
inches = microsecondsToInches(duration); cm = microsecondsToCentimeters(duration);
Serial.print(inches); Serial.print("in, "); Serial.print(cm); Serial.print("cm"); Serial.println();
Now we need to tell the Arduino what make the servo do based on various distance measurements. To do this we will use “if” and “else if” statements in conjunction with the the “Sweep” routine we will build later.
The first part of the code tells the Arduino that if the distance returned by the sensor is less than or equal to twentyfour inches (inches <= 24), then sweep the servo three times. ( {sweep(3) )
if (inches <= 24) {sweep(3);
The second part of the code handles things in a similar way. Basically if the distance returned is greater than twentyfour inches (inches >24) then return the servo to the 0 position and do nothing else ( {myservo.write(pos); ).>
else if (inches >= 24) {myservo.write(pos);
The last thing we need to do in our loop is to set a small delay to let everything settle down before we run the loop again. This is done with a delay statement. Don’t forget to close the loop with a close bracket }.
delay(100); }
While the loop is the last main section of code, we need to write a quick subroutine that will handle the math that converts the microseconds to distance.
First we need to convert Microsectonds to Inches. According to Parallax's (OEM of the original PING sensors) 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: http://www.parallax.com/dl/docs/prod/acc/28015-PING-v1.3.pdf for more information.
long microsecondsToInches(long microseconds) { return microseconds / 74 / 2; }
Now we need to convert Microseconds to Centimeters. 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.
long microsecondsToCentimeters(long microseconds) { return microseconds / 29 / 2; }
Finally we need to build our servo sweep routine.
Now that we know what each section of the code does, lets put it all together and upload it straight to our Arduino. This routine tells the Arduino to start this loop at 0 and increment it by one each time the loop completes. This is how the Arduino knows to stop the loop when a specific number of the Sweep routine has been ran.
First we need to declare the routine ( void sweep ) and then set a variable that declares how many cycles we want the routine to run. (int NUM_OF_CYCLES). Now we need to set that variable to 0 ( int j=0 )and increment it by one each time the routine has been ran. ( j<NUM_OF_CYCLES; j++ )
void sweep(int NUM_OF_CYCLES) for (int j=0; j<NUM_OF_CYCLES; j++) for(pos = 0; pos < 180; pos += 1)
The first movement is from the 0 position to the 180 position, we tell the servio to do this by using a for statement. The Arduino tells the servo to start at position 0. ( for(pos = 0; ) and then to move to 180 in one degree increments. ( pos < 180; pos += 1) )
for(pos = 0; pos < 180; pos += 1)
Now we need to tell the servo to go to the position in variable ‘pos’ ( myservo.write(pos); ) which is 0 in our case. Then we need to tell the servo how long it should wait for the servo to reach the position. ( delay(10); )
myservo.write(pos); delay(10);
Then we can tell the servo to move to position from position 180 back to position 0 using a similar for statement. ( for(pos = 180; pos>=1; pos-=1) )
for(pos = 180; pos>=1; pos-=1)
Now we need to tell the servo to go to the position in variable ‘pos’ ( myservo.write(pos); ) which is 0 in our case. Then we need to tell the servo how long it should wait for the servo to reach the position. ( delay(10); )
myservo.write(pos); delay(10);
Putting all of the code together we get the following sketch.
// AServo activated by an HC-SR04 via Arduino. /* This sketch reads a HC-SR04 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 Arduino then takes this information and initiates a series of sweeps. A sweep is defined as the servo moving its horn from the 0 position to 180 and back to 0 then to -180 and back to 0. This code was developed partially from Ping))) code found in the public domain written by David A. Mellis, and adapted to the HC-SRO4 by Tautvidas Sipavicius, servo code was provided the sweep example that is include in the Arduino IDE, while other portions were written by Charles Gantt and Curtis Gauger from http://www.themakersworkbench.com. This code was written for a tutorial on http://www.themakersworkbench.com, and was designed for use in a project built by The Young Makers of Greater Augusta. */ //Tell the Arduino Ide to include the Servo.h library. #include <Servo.h> //Setup the variables for the HC-SR04 const int trigPin = 6; const int echoPin = 7; // create servo object to control a servo // a maximum of eight servo objects can be created Servo myservo; // variable to store the servo position int pos = 0; void setup() { // initialize serial communication: Serial.begin(9600); // attaches the servo on pin 9 to the servo object myservo.attach(9); } void loop() { // establish variables for duration of the ping, // and the distance result in inches and centimeters: long duration, inches, cm; // The sensor is triggered by a HIGH pulse of 10 or // more microseconds. // Give a short LOW pulse beforehand to ensure a clean HIGH pulse: pinMode(trigPin, OUTPUT); digitalWrite(trigPin, LOW); delayMicroseconds(2); digitalWrite(trigPin, HIGH); delayMicroseconds(10); digitalWrite(trigPin, LOW); // Read the signal from the sensor: 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(echoPin, INPUT); duration = pulseIn(echoPin, HIGH); // convert the time into a distance inches = microsecondsToInches(duration); cm = microsecondsToCentimeters(duration); //Tell the Arduino to print the measurement in the serial console Serial.print(inches); Serial.print("in, "); Serial.print(cm); Serial.print("cm"); Serial.println(); // This if-else statement tells the Arduino at what distance // it should trigger the servo, what the servo should do, // and what it should do if the distance is too far away. if (inches <= 24) {sweep(3); } else if (inches >= 24) {myservo.write(pos); } // Tell the Arduino to wait 0.10 seconds before pinging the // Ultrasonic Sensor again. delay(100); } // Converts the microseconds reading to Inches 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: http://www.parallax.com/dl/docs/prod/acc/28015-PING-v1.3.pdf return microseconds / 74 / 2; } //Converts the Microseconds Reading to Centimeters 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; } //This sub-routein is what dictates the movement of the servo void sweep(int NUM_OF_CYCLES) { // Tells the Arduino to start this loop at 0 and incriment it by 1 // each time the loop completes. This is how the Arduino knows to // stop the loop when a specific number of the // Sweep routein has been ran. for (int j=0; j<NUM_OF_CYCLES; j++) // goes from 0 degrees to 180 degrees for(pos = 0; pos < 180; pos += 1) { // in steps of 1 degree // tell servo to go to position in variable 'pos' myservo.write(pos); // waits 15ms for the servo to reach the position delay(10); } // goes from 180 degrees to 0 degrees for(pos = 180; pos>=1; pos-=1) { // tell servo to go to position in variable 'pos' myservo.write(pos); // waits 15ms for the servo to reach the position delay(10); } }
With the code uploaded, you should be able to move your hand close to and far away from the sensor and it will cause the servo to sweep. It should be noted that the sensor needs a very clear line of sight into open space for this to work. If you have the corner of a desk, or even the surface of the desk too close to the sensor, then it will falsely trigger. I like to set the sensor so that it is slightly hanging over the edge of my desk.
If you would like more Ping sensor tutorials such as the aforementioned relay triggering, just let me know in the comments. Additionally, if you would like to see more tutorials like this, as well as the more of the other types of articles we post here at The Makers Workbench, head over to our Patreon page and consider donating monthly to help us keep the lights on. Every donation counts, and with your support, we can continue to post a couple of tutorials each week.
That wraps up this tutorial. As always, if you have any issues, questions, or thoughts, please leave them in a comment below. We utilize Disqus comments as it is the easiest way for you to signup and organize your comments across tens of thousands of websites across the internet. Thanks for reading, and thanks for keeping the Maker Revolution alive!