The HC-SR04 is a 40kHz ultrasonic ranging module capable of measuring distances from 2 cm to 400 cm with a resolution of 0.3 cm. To interface it with an Arduino Uno R3, connect VCC to 5V, GND to GND, Trig to Pin 9, and Echo to Pin 10. The sensor requires a 10µs HIGH pulse on the Trigger pin to initiate a measurement, and it returns a HIGH pulse on the Echo pin whose duration corresponds to the distance.
While the hardware is simple, acoustic multipath interference, blocking code functions, and 5V/3.3V logic mismatches cause 90% of field failures. This guide provides the exact timing physics, non-blocking production code, and a ranked debugging matrix for when the sensor returns erratic data.
HC-SR04 Technical Specifications & Timing Physics
Before writing code, you must understand the hardware timing constraints. The HC-SR04 does not output distance directly; it outputs a time delay. Understanding the physics of this delay is critical for writing accurate conversion math.
| Parameter | Value | Notes / Constraints |
|---|---|---|
| Operating Voltage | 5V DC | Will not trigger reliably on 3.3V logic without level shifting. |
| Quiescent Current | < 2 mA | Standby mode between trigger pulses. |
| Operating Current | 15 mA | Drawn during the 40kHz ultrasonic burst transmission. |
| Trigger Pulse Width | 10 µs (TTL HIGH) | Must be exactly 10µs or longer to initiate the ranging cycle. |
| Blind Zone | 2 cm | Objects closer than 2cm cause transmit/receive ring overlap. |
| Max Measuring Distance | 400 cm (4 meters) | Signal attenuates below noise floor beyond 4m in standard air. |
| Effectual Beam Angle | < 15° | Cone of detection; off-axis soft objects will not reflect. |
| Speed of Sound Constant | ~58 µs per cm | Round-trip time at 20°C (343 m/s). Varies with temperature. |
t = (2 cm) / 34,300 cm/s = 0.0000583 seconds, or 58.3 µs. If your environment is 30°C, sound travels faster (~349 m/s), and the constant drops to ~57 µs, introducing a 1.5% error at max range.
Parts List & Pin Mapping for Arduino Uno R3
This build targets the Arduino Uno R3 (ATmega328P, 5V logic). If you are using a 3.3V board like the Arduino Due, Nano 33 IoT, or an ESP32, you must use a voltage divider on the Echo pin, as the HC-SR04 outputs a 5V HIGH signal that will permanently damage 3.3V GPIO pins.
Required Components
- Microcontroller: Arduino Uno R3 (or Nano v3 with ATmega328P)
- Sensor: HC-SR04 Ultrasonic Module (4-pin variant)
- Resistors (for 3.3V boards only): 1kΩ and 2kΩ (for Echo pin voltage divider)
- Capacitor (Optional but recommended): 100µF electrolytic across VCC and GND to smooth USB power ripple.
- Wiring: 4x male-to-male jumper wires, half-size breadboard.
Pin Mapping Table
| HC-SR04 Pin | Arduino Uno R3 Pin | Wire Color (Standard) | Function |
|---|---|---|---|
| VCC | 5V | Red | Power supply (4.8V to 5.5V acceptable) |
| Trig | D9 | Yellow | Trigger input (receives 10µs pulse) |
| Echo | D10 | Blue | Echo output (sends timed HIGH pulse) |
| GND | GND | Black | Common ground reference |
Complete Non-Blocking Code (NewPing Library)
Beginners often use the built-in Arduino pulseIn() function to read the HC-SR04. This is a critical mistake in production code. pulseIn() is a blocking function; if the sensor fails to receive an echo, the Arduino will freeze for up to 30 milliseconds waiting for a timeout. In a fast control loop (like a balancing robot or drone), a 30ms freeze causes system failure.
Instead, we use the NewPing library, which utilizes hardware timer interrupts and strict, non-blocking timeouts. Install it via the Arduino IDE Library Manager (Search: "NewPing" by Tim Albritton).
#include <NewPing.h>
// --- PIN DEFINITIONS ---
#define TRIG_PIN 9
#define ECHO_PIN 10
#define MAX_DISTANCE 200 // Maximum distance we want to ping (in cm). 400 is max hardware limit.
// Initialize the NewPing object
NewPing sonar(TRIG_PIN, ECHO_PIN, MAX_DISTANCE);
// Variables for non-blocking timing
unsigned long lastPingTime = 0;
const unsigned long PING_INTERVAL = 50; // Ping every 50ms (20Hz)
void setup() {
Serial.begin(115200);
Serial.println("HC-SR04 Non-Blocking Init...");
}
void loop() {
// Non-blocking delay using millis()
if (millis() - lastPingTime >= PING_INTERVAL) {
lastPingTime = millis();
// Get the ping time in microseconds (0 = timeout/no echo)
unsigned int uS = sonar.ping();
// Convert microseconds to centimeters
// NewPing handles the 58µs math internally via US_ROUNDTRIP_CM
float distance_cm = sonar.convert_cm(uS);
// --- ERROR HANDLING & DATA VALIDATION ---
if (uS == 0) {
// Exact error state: 0 means the ping timed out or exceeded MAX_DISTANCE
Serial.println("Error: Ping timed out (0 cm) - Object out of range or acoustic absorption.");
} else if (distance_cm < 2.0) {
// Hardware blind zone check
Serial.println("Error: Object inside 2cm blind zone.");
} else {
// Valid reading
Serial.print("Distance: ");
Serial.print(distance_cm);
Serial.println(" cm");
}
}
// The CPU is free to do other tasks here (e.g., motor control, LED blinking)
// Example: Blink built-in LED without delaying the sensor
digitalWrite(LED_BUILTIN, (millis() / 500) % 2);
}
Debugging: Ranked Causes for 0 cm and Timeout Errors
When your serial monitor outputs Error: Ping timed out (0 cm) or raw values of 0, the microcontroller is not seeing the Echo pin go HIGH. Here are the first three things to check, ranked by probability based on bench failure rates.
1. The First Three Things to Check
- Verify Common Ground: The Arduino and the HC-SR04 must share the exact same GND plane. If you are powering the sensor from a separate breadboard power supply, the GND of that supply must be jumpered to the Arduino GND.
- Check Trig/Echo Swap: The pins on the HC-SR04 silkscreen are sometimes mislabeled on cheap clones. Swap the yellow and blue wires in software (change
TRIG_PINto 10 andECHO_PINto 9) and re-test. - Measure 5V Rail Under Load: Use a multimeter to measure the VCC pin on the sensor while it is plugged in. USB ports often droop to 4.6V under load. The HC-SR04 requires a minimum of 4.8V to reliably fire the 40kHz transducers.
Ranked Failure Matrix
| Symptom / Serial Output | Root Cause | Fix / Mitigation |
|---|---|---|
Constant 0 cm (Timeout) |
Echo pin not reaching logic HIGH threshold (wiring fault or dead module). | Test Echo pin with multimeter during trigger. Replace module if it stays at 0V. |
Random spikes to MAX_DISTANCE |
Acoustic multipath interference (sound bouncing off adjacent walls or table). | Add a 2-inch cardboard tube over the receiver transducer to narrow the 15° beam angle. |
Reads 400+ cm when object is close |
Target material is acoustically transparent (foam, heavy clothing, angled glass). | Switch to a microwave radar sensor (RCWL-0516) or IR time-of-flight (VL53L0X). |
| Code freezes / Loop stops | Using raw pulseIn(ECHO_PIN, HIGH) without a timeout parameter. |
Replace with pulseIn(ECHO_PIN, HIGH, 30000) or switch to the NewPing library. |
Extending and Simplifying the Build
Depending on your end application, the baseline HC-SR04 setup may need refinement for environmental factors, or it may be the wrong tool entirely.
How to Extend: Temperature Compensation
Because the speed of sound changes by roughly 0.6 m/s for every 1°C change in air temperature, a sensor calibrated at 20°C will read a 1.5% error at 30°C. Over a 4-meter range, that is a 6 cm error. To fix this, wire a BME280 or DS18B20 temperature sensor to the I2C or OneWire bus. Read the ambient temperature and dynamically calculate the speed of sound:
float tempC = bme.readTemperature();
float speedOfSound = 331.4 + (0.6 * tempC); // in m/s
float cmPerMicrosecond = speedOfSound / 10000.0; // Convert to cm/µs
// Adjust your distance calculation accordingly instead of using the hardcoded 58µs constant.
How to Simplify: When to Abandon Ultrasonic
The HC-SR04 is cheap ($1.50 to $3.00 per unit), but it is fundamentally limited by acoustics. Simplify your build by switching technologies if you encounter these scenarios:
- Measuring soft/porous objects: Ultrasonic waves are absorbed by foam, cotton, and carpets. Use the VL53L1X (Time-of-Flight Laser) for reliable readings on soft targets.
- Dusty/Dirty environments: The 40kHz transducers on the HC-SR04 have an open metal mesh that clogs with sawdust or dirt, deadening the resonance. Use a sealed RCWL-0516 microwave radar module, which penetrates plastic enclosures and ignores dust.
- High-speed polling: The HC-SR04 requires a 50ms delay between pings to prevent echo overlap (crosstalk). If you need 100Hz+ polling, use an infrared ToF sensor or an inductive proximity probe.
By understanding the physical limitations of 40kHz sound waves and utilizing non-blocking timer interrupts, you can transform the HC-SR04 from an unreliable toy into a robust ranging instrument for your embedded projects.






