HC-SR04 Ultrasonic Sensor Arduino Tutorial: Wiring + Code

HC-SR04 ultrasonic sensor on a breadboard emitting a cone of sound waves toward a wall and receiving the echo back

To use an HC-SR04 with an Arduino, power it from 5V, send a 10 µs HIGH pulse on TRIG, then time how long ECHO stays HIGH with pulseIn(). Distance in centimetres = that microsecond figure ÷ 58. The divide-by-two for the sound’s round trip is already baked into the 58, which is why the number looks arbitrary at first glance.

What do the HC-SR04’s four pins do?

The HC-SR04 presents four pins in a fixed order — VCC, TRIG, ECHO, GND — silkscreened next to the header — on the front of some batches, the back of others. VCC and GND are power; TRIG is an input you drive; ECHO is an output the module drives back at you.

The HC-SR04 is a 5V part: its datasheet gives a working voltage of DC 5V and a working current of 15 mA. That is a light load, easily supplied by an Uno’s own 5V rail — unlike the servos in our 180° vs 270° vs 360° servo guide. That 5V figure starts to matter the moment you leave the Uno, as the ESP32 section below explains.

How do I wire an HC-SR04 to an Arduino Uno?

The HC-SR04 needs exactly four male-female jumper wires to reach an Uno. TRIG and ECHO can go to any two digital pins — 9 and 10 below are just a convention.

HC-SR04 pin Arduino Uno pin Note
VCC 5V Must be 5V — the 3.3V pin will not reliably run a classic HC-SR04
TRIG D9 (any digital pin) OUTPUT — you pulse this
ECHO D10 (any digital pin) INPUT — the module drives this HIGH
GND GND Shared ground is not optional

Swapping TRIG and ECHO gives you zero readings until you swap back. It is usually survivable but not harmless in principle: your Arduino’s TRIG output ends up wired to the module’s ECHO output, and two outputs push against each other whenever they disagree.

HC-SR04 ultrasonic sensor wired to an Arduino Uno on a breadboard with four jumper wires: VCC to 5V, TRIG to pin 9, ECHO to pin 10, GND to GND
Four wires, no more: VCC to 5V, TRIG to D9, ECHO to D10, GND to GND. Pin order never changes.

What is the basic HC-SR04 Arduino code?

The HC-SR04 needs no library — three digitalWrite() calls plus one pulseIn() is the whole driver. Upload it, open Serial Monitor at 9600 baud, and wave your hand at the sensor.

// HC-SR04 basic distance reader - Arduino Uno
// TRIG on pin 9, ECHO on pin 10.

const int TRIG_PIN = 9;
const int ECHO_PIN = 10;

void setup() {
  Serial.begin(9600);
  pinMode(TRIG_PIN, OUTPUT);
  pinMode(ECHO_PIN, INPUT);
}

void loop() {
  // 1. Start from a clean LOW, then send the 10 us trigger pulse.
  digitalWrite(TRIG_PIN, LOW);
  delayMicroseconds(2);
  digitalWrite(TRIG_PIN, HIGH);
  delayMicroseconds(10);
  digitalWrite(TRIG_PIN, LOW);

  // 2. Measure how long ECHO stays HIGH, in microseconds.
  //    30000 us cap = give up past about 5 m of target distance
  //    (30000 / 58 = 517 cm; the sound itself covers ~10 m out and back).
  //    It also sits under the ~38 ms pulse the module emits when it sees nothing.
  long duration = pulseIn(ECHO_PIN, HIGH, 30000);

  // 3. Convert. 58 = round-trip microseconds per centimetre.
  if (duration == 0) {
    Serial.println("No echo - out of range, or nothing reflected back");
  } else {
    float distanceCm = duration / 58.0;
    Serial.print(distanceCm);
    Serial.println(" cm");
  }

  delay(100);   // datasheet asks for at least 60 ms between pings
}

Why divide by 2, and where does the 58 come from?

The HC-SR04’s ECHO pulse measures a round trip, and that single fact explains both constants. When TRIG gets its 10 µs pulse, the module fires eight cycles of 40 kHz ultrasound, then holds ECHO HIGH for exactly as long as the burst takes to reach your target and return.

