The first reading
Four wires, no library, and about fifteen lines. The only decision in the whole build is which rail feeds the supply pin — and that decision quietly settles the level-shifting question everyone else spends a paragraph on.
Four wires
Pick your board and the wiring changes in exactly one place.
GND to GND, VCC to a rail, and the two signals to two ordinary pins. No resistors, no divider, no level shifter — and that is not a simplification for beginners, it is what the net list says. The supply pin and the signal pins go to the same chip with nothing between them, so they share a voltage, and the voltage is the one you picked.
Get that wrong in the safe direction — 3V3 on an Uno — and it usually still works, with a weaker burst. Get it wrong in the other direction — 5V on an ESP32 — and you have a 5 V signal going into a 3.3 V pin, which is the exact hazard the HC-SR04 needed a divider for, recreated on a board that did not need one.
The sketch
No library. In GPIO mode there is nothing to initialise and no protocol to speak, so a library would be fifteen lines wrapping fifteen lines. The sketch is under The code, below.
Two things in it are worth reading rather than copying.
The timeout. pulseIn takes a third argument, and it matters more here
than almost anywhere else: without it, Arduino waits a full second before
giving up. A sketch that also has to blink an LED or answer a network will
visibly stall every time the sensor sees nothing, and seeing nothing is normal.
25 000 µs is about four metres of round trip, which is past anything this
sensor can honestly report.
Zero is not a distance. When the timeout expires pulseIn returns 0, and 0
microseconds converts perfectly happily to 0.0 cm. That is how a sensor pointed
at a curtain reports an object pressed against its face. Check for it before
converting, and print something that is not a number.
What the 500 µs is about
Every HC-SR04 example ever written sends a 10 µs pulse on TRIG, and you will have seen that number. The library Lonely Binary's own documentation points at for this part holds TRIG high for 500 µs instead.
Both start a measurement. 500 µs is the figure the maintained code uses, so it is the one here — but if you are pasting in an old sketch, the 10 µs in it is not your bug.
Where to go from here
This is the sensor at its simplest and, for most projects, its best: nothing soldered, no bus to share, twenty readings a second. The remaining three modes exist for the cases where two pins is one too many, or the sensor is at the end of a long cable, and each is a bridge of solder away.
The code
Prints a distance about ten times a second, on a board straight out of the box with nothing soldered. Change the two pin numbers at the top to match your wiring and nothing else.
// The first reading: distance over four wires, in GPIO mode.
//
// Wiring, sensor to board. Count the sensor's pins from the SQUARE PAD,
// which is GND -- once it is in a breadboard the printed labels are face
// down and the square pad is the only marking left:
//
// TK50 GND -> board GND
// TK50 VCC -> board 3V3 on an ESP32, ESP32-S3 or Pico; 5V on an Uno
// TK50 ECHO -> Uno D10, ESP32 GPIO 19, ESP32-S3 GPIO 5, Pico GP3
// TK50 TRIG -> Uno D9, ESP32 GPIO 18, ESP32-S3 GPIO 4, Pico GP2
//
// Use the 3V3 rail on a 3.3 V board. There is no regulator and no level
// shifter on the TK50, so ECHO answers at whatever you fed VCC -- from
// 3V3 it is a 3.3 V signal and needs no divider.
//
// Arduino IDE: no library and no special Tools settings. Select your board
// and port, set the Serial Monitor to 115200, and upload.
//
// Both jumpers left open, which is how the board arrives. That is GPIO mode.
const int TRIG_PIN = 9; // Uno D9. ESP32: 18. ESP32-S3: 4. Pico: 2.
const int ECHO_PIN = 10; // Uno D10. ESP32: 19. ESP32-S3: 5. Pico: 3.
// 25 ms of round trip is about 4 m, which is past anything this sensor can
// honestly see. Without a timeout pulseIn waits a full second instead.
const unsigned long ECHO_TIMEOUT_US = 25000UL;
// cm per microsecond at 20 C. Warmer air is faster -- see "Warm air is
// fast air" for when that stops being a rounding error.
const float CM_PER_US = 0.0343;
void setup() {
Serial.begin(115200);
pinMode(TRIG_PIN, OUTPUT);
pinMode(ECHO_PIN, INPUT);
digitalWrite(TRIG_PIN, LOW);
}
void loop() {
// Start a measurement. The supported library holds TRIG high for 500 us;
// the 10 us every HC-SR04 example uses also works, and this is the
// maintained figure.
digitalWrite(TRIG_PIN, HIGH);
delayMicroseconds(500);
digitalWrite(TRIG_PIN, LOW);
// ECHO stays high for the round trip. Zero means the timeout expired.
unsigned long us = pulseIn(ECHO_PIN, HIGH, ECHO_TIMEOUT_US);
if (us == 0) {
// Not a distance. Absorbed, angled away, out of range, or nothing
// there at all -- and there is no way to tell which from here.
Serial.println("no echo");
} else {
// Halve it: the sound crossed the gap twice.
float cm = (us / 2.0) * CM_PER_US;
Serial.print(cm, 1);
Serial.println(" cm");
}
delay(100); // One measurement cycle is 50 ms. Let the last burst die.
}If it prints no echo every time, nothing came back — point it at a wall a metre away before assuming anything is broken, because a jumper or a curtain looks identical to a fault. If it prints a number that is twice what you measured, the divide-by-two went missing.
When it does not work
Point it at a flat wall about a metre away before debugging anything. Soft surfaces, angled surfaces and empty air all produce exactly this, and so does a sensor pointed at the ceiling. If a wall does not fix it, check GND and VCC, then that ECHO and TRIG are not swapped — swapping them gives silence rather than an error.
The divide-by-two is missing or has been applied to the wrong thing. The sound crosses the gap out and back, so halve the microseconds before converting. Multiplying first and halving after works too; doing neither does not.
Check which rail VCC is on. On a 3.3 V board it must be 3V3 — but note that the failure from using 5V is not silence, it is a 5 V echo into a 3.6 V pin, which may read fine for weeks and then not. Also check the pin numbers: the sketch's defaults are Uno pins.
The timeout is being hit. That is 25 ms of waiting every time nothing comes back, which is visible if your loop is doing anything else. It is still far better than the full second pulseIn waits with no timeout at all — which is what the vendor library does.
A GPIO measurement cycle is about 50 ms, so twenty a second is the ceiling and the delay(100) here is deliberately relaxed. Going faster means the previous burst is still echoing around the room when the next one goes out, and those late echoes arrive as short readings.
The table on the back of the board, and what the two pads are wired to.
The two solder bridges →Edit this page — content/books/ultrasonic-sensor/the-first-reading.mdx
Questions about this product
See what other owners have asked, and read their solutions.
Ultrasonic Distance Sensor
Loading discussions…
Discuss this article
Ask about this page. The answer stays here, on the page it belongs to, for whoever hits the same wall next.