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.
| 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. |
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.
| 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)
- Connect the HC-SR04 VCC to the ESP32 VIN (5V).
- Connect the HC-SR04 GND to the ESP32 GND.
- Connect the HC-SR04 Trig pin directly to ESP32 GPIO 5.
- Insert a 1kΩ resistor into the breadboard. Connect one end to the HC-SR04 Echo pin, and the other end to ESP32 GPIO 18.
- 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);
}
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.






