In this tutorial, we will learn together how to use a reference ultrasonic distance sensor HC-SR04 with an Arduino / Genuino board. As a bonus, we will test the accuracy of the measurements in various situations.
Hello everyone !
Sometimes when we carry out a project, we need to measure distances, detect obstacles, etc.
In robotics for example, it is very traditional to have a distance sensor on the front of the robot to avoid hitting a wall in the face.
How to measure a distance
- Infrared sensors have the advantage of being inexpensive, relatively accurate, and available almost anywhere. Unfortunately, they are quite complex to implement due to their non-linearities. You have to apply a complex formula to get a usable measurement. In addition, they are very sensitive to ambient light and to the light reflection coefficient of the surface in front of the sensor.
- (Real) laser distance sensors are extremely precise, but also extremely expensive. A laser distance sensor (per travel time measurement) easily costs more than 200 €, but measures more than 30 meters without problem for some models. It is therefore ultimately a question of budget / use.
- PS There are (fake) laser distance sensors that work by triangulation. Instead of measuring the round trip time of a laser beam, these modules calculate the angle between the laser point and the sensor. These modules are cheaper, but also much less precise.
- Physical sensors, most often a duo comprising a graduated ruler and an optical sensor, are both inexpensive and very accurate. But they are very limited in measurable distance and are therefore generally found in printers.
There remains the ultrasonic sensors, and that's good, that's the subject of this article.
An ultrasonic distance sensor uses the same principle as a laser sensor, but using sound waves (inaudible) instead of a beam of light. They are much cheaper than a laser sensor, but also much less accurate. However, unlike infrared sensors, ambient light and the opacity of the surface in front of the sensor do not affect the measurement.
The HC-SR04 sensor
Operating principle of the sensor
The principle of operation of the sensor is entirely based on the speed of sound.
This is how a measurement takes place:
- We send a HIGH pulse of 10µs to the TRIGGER pin of the sensor.
- The sensor then sends a series of 8 ultrasonic pulses at 40KHz (inaudible to humans, it's more pleasant than a biiiiiiiip).
- The ultrasound propagates through the air until it hits an obstacle and returns in the other direction towards the sensor.
- The sensor detects the echo and closes the measurement.
The signal on the ECHO pin of the sensor remains HIGH during steps 3 and 4, which makes it possible to measure the duration of the round trip of the ultrasound and therefore to determine the distance.
N.B. There is always a silence of fixed duration after the ultrasound emission to avoid prematurely receiving an echo coming directly from the sensor.
Assembly
To carry out this first assembly, we will need:
- An Arduino UNO board (and its USB cable),
- An HC-SR04 sensor,
- A test plate and wires to wire our assembly.
Prototyping view of the assembly
The assembly is disconcertingly simple:
- The 5V power supply from the Arduino board goes to the VCC pin of the sensor.
- The GND pin of the Arduino board goes to the GND pin of the sensor.
- Pin D2 of the Arduino board goes to the TRIGGER pin of the sensor.
- Pin D3 of the Arduino board goes to the ECHO pin of the sensor.
You can choose to use other pins than D2 and D3 if you want. It will suffice to update the pin numbers in the code in the next chapter.
N.B. The test plate is completely optional here. If you have male / female wires, you can directly wire the sensor to the Arduino board
The code
- /* Constants for pins */
- const byte TRIGGER_PIN = 2;
- const byte ECHO_PIN = 3;
- /* Constants for the timeout */
- const unsigned long MEASURE_TIMEOUT = 25000UL; // 25ms = ~8m à 340m/s
- /* Speed of sound in air in mm / us */
- const float SOUND_SPEED = 340.0 / 1000;
We start the code with four constants: two constants for the TRIGGER and ECHO pins of the sensor, a constant which will serve as timeout for the measurement and a constant to define the speed of sound.
The timeout corresponds to the time necessary before considering that there is no obstacle, therefore no possible measure. I chose to use a 25 millisecond timeout (4 meters round trip at 340m / s).
N.B. You will notice that I have declared the speed of sound in millimeters per microsecond. This is necessary, because the measurement of time is done in microseconds and I want to have a result in millimeters at the output of the calculation.
void setup() { /* Initializes the serial port */ Serial.begin(115200); /* Initializes pins */ pinMode(TRIGGER_PIN, OUTPUT); digitalWrite(TRIGGER_PIN, LOW); // The TRIGGER spindle must be at LOW at rest pinMode(ECHO_PIN, INPUT); }
The setup () function initializes the serial port, sets the TRIGGER pin of the sensor to output and LOW, and sets the ECHO pin of the sensor to input. Nothing too exciting.
void loop() { /* 1. Starts a distance measurement by sending a HIGH pulse of 10µs to the TRIGGER pin */ digitalWrite(TRIGGER_PIN, HIGH); delayMicroseconds(10); digitalWrite(TRIGGER_PIN, LOW); /* 2. Measures the time between the sending of the ultrasonic pulse and its echo (if there is one) */ long measure = pulseIn(ECHO_PIN, HIGH, MEASURE_TIMEOUT); /* 3. Calculate the distance from the measured time */ float distance_mm = measure / 2.0 * SOUND_SPEED; /* Displays results in mm, cm and m */ Serial.print(F("Distance: ")); Serial.print(distance_mm); Serial.print(F("mm (")); Serial.print(distance_mm / 10.0, 2); Serial.print(F("cm, ")); Serial.print(distance_mm / 1000.0, 2); Serial.println(F("m)")); /* Timeout to avoid displaying too many results per second */ delay(500);
The loop () function takes care of the measurement and the display.
It first generates the HIGH pulse of 10µs which triggers the measurement. It then measures the time required for a round trip of the ultrasound signal with the pulseIn () function. Finally, it calculates the distance before displaying it on the serial port.
N.B. The pulseIn () function returns 0 if the timeout time is reached. It is therefore possible to manage the absence of obstacles if you wish with an if (measure == 0) { ...}for example.
PS The value returned by pulseIn () must be divided by two before doing the distance calculation. A round trip is equal to twice the measured distance.
The complete code with comments:
/* * Example code for an HC-SR04 ultrasonic sensor. */ /* Constants for pins */ const byte TRIGGER_PIN = 2; const byte ECHO_PIN = 3; /* Constants for the timeout */ const unsigned long MEASURE_TIMEOUT = 25000UL; // 25ms = ~8m à 340m/s /* Speed of sound in air in mm / us */ const float SOUND_SPEED = 340.0 / 1000; /** Setup function() */ void setup() { /* Initialize the serial port */ Serial.begin(115200); /* Initializes pins */ pinMode(TRIGGER_PIN, OUTPUT); digitalWrite(TRIGGER_PIN, LOW); // La broche TRIGGER doit être à LOW au repos pinMode(ECHO_PIN, INPUT); } /** Fonction loop() */ void loop() { /* 1. Starts a distance measurement by sending a HIGH pulse of 10µs to the TRIGGER pin */ digitalWrite(TRIGGER_PIN, HIGH); delayMicroseconds(10); digitalWrite(TRIGGER_PIN, LOW); /* 2. Measures the time between the sending of the ultrasonic pulse and its echo (if there is one) */ long measure = pulseIn(ECHO_PIN, HIGH, MEASURE_TIMEOUT); /* 3. Calculate the distance from the measured time */ float distance_mm = measure / 2.0 * SOUND_SPEED; /* Displays results in mm, cm and m */ Serial.print(F("Distance: ")); Serial.print(distance_mm); Serial.print(F("mm (")); Serial.print(distance_mm / 10.0, 2); Serial.print(F("cm, ")); Serial.print(distance_mm / 1000.0, 2); Serial.println(F("m)")); /* Timeout to avoid displaying too many results per second */ delay(500); }
Bonus: The accuracy of the sensor
Indoor test with a small obstacle
Let's start the tests with a very classic configuration (unfortunately, see box below): an indoor installation, at ground level or on a table, with a small obstacle (here a simple piece of wood).
This is the worst possible situation for this type of ultrasonic sensor. The surface below the sensor generates unwanted echoes. The obstacle is far too small to give "clean" echoes and there are multiple obstacles on the sides of the measurement area
Some tips for use
These ultrasonic sensors need an open area, with a hard, smooth surface of at least 50cm² in front of the sensor to give correct results.
No need to try to measure a distance from a curtain or sound-absorbing surface, it won't work. Likewise, using this kind of sensor at ground level or on a table, or worse, in a tube or a box (already seen), it is definitely not a good way to have correct measurements.
Here are the results of my measurements over a limited range of 10 millimeters to 50 centimeters :
Distance mm | Measure | Error mm | Error % |
|---|---|---|---|
10 | 10,20 | 0,20 | 2,00% |
20 | 18,70 | 1,30 | 6,50% |
30 | 23,46 | 6,54 | 21,80% |
40 | 32,64 | 7,36 | 18,40% |
50 | 42,50 | 7,50 | 15,00% |
60 | 52,02 | 7,98 | 13,30% |
70 | 66,30 | 3,70 | 5,29% |
80 | 76,40 | 3,60 | 4,50% |
90 | 85,34 | 4,66 | 5,18% |
100 | 95,54 | 4,46 | 4,46% |
150 | 144,50 | 5,50 | 3,67% |
200 | 188,70 | 11,30 | 5,65% |
250 | 237,66 | 12,34 | 4,94% |
300 | 286,62 | 13,38 | 4,46% |
350 | 334,56 | 15,44 | 4,41% |
400 | 384,86 | 15,14 | 3,79% |
450 | 433,50 | 16,50 | 3,67% |
500 | 482,46 | 17,54 | 3,51% |
The orange curve is the theory. The blue curve is practice.
We immediately notice that theory and practice are not quite identical. There is a slight drift with an error rate of ~ 5% on average above 7 centimeters.
From a point of view of precision, below ten centimeters, it is big nonsense. If you want more or less correct measurements, you have to forget the first ten centimeters.
Even in these non-optimal conditions, the sensor remains quite usable. We get an accuracy of +/- 2cm, which is not terribly precise, but for a small robot, it could be quite sufficient.
Outdoor test under ideal conditions
Distance cm | Measure | Error cm | Error % |
|---|---|---|---|
10 | 9,35 | 0,65 | 6,50% |
20 | 19,96 | 0,04 | 0,20% |
30 | 29,94 | 0,06 | 0,20% |
40 | 39,97 | 0,03 | 0,08% |
50 | 49,61 | 0,39 | 0,78% |
60 | 59,79 | 0,21 | 0,35% |
70 | 69,95 | 0,05 | 0,07% |
80 | 79,87 | 0,13 | 0,16% |
90 | 90,07 | 0,07 | 0,08% |
100 | 100,08 | 0,08 | 0,08% |
110 | 110,65 | 0,65 | 0,59% |
120 | 119,83 | 0,17 | 0,14% |
130 | 130,63 | 0,63 | 0,48% |
140 | 140,93 | 0,93 | 0,66% |
150 | 151,22 | 1,22 | 0,81% |
160 | 161,11 | 1,11 | 0,69% |
170 | 171,63 | 1,63 | 0,96% |
180 | 181,27 | 1,27 | 0,71% |
190 | 191,37 | 1,37 | 0,72% |
200 | 201,45 | 1,45 | 0,72% |
210 | 208,95 | 1,05 | 0,50% |
220 | 220,08 | 0,08 | 0,04% |
230 | 231,10 | 1,10 | 0,48% |
240 | 240,11 | 0,11 | 0,05% |
250 | 250,87 | 0,87 | 0,35% |
The average error over the full range of 10 to 250 centimeters is 0.67 centimeters. We are far, very far even, from the 3mm precision given by the manufacturer, but it remains interesting for projects that do not require measurements to the nearest millimeter.
PS I hate outdoor testing, especially in winters, the weather never wants to cooperate. I caught a cold in the name of science. A-A-A-Sneeze!
Test results
384 centimeters is the maximum distance I could measure with my sensor. Forget the 4 meters of the manufacturer's sheet, even with a 1m² plasterboard I could not reach this distance. For the precision, whatever the conditions, one remains on a margin of error of 2 cm.
If you want to measure the distance between your robot and an obstacle to the nearest picometer, this sensor is clearly not the best possible choice. On the other hand, if you just want to get an idea of the distance between your robot and the wall in front of your robot is close to 2cm, this is a budget-friendly choice.
This sensor does not break a duck's three legs, but for € 3 you cannot ask for performance worthy of an ultrasonic sensor at € 30, or even € 50.
Conclusion
This tutorial is now complete.
If you liked this tutorial, feel free to comment on it on the forum, share it on social networks and support the site if you like it.












