If you need to measure distance from 2 cm to 400 cm without breaking the bank, the HC-SR04 ultrasonic sensor is the undisputed workhorse of the maker bench. Priced around $1.50 to $3.00 per unit, it provides reliable time-of-flight (ToF) measurements. However, the most common mistake builders make is plugging the 5V Echo pin directly into a 3.3V ESP32 GPIO, which slowly degrades the microcontroller's silicon. This guide covers the exact wiring, the raw-to-distance math, and the edge cases that cause phantom readings in production.
How the HC-SR04 Actually Measures Distance
The HC-SR04 relies on a piezoelectric transducer to emit an eight-cycle burst of 40 kHz ultrasound when triggered. This acoustic pulse travels through the air, bounces off a target, and returns to the receiver transducer. The module's onboard comparator circuit detects the returning echo and flips a logic pin HIGH for the exact duration of the round trip.
Critically, the output is strictly a digital 5V TTL pulse on the Echo pin. It is not an analog voltage, and you do not use an Analog-to-Digital Converter (ADC) to read it. Your microcontroller simply measures the width of this digital HIGH pulse in microseconds (µs) using a hardware timer or an interrupt. The longer the pulse stays HIGH, the further away the object is.
Hardware Specifications and Pinout Wiring
Before wiring, review the hard limits of the module. Pushing the HC-SR04 beyond its 400 cm theoretical max results in timeout errors, and operating it below 4.5V causes the onboard logic to brown out and latch HIGH.
| Parameter | Value | Notes |
|---|---|---|
| Operating Voltage (VCC) | 4.5V to 5.5V DC | Do not power directly from a 3.3V rail |
| Quiescent Current | < 2 mA | Spikes to ~15 mA during the 40 kHz burst |
| Measuring Angle | ~15° cone | Specular reflections occur outside this cone |
| Trigger Pulse Width | 10 µs minimum | TTL HIGH on Trig pin |
| Echo Output Logic | 5V TTL HIGH | Requires voltage divider for 3.3V MCUs |
| Blind Zone | 0 cm to 2 cm | Transmitter ringing masks close-proximity echoes |
Wiring Matrix for 5V and 3.3V Microcontrollers
| HC-SR04 Pin | Arduino Uno (5V) | ESP32 DevKit (3.3V) | Wiring Notes |
|---|---|---|---|
| VCC | 5V Pin | 5V (VIN) Pin | Requires stable 5V; add 100nF cap across VCC/GND if using long USB cables. |
| Trig | Digital Pin 9 | GPIO 5 | 3.3V logic from ESP32 is sufficient to trigger the 5V module's internal threshold. |
| Echo | Digital Pin 10 | GPIO 18 (via Divider) | WARNING: Use a 1kΩ/2kΩ voltage divider. 5V into ESP32 GPIO violates Espressif's absolute maximum ratings and will cause long-term damage. |
| GND | GND | GND | Must share common ground with the microcontroller. |
The Raw-to-Distance Math and Calibration
To convert the raw microsecond pulse width into a physical distance, you must account for the speed of sound and the fact that the pulse represents a round trip.
According to Michigan Tech's physics department, the speed of sound in dry air at 20°C (68°F) is approximately 343 meters per second, which translates to 0.0343 centimeters per microsecond (cm/µs). Since the sound wave travels to the object and back, the total distance covered is twice the distance to the target.
Distance (cm) = (Pulse_Width_µs × 0.0343) / 2Simplified for integer math:
Distance (cm) = Pulse_Width_µs / 58.3
Calibration and Temperature Scaling: The "divide by 58" shortcut assumes a room temperature of ~20°C. However, the speed of sound changes with temperature according to the formula v = 331.3 + (0.606 × T) where T is in Celsius. If your sensor is deployed in a 35°C greenhouse, the speed of sound increases to ~352.5 m/s. At 35°C, the correct divisor drops to roughly 56.8. For high-precision indoor robotics, wire a DS18B20 temperature sensor alongside the HC-SR04 and calculate the divisor dynamically in your loop.
Real-World Interference, Blind Zones, and Alternatives
The HC-SR04 is notorious for throwing phantom readings (e.g., suddenly jumping to 300 cm when an object is 20 cm away). Here is what actually causes those failures on the bench:
- Acoustic Crosstalk: If you have two HC-SR04 modules facing the same area and trigger them simultaneously, Sensor A will hear Sensor B's echo. Fix: Trigger sensors sequentially with a 50 ms delay between readings.
- Specular Reflection (Angled Surfaces): If sound hits a smooth, angled wall (greater than 15° off-axis), it bounces away from the receiver rather than back to it. The sensor times out and returns 0 or the max timeout value.
- Soft/Acoustically Dead Materials: Foam, heavy curtains, and fiberglass insulation absorb 40 kHz frequencies. The sensor will read these materials as being much further away than they are, or fail to register them entirely.
- Power Rail Noise: The 15 mA current spike during the 40 kHz transmission causes a voltage droop on poorly regulated 5V rails, resetting the module's internal logic. Fix: Solder a 100 nF (0.1 µF) ceramic decoupling capacitor directly across the VCC and GND pins on the back of the PCB.
When to switch to Radar: If your application involves detecting humans through drywall, or measuring liquid levels in a dusty silo where acoustic waves scatter, abandon the HC-SR04. Switch to an RCWL-0516 microwave radar module ($1.20) for motion, or an A02YYUW waterproof ultrasonic sensor ($18.00) for harsh industrial environments. The HC-SR04 is strictly for clean, line-of-sight, indoor air measurements.
Step-by-Step ESP32 Integration with NewPing
Never use the raw Arduino pulseIn() function for production code. It blocks execution and lacks proper timeout handling, which can freeze your main loop for seconds if an echo is never received. Instead, use the NewPing library, which utilizes hardware timers to handle the math and timeouts asynchronously.
- Build the Voltage Divider: Connect a 1kΩ resistor from the HC-SR04 Echo pin to ESP32 GPIO 18. Connect a 2kΩ resistor from GPIO 18 to GND. This drops the 5V Echo signal down to a safe ~3.33V.
- Wire Power and Trigger: Connect VCC to ESP32 VIN (5V), GND to GND, and Trig to GPIO 5.
- Install the Library: In the Arduino IDE Library Manager, search for and install "NewPing" by Tim Eckel.
- Upload the Code: Use the following optimized sketch.
#include <NewPing.h>
#define TRIG_PIN 5
#define ECHO_PIN 18
#define MAX_DISTANCE 400 // Max distance to ping (cm)
// Initialize NewPing object
NewPing sonar(TRIG_PIN, ECHO_PIN, MAX_DISTANCE);
void setup() {
Serial.begin(115200);
Serial.println("HC-SR04 ESP32 Integration Ready");
}
void loop() {
// ping_cm() handles the trigger, echo timing, and math internally
unsigned int distance_cm = sonar.ping_cm();
if (distance_cm == 0) {
Serial.println("Out of range or timeout");
} else {
Serial.print("Distance: ");
Serial.print(distance_cm);
Serial.println(" cm");
}
delay(50); // Wait 50ms between pings (min recommended to avoid echo overlap)
}
By using NewPing, the library automatically caps the timeout at roughly 24 ms (the time it takes sound to travel 400 cm and back). If no echo returns, the function cleanly exits and returns 0, preventing the multi-second lockups common with raw pulseIn() implementations. For battery-powered ESP32 deployments, replace the delay(50) with a non-blocking millis() timer and put the ESP32 into light sleep between pings to drop average current draw below 5 mA.






