The HC-SR04 is the sensor every Indian robotics kit ships with, and the one that produces the most “my robot keeps stopping” messages. The wiring is four wires and the formula is one division. The readings still go wrong, because of timing, geometry and the surfaces in front of the robot.
This guide is the reliable version: what the sensor actually measures, the code to read it without blocking your robot for a second, why a single reading should never drive a motor decision, and the specific faults that cause jumps, zeros and phantom obstacles.
What the sensor measures
The module fires a burst of 40 kHz ultrasound from one cylinder and listens with the other. It reports the round-trip time as the width of a pulse on the echo pin. Sound travels about 343 metres per second at 20 °C, so the distance in centimetres is the echo pulse in microseconds divided by 58 — the factor already accounts for the journey out and back.
| Spec | Value | What it means for a robot |
|---|---|---|
| Supply | 5 V, about 15 mA | Do not power it from a 3.3 V board pin without checking the module |
| Range | 2 cm to 400 cm | Below 2 cm the reading is unreliable, which matters in a tight maze |
| Beam angle | About 15° | It sees a cone, not a point; narrow gaps can be missed |
| Trigger | 10 µs high pulse | Too short and it does not fire |
| Measurement cycle | About 60 ms recommended | Firing faster can return echoes from the previous ping |
| Output | 5 V echo pulse | Needs a divider on a 3.3 V ESP32 |
Wiring
| Module pin | Arduino Uno | ESP32 |
|---|---|---|
| VCC | 5 V | 5 V (VIN), not 3V3 |
| Trig | D9 | GPIO 13 (3.3 V trigger works on most modules) |
| Echo | D10 | GPIO 34 through a 1 kΩ / 2 kΩ divider |
| GND | GND | GND |
Code that does not block the robot
The usual example uses pulseIn() with no timeout. When there is no echo — a soft curtain, an angled wall, a sensor pointed at the ceiling — the function waits up to a second by default. A robot that stops for a second in front of a curtain looks like a software freeze. Always pass a timeout, and treat a timeout as “unknown”, not as “clear”.
const int TRIG = 9;
const int ECHO = 10;
long readCmOnce() {
digitalWrite(TRIG, LOW);
delayMicroseconds(2);
digitalWrite(TRIG, HIGH);
delayMicroseconds(10);
digitalWrite(TRIG, LOW);
unsigned long us = pulseIn(ECHO, HIGH, 25000UL); // 25 ms cap
if (us == 0) return -1; // no echo
return (long)(us / 58);
}
long readCm() {
long samples[3];
for (int i = 0; i < 3; i++) {
samples[i] = readCmOnce();
delay(10);
}
// ignore failed reads, then take the median
long a = samples[0], b = samples[1], c = samples[2];
if (a < 0) a = 999;
if (b < 0) b = 999;
if (c < 0) c = 999;
long hi = max(a, max(b, c));
long lo = min(a, min(b, c));
return a + b + c - hi - lo; // middle value
}Three reads and a median cost about 30–40 ms. That is acceptable if the robot is not moving fast. If it is, run the sensor on a schedule — every 50 ms — and let the motors use the last valid distance between readings. The point is that no sensor call should own the main loop for an unpredictable length of time.
Why one reading is not enough
An ultrasonic sensor returns a plausible wrong number more often than an obvious zero. A chair leg at an angle may reflect nothing; a soft sofa may absorb the pulse; a second robot’s sensor may fire at the same moment and produce a phantom wall. If a single bad reading triggers a full stop or a turn, the robot will behave randomly in a room full of furniture.
Use three defences together: a timeout, a median of three readings, and a rule in your logic that requires two consecutive close readings before stopping. That combination removes most false stops without adding a filter library.
Angle, dead zone and temperature
The 15° beam means the sensor reports the nearest object inside a cone, not the distance straight ahead. A robot can “see” a wall to its side and brake for an obstacle that is not in its path. Mount the sensor low, facing forward, and test against a box rather than a wall to understand the cone in practice.
The 2 cm dead zone matters in a maze or a sumo ring, where walls get close. Below that distance the module may report a stale value or nothing. If your robot needs to know about objects nearer than 2 cm, use an IR proximity sensor or a short-range ToF sensor instead.
Temperature changes the speed of sound by roughly 0.6 m/s per degree Celsius. Over a 20 °C swing the error is around 3%, which is a few centimetres at 2 metres. For a student robot that is usually irrelevant; for a measuring instrument, compensate using the ambient temperature.
Two sensors on one robot
Mounting two HC-SR04 modules facing the same direction causes cross-talk: sensor A hears sensor B’s ping and reports a distance that belongs to neither. Fire them alternately, with at least 60 ms between pings, or use modules with a single transducer that cannot hear each other as easily. If you need three or more directions, a small servo that sweeps one sensor is often more reliable than four fixed modules.
Where this sensor earns its place
It is good at one job: detecting a large, flat, hard obstacle at 10–150 cm in front of a slow robot. That covers obstacle avoiders, parking sensors, bin lids and simple wall following. It is not good at detecting soft or angled objects, measuring liquid levels in a narrow tank, or anything that needs millimetre precision.
For the build side, the obstacle avoider and wall follower sheets use this sensor directly. The readings-jumping checklist goes deeper on electrical noise, and the component page covers variants and prices.
For range, cost and failure modes side by side, see LiDAR vs ultrasonic vs IR.
Once the readings are stable, put them to work in an obstacle avoiding robot.
Sources and further reading
- HC-SR04 datasheet for range, beam angle, current draw and timing.
- Arduino language reference: pulseIn() for the timeout behaviour described above.
- Arduino language reference: millis() for scheduling sensor reads without blocking the loop.
Last reviewed: October 2026. Specifications are taken from the HC-SR04 datasheet; the temperature figure is the standard approximation for dry air. Clone modules vary, so verify range and dead zone on your own unit. Not bench-tested by this site.


