Skip to content

update contributed sonar library to make even better! #1

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 4 commits into from
Sep 7, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
162 changes: 84 additions & 78 deletions hcsr04.py → adafruit_hcsr04.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,19 +33,26 @@
.. warning::

The HC-SR04 uses 5V logic, so you will have to use a `level shifter
<https://www.adafruit.com/product/2653?q=level%20shifter&>`_ between it
and your CircuitPython board (which uses 3.3V logic).
<https://www.adafruit.com/product/2653?q=level%20shifter&>`_ or simple
voltage divider between it and your CircuitPython board (which uses 3.3V logic)

* Authors:

- Mike Mabey
- Jerry Needell - modified to add timeout while waiting for echo (2/26/2018)
- ladyada - compatible with `distance` property standard, renaming, Pi compat
"""
import board
from digitalio import DigitalInOut, DriveMode
from pulseio import PulseIn

import time
import board
from digitalio import DigitalInOut, Direction

_USE_PULSEIO = False
try:
from pulseio import PulseIn
_USE_PULSEIO = True
except ImportError:
pass # This is OK, we'll try to bitbang it!

class HCSR04:
"""Control a HC-SR04 ultrasonic range sensor.
Expand All @@ -54,39 +61,45 @@ class HCSR04:

::

with HCSR04(trig, echo) as sonar:
import time
import board

import adafruit_hcsr04

sonar = adafruit_hcsr04.HCSR04(trigger_pin=board.D2, echo_pin=board.D3)


while True:
try:
while True:
print(sonar.dist_cm())
sleep(2)
except KeyboardInterrupt:
print((sonar.distance,))
except RuntimeError:
print("Retrying!")
pass
time.sleep(0.1)
"""
def __init__(self, trig_pin, echo_pin, timeout_sec=.1):
def __init__(self, trigger_pin, echo_pin, *, timeout=0.1):
"""
:param trig_pin: The pin on the microcontroller that's connected to the
:param trigger_pin: The pin on the microcontroller that's connected to the
``Trig`` pin on the HC-SR04.
:type trig_pin: str or microcontroller.Pin
:type trig_pin: microcontroller.Pin
:param echo_pin: The pin on the microcontroller that's connected to the
``Echo`` pin on the HC-SR04.
:type echo_pin: str or microcontroller.Pin
:param float timeout_sec: Max seconds to wait for a response from the
:type echo_pin: microcontroller.Pin
:param float timeout: Max seconds to wait for a response from the
sensor before assuming it isn't going to answer. Should *not* be
set to less than 0.05 seconds!
"""
if isinstance(trig_pin, str):
trig_pin = getattr(board, trig_pin)
if isinstance(echo_pin, str):
echo_pin = getattr(board, echo_pin)
self.dist_cm = self._dist_two_wire
self.timeout_sec = timeout_sec

self.trig = DigitalInOut(trig_pin)
self.trig.switch_to_output(value=False, drive_mode=DriveMode.PUSH_PULL)

self.echo = PulseIn(echo_pin)
self.echo.pause()
self.echo.clear()
self._timeout = timeout
self._trig = DigitalInOut(trigger_pin)
self._trig.direction = Direction.OUTPUT

if _USE_PULSEIO:
self._echo = PulseIn(echo_pin)
self._echo.pause()
self._echo.clear()
else:
self._echo = DigitalInOut(echo_pin)
self._echo.direction = Direction.INPUT

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

def deinit(self):
"""De-initialize the trigger and echo pins."""
self.trig.deinit()
self.echo.deinit()
self._trig.deinit()
self._echo.deinit()

def dist_cm(self):
@property
def distance(self):
"""Return the distance measured by the sensor in cm.

This is the function that will be called most often in user code. The
distance is calculated by timing a pulse from the sensor, indicating
how long between when the sensor sent out an ultrasonic signal and when
it bounced back and was received again.

If no signal is received, the return value will be ``-1``. This means
If no signal is received, we'll throw a RuntimeError exception. This means
either the sensor was moving too fast to be pointing in the right
direction to pick up the ultrasonic signal when it bounced back (less
likely), or the object off of which the signal bounced is too far away
Expand All @@ -119,51 +133,43 @@ def dist_cm(self):
:return: Distance in centimeters.
:rtype: float
"""
# This method only exists to make it easier to document. See either
# _dist_one_wire or _dist_two_wire for the actual implementation. One
# of those two methods will be assigned to be used in place of this
# method on instantiation.
pass
return self._dist_two_wire() # at this time we only support 2-wire meausre

def _dist_two_wire(self):
self.echo.clear() # Discard any previous pulse values
self.trig.value = 1 # Set trig high
time.sleep(0.00001) # 10 micro seconds 10/1000/1000
self.trig.value = 0 # Set trig low
timeout = time.monotonic()
self.echo.resume()
while len(self.echo) == 0:
# Wait for a pulse
if (time.monotonic() - timeout) > self.timeout_sec:
self.echo.pause()
return -1
self.echo.pause()
if self.echo[0] == 65535:
return -1

return (self.echo[0] / 2) / (291 / 10)


def test(trig, echo, delay=2):
"""Create and get distances from an :class:`HCSR04` object.

This is meant to be helpful when first setting up the HC-SR04. It will get
a distance every ``delay`` seconds and print it to standard out.

:param trig: The pin on the microcontroller that's connected to the
``Trig`` pin on the HC-SR04.
:type trig: str or microcontroller.Pin
:param echo: The pin on the microcontroller that's connected to the
``Echo`` pin on the HC-SR04.
:type echo: str or microcontroller.Pin
:param delay: Seconds to wait between triggers.
:type delay: int or float
:rtype: None
"""
with HCSR04(trig, echo) as sonar:
try:
while True:
print(sonar.dist_cm())
time.sleep(delay)
except KeyboardInterrupt:
pass
if _USE_PULSEIO:
self._echo.clear() # Discard any previous pulse values
self._trig.value = True # Set trig high
time.sleep(0.00001) # 10 micro seconds 10/1000/1000
self._trig.value = False # Set trig low

pulselen = None
timestamp = time.monotonic()
if _USE_PULSEIO:
self._echo.resume()
while len(self._echo) == 0:
# Wait for a pulse
if (time.monotonic() - timestamp) > self._timeout:
self._echo.pause()
raise RuntimeError("Timed out")
self._echo.pause()
pulselen = self._echo[0]
else:
# OK no hardware pulse support, we'll just do it by hand!
# hang out while the pin is low
while not self._echo.value:
if time.monotonic() - timestamp > self._timeout:
raise RuntimeError("Timed out")
timestamp = time.monotonic()
# track how long pin is high
while self._echo.value:
if time.monotonic() - timestamp > self._timeout:
raise RuntimeError("Timed out")
pulselen = time.monotonic() - timestamp
pulselen *= 1000000 # convert to us to match pulseio
if pulselen >= 65535:
raise RuntimeError("Timed out")

# positive pulse time, in seconds, times 340 meters/sec, then
# divided by 2 gives meters. Multiply by 100 for cm
# 1/1000000 s/us * 340 m/s * 100 cm/m * 2 = 0.017
return (pulselen * 0.017)
13 changes: 13 additions & 0 deletions examples/simpletest.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import time
import board
import adafruit_hcsr04

sonar = adafruit_hcsr04.HCSR04(trigger_pin=board.D5, echo_pin=board.D6)

while True:
try:
print((sonar.distance,))
except RuntimeError:
print("Retrying!")
pass
time.sleep(0.1)