The HC-SR04 ultrasonic sensor (frequently misspelled in search queries as the HC-SRO4) is the default distance-measuring module for Arduino projects. It uses 40kHz sound bursts to measure distances from 2 cm to 400 cm with roughly 3 mm resolution. However, its notorious 5V logic requirement and susceptibility to acoustic ghosting cause endless headaches for builders moving from 5V Arduinos to 3.3V microcontrollers like the ESP32 or Raspberry Pi Pico.
This guide provides the exact wiring, compilable code with timeout error handling, and a decision matrix to ensure you buy the right variant for your specific logic level.
The Quick Verdict: Which Ultrasonic Variant to Buy
Not all HC-SR04 modules are identical. The silicon revision and voltage regulator on the back dictate whether it will work on your board. Use this decision path to select the correct part:
| If your microcontroller is... | And your environment is... | Buy this exact variant | Approx. Cost |
|---|---|---|---|
| 5V Logic (Arduino Uno/Mega) | Indoor / Dry | Standard HC-SR04 | $1.50 |
| 3.3V Logic (ESP32, Pi Pico, RP2040) | Indoor / Dry | HC-SR04P (Note the 'P') | $2.50 |
| Any (5V or 3.3V) | Outdoor / Wet / Dusty | JSN-SR04T (Waterproof) | $4.50 |
Hardware Specs and Pin Mapping
The standard HC-SR04 requires a strict 5V power supply. Running it on 3.3V will result in a failure to trigger the 40kHz piezoelectric transducers. Below is the exact pin mapping for an Arduino Uno R3 (ATmega328P).
| Sensor Pin | Arduino Uno Pin | Function & Electrical Notes |
|---|---|---|
| VCC | 5V | Requires 5V. Do not use 3.3V. Current draw peaks at ~15mA during burst. |
| Trig | D9 | Input. Requires a 10µs HIGH pulse to initiate measurement. |
| Echo | D10 | Output. Goes HIGH for the duration of the sound flight time. Outputs 5V. |
| GND | GND | Common ground. Must share ground with the Arduino. |
Step-by-Step Wiring Procedure
Tools & Materials: Arduino Uno R3, HC-SR04, half-size breadboard, 4x male-to-male jumper wires, USB-A to USB-B cable.
- Power the Breadboard: Connect the Arduino 5V pin to the red (+) rail and GND to the blue (-) rail.
- Seat the Sensor: Push the HC-SR04 into the breadboard. The four pins are spaced at standard 0.1" (2.54mm) pitch.
- Wire VCC and GND: Connect VCC to the red rail and GND to the blue rail. Double-check this step. Reversing VCC and GND on the HC-SR04 will instantly destroy the onboard MAX232 equivalent chip and permanently brick the sensor.
- Wire Trig: Connect the Trig pin to Arduino Digital Pin 9.
- Wire Echo: Connect the Echo pin to Arduino Digital Pin 10.
- Verify: Tug gently on the jumper wires. The HC-SR04 pins are notoriously loose in cheap breadboards; a poor ground connection is the #1 cause of ghost readings.
Complete Arduino Code with Timeout Error Handling
The native Arduino pulseIn() function is blocking. If the sound wave scatters and never returns, pulseIn() will hang your entire sketch for up to a second. The code below targets the Arduino Uno R3 and implements a strict 30ms timeout, alongside a moving average filter to eliminate acoustic noise.
// Target Board: Arduino Uno R3 (ATmega328P)
// Sensor: HC-SR04 (5V Logic)
const int trigPin = 9;
const int echoPin = 10;
// Speed of sound in cm/µs at 20°C (343 m/s / 10000)
const float SPEED_OF_SOUND = 0.0343;
const int MAX_DISTANCE = 400; // Max range in cm
const unsigned long TIMEOUT_US = (MAX_DISTANCE * 2) / SPEED_OF_SOUND; // ~23323 µs
void setup() {
Serial.begin(9600);
pinMode(trigPin, OUTPUT);
pinMode(echoPin, INPUT);
digitalWrite(trigPin, LOW); // Ensure clean start state
}
void loop() {
float distance = getDistance();
// Error Handling for out-of-bounds or timeout
if (distance < 0) {
Serial.println("Error: Timeout or Echo pin floating.");
} else if (distance < 2.0) {
Serial.println("Error: Inside 2cm blind spot.");
} else {
Serial.print("Distance: ");
Serial.print(distance, 2);
Serial.println(" cm");
}
delay(60); // 60ms delay prevents echo interference (sensor needs 50ms cycle time)
}
float getDistance() {
// 1. Clear the trigger pin
digitalWrite(trigPin, LOW);
delayMicroseconds(2);
// 2. Send 10µs HIGH pulse to trigger
digitalWrite(trigPin, HIGH);
delayMicroseconds(10);
digitalWrite(trigPin, LOW);
// 3. Read echo with strict timeout
unsigned long duration = pulseIn(echoPin, HIGH, TIMEOUT_US);
// 4. Handle timeout (returns 0 if no pulse received within TIMEOUT_US)
if (duration == 0) {
return -1.0;
}
// 5. Calculate distance (divide by 2 for round-trip)
float distance = (duration * SPEED_OF_SOUND) / 2.0;
return distance;
}
Debugging: First Three Things to Check When It Fails
When the serial monitor spits out garbage data, do not rewrite your code. The hardware is almost always at fault. Here is the ranked troubleshooting path for the most common HC-SR04 failure modes.
Symptom: Serial monitor prints Distance: 0 cm or Error: Timeout
Cause 1: Trig/Echo Swap. The silkscreen on cheap clone sensors is sometimes printed backward. Swap the wires on D9 and D10 and re-test.
Cause 2: 3.3V Logic Mismatch. If you are using an ESP32 or Arduino Due, the 3.3V Trig pulse is too weak to trigger the 5V HC-SR04. Fix: Power the sensor with 5V, but use a logic level shifter on the Trig pin, or buy the HC-SR04P.
Cause 3: USB Brownout. The HC-SR04 draws a sharp 15mA spike when firing. If powered from a weak laptop USB port, the Arduino's 5V rail dips, resetting the sensor mid-flight. Fix: Add a 100µF electrolytic capacitor across the VCC and GND rails on the breadboard to buffer the spike.
Symptom: Serial monitor prints Distance: 452 cm (or random massive numbers)
Cause 1: Acoustic Ghosting. The sound wave is bouncing off a nearby wall or desk surface and returning late. Fix: Elevate the sensor at least 6 inches above your desk and ensure the field of view (15° cone) is clear of obstructions.
Cause 2: Missing Ground Reference. If the Arduino and sensor do not share a common ground, the Echo pin's HIGH signal floats, causing pulseIn() to read electrical noise as a massive duration. Fix: Verify the GND jumper wire is securely seated.
Extending the Build: Native pulseIn() vs. NewPing Library
The native code provided above is perfect for learning and simple projects. However, if you are building a robot that requires continuous, non-blocking distance polling, you must change your approach.
| Feature | Native pulseIn() |
NewPing Library |
|---|---|---|
| Blocking? | Yes (halts CPU until echo returns) | No (uses timer interrupts) |
| Multi-Sensor Support | Poor (requires manual 60ms delays per sensor) | Excellent (handles up to 15 sensors asynchronously) |
| Memory Overhead | Zero (built-in) | ~1.5 KB Flash |
| Best Used For | Tank level monitors, simple alarms | RC cars, drones, rover navigation |
If you choose to extend your build to a multi-sensor rover, install the NewPing Library via the Arduino Library Manager. It natively handles the 400cm out-of-bounds errors and eliminates the need for manual timeout math.
For deeper electrical analysis of the 40kHz transducer drive circuit and impedance matching, refer to Texas Instruments' application notes on ultrasonic sensing. Understanding the analog front-end explains why the HC-SR04 struggles with soft, sound-absorbing materials like foam or heavy curtains.






