What is the HC-SR04?
The HC-SR04 is a low-cost ultrasonic distance sensor capable of measuring distances from 2 cm to 400 cm with an accuracy of about 3 mm. It works by sending out a burst of ultrasonic sound (40 kHz) and measuring how long it takes for the echo to return — the same principle bats use to navigate in the dark.
It runs on 5V, works directly with Arduino, and costs under ₹50 / $1. That makes it one of the most popular sensors in the maker community.
How It Works
- Arduino sends a 10µs HIGH pulse to the TRIG pin
- The sensor fires 8 ultrasonic pulses at 40 kHz
- Those pulses bounce off the nearest object
- The ECHO pin goes HIGH for the duration it took the echo to return
- Arduino measures that duration and converts it to distance
Distance Formula
Distance (cm) = Duration (µs) / 58
Distance (inches) = Duration (µs) / 148
This comes from the speed of sound (~343 m/s at room temperature) divided by 2 (round trip).
Components Required
| Component | Quantity |
|---|---|
| Arduino Uno | 1 |
| HC-SR04 Ultrasonic Sensor | 1 |
| Jumper wires | 4 |
| Breadboard | 1 |
| 16x2 LCD (optional) | 1 |
| 10kΩ potentiometer (for LCD) | 1 |
HC-SR04 Pin Diagram
The sensor has 4 pins:
| Pin | Label | Description |
|---|---|---|
| 1 | VCC | Power — connect to Arduino 5V |
| 2 | TRIG | Trigger input — connect to any digital pin |
| 3 | ECHO | Echo output — connect to any digital pin |
| 4 | GND | Ground — connect to Arduino GND |
Note: The ECHO pin outputs 5V logic. On 3.3V boards like ESP32 or Raspberry Pi, use a voltage divider (1kΩ + 2kΩ resistors) on the ECHO line to avoid damaging the GPIO.
Wiring — Arduino Uno
| HC-SR04 Pin | Arduino Pin |
|---|---|
| VCC | 5V |
| GND | GND |
| TRIG | Digital Pin 9 |
| ECHO | Digital Pin 10 |
Connect VCC to the Arduino's 5V rail and GND to GND. TRIG goes to D9 and ECHO to D10. That's it — no resistors needed for Arduino Uno.
Basic Code — Serial Monitor Output
This sketch measures distance every 500ms and prints it to the Serial Monitor.
// HC-SR04 Ultrasonic Distance Sensor
// Prints distance to Serial Monitor
const int TRIG_PIN = 9;
const int ECHO_PIN = 10;
void setup() {
Serial.begin(9600);
pinMode(TRIG_PIN, OUTPUT);
pinMode(ECHO_PIN, INPUT);
Serial.println("HC-SR04 Distance Meter");
}
void loop() {
long duration;
float distanceCm;
float distanceInch;
// Clear the TRIG pin
digitalWrite(TRIG_PIN, LOW);
delayMicroseconds(2);
// Send 10µs HIGH pulse to TRIG
digitalWrite(TRIG_PIN, HIGH);
delayMicroseconds(10);
digitalWrite(TRIG_PIN, LOW);
// Read ECHO pulse duration (in microseconds)
duration = pulseIn(ECHO_PIN, HIGH);
// Convert to distance
distanceCm = duration / 58.0;
distanceInch = duration / 148.0;
// Print results
Serial.print("Distance: ");
Serial.print(distanceCm, 1);
Serial.print(" cm / ");
Serial.print(distanceInch, 1);
Serial.println(" inches");
delay(500);
}
Upload this sketch, open the Serial Monitor at 9600 baud, and bring your hand close to the sensor. You'll see the distance update in real time.
Code with 16x2 LCD Display
Display the distance directly on an LCD — no computer needed.
Additional Wiring for LCD
| LCD Pin | Arduino Pin |
|---|---|
| VSS | GND |
| VDD | 5V |
| V0 | Potentiometer wiper |
| RS | Digital Pin 12 |
| RW | GND |
| E | Digital Pin 11 |
| D4 | Digital Pin 5 |
| D5 | Digital Pin 4 |
| D6 | Digital Pin 3 |
| D7 | Digital Pin 2 |
| A (backlight +) | 5V |
| K (backlight −) | GND |
Code
// HC-SR04 + 16x2 LCD Distance Meter
#include <LiquidCrystal.h>
// LCD pins: RS, E, D4, D5, D6, D7
LiquidCrystal lcd(12, 11, 5, 4, 3, 2);
const int TRIG_PIN = 9;
const int ECHO_PIN = 10;
void setup() {
lcd.begin(16, 2);
pinMode(TRIG_PIN, OUTPUT);
pinMode(ECHO_PIN, INPUT);
lcd.setCursor(0, 0);
lcd.print(" SolderHub.com ");
lcd.setCursor(0, 1);
lcd.print(" Distance Meter ");
delay(2000);
lcd.clear();
}
void loop() {
long duration;
float distanceCm;
// Trigger pulse
digitalWrite(TRIG_PIN, LOW);
delayMicroseconds(2);
digitalWrite(TRIG_PIN, HIGH);
delayMicroseconds(10);
digitalWrite(TRIG_PIN, LOW);
// Measure echo
duration = pulseIn(ECHO_PIN, HIGH);
distanceCm = duration / 58.0;
// Display
lcd.setCursor(0, 0);
lcd.print("Distance: ");
lcd.setCursor(0, 1);
if (distanceCm > 400 || distanceCm <= 0) {
lcd.print("Out of range ");
} else {
lcd.print(distanceCm, 1);
lcd.print(" cm ");
}
delay(300);
}
Adding a Buzzer Alert (Parking Sensor Style)
Add an active buzzer that beeps faster as an object gets closer — exactly like a car parking sensor.
Extra Component
| Component | Quantity |
|---|---|
| Active buzzer | 1 |
Connect buzzer + to Digital Pin 8, − to GND.
#include <LiquidCrystal.h>
LiquidCrystal lcd(12, 11, 5, 4, 3, 2);
const int TRIG_PIN = 9;
const int ECHO_PIN = 10;
const int BUZZER_PIN = 8;
void setup() {
lcd.begin(16, 2);
pinMode(TRIG_PIN, OUTPUT);
pinMode(ECHO_PIN, INPUT);
pinMode(BUZZER_PIN, OUTPUT);
}
void loop() {
// Trigger
digitalWrite(TRIG_PIN, LOW);
delayMicroseconds(2);
digitalWrite(TRIG_PIN, HIGH);
delayMicroseconds(10);
digitalWrite(TRIG_PIN, LOW);
long duration = pulseIn(ECHO_PIN, HIGH);
float dist = duration / 58.0;
// LCD display
lcd.setCursor(0, 0);
lcd.print("Distance: ");
lcd.setCursor(0, 1);
if (dist > 400 || dist <= 0) {
lcd.print("Out of range ");
noTone(BUZZER_PIN);
} else {
lcd.print(dist, 1);
lcd.print(" cm ");
// Buzzer: beep interval shrinks as object gets closer
if (dist < 10) {
// Continuous beep — very close
tone(BUZZER_PIN, 1000);
delay(100);
} else if (dist < 20) {
tone(BUZZER_PIN, 1000, 80);
delay(150);
} else if (dist < 50) {
tone(BUZZER_PIN, 800, 60);
delay(300);
} else {
noTone(BUZZER_PIN);
delay(100);
}
}
}
Understanding pulseIn()
pulseIn(pin, HIGH) waits for the ECHO pin to go HIGH, starts a timer, then stops when it goes LOW — returning the duration in microseconds.
Default timeout is 1 second. If no echo returns within 1 second, pulseIn() returns 0. You can set a custom timeout:
// Timeout after 30ms (good for max 400cm range)
duration = pulseIn(ECHO_PIN, HIGH, 30000);
This prevents the code from hanging when no object is in range.
Improving Accuracy with Averaging
A single reading can be noisy. Take multiple readings and average them:
float getDistance() {
long total = 0;
for (int i = 0; i < 5; i++) {
digitalWrite(TRIG_PIN, LOW);
delayMicroseconds(2);
digitalWrite(TRIG_PIN, HIGH);
delayMicroseconds(10);
digitalWrite(TRIG_PIN, LOW);
total += pulseIn(ECHO_PIN, HIGH, 30000);
delay(10);
}
return (total / 5.0) / 58.0;
}
Call getDistance() instead of doing the pulseIn inline — smoother, more stable readings.
Troubleshooting
| Problem | Likely Cause | Fix |
|---|---|---|
| Always reads 0 | TRIG/ECHO pins swapped | Double-check wiring |
| Reads out of range constantly | Object too close or too far | Stay between 2–400 cm |
| Very noisy readings | Power supply noise | Add 100µF capacitor across VCC and GND |
| Works but jumps around | No averaging | Use the 5-reading average above |
| ESP32 shows garbage | 5V ECHO to 3.3V pin | Add voltage divider on ECHO |
pulseIn freezes sketch | No timeout set | Add 30000 timeout parameter |
Extensions to Try
- OLED display — use a 0.96" I2C OLED instead of LCD for a cleaner look
- Multiple sensors — point two HC-SR04s left and right for wider detection
- Water level monitor — mount above a tank pointing down
- Blind spot detector — mount on a vehicle bumper with a buzzer
- Gesture control — detect hand distance to control LEDs or servo position
Related Projects on SolderHub
- HC-SR04 Parking Alert with Arduino, LCD & Buzzer
- Arduino Mega 2560 Pinout — Complete Reference
- ESP32 Web Server — Control LED from Any Device
Published on SolderHub — the electronics platform with live Wokwi simulations, circuit diagrams, and production-ready code.




