Source code for openbricks.drivers.hcsr04

# SPDX-License-Identifier: MIT
"""
HC-SR04 ultrasonic distance sensor.

The HC-SR04 has two pins: ``trig`` (input — we drive it) and ``echo``
(output — it pulls high for as long as the round-trip echo took).
Sequence:

  1. Drive ``trig`` high for ~10 µs.
  2. Read the duration of the resulting pulse on ``echo``.
  3. distance_mm = pulse_us × speed_of_sound_mm_per_us / 2.

Speed of sound in air at room temperature is ~0.343 mm/µs, so a
60 cm target gives ~3.5 ms of echo pulse — well within the 30 ms
default timeout.

This driver is pure Python — there's no closed-loop control on a
range sensor, so the 1 kHz hot-path concern doesn't apply. The
``machine.time_pulse_us`` builtin does the actual measurement;
typical call latency is ~1 ms (pulse round-trip) which is fine for
the cold-path use cases (line-following, wall avoidance, mission
"approach until N mm" loops at <100 Hz).
"""

import machine
import time

from openbricks.distance import DistanceSensor


# Speed of sound in air at ~20 °C, in mm per µs. Round-trip; we
# halve it inside ``distance_mm``.
_SPEED_MM_PER_US = 0.343
_DEFAULT_TIMEOUT_US = 30_000   # ~5 m round-trip; matches sensor's stated max range


[docs] class HCSR04(DistanceSensor): """HC-SR04 ultrasonic distance sensor. Args: trig: GPIO pin number wired to the sensor's ``TRIG`` pin. echo: GPIO pin number wired to ``ECHO``. timeout_us: how long to wait for the echo. ``time_pulse_us`` returns ``-1`` past this; we map that to ``-1`` from :meth:`distance_mm` to mean "no return". """ def __init__(self, trig, echo, timeout_us=_DEFAULT_TIMEOUT_US): self._trig = machine.Pin(trig, machine.Pin.OUT, value=0) self._echo = machine.Pin(echo, machine.Pin.IN) self._timeout_us = int(timeout_us)
[docs] def distance_mm(self): """Return the round-trip distance to the nearest reflector ahead, in millimetres, or ``-1`` if no echo arrived inside ``timeout_us``. """ # 10 µs pulse on TRIG; the sensor's MCU detects the rising # edge and emits an 8-cycle 40 kHz burst. self._trig.value(0) time.sleep_us(2) self._trig.value(1) time.sleep_us(10) self._trig.value(0) try: pulse_us = machine.time_pulse_us( self._echo, 1, self._timeout_us) except OSError: return -1 if pulse_us < 0: return -1 return int(pulse_us * _SPEED_MM_PER_US / 2.0)