Sound travels at roughly 343 m/s in room-temperature air, or about 0.0343 cm per microsecond. The pulse covers the distance twice, so distance = (time × 0.0343) ÷ 2 — that is time ÷ 58.3, which the datasheet rounds to ÷ 58. (The datasheet also prints 340 m/s beside that ÷ 58, which would give ÷ 58.8 — a small inconsistency of its own.) Many sketches instead write duration / 29.1 / 2, which is ÷ 58.2. All of these land within 1% of each other, and air temperature moves the speed of sound further than that.

Timing diagram showing a 10 microsecond trigger pulse, an eight-cycle 40 kHz ultrasonic burst travelling to a wall and back, and the resulting echo pulse whose width equals the round-trip time
The 10 µs trigger fires eight 40 kHz cycles; ECHO stays HIGH for the whole out-and-back journey — hence the ÷2.

Prefer to see the whole cycle on video? This walkthrough covers it end to end:

What is the HC-SR04’s real range, and what makes readings fail?

The HC-SR04 is specified for 2 cm to 400 cm, with ranging accuracy reaching 3 mm and an effectual measuring angle under 15°. That 15° cone is the spec beginners never account for.

Spec Value What it means for your build
Range 2 cm – 400 cm Below 2 cm is a blind zone: the 8-cycle 40 kHz burst takes 200 µs to transmit, but a 2 cm round trip takes only about 116 µs, so the echo gets back before the module has finished sending
Accuracy up to 3 mm Best case: a flat hard target, square on
Measuring angle under 15° A cone, not a laser — it sees the nearest thing in the cone
Working voltage / current 5V DC / 15 mA Safe on an Uno’s 5V pin, unlike a servo
Burst frequency 40 kHz, 8 cycles Inaudible; nearby sensors cross-talk
Measurement cycle 60 ms minimum Ping sooner and the previous burst has not faded, so its lingering echo lands in the next measurement
Module size 45 × 20 × 15 mm Size the enclosure cut-out around both transducer cans

Four target types defeat the sensor regardless of your code. Soft or fibrous surfaces — cloth, foam, a stuffed toy — absorb the burst instead of reflecting it. Angled surfaces bounce it sideways like light off a mirror.

Objects smaller than the beam cone get missed entirely, and anything under 2 cm sits in the blind zone. Before you blame the sketch, check what you are pointing the sensor at.

Comparison of ultrasonic reflection: a flat hard wall reflecting the sound burst straight back versus foam absorbing it and an angled panel deflecting it sideways
Flat and hard reflects cleanly; soft absorbs; angled deflects away. The target matters more than the code.

How do I stop random 0 readings and jitter?

When the HC-SR04 finds nothing, it does not leave ECHO low — the datasheet says the pin gives a 38 ms high pulse instead. Read that with no timeout and you get about 38000 µs, which converts to a bogus 655 cm rather than an obvious error. The 30000 µs timeout in the sketch above sits just under that 38 ms, so a no-obstacle ping comes back as a clean 0. The one-second freeze people complain about is a different failure: when ECHO never rises at all — a loose wire, no shared ground, a dead module — pulseIn() blocks for its full one-second default. Either way, pass an explicit timeout.

For jitter, take several readings and use the middle one. A median rejects wild values far better than an average, because one bad reading cannot drag the result.

// Median of 5 readings - rejects the occasional wild value.
// Uses the TRIG_PIN / ECHO_PIN constants from the sketch above.

long readOnce() {
  digitalWrite(TRIG_PIN, LOW);
  delayMicroseconds(2);
  digitalWrite(TRIG_PIN, HIGH);
  delayMicroseconds(10);
  digitalWrite(TRIG_PIN, LOW);
  return pulseIn(ECHO_PIN, HIGH, 30000);
}

float readMedianCm() {
  long s[5];
  for (int i = 0; i < 5; i++) {
    s[i] = readOnce();
    delay(60);                 // respect the 60 ms measurement cycle
  }
  for (int i = 1; i < 5; i++) {          // insertion sort, 5 items
    long k = s[i];
    int j = i - 1;
    while (j >= 0 && s[j] > k) { s[j + 1] = s[j]; j--; }
    s[j + 1] = k;
  }
  if (s[2] == 0) return -1.0;  // middle reading timed out
  return s[2] / 58.0;
}

