The HC-SR04 Sensing Principle and Output Signal

The HC-SR04 measures distance using time-of-flight (ToF) acoustics. It triggers a 40 kHz piezoelectric transmitter to emit an eight-pulse ultrasonic burst, then immediately switches to listening mode on a separate receiver transducer. When these sound waves hit a solid object, they reflect back as an echo, and the sensor calculates distance based on the exact microsecond delay between transmission and reception.

The output is strictly a 5V digital pulse, not an analog voltage or a 4-20mA current loop. The Echo pin goes HIGH (5V) for a duration exactly proportional to the distance measured. Because the output swings to 5V, interfacing this sensor with a 3.3V microcontroller like the ESP32 or Raspberry Pi Pico requires a voltage divider on the Echo pin to prevent frying the GPIO. Never connect the HC-SR04 Echo pin directly to a 3.3V logic input.

Wiring the Ultrasonic Sensor HC-SR04 to 5V and 3.3V Microcontrollers

The HC-SR04 requires a stable 5V supply to properly drive its piezoelectric transducers. While some clones claim to operate at 3.3V, bench testing consistently shows erratic triggering and reduced range when VCC drops below 4.5V. Always power the module from a 5V rail, and use a logic level shifter or resistor voltage divider for the signal lines if your microcontroller operates at 3.3V.

Bench Tip: Decoupling the VCC Rail
The HC-SR04 draws short current spikes (up to 15mA) during the ultrasonic burst. If you are running motors or servos on the same 5V rail, place a 100nF ceramic capacitor directly across the sensor's VCC and GND pins to prevent voltage sags from resetting your microcontroller.
HC-SR04 Pin Function Arduino Uno (5V Logic) ESP32 (3.3V Logic)
VCC Power Supply (4.5V - 5.5V) 5V Pin VIN or 5V Pin
Trig Trigger Input (10µs HIGH) Digital Pin 9 GPIO 5 (Direct)
Echo Echo Output (5V Pulse) Digital Pin 10 GPIO 18 (Via Voltage Divider)
GND Ground GND GND

The 3.3V Voltage Divider: To step the 5V Echo signal down to a safe 3.3V for the ESP32, use a 1kΩ resistor (R1) in series with the Echo pin, and a 2kΩ resistor (R2) from the ESP32 GPIO to GND. This yields Vout = 5V * (2k / (1k + 2k)) = 3.33V, which is well within the ESP32 GPIO absolute maximum ratings.

Raw-to-Unit Math: Converting Echo Pulses to Centimeters

The microcontroller measures the width of the HIGH pulse on the Echo pin in microseconds (µs). To convert this raw timing data into physical distance, we rely on the speed of sound. At 20°C (68°F), sound travels through dry air at approximately 343 meters per second, or 0.0343 cm/µs.

Because the ultrasonic burst must travel to the target and back, the measured time is double the actual distance. Therefore, the raw-to-unit math is:

  • Distance (cm) = (Pulse Width in µs × 0.0343) / 2
  • Distance (cm) = Pulse Width in µs / 58.3
  • Distance (inches) = Pulse Width in µs / 148.0

Calibration and Scaling: The 58.3 divisor assumes a room temperature of 20°C. If your sensor is deployed in an unheated garage or a hot greenhouse, the speed of sound shifts. According to engineering thermodynamic tables, the speed of sound changes by roughly 0.6 m/s for every 1°C change. For high-precision applications, read a local temperature sensor (like a BME280) and dynamically calculate the divisor: divisor = 2 / ((331.4 + (0.6 * tempC)) * 0.0001).

Here is the robust C++ implementation using the Arduino pulseIn() function, complete with a timeout to prevent your code from hanging if the sensor misses an echo.

// Pin Definitions
const int trigPin = 5;
const int echoPin = 18; // Use voltage divider if on ESP32

// Timeout: 25000µs = ~4.25 meters (Max range of HC-SR04 is 4m)
const long timeout = 25000; 

void setup() {
  Serial.begin(115200);
  pinMode(trigPin, OUTPUT);
  pinMode(echoPin, INPUT);
}

