The HC-SR04 ultrasonic sensor measures distances from 2 cm to 400 cm by timing 40 kHz sound wave echoes. When wiring an HC-SR04 sensor to an Arduino Uno R3, you connect VCC to 5V, GND to GND, Trig to Pin 9, and Echo to Pin 10. However, if you are using a 3.3V board like the ESP32 or Arduino Nano 33 IoT, the 5V Echo return will fry your GPIO unless you use a voltage divider or the 3.3V-compatible HC-SR04+ variant. This guide covers the exact hardware specs, a glitch-filtered code implementation, and how to debug the notorious serial monitor errors that plague this specific module.

HC-SR04 vs Alternatives: Spec Sheet & Selection Matrix

Before soldering, verify you have the right module for your environment. The standard HC-SR04 is a bench prototype part, not an industrial sensor. Below is a data-dense comparison of the most common 40 kHz ultrasonic modules available in 2026, highlighting why you might need to upgrade based on your operating environment.

Model Logic Level Min / Max Range Beam Angle Blind Zone Avg Price (2026) Best Application
HC-SR04 5V Only 2 cm / 400 cm ~15° < 2 cm $1.50 Indoor robotics, basic Arduino tutorials
HC-SR04+ 3.3V & 5V 2 cm / 400 cm ~15° < 2 cm $2.20 ESP32, Raspberry Pi Pico, 3.3V MCUs
JSN-SR04T 5V Only 20 cm / 600 cm ~12° < 20 cm $4.50 Outdoor, waterproof, car reverse parking
MaxBotix MB1010 2.5V - 5.5V 0 cm / 500 cm ~42° 0 cm (No blind zone) $29.99 Industrial tank level, precision medical
Pro-Tip: The Blind Zone. The HC-SR04 cannot read distances under 2 cm because the transducer is still physically ringing from the transmit pulse when the echo would theoretically return. If your project requires touching-distance measurement, you must switch to a MaxBotix LV-MaxSonar or a Time-of-Flight (ToF) IR sensor like the VL53L0X.

Hardware Build and Pin Mapping

This build targets the Arduino Uno R3 (ATmega328P), which operates at 5V logic. This allows direct wiring to the standard HC-SR04 without level shifters. If you are using an ESP32 or Arduino Uno R4 WiFi, you must use a 10kΩ/20kΩ voltage divider on the Echo pin, or purchase the HC-SR04+ variant listed above.

Parts List

  • Microcontroller: Arduino Uno R3 (ATmega328P) - ~$27.00 (Official) / ~$14.00 (Clone)
  • Sensor: Standard HC-SR04 (4-pin variant) - ~$1.50
  • Wiring: 4x Male-to-Male jumper wires (Dupont 2.54mm pitch)
  • Prototyping: Half-size 400-point solderless breadboard
  • Optional (for 3.3V boards): 1x 10kΩ and 1x 20kΩ carbon film resistor (1/4W) for voltage division.

Pin Mapping Table

HC-SR04 Pin Arduino Uno R3 Pin Wire Color (Standard) Function
VCC 5V Red Powers the internal oscillator and transducer boost converter
Trig Digital Pin 9 Yellow Receives a 10µs HIGH pulse to initiate the measurement
Echo Digital Pin 10 Green Outputs a HIGH pulse proportional to the distance measured
GND GND Black Common ground reference

Glitch-Filtered Arduino Code

The most common complaint with the HC-SR04 is random, massive distance spikes (e.g., jumping from 45 cm to 3400 cm for a single reading). This happens because the cheap internal microcontroller on the sensor occasionally misses an echo or suffers from acoustic crosstalk, pulling the Echo pin HIGH indefinitely until the watchdog resets it.

The code below avoids the standard pulseIn() blocking trap by implementing a strict timeout and a 5-sample median filter. This sorts the readings and discards the highest and lowest outliers, guaranteeing a stable serial output.

/*
 * HC-SR04 Ultrasonic Sensor with Median Glitch Filter
 * Target Board: Arduino Uno R3 (5V Logic)
 * Author: ElectricalFlux Bench Team
 */

#define TRIG_PIN 9
#define ECHO_PIN 10
#define MAX_DISTANCE 400 // Max practical range in cm
#define TIMEOUT_US 25000 // 400cm takes ~23ms; 25ms timeout prevents infinite blocking

void setup() {
  Serial.begin(115200);
  pinMode(TRIG_PIN, OUTPUT);
  pinMode(ECHO_PIN, INPUT);
  digitalWrite(TRIG_PIN, LOW); // Ensure clean start state
}

void loop() {
  long stableDistance = readMedianDistance();
  
  Serial.print("Distance: ");
  Serial.print(stableDistance);
  Serial.println(" cm");
  
  delay(100); // Update rate ~10Hz (safe for 40kHz decay)
}

