The Arduino HC-SR04 is a 5V ultrasonic distance sensor that measures range from 2 cm to 400 cm with roughly 3 mm resolution. When wiring it to an Arduino Uno Rev3, connect VCC to 5V, GND to GND, Trig to Pin 9, and Echo to Pin 10. To get a reading, send a 10-microsecond HIGH pulse to the Trig pin, then measure the Echo pin's HIGH duration using pulseIn() with a timeout to prevent code blocking. Divide the resulting microseconds by 58.2 to get centimeters.

Difficulty: Beginner | Time Required: 20 Minutes | Target Board: Arduino Uno Rev3 (ATmega328P)

HC-SR04 Specifications and Operating Limits

Before wiring the sensor, it is critical to understand its physical and electrical boundaries. The HC-SR04 uses two 40 kHz aluminum mesh transducers—one for transmitting acoustic bursts and one for receiving echoes. Pushing the sensor beyond its rated blind zone or maximum range will result in erroneous data or infinite blocking loops in your firmware.

Parameter Specification / Value Engineering Notes
Operating Voltage 5V DC Brownouts occur below 4.5V; do not power directly from a 3.3V pin.
Quiescent Current 2 mA Spikes to ~15 mA during the 40 kHz transmit burst.
Measuring Range 2 cm to 400 cm Readings below 2 cm (the blind zone) are highly unreliable.
Resolution ~3 mm (0.1 inches) Limited by the speed of sound and ATmega328P timer granularity.
Measuring Angle ~15° (Effective cone) Acoustic beam spreads; off-axis targets may cause multipath errors.
Trigger Pulse Width 10 µs (Minimum) Pulses shorter than 10 µs will fail to initiate the sensor's internal MCU.
Acoustic Frequency 40 kHz Susceptible to interference from other 40 kHz sensors in the same room.

Data sourced from standard HC-SR04 datasheets and validated against Adafruit's PID 3942 hardware testing.

Parts List and Pin Mapping

For this build, we are targeting the standard 5V logic ecosystem. If you are adapting this to a 3.3V board like an ESP32 or Raspberry Pi Pico, you must use a voltage divider on the Echo pin to prevent frying the microcontroller's GPIO.

Required Components

  • Microcontroller: Arduino Uno Rev3 (ABX00066) or compatible ATmega328P clone.
  • Sensor: HC-SR04 Ultrasonic Module (Adafruit PID 3942 or generic equivalent).
  • Prototyping: Half-size solderless breadboard and male-to-male jumper wires.
  • Level Shifting (Optional for 3.3V boards): 1x 1kΩ and 1x 2kΩ resistor for Echo pin voltage division.

Pin Mapping Table

HC-SR04 Pin Arduino Uno Rev3 Pin Wire Color (Standard) Function
VCC 5V Red Power supply (Requires clean 5V, ~15mA peak)
Trig Digital Pin 9 Yellow Input: Receives 10µs HIGH pulse to start measurement
Echo Digital Pin 10 Blue Output: Goes HIGH for duration of sound round-trip
GND GND Black Common ground reference

Step-by-Step Wiring and Production-Ready Code

Follow these physical wiring steps before uploading the firmware. Ensure the Arduino is disconnected from USB power while inserting components into the breadboard to prevent accidental short circuits.

  1. Insert the HC-SR04 into the breadboard, straddling the center divider so the four pins sit in independent rows.
  2. Connect the Red jumper from the VCC pin to the Arduino's 5V rail.
  3. Connect the Black jumper from the GND pin to the Arduino's GND rail.
  4. Connect the Yellow jumper from the Trig pin to Digital Pin 9.
  5. Connect the Blue jumper from the Echo pin to Digital Pin 10.
  6. Verify all connections with a multimeter in continuity mode before applying power.
Bench Tip: The HC-SR04's internal microcontroller requires a brief initialization period on boot. Always include a 500ms delay() at the end of your setup() function to allow the sensor's internal state machine to stabilize before the first trigger pulse.

Complete C++ Firmware (Arduino IDE)

This code targets the Arduino Uno Rev3. It includes explicit pin definitions, a timeout mechanism to prevent the pulseIn() function from blocking the main loop indefinitely, and basic error handling for out-of-range readings.

// Target Board: Arduino Uno Rev3 (ATmega328P)
// Sensor: HC-SR04 Ultrasonic Distance Sensor

#define TRIG_PIN 9
#define ECHO_PIN 10
#define TIMEOUT_US 30000 // 30ms timeout (~5 meters max theoretical range)
#define MEASUREMENT_INTERVAL_MS 60 // Min time between readings to prevent echo overlap

