1- import time
2- from machine import Pin , Timer
1+ import machine , time
2+ from machine import Pin
33
44class Rangefinder :
55
@@ -16,17 +16,15 @@ def get_default_rangefinder(cls):
1616
1717 def __init__ (self , trigger_pin : int | str = "RANGE_TRIGGER" , echo_pin : int | str = "RANGE_ECHO" , timeout_us :int = 500 * 2 * 30 ):
1818 """
19- A non-blocking class for using the HC-SR04 Ultrasonic Rangefinder.
19+ A basic class for using the HC-SR04 Ultrasonic Rangefinder.
2020 The sensor range is between 2cm and 4m.
21- Measurements are taken continuously in the background using a timer
22- and pin IRQ, so distance() returns immediately with the most recent value.
2321 Timeouts will return a MAX_VALUE (65535) instead of raising an exception.
2422
2523 :param trigger_pin: The number of the pin on the microcontroller that's connected to the ``Trig`` pin on the HC-SR04.
2624 :type trigger_pin: int
2725 :param echo_pin: The number of the pin on the microcontroller that's connected to the ``Echo`` pin on the HC-SR04.
2826 :type echo_pin: int
29- :param timeout_us: Max microseconds to wait for a response from the sensor before assuming it isn't going to answer. By default set to 30,000 us (0.03 s)
27+ :param timeout_us: Max microseconds seconds to wait for a response from the sensor before assuming it isn't going to answer. By default set to 30,000 us (0.03 s)
3028 :type timeout_us: int
3129 """
3230 self .timeout_us = timeout_us
@@ -38,80 +36,56 @@ def __init__(self, trigger_pin: int|str = "RANGE_TRIGGER", echo_pin: int|str = "
3836 # Init echo pin (in)
3937 self .echo = Pin (echo_pin , mode = Pin .IN , pull = None )
4038
41- self .cms = self .MAX_VALUE
42- self ._echo_start = 0
43- self ._waiting_for_echo = False
44- self ._trigger_time = 0
45- self ._first_reading_done = False
39+ self .cms = 0
40+ self .last_echo_time = 0
41+ self .cache_time_us = 3000
4642
47- # Register echo pin IRQ for both rising and falling edges
48- self .echo .irq (trigger = Pin .IRQ_RISING | Pin .IRQ_FALLING , handler = self ._echo_handler )
49-
50- # Start a virtual timer to periodically send trigger pulses
51- # 60ms period matches the HC-SR04 recommended minimum cycle time
52- self ._timer = Timer (- 1 )
53- self ._timer .init (period = 60 , callback = self ._trigger_ping )
54-
55- def _trigger_ping (self , t ):
43+ def _send_pulse_and_wait (self ):
5644 """
57- Timer callback that sends a trigger pulse to the HC-SR04.
58- Only ~15us of work per call (negligible blocking).
59- Also detects timeouts from previous measurements.
45+ Send the pulse to trigger and listen on echo pin.
46+ We use the method `machine.time_pulse_us()` to get the microseconds until the echo is received.
6047 """
61- if self ._waiting_for_echo :
62- # Check if previous measurement timed out
63- if time .ticks_diff (time .ticks_us (), self ._trigger_time ) > self .timeout_us :
64- self .cms = self .MAX_VALUE
65- self ._waiting_for_echo = False
66- self ._first_reading_done = True
67- else :
68- # Still waiting for a valid echo, skip this trigger
69- return
70-
71- # Send trigger pulse
72- self ._trigger .value (0 )
48+ self ._trigger .value (0 ) # Stabilize the sensor
7349 self ._delay_us (5 )
7450 self ._trigger .value (1 )
51+ # Send a 10us pulse.
7552 self ._delay_us (10 )
7653 self ._trigger .value (0 )
77- self ._trigger_time = time .ticks_us ()
78- self ._waiting_for_echo = True
79-
80- def _echo_handler (self , pin ):
81- """
82- Pin IRQ handler for the echo pin.
83- Rising edge: record start time.
84- Falling edge: compute distance from pulse width.
85- """
86- if pin .value () == 1 :
87- # Rising edge - echo pulse started
88- self ._echo_start = time .ticks_us ()
89- else :
90- # Falling edge - echo pulse ended
91- if self ._waiting_for_echo :
92- pulse_time = time .ticks_diff (time .ticks_us (), self ._echo_start )
93- if pulse_time > 0 :
94- # Sound speed 343.2 m/s = 0.034320 cm/us = 1cm per 29.1us
95- # Divide by 2 because pulse travels to target and back
96- self .cms = (pulse_time / 2 ) / 29.1
97- else :
98- self .cms = self .MAX_VALUE
99- self ._waiting_for_echo = False
100- self ._first_reading_done = True
54+ try :
55+ pulse_time = machine .time_pulse_us (self .echo , 1 , self .timeout_us )
56+ return pulse_time
57+ except OSError as exception :
58+ raise exception
10159
10260 def distance (self ) -> float :
10361 """
104- Get the most recent distance measurement in centimeters.
105- Blocks until the first measurement is available, then returns
106- immediately on all subsequent calls.
62+ Get the distance in centimeters by measuring the echo pulse time
10763 """
108- while not self ._first_reading_done :
109- pass
64+ if time .ticks_diff (time .ticks_us (), self .last_echo_time ) < self .cache_time_us and not (self .cms == 65535 or self .cms == 0 ):
65+ return self .cms
66+
67+ try :
68+ pulse_time = self ._send_pulse_and_wait ()
69+ if pulse_time <= 0 :
70+ return self .MAX_VALUE
71+ except OSError as exception :
72+ # We don't want programs to crash if the HC-SR04 doesn't see anything in range
73+ # So we catch those errors and return 65535 instead
74+ if exception .args [0 ] == 110 : # 110 = ETIMEDOUT
75+ return self .MAX_VALUE
76+ raise exception
77+
78+ # To calculate the distance we get the pulse_time and divide it by 2
79+ # (the pulse walk the distance twice) and by 29.1 becasue
80+ # the sound speed on air (343.2 m/s), that It's equivalent to
81+ # 0.034320 cm/us that is 1cm each 29.1us
82+ self .cms = (pulse_time / 2 ) / 29.1
83+ self .last_echo_time = time .ticks_us ()
11084 return self .cms
11185
11286 def _delay_us (self , delay :int ):
11387 """
114- Custom implementation of time.sleep_us(), used to get around the bug in MicroPython where time.sleep_us()
88+ Custom implementation of time.sleep_us(), used to get around the bug in MicroPython where time.sleep_us()
11589 doesn't work properly and causes the IDE to hang when uploading the code
11690 """
11791 start = time .ticks_us ()
0 commit comments