MicroPython HC-SR04 distance, timed by hand, on an ESP32-DevKitC V4
MicroPython on an ESP32: a pulse on TRIG (GPIO 13) and the ECHO on GPIO 14 timed with ticks_us() give the distance in centimeters. The whole build, an ESP32-DevKitC V4 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 $13.10. On Mokxi they're free.
What you'll learn
- Timing a pulse by hand in MicroPython
- Converting echo time to distance
- A voltage divider to protect a 3.3 V pin
- Timeouts so the loop never hangs
- Difficulty: Intermediate
- Time: About 20 minutes
- Board: ESP32-DevKitC V4
- Parts: 5
How it works
The HC-SR04 sends a burst of sound when TRIG (GPIO 13) gets a short pulse. Then ECHO goes high and stays high until the sound bounces back. The sensor runs on 5 V, but the ESP32 pins want 3.3 V, so ECHO goes through a 1k and 2k resistor divider before reaching GPIO 14.
The MicroPython code sends a 20 microsecond pulse and times how long ECHO stays high. Sound travels at about 340 m/s and goes out and back, so the distance is half the time at that speed. It prints the result twice a second.
Parts list
5 parts, plus the jumper wires. They retail for about $13.10; on Mokxi they're free.
- 1 × ESP32-DevKitC V4
- 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
These 3 starter kits have 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 |
|---|---|
| Resistor, 1k Ω pin 2 | ESP32-DevKitC V4 pin 14 |
| Resistor, 2k Ω pin 1 | ESP32-DevKitC V4 pin 14 |
| Ultrasonic ranger pin TRIG | ESP32-DevKitC V4 pin 13 |
| 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, timed by hand: a short pulse on TRIG (GPIO 13),
# then time how long ECHO (GPIO 14) stays high. Sound goes out and
# back at 340 m/s, so the distance is half the time at that speed.
from machine import Pin
import time
trig = Pin(13, Pin.OUT, value=0)
echo = Pin(14, Pin.IN)
def distance_cm():
trig.value(1)
time.sleep_us(20) # the sensor needs 10 us or more
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
rose = time.ticks_us()
while echo.value() == 1: # and for it to end
if time.ticks_diff(time.ticks_us(), rose) > 30000:
return None
us = time.ticks_diff(time.ticks_us(), rose)
return us * 0.034 / 2
while True:
cm = distance_cm()
print("Distance: %.2f cm" % cm if cm is not None else "Out of range")
time.sleep_ms(500)
Code walkthrough
The key functions of the sketch, and what each one does.
distance_cm main.py
Pulses trig high for 20 us, then waits for echo to rise and records the time with time.ticks_us. It then waits for echo to fall and measures how long it stayed high. If either wait passes 30000 us it returns None. Otherwise it returns the time times 0.034, divided by 2.
def distance_cm():
trig.value(1)
time.sleep_us(20) # the sensor needs 10 us or more
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
rose = time.ticks_us()
while echo.value() == 1: # and for it to end
if time.ticks_diff(time.ticks_us(), rose) > 30000:
return None
us = time.ticks_diff(time.ticks_us(), rose)
return us * 0.034 / 2Troubleshooting
The usual mistakes with this circuit, and the fix for each.
- It always prints "Out of range" This project
Check TRIG on GPIO 13, ECHO through the divider to GPIO 14, and 5 V on the sensor's VCC. - Wrong distances This project
The divider must be 1k from ECHO to GPIO 14 and 2k from GPIO 14 to ground. Swapped values give the wrong voltage. - 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 board is not found, or upload fails with a connecting message. ESP32-DevKitC V4
Use a USB cable that carries data, not charge only. Pick the ESP32 Dev Module board. If it still fails, hold BOOT while uploading starts, then release. - Some pins never read right, or the board will not boot with a wire attached. ESP32-DevKitC V4
GPIO34 to 39 are input only and have no pull-ups. GPIO6 to 11 run the flash, so leave them alone. GPIO0, 2 and 12 are boot pins; avoid pulling them the wrong way at reset.
Change it and keep it
Open it in the editor, change the circuit or the code, and keep your version in a free account.