The Sensing Principle: How the HC-SR04 Actually Works

The HC-SR04 measures distance using acoustic time-of-flight (ToF). When triggered, the module's piezoelectric transmitter emits a burst of eight 40 kHz ultrasonic pulses into the air. These sound waves travel outward until they strike a physical object, scattering and reflecting back toward the module's receiver transducer.

The onboard logic circuit times the exact duration between the initial transmission and the arrival of the returning echo. By multiplying this elapsed time by the speed of sound in air and dividing by two to account for the round-trip journey, your microcontroller calculates the physical distance to the target. The module outputs this measurement strictly as a digital time-pulse, not an analog voltage.

Pinout, Wiring, and Power Requirements

The HC-SR04 operates natively at 5V. While it can be powered by a 5V supply, interfacing it directly with 3.3V microcontrollers like the ESP32 or Raspberry Pi Pico requires careful signal conditioning to prevent silicon damage.

HC-SR04 Pinout and Wiring Specifications
Pin Function Voltage Level Connection Notes
VCC Power Supply 5.0V DC Requires 5V. Do not power with 3.3V; the internal oscillator will fail to generate 40 kHz.
Trig Trigger Input TTL 5V / 3.3V Accepts a 10µs HIGH pulse to initiate measurement. 3.3V logic is sufficient to trigger it.
Echo Echo Output TTL 5V Outputs a 5V HIGH pulse. Must use a voltage divider when connecting to 3.3V GPIO pins.
GND Ground 0V Connect to the common ground of your microcontroller and power supply.
ESP32 Voltage Divider: To drop the 5V Echo signal down to a safe 3.3V for the ESP32, use a simple resistor divider. Connect a 1kΩ resistor between the Echo pin and the ESP32 GPIO, and a 2kΩ resistor between that same GPIO and GND. This yields approximately 3.33V, safely within the ESP32 GPIO absolute maximum ratings.

The Output Signal: Raw Math and Physical Units

The output of the HC-SR04 is a digital HIGH pulse on the Echo pin. The width of this pulse, measured in microseconds (µs), is directly proportional to the distance. There is no analog voltage scaling or I2C register reading involved; you are measuring time.

To convert the raw microsecond reading into physical units, we rely on the speed of sound. At 20°C in dry air, sound travels at approximately 343 meters per second, which translates to 0.0343 cm/µs. Because the sound travels to the object and back, we must divide the total travel time by two.

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

If your pulseIn() function returns 1166 µs, the distance is 1166 / 58.3 = 20.0 cm. This raw-to-unit math is universal for 40 kHz ultrasonic modules, regardless of the microcontroller you are using.

Interference, Calibration, and Edge Cases