void setup() {
  Serial.begin(115200);
  
  pinMode(TRIG_PIN, OUTPUT);
  pinMode(ECHO_PIN, INPUT);
  
  // CRITICAL: Ensure Trigger pin is LOW on boot to prevent phantom reads
  digitalWrite(TRIG_PIN, LOW);
  
  delay(500); // Allow HC-SR04 internal MCU to initialize
  Serial.println("HC-SR04 Initialized. Starting measurements...");
}

void loop() {
  long duration;
  float distance_cm;

  // 1. Clear the trigger pin to ensure a clean pulse
  digitalWrite(TRIG_PIN, LOW);
  delayMicroseconds(2);

  // 2. Send the 10us trigger pulse
  digitalWrite(TRIG_PIN, HIGH);
  delayMicroseconds(10);
  digitalWrite(TRIG_PIN, LOW);

  // 3. Read the echo pin with a timeout to prevent infinite blocking
  // pulseIn returns 0 if the timeout is reached before a pulse completes
  duration = pulseIn(ECHO_PIN, HIGH, TIMEOUT_US);

  if (duration == 0) {
    Serial.println("Error: Timeout - No echo received or target out of range.");
  } else {
    // Calculate distance
    // Speed of sound at 20C is ~343 m/s (29.1 us per cm)
    // Divide by 2 because the sound travels to the target and back
    // 29.1 * 2 = 58.2
    distance_cm = duration / 58.2;
    
    Serial.print("Distance: ");
    Serial.print(distance_cm);
    Serial.println(" cm");
  }

  delay(MEASUREMENT_INTERVAL_MS); 
}

For more details on how the microcontroller handles the timing, refer to the official Arduino pulseIn() documentation.

Debugging: Fixing '0 cm' and '4000+ cm' Readings

Ultrasonic sensors are notoriously noisy in real-world environments. If your serial monitor is printing garbage data, follow this decision tree.

The First 3 Things to Check When It Fails

  1. Verify the 5V Rail: Use a multimeter to check the voltage at the sensor's VCC and GND pins. If it reads below 4.5V, the sensor's internal oscillator will fail to generate the 40 kHz burst. Power it directly from the Arduino's 5V pin, not a daisy-chained breadboard rail.
  2. Check GND Continuity: A floating ground will cause the Echo pin to drift high, resulting in massive, erratic distance values. Ensure the black wire has a solid connection to the Arduino's GND.
  3. Confirm Trig Pin Initialization: If you forget digitalWrite(TRIG_PIN, LOW); in the setup() function, the very first reading will often fail or return a massive number because the pin state is undefined on boot.

Symptom and Cause Matrix

Symptom / Serial Output Most Likely Cause Fix
Error: Timeout (Reads 0.00) Target is beyond 4 meters, or target is sound-absorbing (foam, heavy curtains). Move target closer or attach a hard, flat reflector (wood/plastic) to the target.
Distance: 4125.50 cm (Erratic high) Acoustic crosstalk from another 40 kHz sensor, or multipath bouncing off angled walls. Isolate the sensor, add acoustic dampening foam around the transducer barrels, or stagger sensor polling.
Code freezes completely pulseIn() called without the third timeout parameter. Add TIMEOUT_US as the third argument to pulseIn() as shown in the code block above.
Readings jump between 2cm and 15cm rapidly Target is inside the 2cm 'blind zone' where transmit and receive waves overlap. Move the target at least 3 cm away from the sensor face.

Extending and Simplifying the Build

Once you have a single sensor working reliably, you will likely want to scale the project or optimize the firmware for multitasking.

How to Simplify: Use the NewPing Library

The native pulseIn() function is blocking—meaning the Arduino can do absolutely nothing else while waiting for the echo. If you need to run motors or read buttons simultaneously, replace the raw code with the NewPing library. NewPing utilizes hardware timer interrupts to measure the echo pulse in the background, freeing up your loop() function. It also natively handles the 60ms delay and filters out-of-range pings automatically.

How to Extend: Multi-Sensor Polling

If you need to mount three HC-SR04 sensors on a robot chassis for obstacle avoidance, do not fire them simultaneously. The 40 kHz acoustic waves will cross-pollinate, and Sensor A will read Sensor B's echo.

To fix this, wire all VCC and GND pins in parallel, but assign unique digital pins to each Trig and Echo pair. In your code, poll them sequentially: fire Sensor 1, wait 60ms, fire Sensor 2, wait 60ms, fire Sensor 3.

When to Ditch the HC-SR04 Entirely

The HC-SR04 is excellent for hobbyist bench work, but it struggles in high-vibration or electrically noisy environments. If your project requires industrial reliability, consider upgrading to an I2C-based ultrasonic sensor like the RCWL-1601 or the A02YYUW. These modules handle the timing and signal processing internally, outputting clean distance data over I2C or UART, completely eliminating the need for pulseIn() and freeing up your microcontroller's GPIO pins.