If you are looking for reliable Arduino sonar sensor code, the direct answer is to avoid basic delay-based loops and instead use the pulseIn() function with a strict microsecond timeout, paired with a median filter to eliminate acoustic ghost readings. The code provided below targets the Arduino Uno R3 and the newer Arduino Uno R4 Minima, utilizing 5V logic to interface directly with the standard HC-SR04 ultrasonic module.
Ultrasonic sensors are notorious for throwing erratic values when placed near angled walls or soft fabrics. By implementing a 30-millisecond timeout and a 5-sample median sort in your C++ sketch, you prevent the main loop from hanging indefinitely and filter out multipath acoustic reflections. Below is the complete bench-tested guide to wiring, coding, and debugging your sonar build.
Project Spec Sheet & Parts List
Before writing a single line of code, verify your hardware. The standard HC-SR04 is fine for indoor robotics, but if you are building an outdoor rover or a sump-pump monitor, you need the waterproof variant.
| Component | Exact Variant | Specs & Notes | Est. Price (2026) |
|---|---|---|---|
| Microcontroller | Arduino Uno R3 or R4 Minima | ATmega328P or Renesas RA4M1. 5V logic. | $24 - $28 |
| Sonar Sensor (Standard) | HC-SR04 | 2cm - 400cm range. 15° beam angle. 5V VCC. | $1.50 - $3.00 |
| Sonar Sensor (Waterproof) | JSN-SR04T | 20cm - 400cm range. Sealed transducer. 5V VCC. | $5.00 - $8.00 |
| Jumper Wires | 22 AWG Dupont (M-M) | Keep under 30cm to prevent signal degradation. | $4.00 (pack) |
Pin Mapping & Wiring Rules
The HC-SR04 uses a simple two-wire synchronous interface (Trigger and Echo). While you can use any digital pins, assigning them to standard I/O pins avoids conflicts with hardware serial or I2C buses.
| HC-SR04 Pin | Arduino Uno Pin | Wire Color (Standard) | Function |
|---|---|---|---|
| VCC | 5V | Red | Power supply (4.8V - 5.5V DC) |
| Trig | Digital 9 | Yellow | Input: Receives 10µs HIGH pulse |
| Echo | Digital 10 | Blue | Output: Sends HIGH pulse proportional to distance |
| GND | GND | Black | Common ground reference |
ESP32 / 3.3V Board Warning: If you adapt this Arduino sonar sensor code for an ESP32 or Raspberry Pi Pico, the Echo pin outputs a 5V HIGH signal when the HC-SR04 is powered by 5V. Feeding 5V into a 3.3V GPIO will fry the microcontroller. You must use a voltage divider (e.g., 1kΩ and 2kΩ resistors) on the Echo line to step it down to 3.3V.
The Compilable Arduino Sonar Sensor Code
This sketch avoids the pitfalls of basic tutorials. It includes a strict timeout on the pulseIn() function—preventing the board from freezing if the sound wave scatters—and a median filter to discard acoustic anomalies. For more on how pulseIn() handles microsecond timing at the hardware level, refer to the official Arduino pulseIn reference.
/*
* Reliable Arduino Sonar Sensor Code
* Target: Arduino Uno R3 / R4 Minima
* Sensor: HC-SR04 or JSN-SR04T
*/
// Pin Definitions
#define TRIG_PIN 9
#define ECHO_PIN 10
// Physics Constants
#define SOUND_SPEED_CM_PER_US 0.0343 // Speed of sound at 20°C
#define TIMEOUT_US 30000 // 30ms timeout (max range ~5 meters)
#define SAMPLE_SIZE 5 // Median filter window
unsigned long getMedianDistance();
void setup() {
Serial.begin(115200);
pinMode(TRIG_PIN, OUTPUT);
pinMode(ECHO_PIN, INPUT);
// Ensure trigger pin is low on startup
digitalWrite(TRIG_PIN, LOW);
Serial.println("Sonar Sensor Initialized.");
}
void loop() {
unsigned long distance = getMedianDistance();
// Error Handling & Output
if (distance == 0) {
Serial.println("Error: Timeout or No Echo (Check Wiring/Power)");
} else if (distance > 400) {
Serial.println("Error: Out of Range (>400cm)");
} else {
Serial.print("Distance: ");
Serial.print(distance);
Serial.println(" cm");
}
delay(250); // 4Hz polling rate (HC-SR04 max is ~20Hz, 4Hz prevents echo overlap)
}
// Function to get a single raw reading with timeout
unsigned long getRawDistance() {
digitalWrite(TRIG_PIN, LOW);
delayMicroseconds(2);
digitalWrite(TRIG_PIN, HIGH);
delayMicroseconds(10);
digitalWrite(TRIG_PIN, LOW);
// Read echo with strict timeout to prevent blocking
unsigned long duration = pulseIn(ECHO_PIN, HIGH, TIMEOUT_US);
if (duration == 0) return 0; // Timeout occurred
// Calculate distance: (duration / 2) * speed of sound
return (duration / 2) * SOUND_SPEED_CM_PER_US;
}
// Median filter to eliminate ghost readings from multipath reflections
unsigned long getMedianDistance() {
unsigned long samples[SAMPLE_SIZE];
for (int i = 0; i < SAMPLE_SIZE; i++) {
samples[i] = getRawDistance();
delay(10); // Small gap between pings to let acoustic ringing settle
}
// Simple bubble sort for small array
for (int i = 0; i < SAMPLE_SIZE - 1; i++) {
for (int j = 0; j < SAMPLE_SIZE - i - 1; j++) {
if (samples[j] > samples[j + 1]) {
unsigned long temp = samples[j];
samples[j] = samples[j + 1];
samples[j + 1] = temp;
}
}
}
// Return the middle value
return samples[SAMPLE_SIZE / 2];
}
Debugging: Fixing '0 cm' and '400 cm' Ghost Readings
When your serial monitor spits out bad data, do not immediately rewrite the code. Hardware and physics are usually the culprits. Here is the ranked decision tree for the most common failure modes.
Symptom 1: Serial Monitor prints 'Error: Timeout or No Echo (Check Wiring/Power)'
This means pulseIn() hit the 30,000µs timeout and returned 0. The sensor never pulled the Echo pin HIGH.
- Check 1: Power Rail Continuity. Use a multimeter to measure DC voltage between the sensor's VCC and GND pins directly at the module. If it reads below 4.7V, your breadboard power rails are loose, or your USB cable is suffering from voltage drop. The HC-SR04 will fail to generate the 40kHz burst without a solid 5V.
- Check 2: Trig/Echo Swap. It is incredibly common to plug the yellow and blue wires into the wrong pins. Verify that D9 goes to Trig and D10 goes to Echo.
- Check 3: Common Ground. If you are using an external 5V power supply for the sensor, its GND must be tied directly to the Arduino's GND. Without a shared reference, the Arduino cannot read the Echo pulse.
Symptom 2: Serial Monitor prints 'Error: Out of Range (>400cm)' when an object is close
This happens when the Echo pin stays HIGH longer than expected, or the sensor is experiencing acoustic ringing.
- Cause: Missed Trigger. If the 10µs trigger pulse is slightly too short due to interrupt latency, the sensor might fire a partial burst, confusing its internal flip-flop. The code above uses
delayMicroseconds(10)which is highly accurate on AVR boards. - Cause: Acoustic Multipath. If the sensor is mounted inside a 3D-printed shroud or near a angled baffle, the 40kHz sound waves bounce around the enclosure before exiting. This is called 'acoustic ringing'. Fix: Add a 20ms delay between pings, or line the inside of your sensor shroud with acoustic dampening foam.
Extending and Simplifying the Build
Once you have the raw Arduino sonar sensor code running, you will likely want to integrate it into a larger system. Here is how to scale the project without reinventing the wheel.
Simplifying with the NewPing Library
If you are managing multiple ultrasonic sensors (e.g., a 3-sensor collision avoidance rover), writing custom median filters and timers for each pin becomes messy. The NewPing library is the industry standard for this. It handles the timer interrupts and ping scheduling natively, allowing you to ping up to 15 sensors without blocking the main loop. It also includes built-in median filtering via ping_median(iterations).
Adding an I2C OLED Display
To make the build standalone, add a 0.96-inch SSD1306 I2C OLED display. Wire the SDA to A4 and SCL to A5 on the Uno. Use the Adafruit_SSD1306 library to render the distance value. Because I2C operates on a separate hardware bus, it will not interfere with the microsecond-precise timing required by the pulseIn() function on the digital pins.
Arduino Sonar Sensor Code FAQ
Can I use the HC-SR04 sonar sensor with an ESP32 using this exact code?
You can use the logic, but you must change the pin definitions to valid ESP32 GPIOs (avoid strapping pins like GPIO 0, 2, 12, and 15). More importantly, the HC-SR04 outputs a 5V Echo signal. The ESP32 is strictly 3.3V tolerant. You must build a voltage divider using a 1kΩ resistor (series) and a 2kΩ resistor (to ground) on the Echo line to step the 5V pulse down to a safe ~3.3V before it hits the ESP32 GPIO.
Why does my Arduino sonar sensor code freeze the entire robot after a few minutes?
This is almost always caused by using pulseIn(ECHO_PIN, HIGH) without a timeout parameter. If the sensor misses an echo (due to a soft fabric target absorbing the sound, or a wiring glitch), the Echo pin never goes HIGH. The pulseIn() function will wait indefinitely, effectively bricking your main loop until you press the hardware reset button. Always use the 3-argument version: pulseIn(pin, HIGH, timeout_us).
How do I convert the distance from centimeters to inches in the code?
Divide the final centimeter value by 2.54. In the code provided, you can change the return line in the getMedianDistance() function to return (samples[SAMPLE_SIZE / 2] / 2.54); and update your Serial print statement to read 'inches'. Note that the speed of sound changes slightly with temperature and humidity, but for hobbyist robotics at room temperature (20°C), the 0.0343 cm/µs constant is accurate to within 1%.
What is the maximum polling rate for the HC-SR04 sensor?
The HC-SR04 requires a minimum of 50 milliseconds between trigger pulses to allow the 40kHz acoustic ringing to dissipate and to prevent the previous echo from overlapping with the next ping. This equates to a maximum theoretical polling rate of 20Hz. However, for reliable operation with a median filter (which takes 5 samples), a 250ms delay (4Hz) in the main loop is the sweet spot for mobile robots.