void loop() {
  // Clear the trigger pin
  digitalWrite(trigPin, LOW);
  delayMicroseconds(2);
  
  // Trigger the 8-pulse burst
  digitalWrite(trigPin, HIGH);
  delayMicroseconds(10);
  digitalWrite(trigPin, LOW);
  
  // Read the echo pin, returns 0 if timeout is reached
  long duration = pulseIn(echoPin, HIGH, timeout);
  
  // Raw-to-Unit Math
  float distance_cm = duration / 58.3;
  
  if (duration == 0) {
    Serial.println("Error: Timeout / Out of Range");
  } else {
    Serial.print("Distance: ");
    Serial.print(distance_cm);
    Serial.println(" cm");
  }
  
  delay(60); // Wait 60ms between readings to avoid acoustic cross-talk
}

Real-World Interference and Troubleshooting

The HC-SR04 is incredibly reliable on a clean workbench, but real-world environments introduce acoustic and electrical interference that can skew your readings. Here is what actually causes erratic data in the field:

  • Acoustic Cross-Talk: If you mount multiple HC-SR04 sensors on the same robot or tank level monitor, they will trigger each other's receivers. Fix: Never trigger them simultaneously. Fire them sequentially in software, leaving at least 60ms between each trigger to allow the 4-meter echo window to expire.
  • Soft and Angled Targets: Ultrasonic waves bounce off hard, flat surfaces like walls and water. They are absorbed by soft fabrics (like clothing or acoustic foam) and deflected away from the receiver by surfaces angled greater than 15 degrees. Fix: Do not use the HC-SR04 for detecting people or soft goods; switch to a 24GHz mmWave radar sensor like the RCWL-0516 or LD2410.
  • Electrical Noise on Long Runs: Running jumper wires longer than 30cm turns them into antennas that pick up EMI from nearby motors or switching power supplies, causing false triggers. Fix: Keep wires under 15cm, use shielded cable for longer runs, and add a 10kΩ pull-down resistor on the Echo pin to keep the line firmly LOW when idle.

HC-SR04 Interfacing FAQ

Why is my ultrasonic sensor HC-SR04 reading 0 or stuck at maximum distance?

A constant reading of 0 (or the exact timeout value) usually means the Echo pin never went HIGH. First, verify your wiring—specifically, ensure the Trig pin is actually receiving the 10µs HIGH pulse. Second, if using an ESP32 or Pi Pico, check your voltage divider; if the resistors are swapped, the 3.3V GPIO might not be registering the incoming 5V logic threshold correctly. Finally, ensure the sensor has a clear line of sight; if it is pointed at a sound-absorbing curtain, the echo will never return.

Can I power the ultrasonic sensor HC-SR04 directly from a 3.3V microcontroller pin?

No. While the logic inputs (Trig) will accept 3.3V, the HC-SR04 requires a minimum of 4.5V on the VCC pin to generate enough acoustic pressure from the piezoelectric transmitter. Powering it from a 3.3V rail will result in a maximum range of only a few centimeters, if it triggers at all. Always power VCC from a 5V source, even when using 3.3V logic microcontrollers.

How do I wire multiple HC-SR04 sensors to one Arduino without cross-talk?

You can share the same 5V and GND rails for all sensors, but each sensor needs its own dedicated Echo pin. You can technically share a single Trig pin across multiple sensors by wiring them in parallel, but this forces all sensors to fire at the exact same millisecond, guaranteeing severe acoustic cross-talk. The best practice is to use individual Trig and Echo pins for each module, and poll them sequentially in your loop() with a 60ms delay between each reading.

What is the actual blind spot and maximum reliable range of the HC-SR04?

The HC-SR04 has a physical blind spot of about 2 centimeters. If an object is closer than 2cm, the Echo pin may stay HIGH indefinitely or return garbage data because the receiver transducer is still ringing from the transmitter's burst. The theoretical maximum range is 400cm (4 meters), but in standard room-temperature air, reliable, repeatable readings top out around 300cm to 350cm. Beyond that, signal attenuation and beam dispersion cause frequent timeouts.