Skip to content
Tutorial
12 min read
July 6, 2026

Interfacing HC-SR04 Ultrasonic Sensor with Arduino

Measure distance accurately using ultrasonic sound waves and an Arduino Uno — no complex math, no expensive hardware.

Interfacing HC-SR04 Ultrasonic Sensor with Arduino

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

  1. Arduino sends a 10µs HIGH pulse to the TRIG pin
  2. The sensor fires 8 ultrasonic pulses at 40 kHz
  3. Those pulses bounce off the nearest object
  4. The ECHO pin goes HIGH for the duration it took the echo to return
  5. 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

ComponentQuantity
Arduino Uno1
HC-SR04 Ultrasonic Sensor1
Jumper wires4
Breadboard1
16x2 LCD (optional)1
10kΩ potentiometer (for LCD)1

HC-SR04 Pin Diagram

The sensor has 4 pins:

PinLabelDescription
1VCCPower — connect to Arduino 5V
2TRIGTrigger input — connect to any digital pin
3ECHOEcho output — connect to any digital pin
4GNDGround — 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 PinArduino Pin
VCC5V
GNDGND
TRIGDigital Pin 9
ECHODigital 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 PinArduino Pin
VSSGND
VDD5V
V0Potentiometer wiper
RSDigital Pin 12
RWGND
EDigital Pin 11
D4Digital Pin 5
D5Digital Pin 4
D6Digital Pin 3
D7Digital 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

ComponentQuantity
Active buzzer1

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

ProblemLikely CauseFix
Always reads 0TRIG/ECHO pins swappedDouble-check wiring
Reads out of range constantlyObject too close or too farStay between 2–400 cm
Very noisy readingsPower supply noiseAdd 100µF capacitor across VCC and GND
Works but jumps aroundNo averagingUse the 5-reading average above
ESP32 shows garbage5V ECHO to 3.3V pinAdd voltage divider on ECHO
pulseIn freezes sketchNo timeout setAdd 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


Published on SolderHub — the electronics platform with live Wokwi simulations, circuit diagrams, and production-ready code.

Signal In

Built from what makers search for

The lookups piling up in SolderHub's search bar every day shape what we publish next — new component pages, board guides, and firmware notes, sent out as they go live.

By subscribing you agree to our Privacy Policy. Unsubscribe anytime.