MicroPython HC-SR04 on a Pico, timed by hand
MicroPython on a Raspberry Pi Pico: an HC-SR04 with TRIG on GP19 and ECHO on GP18, its echo timed with ticks_us, prints centimeters twice a second. The whole build, a Raspberry Pi Pico and 3 more parts, runs here in your browser on the firmware below; open it in the editor to change the wiring or the code and run it again.
Teaching? Assign it to your class in one click.
Intermediate About 20 minutes to build and run. Runs in your browser. Free, and no account needed.
The parts retail for about $7.10. On Mokxi they're free.
What you'll learn
- Timing a pulse by hand
- Turning echo time into distance
- A resistor divider for a 3.3 V input
- Timeouts that stop the loop from hanging
- Difficulty: Intermediate
- Time: About 20 minutes
- Board: Raspberry Pi Pico
- Parts: 5
How it works
The HC-SR04 sends out a burst of sound when TRIG (GP19) gets a short pulse. ECHO then goes high until the sound comes back. The sensor runs on 5 V, so ECHO goes through a 1k and 2k divider to bring it down to 3.3 V for GP18.
The MicroPython code sends a 20 microsecond pulse and times how long ECHO stays high with ticks_us. Sound goes there and back at 343 m/s, so the distance is the time times 0.0343 centimeters per microsecond, divided by 2. It prints a result twice a second.
Parts list
5 parts, plus the jumper wires. They retail for about $7.10; on Mokxi they're free.
- 1 × Raspberry Pi PicoIn Fight Stick Kit.
- 1 × Ultrasonic rangerIn Halloween Prop Kit.
- 1 × Resistor, 1k ΩIn Starter Kit, Claw Machine Kit, Halloween Prop Kit.
- 1 × Resistor, 2k ΩIn Starter Kit, Claw Machine Kit, Halloween Prop Kit.
- 1 × Power, 5 V
Kits that can build it
This starter kit has every part in the circuit. Each kit's page lists what else it can build.
Wiring table
Every connection, pin by pin, read from the circuit itself. Each row joins the pin on the left to the pin on the right, by a jumper wire or a breadboard strip.
| Part and pin | Connects to |
|---|---|
| Ultrasonic ranger pin TRIG | Raspberry Pi Pico pin GP19 |
| Resistor, 1k Ω pin 2 | Raspberry Pi Pico pin GP18 |
| Resistor, 2k Ω pin 1 | Raspberry Pi Pico pin GP18 |
| Ultrasonic ranger pin VCC | 5 V |
| Resistor, 1k Ω pin 1 | Ultrasonic ranger pin ECHO |
| Ultrasonic ranger pin GND | Ground |
| Resistor, 2k Ω pin 2 | Ground |
The code
The MicroPython code exactly as the editor opens it. Change a line there and press Run: the board runs it on the official MicroPython firmware, with nothing to compile.
# Ultrasonic ranging by hand: a 20 us pulse on TRIG (GP19), then time how
# long ECHO (GP18) stays high. Sound goes there and back at 343 m/s.
from machine import Pin
import time
trig = Pin(19, Pin.OUT, value=0)
echo = Pin(18, Pin.IN)
def distance_cm():
trig.value(1)
time.sleep_us(20) # 20 us: the datasheet asks for at least 10
trig.value(0)
start = time.ticks_us()
while echo.value() == 0: # wait for the echo to start
if time.ticks_diff(time.ticks_us(), start) > 30000:
return None
began = time.ticks_us()
while echo.value() == 1: # and time it
if time.ticks_diff(time.ticks_us(), began) > 30000:
return None
return time.ticks_diff(time.ticks_us(), began) * 0.0343 / 2
while True:
d = distance_cm()
print("distance: %.1f cm" % d if d is not None else "nothing in range")
time.sleep_ms(500)
Code walkthrough
The key functions of the sketch, and what each one does.
distance_cm main.py
Sets trig high for 20 us, then low. It waits for echo to go high, then times how long it stays high. If either wait lasts more than 30000 us, it returns None, which the main loop prints as "nothing in range". Otherwise it returns the time times 0.0343 divided by 2.
def distance_cm():
trig.value(1)
time.sleep_us(20) # 20 us: the datasheet asks for at least 10
trig.value(0)
start = time.ticks_us()
while echo.value() == 0: # wait for the echo to start
if time.ticks_diff(time.ticks_us(), start) > 30000:
return None
began = time.ticks_us()
while echo.value() == 1: # and time it
if time.ticks_diff(time.ticks_us(), began) > 30000:
return None
return time.ticks_diff(time.ticks_us(), began) * 0.0343 / 2Troubleshooting
The usual mistakes with this circuit, and the fix for each.
- It always says "nothing in range" This project
Check TRIG on GP19, ECHO through the divider to GP18, and 5 V on the sensor's VCC. - ECHO goes straight to the Pico This project
Keep the divider: 1k from ECHO to GP18, and 2k from GP18 to ground. - Distance reads zero, or never changes. Ultrasonic ranger
Power it from 5 V and share ground. Send a 10 microsecond HIGH pulse on TRIG, then time ECHO with pulseIn. Check TRIG and ECHO are not swapped. - Readings jump around or are wrong on a 3.3 V board. Ultrasonic ranger
ECHO outputs 5 V, so divide it down for 3.3 V boards. Aim at flat, hard surfaces. The range is about 2 cm to 4 m, and soft or angled targets bounce the sound away. - The LED is dim, burns out, or the pin reads wrong because the resistor value is off. Resistor
Read the color bands again. A 220 ohm to 330 ohm resistor suits a typical LED on 5 V. 10k is a common pull-up or pull-down value. Check with a multimeter if unsure. - A pull-up or pull-down resistor is missing, so the input floats and reads random values. Resistor
Tie the input to 5 V or ground through about 10k. Or use INPUT_PULLUP and wire the switch to ground. - The Pico does not show up as a drive, or the program will not load. Raspberry Pi Pico
Hold the BOOTSEL button while you plug in USB. A drive named RPI-RP2 appears. Then copy the UF2 file onto it. - A 5 V signal or sensor is wired to a GPIO pin. Raspberry Pi Pico
Pico GPIO pins are 3.3 V only and are not 5 V tolerant. Use a level shifter or a voltage divider for any 5 V output going into the board.
Change it and keep it
Open it in the editor, change the circuit or the code, and keep your version in a free account.