Ultrasonic distance sensors
What you’ll learn
- How ultrasonic sensors measure distance
- How to wire and code the HC-SR04
- Its limits, and how to work around them
Echoes
The HC-SR04 sends a burst of ultrasound (40 kHz, too high for people to hear) and listens for the echo, like a bat. Sound travels at about 343 m/s (0.0343 cm per microsecond), so the time for the echo to come back gives the distance:
distance (cm) = echo time (µs) × 0.0343 ÷ 2
(÷ 2 because the sound goes there and back.) A handy shortcut: cm ≈ µs ÷ 58.
Wiring
| HC-SR04 pin | Arduino |
|---|---|
| VCC | 5V |
| GND | GND |
| TRIG | any digital pin (e.g. 3) |
| ECHO | any digital pin (e.g. 2) |
To measure: set TRIG HIGH for 10 µs, then time how long ECHO stays HIGH with pulseIn().
Limits
- Range roughly 2 cm to 4 m.
- Soft things (curtains, clothes) absorb sound; angled surfaces bounce it away, so readings can jump.
- Two sensors firing at once can hear each other’s echoes: trigger them one after the other.
- Readings are noisy: take a few and use the median.
Example: a distance function
float readCm(int trig, int echo) { digitalWrite(trig, LOW); delayMicroseconds(2); digitalWrite(trig, HIGH); delayMicroseconds(10); digitalWrite(trig, LOW); long us = pulseIn(echo, HIGH, 30000); // time out after 30 ms if (us == 0) return 400; // no echo: treat as far away return us * 0.0343 / 2;}Example: median of three
float a = readCm(3, 2), b = readCm(3, 2), c = readCm(3, 2);float median = max(min(a, b), min(max(a, b), c));One bad reading can’t fool the median.
Example: a parking sensor
Beep faster as you get closer: delay(map(constrain(cm, 5, 100), 5, 100, 50, 600)); between beeps.
Try it in the virtual Arduino
Press Start simulator, replace diagram.json, paste the sketch, press ▶. Click the sensor and drag its distance slider: the Serial Monitor should follow it.
{ "version": 1, "author": "RoboLearn", "editor": "wokwi", "parts": [ { "type": "wokwi-arduino-uno", "id": "uno", "top": 0, "left": 0, "attrs": {} }, { "type": "wokwi-hc-sr04", "id": "sonar", "top": -110, "left": 90, "attrs": { "distance": "100" } } ], "connections": [ [ "sonar:VCC", "uno:5V", "red", [] ], [ "sonar:GND", "uno:GND.1", "black", [] ], [ "sonar:TRIG", "uno:3", "blue", [] ], [ "sonar:ECHO", "uno:2", "green", [] ] ], "dependencies": {}}const int TRIG = 3;const int ECHO = 2;
void setup() { pinMode(TRIG, OUTPUT); pinMode(ECHO, INPUT); Serial.begin(9600);}
void loop() { digitalWrite(TRIG, LOW); delayMicroseconds(2); digitalWrite(TRIG, HIGH); // a 10 µs pulse starts a measurement delayMicroseconds(10); digitalWrite(TRIG, LOW);
long us = pulseIn(ECHO, HIGH, 30000); // echo time in µs (0 = nothing within ~5 m) float cm = us * 0.0343 / 2; // sound: 0.0343 cm/µs, there and back Serial.print(cm); Serial.println(" cm"); delay(100);}Runs on Wokwi, free, no account needed.
Try it on a robot
The robot simulator’s distance() works like an ultrasonic sensor pointing forwards.
Robot commands
motors(left, right): wheel power, −100 to 100lineLeft(),lineRight():trueover the black linedistance(): cm to the nearest thing ahead (max 200)distanceLeft(),distanceRight(): cm to the sideslinePosition(): where the line is, −1 (left) to 1 (right), ornullmillis(): milliseconds since Runprint(value): show a message belowsetup()runs once;loop()runs 50 times a second
Check the mathsEasy
An echo takes 1,160 µs. How far away is the object? 1160 × 0.0343 ÷ 2 ≈ 19.9 cm (or 1160 ÷ 58 = 20 cm).
Too close warningMedium
In the virtual Arduino, turn the built-in LED on when something is closer than 20 cm.
After calculating cm, add digitalWrite(LED_BUILTIN, cm < 20 ? HIGH : LOW); and pinMode(LED_BUILTIN, OUTPUT); in setup().
Smart turningHard
In the robot simulator, when blocked, turn towards whichever side has more space.
Show an answer
function loop() { if (distance() < 25) { if (distanceLeft() > distanceRight()) motors(-40, 40); else motors(40, -40); } else motors(60, 60);}Quick check
Finished this lesson?