forked from fossasia/pslab-python
-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Implement HCSR04 to replace firmware implementation (closes fossasia#208
- Loading branch information
Showing
2 changed files
with
125 additions
and
49 deletions.
There are no files selected for viewing
This file was deleted.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,125 @@ | ||
"""Ultrasonic distance sensors.""" | ||
import time | ||
|
||
from pslab.instrument.logic_analyzer import LogicAnalyzer | ||
from pslab.instrument.waveform_generator import PWMGenerator | ||
from pslab.serial_handler import SerialHandler | ||
|
||
|
||
class HCSR04(LogicAnalyzer, PWMGenerator): | ||
"""Read data from ultrasonic distance sensor HC-SR04/HC-SR05. | ||
These sensors can measure distances between 2 cm to 4 m (SR04) / 4.5 m | ||
(SR05). | ||
Sensors must have separate trigger and echo pins. First a 10 µs pulse is | ||
output on the trigger pin. The trigger pin must be connected to the TRIG | ||
pin on the sensor prior to use. | ||
Upon receiving this pulse, the sensor emits a sequence of sound pulses, and | ||
the logic level of its echo pin is also set high. The logic level goes LOW | ||
when the sound packet returns to the sensor, or when a timeout occurs. | ||
Timeout occurs if no echo is received within the time slot determinded by | ||
the sensor's maximum range. | ||
The ultrasound sensor outputs a series of eight sound pulses at 40 kHz, | ||
which corresponds to a time period of 25 µs per pulse. These pulses reflect | ||
off of the nearest object in front of the sensor, and return to it. The | ||
time between sending and receiving of the pulse packet is used to estimate | ||
the distance. If the reflecting object is either too far away or absorbs | ||
sound, less than eight pulses may be received, and this can cause a | ||
measurement error of 25 µs which corresponds to 8 mm. | ||
Parameters | ||
---------- | ||
device : :class:`SerialHandler` | ||
Serial connection to PSLab device. | ||
trig : str, optional | ||
Name of the trigger pin. Defaults to SQ1. | ||
echo : str, optional | ||
Name of the echo pin. Defaults to LA1. | ||
Example | ||
------- | ||
In this example the sensor's Vcc pin is connected to the PSLab's PV1 pin, | ||
the Trig pin is connected to SQ1, Echo to LA1, and Gnd to GND. | ||
>>> import pslab | ||
>>> from pslab.external.hcsr04 import HCSR04 | ||
>>> psl = pslab.ScienceLab() | ||
>>> distance_sensor = HCSR04(psl) | ||
>>> psl.power_supply.pv1 = 5 # Power the sensor. | ||
>>> distance_sensor.estimate_distance() | ||
2.36666667 | ||
""" | ||
|
||
def __init__( | ||
self, | ||
device: SerialHandler, | ||
trig: str = "SQ1", | ||
echo: str = "LA1", | ||
): | ||
self._device = device | ||
LogicAnalyzer.__init__(self, self._device) | ||
PWMGenerator.__init__(self, self._device) | ||
self._trig = trig | ||
self._echo = echo | ||
self._measure_period = 60e-3 # Minimum recommended by datasheet. | ||
self._trigger_pulse_length = 10e-6 | ||
|
||
def estimate_distance( | ||
self, | ||
average: int = 10, | ||
speed_of_sound: float = 340, | ||
timeout_distance: float = 4, | ||
): | ||
"""Estimate distance to an object. | ||
Parameters | ||
---------- | ||
average : int, optional | ||
Number of times to repeat the measurement and average the results. | ||
Defaults to 10. | ||
speed_of_sound : float, optional | ||
Speed of sound in air. Defaults to 340 m/s. | ||
timeout_distance : float, optional | ||
Maximum distance to object. Defaults to 4 m. Measurement times out | ||
if the echo is not received within: | ||
timeout = timeout_distance / speed_of_sound * 2 seconds | ||
Returns | ||
------- | ||
distance : float | ||
Distance to object in meters. | ||
Raises | ||
------ | ||
RuntimeError if the ECHO pin is not LOW at start of measurement. | ||
TimeoutError if the end of the ECHO pulse is not detected (i.e. the | ||
object is too far away). | ||
""" | ||
timeout = timeout_distance / speed_of_sound * 2 | ||
self.capture( | ||
channels=self._echo, | ||
events=2 * average, | ||
block=False, | ||
timeout=timeout, | ||
) | ||
self.generate( | ||
channels=self._trig, | ||
frequency=self._measure_period**-1, | ||
duty_cycles=self._trigger_pulse_length / self._measure_period, | ||
) | ||
time.sleep(self._measure_period * average) | ||
self.set_state(**{self._trig.lower(): 0}) | ||
(t,) = self.fetch_data() | ||
self._sanity_check(len(t), 2 * average) | ||
high_times = t[::2] - t[1::2] | ||
return speed_of_sound * high_times.mean() / 2 * 1e-6 | ||
|
||
def _sanity_check(self, events: int, expected_events: int): | ||
if self.get_initial_states()[self._echo]: | ||
raise RuntimeError("ECHO pin was HIGH with measurement started.") | ||
|
||
if events < expected_events: | ||
raise TimeoutError |