For reliable distance measurement, bypass the blocking pulseIn() function and use the NewPing library with an Arduino Uno R3 or Nano v3 (ATmega328P, 5V logic). The HC-SR04 ultrasonic sensor calculates distance using the speed of sound (343 m/s at 20°C), requiring exactly 58.2 microseconds per centimeter. Raw pulseIn() code often freezes your microcontroller if no echo returns; NewPing implements hardware timer interrupts and a 30ms timeout to prevent this.



Project Spec Sheet
Difficulty: Beginner/Intermediate
Time Required: 20 minutes
Estimated Cost: $6 - $28 (depending on board clone vs. official)

Hardware Spec Sheet & Parts List

Before writing a single line of ultrasonic sensor Arduino code, you must understand the physical limitations of the hardware. The HC-SR04 is an inexpensive, open-air time-of-flight sensor. It is strictly a 5V device. The Echo pin outputs a 5V HIGH signal when receiving the return ping. If you wire this directly to a 3.3V microcontroller (like an ESP32 or Raspberry Pi Pico) without a voltage divider, you risk frying the GPIO pin.

ParameterHC-SR04 SpecificationPractical Bench Notes
Operating Voltage5V DCTolerates 4.5V - 5.5V. Brownouts below 4.2V cause 0cm reads.
Operating Current15 mA (active)Quiescent current is ~2mA. Safe for direct GPIO power on small setups.
Measuring Range2 cm to 400 cmBlind spot (dead zone) is < 2cm. Max range drops on soft targets.
Resolution0.3 cmDictated by the 40kHz wavelength (~8.5mm) and timer precision.
Beam Angle~15 degreesHighly directional, but side-lobes can catch table edges.

Required Parts

  • Microcontroller: Arduino Uno R3 or Nano v3 (ATmega328P). Target board for this code.
  • Sensor: HC-SR04 Ultrasonic Module (Standard 4-pin variant).
  • Wiring: 4x Male-to-Male or Male-to-Female jumper wires (22 AWG stranded).
  • Prototyping: Half-size solderless breadboard.
  • Tools: Digital Multimeter (DMM) for debugging 5V rail integrity.

Pin Mapping & Wiring Steps

Wiring the HC-SR04 is straightforward, but loose breadboard connections are the number one cause of erratic readings. Ensure your jumper wires are fully seated.

HC-SR04 PinArduino Uno R3 PinWire Color (Standard)
VCC5VRed
TrigDigital Pin 9Yellow
EchoDigital Pin 10Blue
GNDGNDBlack
  1. De-energize the board: Unplug the Arduino USB cable before wiring.
  2. Connect Power: Route the red wire from the sensor VCC to the Arduino 5V pin, and the black wire to GND.
  3. Connect Signal: Wire the Trig pin to Digital 9 and Echo to Digital 10.
  4. Verify Voltage: Before plugging in the sensor, power the Arduino and use your DMM to measure the 5V pin to GND. It should read between 4.8V and 5.1V. If it reads 3.3V, your onboard voltage regulator is faulty or you are using a 3.3V Pro Mini variant.
  5. Power Up: Connect the sensor and plug the Arduino into your PC via USB.

The Reliable Code: NewPing Implementation

The native Arduino pulseIn() function waits for a pin to go HIGH and then LOW. If the ultrasonic wave scatters and never returns, pulseIn() will block your entire sketch for up to 1 second (its default timeout). In a robotics or timing-critical application, a 1-second freeze is catastrophic.

Instead, we use the NewPing library by Tim Eckel. It uses hardware timer interrupts to measure the echo pulse without blocking the main loop, and it includes a built-in median filter to discard acoustic anomalies.

Prerequisite: Open the Arduino IDE, go to Sketch > Include Library > Manage Libraries, search for NewPing, and install it.

#include <NewPing.h>

// --- PIN DEFINITIONS ---
#define TRIGGER_PIN  9
#define ECHO_PIN     10
#define MAX_DISTANCE 400 // Maximum distance we want to ping for (in cm)
#define PING_INTERVAL 35 // Minimum ms between pings (29ms is sensor limit)

// Initialize NewPing object
NewPing sonar(TRIGGER_PIN, ECHO_PIN, MAX_DISTANCE);

void setup() {
  Serial.begin(115200);
  Serial.println("HC-SR04 Ultrasonic Sensor Initialized.");
}

void loop() {
  // Wait between pings to avoid echo interference
  delay(PING_INTERVAL);
  
  // ping_median(iterations) sends 5 pings, discards outliers, returns median
  // US_ROUNDTRIP_CM is a built-in constant (~58.2) for cm conversion
  unsigned int medianMicroseconds = sonar.ping_median(5);
  unsigned int cm = medianMicroseconds / US_ROUNDTRIP_CM;

  // Error Handling: NewPing returns 0 if no echo is received within MAX_DISTANCE
  if (cm == 0) {
    Serial.println("Error: Out of range or no echo received (Timeout).");
  } else {
    Serial.print("Distance: ");
    Serial.print(cm);
    Serial.println(" cm");
  }
}

