Skip to content

Commit 3043dfa

Browse files
authored
Merge pull request #1 from ladyada/master
update contributed sonar library to make even better!
2 parents ad6afff + 51b04ec commit 3043dfa

File tree

2 files changed

+97
-78
lines changed

2 files changed

+97
-78
lines changed

hcsr04.py renamed to adafruit_hcsr04.py

Lines changed: 84 additions & 78 deletions
Original file line numberDiff line numberDiff line change
@@ -33,19 +33,26 @@
3333
.. warning::
3434
3535
The HC-SR04 uses 5V logic, so you will have to use a `level shifter
36-
<https://www.adafruit.com/product/2653?q=level%20shifter&>`_ between it
37-
and your CircuitPython board (which uses 3.3V logic).
36+
<https://www.adafruit.com/product/2653?q=level%20shifter&>`_ or simple
37+
voltage divider between it and your CircuitPython board (which uses 3.3V logic)
3838
3939
* Authors:
4040
4141
- Mike Mabey
4242
- Jerry Needell - modified to add timeout while waiting for echo (2/26/2018)
43+
- ladyada - compatible with `distance` property standard, renaming, Pi compat
4344
"""
44-
import board
45-
from digitalio import DigitalInOut, DriveMode
46-
from pulseio import PulseIn
45+
4746
import time
47+
import board
48+
from digitalio import DigitalInOut, Direction
4849

50+
_USE_PULSEIO = False
51+
try:
52+
from pulseio import PulseIn
53+
_USE_PULSEIO = True
54+
except ImportError:
55+
pass # This is OK, we'll try to bitbang it!
4956

5057
class HCSR04:
5158
"""Control a HC-SR04 ultrasonic range sensor.
@@ -54,39 +61,45 @@ class HCSR04:
5461
5562
::
5663
57-
with HCSR04(trig, echo) as sonar:
64+
import time
65+
import board
66+
67+
import adafruit_hcsr04
68+
69+
sonar = adafruit_hcsr04.HCSR04(trigger_pin=board.D2, echo_pin=board.D3)
70+
71+
72+
while True:
5873
try:
59-
while True:
60-
print(sonar.dist_cm())
61-
sleep(2)
62-
except KeyboardInterrupt:
74+
print((sonar.distance,))
75+
except RuntimeError:
76+
print("Retrying!")
6377
pass
78+
time.sleep(0.1)
6479
"""
65-
def __init__(self, trig_pin, echo_pin, timeout_sec=.1):
80+
def __init__(self, trigger_pin, echo_pin, *, timeout=0.1):
6681
"""
67-
:param trig_pin: The pin on the microcontroller that's connected to the
82+
:param trigger_pin: The pin on the microcontroller that's connected to the
6883
``Trig`` pin on the HC-SR04.
69-
:type trig_pin: str or microcontroller.Pin
84+
:type trig_pin: microcontroller.Pin
7085
:param echo_pin: The pin on the microcontroller that's connected to the
7186
``Echo`` pin on the HC-SR04.
72-
:type echo_pin: str or microcontroller.Pin
73-
:param float timeout_sec: Max seconds to wait for a response from the
87+
:type echo_pin: microcontroller.Pin
88+
:param float timeout: Max seconds to wait for a response from the
7489
sensor before assuming it isn't going to answer. Should *not* be
7590
set to less than 0.05 seconds!
7691
"""
77-
if isinstance(trig_pin, str):
78-
trig_pin = getattr(board, trig_pin)
79-
if isinstance(echo_pin, str):
80-
echo_pin = getattr(board, echo_pin)
81-
self.dist_cm = self._dist_two_wire
82-
self.timeout_sec = timeout_sec
83-
84-
self.trig = DigitalInOut(trig_pin)
85-
self.trig.switch_to_output(value=False, drive_mode=DriveMode.PUSH_PULL)
86-
87-
self.echo = PulseIn(echo_pin)
88-
self.echo.pause()
89-
self.echo.clear()
92+
self._timeout = timeout
93+
self._trig = DigitalInOut(trigger_pin)
94+
self._trig.direction = Direction.OUTPUT
95+
96+
if _USE_PULSEIO:
97+
self._echo = PulseIn(echo_pin)
98+
self._echo.pause()
99+
self._echo.clear()
100+
else:
101+
self._echo = DigitalInOut(echo_pin)
102+
self._echo.direction = Direction.INPUT
90103

91104
def __enter__(self):
92105
"""Allows for use in context managers."""
@@ -98,18 +111,19 @@ def __exit__(self, exc_type, exc_val, exc_tb):
98111

99112
def deinit(self):
100113
"""De-initialize the trigger and echo pins."""
101-
self.trig.deinit()
102-
self.echo.deinit()
114+
self._trig.deinit()
115+
self._echo.deinit()
103116

104-
def dist_cm(self):
117+
@property
118+
def distance(self):
105119
"""Return the distance measured by the sensor in cm.
106120
107121
This is the function that will be called most often in user code. The
108122
distance is calculated by timing a pulse from the sensor, indicating
109123
how long between when the sensor sent out an ultrasonic signal and when
110124
it bounced back and was received again.
111125
112-
If no signal is received, the return value will be ``-1``. This means
126+
If no signal is received, we'll throw a RuntimeError exception. This means
113127
either the sensor was moving too fast to be pointing in the right
114128
direction to pick up the ultrasonic signal when it bounced back (less
115129
likely), or the object off of which the signal bounced is too far away
@@ -119,51 +133,43 @@ def dist_cm(self):
119133
:return: Distance in centimeters.
120134
:rtype: float
121135
"""
122-
# This method only exists to make it easier to document. See either
123-
# _dist_one_wire or _dist_two_wire for the actual implementation. One
124-
# of those two methods will be assigned to be used in place of this
125-
# method on instantiation.
126-
pass
136+
return self._dist_two_wire() # at this time we only support 2-wire meausre
127137

128138
def _dist_two_wire(self):
129-
self.echo.clear() # Discard any previous pulse values
130-
self.trig.value = 1 # Set trig high
131-
time.sleep(0.00001) # 10 micro seconds 10/1000/1000
132-
self.trig.value = 0 # Set trig low
133-
timeout = time.monotonic()
134-
self.echo.resume()
135-
while len(self.echo) == 0:
136-
# Wait for a pulse
137-
if (time.monotonic() - timeout) > self.timeout_sec:
138-
self.echo.pause()
139-
return -1
140-
self.echo.pause()
141-
if self.echo[0] == 65535:
142-
return -1
143-
144-
return (self.echo[0] / 2) / (291 / 10)
145-
146-
147-
def test(trig, echo, delay=2):
148-
"""Create and get distances from an :class:`HCSR04` object.
149-
150-
This is meant to be helpful when first setting up the HC-SR04. It will get
151-
a distance every ``delay`` seconds and print it to standard out.
152-
153-
:param trig: The pin on the microcontroller that's connected to the
154-
``Trig`` pin on the HC-SR04.
155-
:type trig: str or microcontroller.Pin
156-
:param echo: The pin on the microcontroller that's connected to the
157-
``Echo`` pin on the HC-SR04.
158-
:type echo: str or microcontroller.Pin
159-
:param delay: Seconds to wait between triggers.
160-
:type delay: int or float
161-
:rtype: None
162-
"""
163-
with HCSR04(trig, echo) as sonar:
164-
try:
165-
while True:
166-
print(sonar.dist_cm())
167-
time.sleep(delay)
168-
except KeyboardInterrupt:
169-
pass
139+
if _USE_PULSEIO:
140+
self._echo.clear() # Discard any previous pulse values
141+
self._trig.value = True # Set trig high
142+
time.sleep(0.00001) # 10 micro seconds 10/1000/1000
143+
self._trig.value = False # Set trig low
144+
145+
pulselen = None
146+
timestamp = time.monotonic()
147+
if _USE_PULSEIO:
148+
self._echo.resume()
149+
while len(self._echo) == 0:
150+
# Wait for a pulse
151+
if (time.monotonic() - timestamp) > self._timeout:
152+
self._echo.pause()
153+
raise RuntimeError("Timed out")
154+
self._echo.pause()
155+
pulselen = self._echo[0]
156+
else:
157+
# OK no hardware pulse support, we'll just do it by hand!
158+
# hang out while the pin is low
159+
while not self._echo.value:
160+
if time.monotonic() - timestamp > self._timeout:
161+
raise RuntimeError("Timed out")
162+
timestamp = time.monotonic()
163+
# track how long pin is high
164+
while self._echo.value:
165+
if time.monotonic() - timestamp > self._timeout:
166+
raise RuntimeError("Timed out")
167+
pulselen = time.monotonic() - timestamp
168+
pulselen *= 1000000 # convert to us to match pulseio
169+
if pulselen >= 65535:
170+
raise RuntimeError("Timed out")
171+
172+
# positive pulse time, in seconds, times 340 meters/sec, then
173+
# divided by 2 gives meters. Multiply by 100 for cm
174+
# 1/1000000 s/us * 340 m/s * 100 cm/m * 2 = 0.017
175+
return (pulselen * 0.017)

examples/simpletest.py

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
import time
2+
import board
3+
import adafruit_hcsr04
4+
5+
sonar = adafruit_hcsr04.HCSR04(trigger_pin=board.D5, echo_pin=board.D6)
6+
7+
while True:
8+
try:
9+
print((sonar.distance,))
10+
except RuntimeError:
11+
print("Retrying!")
12+
pass
13+
time.sleep(0.1)

0 commit comments

Comments
 (0)