The NewPing library does all this for you: it sets a timeout automatically, avoids pulseIn() entirely, and ships a ping_median() helper. Plain pulseIn() is fine for learning and for one sensor; reach for NewPing when you run several sensors or cannot afford a blocking loop.

Can I use an HC-SR04 with an ESP32 or another 3.3V board?

A 3.3V trigger usually works — the datasheet calls TRIG a TTL input, and 3.3V clears a TTL threshold — though it is marginal on modules whose on-board microcontroller uses 5V CMOS input levels. ECHO is the real problem: it drives back a full 5V, and Espressif rates ESP32 GPIOs to 3.6V, telling you to add a voltage divider above that. Wire ECHO straight to an ESP32 and you are running the pin out of spec.

The fix is a two-resistor divider on ECHO only: 1 kΩ from ECHO to the GPIO, 2 kΩ from that same node to GND. That drops 5V to 5 × (2 ÷ 3) = 3.33V. TRIG connects directly, no divider needed. Alternatively, buy the HC-SR04P variant, which runs on 3V–5.5V; the classic HC-SR04 is a 5V part. ESP32 not showing up at all? That is a separate problem — see our guide on fixing an ESP32 that is not detected.

Common mistakes we see from real customers

The HC-SR04 draws more questions from school RBT and final-year-project builders than almost any other sensor we sell, and the same handful of failures come back week after week. None of them are sensor defects.

The most common one is a module whose pin labels are faded, missing or barely legible — the tiny silkscreen beside the header does not survive much handling. The order is always VCC, TRIG, ECHO, GND, so read the pins off the table above instead of guessing, and rule that out before deciding the module is faulty.

Another regular is readings that come out exactly double. That is the missing ÷2 — divide by 58, not 29. Close behind is a sketch that seems to hang or stutter, which is almost always pulseIn() called with no timeout argument, blocking for its full one-second default whenever ECHO never rises at all — a loose wire, a missing shared ground or a dead module.

Then there is the 15° cone. Builders aim at a target and forget that a table edge, a wall or their own enclosure sits inside the beam, so the sensor faithfully reports that nearer object instead. Mount the module looking into clear air.

Finally, ground. A sensor fed from one supply and signalling into an Arduino with no shared GND produces readings that look like pure noise — tie the grounds together. Our LM2596 buck converter guide covers building a clean 5V rail.

FAQ

How far can an HC-SR04 measure?

2 cm to 400 cm, with accuracy reaching about 3 mm in good conditions. Under 2 cm is a blind zone — the reading stops tracking and sticks near the module’s floor — and beyond roughly 4 m the echo is too weak to detect.

Why does my HC-SR04 always read 0 or the same number?

A constant 0 means pulseIn() timed out — usually TRIG and ECHO swapped, no shared GND, or the target absorbs sound. A value stuck near 655 cm is the module’s documented “no obstacle” output, a 38 ms ECHO pulse, so it is seeing nothing at all. A value that never changes can also mean you are pinging faster than the 60 ms measurement cycle allows, so each read still catches the previous burst’s echo.

Do I need a library to use the HC-SR04?

No. Three digitalWrite() calls plus one pulseIn() is the entire driver. NewPing is worth adding when you run several sensors or need non-blocking timing, but it is optional.

Can I connect an HC-SR04 directly to an ESP32?

TRIG yes, ECHO no. ECHO outputs 5V and ESP32 GPIOs are not 5V tolerant, so put a 1 kΩ / 2 kΩ divider on ECHO, or use the 3V–5.5V HC-SR04P variant instead.

Why is my measured distance exactly double the real one?

You divided by 29 instead of 58. The ECHO pulse covers the sound’s full out-and-back journey, so the round trip has to be halved — divide the microseconds by 58 to get centimetres.

Last updated July 2026. Stuck? Chat with us on WhatsApp.

Leave a Reply

Your email address will not be published. Required fields are marked *