Measure Distance with HC-SR04

Draft. This tutorial has not been checked against the physical product yet. Confirm wiring, pin assignments and voltage limits against the product documentation before building it.
DifficultyBeginner
CategorySensors
Supported boardsArduino Uno R3 and compatible ATmega328P boards
Time needed50 minutes
PrerequisitesSeeing What Your Program Is Doing
Tested boardNot yet verified
Tested onNot yet verified
Arduino IDE versionNot yet verified
LibraryNot yet verified

What it does

The HC-SR04 measures distance by timing an echo. It sends a short burst of ultrasound, too high to hear, and reports how long the reflection took to come back. Sound travels at a known speed, so that time converts directly into a distance.

It is the cheapest reliable way to sense “how far away is that”, and it turns up in parking sensors, water tank level monitors, obstacle-avoiding robots and automatic bins. It is also a good introduction to a sensor that measures time rather than voltage, which is a different technique from the analog sensors.

Before you begin

Required parts and tools

  • An Arduino Uno or compatible board, already set up.
  • One HC-SR04 module.
  • Four jumper wires, and a breadboard if your wires are male-to-male.
  • A ruler or tape measure, for checking the readings honestly.

How the measurement works

Four steps, and the sketch mirrors them exactly.

  1. You hold TRIG high for at least 10 microseconds.
  2. The module emits eight pulses at 40 kHz.
  3. When the echo returns, the module raises ECHO and holds it high for exactly as long as the round trip took.
  4. Your sketch measures that pulse width and converts it.

The conversion has to halve the time, because the sound travelled out and back:

distance in cm = (pulse duration in microseconds * 0.0343) / 2

which is the same as dividing by 58. The number 0.0343 is the speed of sound in centimetres per microsecond at ordinary room temperature.

That last detail matters more than it looks. The speed of sound rises with temperature, so a sensor calibrated in a cool room reads slightly short in the afternoon heat. For a parking sensor this is irrelevant; for a measuring instrument it is not.

What it can and cannot see

  • Useful range is roughly 2 cm to 400 cm. Below about 2 cm the echo returns before the module is listening.
  • The beam spreads at roughly 15 degrees, so it sees a cone, not a line. A narrow object at an angle may be missed entirely.
  • Hard flat surfaces facing the sensor work best. Cloth, foam and fur absorb the sound. A surface at a steep angle reflects it away like light off a mirror, and the module hears nothing.
  • Leave at least 60 milliseconds between measurements, so the previous echo has died away. A faster loop measures the tail of the last ping and reports nonsense.

Voltage, and a warning for 3.3 V boards

The HC-SR04 runs from 5 V, and its ECHO pin outputs 5 V. That is correct for an Uno.

On an ESP32, ESP8266 or any other 3.3 V board, connecting ECHO directly can damage the input. It needs a voltage divider or a level converter. This tutorial is written for the Uno, where no such step is required.

Safety notes

The sound is inaudible and harmless at these levels, and the circuit is low voltage. Wire it with the USB cable unplugged as usual.

Pin reference

Pin or label Connect to Purpose Notes
VCC Uno 5V Power 5 V, not 3.3 V
TRIG Uno D9 Start a measurement Needs a 10 microsecond pulse
ECHO Uno D10 Returns the timing 5 V output, safe on an Uno only
GND Uno GND Common ground

TRIG and ECHO are ordinary digital pins and can be moved, as long as the sketch agrees.

HC-SR04 wired to an Arduino Uno, VCC to 5V, TRIG to pin 9, ECHO to pin 10 and GND to ground
Four wires. The two in the middle are the ones people swap.

Wiring steps

  1. Unplug the USB cable.
  2. Connect GND to a GND pin on the Uno.
  3. Connect VCC to 5V.
  4. Connect TRIG to digital pin 9.
  5. Connect ECHO to digital pin 10.
  6. Check TRIG and ECHO are not swapped. Swapped, the module appears completely dead, which sends people looking for a fault that is not there.
  7. Stand the module so it faces a clear space, with nothing within a few centimetres.
  8. Plug the USB cable back in.

Software and libraries

No library is required. pulseIn() is part of the Arduino core and does the measuring.

Libraries such as NewPing exist and add convenience, but the point of this tutorial is to see what the module is actually doing. Once that is clear, a library is a reasonable choice.

Minimal working example

// Siratek: measuring distance with an HC-SR04.

