Here are two solid Python options for the [HC-SR04](https://www.ampheo.com/product/hc-sr04-26835950) [ultrasonic sensor](https://www.onzuu.com/category/ultrasonic-receivers-transmitters) on [Raspberry Pi](https://www.ampheo.com/c/raspberry-pi/raspberry-pi-boards): a quick RPi.GPIO version and a more accurate pigpio version. ![IMG_20180803_003649](https://hackmd.io/_uploads/By5JdgIJWe.jpg) **Wiring (very important)** * VCC → 5V * GND → GND * TRIG → Pi GPIO (3.3V output OK) (example uses GPIO23) * ECHO → Pi GPIO via level shift to 3.3V (use a voltage divider, e.g., 1 kΩ + 2 kΩ). Do NOT feed 5V ECHO directly into the Pi. **Option A — Simple (RPi.GPIO)** ``` # file: hcsr04_gpio.py # python3 hcsr04_gpio.py import time import RPi.GPIO as GPIO TRIG = 23 # BCM numbering ECHO = 24 GPIO.setmode(GPIO.BCM) GPIO.setup(TRIG, GPIO.OUT, initial=GPIO.LOW) GPIO.setup(ECHO, GPIO.IN) def distance_cm(temp_c=20.0): """Return one distance sample in cm. Uses blocking timing.""" # Speed of sound (m/s) ~ 331.3 + 0.606*TempC sos = 331.3 + 0.606 * temp_c # m/s time.sleep(0.0002) # settle # 10 µs pulse on TRIG GPIO.output(TRIG, True) time.sleep(10e-6) GPIO.output(TRIG, False) # Wait for echo to go high t0 = time.time() while GPIO.input(ECHO) == 0: start = time.time() if start - t0 > 0.02: # 20 ms timeout return None # Measure high pulse width t0 = time.time() while GPIO.input(ECHO) == 1: end = time.time() if end - t0 > 0.04: # 40 ms timeout (≈ 6.8 m range) return None pulse = end - start # seconds high # distance = (time * speed_of_sound)/2 dist_m = (pulse * sos) / 2.0 return dist_m * 100.0 # cm try: while True: d = distance_cm(temp_c=22.0) if d is None: print("No echo / timeout") else: print(f"{d:6.2f} cm") time.sleep(0.2) except KeyboardInterrupt: pass finally: GPIO.cleanup() ``` **Run** ``` pip install RPi.GPIO python3 hcsr04_gpio.py ``` **Option B — More accurate (pigpio, edge-timed callbacks)** ``` # file: hcsr04_pigpio.py # python3 hcsr04_pigpio.py import time import pigpio TRIG = 23 ECHO = 24 pi = pigpio.pi() if not pi.connected: raise SystemExit("pigpio daemon not running. Start with: sudo pigpiod") pi.set_mode(TRIG, pigpio.OUTPUT) pi.set_mode(ECHO, pigpio.INPUT) pi.write(TRIG, 0) class PulseTimer: def __init__(self, pin): self._rise = None self.width = None self.cb = pi.callback(pin, pigpio.EITHER_EDGE, self._cb) def _cb(self, gpio, level, tick): # tick is in microseconds (wraps ~ 72 minutes) if level == 1: self._rise = tick elif level == 0 and self._rise is not None: dt = pigpio.tickDiff(self._rise, tick) # microseconds self.width = dt / 1e6 # seconds pulse = PulseTimer(ECHO) def distance_cm(temp_c=20.0, timeout_s=0.04): sos = 331.3 + 0.606 * temp_c # m/s pulse.width = None # 10 µs trigger pi.gpio_trigger(TRIG, 10, 1) t0 = time.time() while pulse.width is None and (time.time() - t0) < timeout_s: time.sleep(0.0002) if pulse.width is None: return None dist_m = (pulse.width * sos) / 2.0 return dist_m * 100.0 try: while True: d = distance_cm(22.0) print("No echo / timeout" if d is None else f"{d:6.2f} cm") time.sleep(0.2) except KeyboardInterrupt: pass finally: pulse.cb.cancel() pi.stop() ``` **Run** ``` sudo apt install -y pigpio sudo systemctl enable --now pigpiod pip install pigpio python3 hcsr04_pigpio.py ``` **Notes & tips** * Typical reliable range: 2 cm → ~4 m. Very close objects can saturate; far ones may timeout. * Add a small median filter over 3–5 samples if your readings jump. * Temperature compensation included above; set temp_c if you know ambient temp. * If you see constant timeouts, check: level shifting on ECHO, common GND, and cable length (<20 cm) for TRIG/ECHO.