Debugging: First Three Things to Check When It Fails

When your serial monitor misbehaves, follow this ranked decision path. These are the three most common failure modes on the bench.

1. Symptom: Serial monitor prints Error: Out of range... or 0 cm constantly.

Most Likely Cause: Power brownout or missing 5V rail.
The Fix: The HC-SR04 requires a solid 5V supply to drive the 40kHz transducers. If powered via a weak USB port, the voltage may droop to 4.2V when the sensor fires, causing it to fail silently. Measure the VCC pin on the sensor with a DMM while the code is running. If it drops below 4.5V during a ping, power the Arduino via the DC barrel jack with a 7.5V-9V wall adapter, or use a dedicated 5V buck converter for the sensor.

2. Symptom: Wildly fluctuating numbers (e.g., 12cm, 85cm, 4cm, 140cm).

Most Likely Cause: Acoustic multipath interference or soft target absorption.
The Fix: The 15-degree beam angle bounces off nearby desk clutter, walls, or the breadboard itself. The ping_median(5) function in the code above mathematically solves 90% of this by throwing out the highest and lowest outliers. Physically, ensure the sensor is mounted at least 2 inches above your table surface to avoid capturing table-edge reflections.

3. Symptom: Compilation Error: fatal error: NewPing.h: No such file or directory

Most Likely Cause: Library not installed or installed in the wrong directory.
The Fix: In the Arduino IDE, go to Tools > Manage Libraries. Ensure you are installing the library authored by Tim Eckel. If you manually downloaded a ZIP, use Sketch > Include Library > Add .ZIP Library rather than just dropping the folder into your documents.

Extending and Simplifying the Build

Depending on your end goal, the standard HC-SR04 and raw GPIO wiring might not be the optimal long-term solution.

How to Simplify (I2C Alternative):
If you are running out of GPIO pins or building a multi-sensor array, ditch the HC-SR04 and switch to an I2C ultrasonic sensor like the DFRobot SEN0304. It communicates over the standard I2C bus (SDA/SCL), meaning you can daisy-chain up to 16 sensors on just two Arduino pins using an I2C multiplexer (like the TCA9548A). It also handles all the timing math on its internal MCU, freeing up your ATmega328P.

How to Extend (Outdoor/Waterproof):
For sump pump controllers, outdoor tank level monitoring, or damp environments, the open-mesh HC-SR04 will corrode and fail within weeks. Upgrade to the JSN-SR04T waterproof ultrasonic sensor. It uses the exact same Arduino code and trigger/echo logic, but features a sealed, cabled transducer. Bench Warning: The JSN-SR04T has a much larger blind spot. While the HC-SR04 can read down to 2cm, the JSN-SR04T cannot accurately read targets closer than 20cm. Adjust your MAX_DISTANCE and physical mounting depth accordingly.

Frequently Asked Questions

Can I use this ultrasonic sensor Arduino code on an ESP32 or 3.3V board?

You can use the logic and library, but you cannot wire it directly. The HC-SR04 Echo pin outputs a 5V HIGH signal. Feeding 5V into an ESP32 GPIO pin (which is strictly 3.3V tolerant) will permanently damage the silicon. You must use a voltage divider (e.g., a 1kΩ and 2kΩ resistor network) on the Echo wire to step the 5V down to ~3.3V before it reaches the ESP32. Alternatively, buy the US-025 sensor variant, which is native 3.3V compatible.

Why does my HC-SR04 read exactly 0 cm or max distance constantly?

A constant 0 cm (or 400+ cm on raw pulseIn code) means the trigger pulse was sent, but the echo was never received. Aside from the 5V power brownout mentioned in the debugging section, check your Trigger pin. The HC-SR04 requires a minimum 10-microsecond HIGH pulse on the Trig pin to fire. If your wiring is loose or your breadboard contacts are oxidized, the trigger signal may be degrading before it reaches the sensor IC.

How do I filter out jittery ultrasonic sensor readings in code?

Acoustic sensors are inherently noisy due to temperature gradients, air currents, and multipath reflections. The best software approach is a median filter, which is built into the sonar.ping_median(iterations) function used in our code block. Unlike a simple moving average (which gets dragged down by a single massive false reading), a median filter sorts the results and picks the middle value, effectively ignoring extreme outliers. For physical filtering, add a small piece of acoustic foam around the sides of the receiver transducer to narrow the 15-degree beam angle.

What is the dead zone of the HC-SR04 sensor?

The HC-SR04 has a physical blind spot of approximately 2 centimeters. When the trigger fires, the receiver transducer is physically deafened by the sheer volume of the transmitter ringing right next to it. It takes a few milliseconds for the receiver circuitry to recover and listen for the echo. If an object is closer than 2cm, the echo returns before the receiver is ready, resulting in a timeout (0 cm) error. If you need sub-centimeter proximity detection, you must switch to an infrared Time-of-Flight (ToF) sensor like the VL53L0X.