Ultrasonic sensors are highly susceptible to environmental and geometric interference. Understanding these failure modes is critical for reliable deployments.

  • Acoustic Absorption and Scattering: Soft materials like clothing, foam, or thick carpets absorb 40 kHz sound waves, resulting in no echo. Similarly, objects angled more than 15 degrees away from the sensor's centerline will scatter the sound away from the receiver, causing false 'out of range' readings.
  • Temperature Drift: The speed of sound increases by about 0.6 m/s for every 1°C rise in temperature. If you deploy this sensor in a 35°C greenhouse, the speed of sound is ~352 m/s. The standard divisor of 58.3 will introduce a 2.5% error. For high-precision applications, read the ambient temperature and adjust your divisor dynamically (e.g., use 56.5 at 35°C).
  • Cross-Talk: If you wire multiple HC-SR04 modules to the same system and trigger them simultaneously, they will read each other's acoustic echoes. You must poll them sequentially in code, waiting for one to finish before triggering the next.
  • The 'Zero' Timeout Glitch: If the sensor receives no echo, the Echo pin may stay HIGH indefinitely, hanging your microcontroller. Always implement a strict timeout in your code (e.g., 25,000 µs, which corresponds to the sensor's maximum ~4-meter range).

Decision Tree: Which Ultrasonic Variant Should You Buy?

The market is flooded with HC-SR04 clones and variants. Use this decision path to select the exact part number for your specific environment.

Ultrasonic Sensor Selection Matrix
If your project requires... Then buy this exact module... Why?
Standard indoor use with a 5V Arduino Uno/Mega HC-SR04 (Standard) Cheapest option (~$1.50). Direct 5V wiring, no level shifting needed.
Indoor use with a 3.3V ESP32, Pico, or STM32 HC-SR04P (or RCWL-1601) The 'P' variant has an onboard 3.3V voltage regulator and logic level shifters. Eliminates the need for external breadboard resistors.
Outdoor use, liquid level sensing, or high humidity JSN-SR04T (Waterproof) Features a sealed, cabled piezoelectric transducer that can be submerged or exposed to rain. (~$4.00).
High-speed counting on a conveyor belt RCWL-1605 or mmWave radar Standard HC-SR04 has a 60ms measurement cycle limit. For sub-10ms response times, abandon ultrasonic for mmWave or ToF LiDAR.

Default Recommendation: If you are building a standard robotics or room-mapping project on an ESP32, buy the HC-SR04P. It saves you 15 minutes of soldering or breadboarding a voltage divider and eliminates the risk of accidentally frying a 3.3V GPIO pin.

Complete ESP32 and Arduino Implementation

Follow these steps to wire and program the sensor. This implementation uses a strict timeout to prevent the microcontroller from hanging when no echo is received.

Step 1: Hardware Wiring (ESP32 with Standard HC-SR04)

  1. Connect the HC-SR04 VCC to the ESP32 VIN (5V).
  2. Connect the HC-SR04 GND to the ESP32 GND.
  3. Connect the HC-SR04 Trig pin directly to ESP32 GPIO 5.
  4. Insert a 1kΩ resistor into the breadboard. Connect one end to the HC-SR04 Echo pin, and the other end to ESP32 GPIO 18.
  5. Insert a 2kΩ resistor into the breadboard. Connect one end to the junction of the 1kΩ resistor and GPIO 18, and the other end to the GND rail.

Step 2: Upload the Firmware

Copy the following C++ code into your Arduino IDE. This code handles the trigger pulse, measures the echo width with a timeout, and applies the raw-to-cm math. The physics constants referenced here align with standard acoustic engineering baselines for air at 20°C.

// Pin Definitions
const int trigPin = 5;
const int echoPin = 18;

// Physics Constants
const float SPEED_OF_SOUND_CM_PER_US = 0.0343; // At 20C dry air
const int MAX_DISTANCE_CM = 400;
const long MAX_TIMEOUT_US = (MAX_DISTANCE_CM * 2) / SPEED_OF_SOUND_CM_PER_US; // ~23323 us

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

void loop() {
  // 1. Clear the trigger pin
  digitalWrite(trigPin, LOW);
  delayMicroseconds(2);
  
  // 2. Send 10us HIGH pulse to trigger measurement
  digitalWrite(trigPin, HIGH);
  delayMicroseconds(10);
  digitalWrite(trigPin, LOW);
  
  // 3. Read the echo pin, enforcing a strict timeout
  long duration = pulseIn(echoPin, HIGH, MAX_TIMEOUT_US);
  
  // 4. Handle timeout / no-echo edge case
  if (duration == 0) {
    Serial.println("Error: Out of range or acoustic scattering.");
  } else {
    // 5. Raw to Unit Math
    float distance_cm = (duration * SPEED_OF_SOUND_CM_PER_US) / 2.0;
    
    Serial.print("Distance: ");
    Serial.print(distance_cm);
    Serial.println(" cm");
  }
  
  // Wait 60ms before next reading (HC-SR04 hardware limit)
  delay(60); 
}
Pro-Tip for Production Code: While pulseIn() is fine for basic scripts, it is a blocking function. If your project also needs to drive motors or read buttons simultaneously, replace this logic with the NewPing library or use ESP32 hardware timers with interrupt service routines (ISRs) to capture the Echo pin's rising and falling edges in the background.