const int trigPin = 9;
const int echoPin = 10;

// Give up after this long. 30000 us is roughly 5 m, beyond the module's range.
const unsigned long echoTimeout = 30000;

void setup() {
  pinMode(trigPin, OUTPUT);
  pinMode(echoPin, INPUT);
  digitalWrite(trigPin, LOW);   // Make sure we start from a known state.

  Serial.begin(9600);
  Serial.println("Siratek: distance sensor starting.");
}

void loop() {
  // A clean 10 microsecond trigger pulse.
  digitalWrite(trigPin, LOW);
  delayMicroseconds(2);
  digitalWrite(trigPin, HIGH);
  delayMicroseconds(10);
  digitalWrite(trigPin, LOW);

  // Measure how long ECHO stays high. Returns 0 if nothing comes back.
  unsigned long duration = pulseIn(echoPin, HIGH, echoTimeout);

  if (duration == 0) {
    Serial.println("no echo");
  } else {
    float cm = (duration * 0.0343) / 2.0;
    Serial.print("distance = ");
    Serial.print(cm, 1);
    Serial.println(" cm");
  }

  delay(100);   // At least 60 ms between measurements.
}

The timeout is the part people leave out. Without it, pulseIn() waits about a second before giving up, and a sensor pointed at open space makes the whole program crawl.

Averaging, because single readings lie

One measurement will occasionally be wrong. Taking the middle of three readings throws out a single bad one without smearing genuine movement the way an average does.

float measureOnce() {
  digitalWrite(trigPin, LOW);
  delayMicroseconds(2);
  digitalWrite(trigPin, HIGH);
  delayMicroseconds(10);
  digitalWrite(trigPin, LOW);

  unsigned long duration = pulseIn(echoPin, HIGH, echoTimeout);
  if (duration == 0) {
    return -1.0;              // No echo.
  }
  return (duration * 0.0343) / 2.0;
}

Call that three times with a 60 millisecond gap, discard any that return −1, and use the middle of what is left.

Expected result

Point the sensor at a wall and the Serial Monitor at 9600 baud should report a distance that matches a tape measure within a centimetre or so:

Siratek: distance sensor starting.
distance = 24.7 cm
distance = 24.6 cm
distance = 24.8 cm

Move your hand towards it and the number should fall smoothly.

Check it against a ruler at 10 cm, 50 cm and 100 cm before trusting it in a project. If the readings are consistently out by the same proportion at every distance, the conversion constant is the thing to adjust, not the wiring.

Pointed at open space with nothing in range, no echo is the correct output, not a failure.

Troubleshooting

Symptom Likely cause Check or fix
Always no echo TRIG and ECHO swapped Swap them, or swap the pin numbers
Always no echo, wiring confirmed Nothing within range, or an absorbent target Point it at a hard flat wall
Always 0 or a tiny value Object closer than about 2 cm Move it further away
Readings jump wildly Measuring faster than every 60 ms Increase the delay
Readings drift slowly all day Room temperature changing Expected; compensate only if accuracy matters
Works on a wall, not on a person Clothing absorbs ultrasound Expected behaviour of this sensor
Misses a narrow object The beam is a cone, not a line Aim more carefully, or use an infrared sensor
Whole program runs slowly pulseIn() without a timeout Add the timeout argument
Module dead on a 3.3 V board ECHO overdriving the input Use a divider or level converter
Readings halved or doubled Forgot to divide by two, or divided twice Check the conversion line

Next steps

  • Easier first: Seeing What Your Program Is Doing, since this sensor is read entirely through the Serial Monitor.
  • Related product: the waterproof JSN-SR04T, which works the same way outdoors.
  • Next project: sound the passive buzzer faster as something gets closer, which is a parking sensor in about twenty lines.

Sources and attribution

All text, wiring guidance and code in this tutorial are original Siratek work.

  • HC-SR04 module documentation, for the trigger pulse width, the 40 kHz burst and the recommended gap between measurements.
  • The speed of sound figure is the standard value at ordinary room temperature.
  • Arduino core documentation for pulseIn() and delayMicroseconds().

Stated range and beam angle vary between suppliers of this module and should be confirmed against the part in hand. No third-party tutorial text, diagrams, photographs or code has been reproduced here.

Product used in this tutorial: Ultrasonic HC-SR04 Sensor
Shop
Sidebar
0 Wishlist
0 Cart