long readMedianDistance() {
  long samples[5];
  
  for (int i = 0; i < 5; i++) {
    // 1. Clear the trigger pin
    digitalWrite(TRIG_PIN, LOW);
    delayMicroseconds(2);
    
    // 2. Send exactly 10µs HIGH pulse (HC-SR04 hardware requirement)
    digitalWrite(TRIG_PIN, HIGH);
    delayMicroseconds(10);
    digitalWrite(TRIG_PIN, LOW);
    
    // 3. Read the echo with strict timeout
    long duration = pulseIn(ECHO_PIN, HIGH, TIMEOUT_US);
    
    // 4. Convert to cm (Speed of sound = 343 m/s -> 0.0343 cm/µs)
    // Divide by 2 for the round-trip
    if (duration == 0 || duration > 23500) {
      samples[i] = MAX_DISTANCE; // Assign max distance for timeouts/glitches
    } else {
      samples[i] = (duration * 0.0343) / 2;
    }
    
    delay(25); // Mandatory 25ms pause to let acoustic ringing dissipate
  }
  
  // Simple bubble sort for 5 elements to find the median
  for (int i = 0; i < 4; i++) {
    for (int j = 0; j < 4 - i; j++) {
      if (samples[j] > samples[j+1]) {
        long temp = samples[j];
        samples[j] = samples[j+1];
        samples[j+1] = temp;
      }
    }
  }
  
  return samples[2]; // Return the middle (median) value
}
Temperature Compensation Note: The math 0.0343 cm/µs assumes an ambient temperature of 20°C (68°F). If you are using this sensor in an unheated garage or outdoors in winter (e.g., 0°C), the speed of sound drops, and your sensor will read roughly 3% short. For high-precision tank level monitoring, wire a DS18B20 temperature sensor and apply the formula: v = 331.4 + (0.6 * Temp_C) to dynamically adjust your multiplier.

Debugging: First Three Things to Check When It Fails

When your serial monitor starts misbehaving, do not immediately rewrite your code. 90% of HC-SR04 failures are hardware or timing related. Here is the exact diagnostic sequence based on the error string you are seeing.

Symptom 1: Serial monitor stuck on Distance: 0 cm

Ranked Causes:

  1. Fried Echo Pin (3.3V Board): If you connected a 5V HC-SR04 Echo pin directly to an ESP32 or Raspberry Pi Pico GPIO, you have likely burned out the internal clamping diode on the MCU. Fix: Move the Echo wire to a new GPIO pin and implement a voltage divider (20kΩ from Echo to GPIO, 10kΩ from GPIO to GND).
  2. Charge-Only USB Cable: Your Arduino is running the sketch, but the data lines (D+ / D-) in your USB cable are missing. Fix: Swap to a verified data-sync USB cable.
  3. Dead Sensor Module: The internal STC microcontroller on the HC-SR04 has failed, or the 40kHz crystal oscillator is dead. Fix: Replace the sensor; they are largely unrepairable due to epoxy potting.

Symptom 2: Random massive spikes like Distance: 3452 cm

Ranked Causes:

  1. Missing Timeout in pulseIn(): If you use pulseIn(ECHO_PIN, HIGH) without the third timeout parameter, and the sensor misses the echo, the Arduino will hang waiting for a falling edge that never comes, eventually overflowing the variable. Fix: Always use pulseIn(pin, HIGH, 25000) as shown in the code above.
  2. Acoustic Crosstalk: You have multiple HC-SR04 sensors firing in the same room, or you are pinging faster than 20Hz. The sensor is hearing the echo from a previous ping or a neighboring sensor. Fix: Enforce a minimum 25ms delay() between pings.
  3. Soft Target Absorption: You are pointing the sensor at a couch, curtain, or angled wall. The 40kHz wave is being absorbed or reflected away from the receiver. Fix: Test against a flat, hard surface like a book or wall.

Symptom 3: Complete sketch hang (No serial output)

Ranked Causes:

  1. Trigger Pulse Width Incorrect: The HC-SR04 hardware requires a minimum of 10µs HIGH pulse on the Trig pin to initiate a measurement. If you use delayMicroseconds(5), the internal chip will ignore the trigger, the Echo pin will never go HIGH, and a poorly written blocking loop will freeze. Fix: Ensure delayMicroseconds(10) is exact.

Extending and Simplifying the Build

How to Simplify: The NewPing Library

If you do not want to manage raw timing and median filters yourself, the community-standard NewPing library handles timeouts, crosstalk delays, and median filtering natively. It strips out the 15ms blocking delay inherent in the standard pulseIn() function, freeing up your Arduino's main loop to handle motor control or WiFi tasks. Simply install "NewPing" via the Arduino Library Manager and use sonar.ping_median(5) to get the exact same filtered result in one line of code.

How to Extend: I2C Multiplexing and Fluid Sensing

The HC-SR04 is strictly a 4-pin parallel interface device; it does not have an I2C address. If your project requires multiple sensors (e.g., a 4-wheel obstacle avoidance robot), you will run out of GPIO pins and suffer from acoustic crosstalk.

The Extension Path: Use an I2C multiplexer like the TCA9548A paired with I2C-based ultrasonic sensors (like the DFRobot URM09), or build a simple transistor switching circuit to power-cycle individual HC-SR04 modules one at a time via N-channel MOSFETs (like the 2N7000) on their GND lines.

Fluid Level Sensing: To use the HC-SR04 for water tank depth, mount it inside a PVC pipe cap facing downward into the tank. The PVC pipe acts as an acoustic waveguide, narrowing the 15° beam angle and preventing false echoes off the tank walls. Just remember to invert your math: Fluid_Level = Tank_Height - Sensor_Distance.

Final Bench Note: According to SparkFun's ultrasonic sensor guides, the physical mesh covering the transducers is highly susceptible to dust and moisture buildup. If your sensor suddenly starts reading 2cm regardless of the actual distance, inspect the silver mesh. A quick blast of compressed air to clear debris from the receiver transducer often revives a seemingly dead module.