Source code for drivers.ultrasonic
# DIYables_MicroPython_Ultrasonic_Sensor.py
from pyb import Pin
import utime
import gc
[docs]
class UltrasonicSensor:
def __init__(self, trig_pin, echo_pin):
self.trig = Pin(trig_pin, Pin.OUT)
self.echo = Pin(echo_pin, Pin.IN)
self.detection_threshold = float('inf') # Initially set to infinity
self.filter_enabled = False # Filter is disabled by default
self.num_samples = 1 # Default number of samples to 1 when filter is disabled
self.distances = [] # Store measurements
[docs]
def loop(self):
"""Perform a measurement cycle and update the list of distances."""
# Ensure the trigger pin is low for a clean pulse
self.trig.value(0)
utime.sleep_us(5)
# Send a 10 microsecond pulse to start the measurement
self.trig.value(1)
utime.sleep_us(10)
self.trig.value(0)
start_time = utime.ticks_us()
timeout = 10000 # Timeout in microseconds (e.g., 10 milliseconds ~ 170cm)
# Wait for the echo to start
while self.echo.value() == 0:
if utime.ticks_diff(utime.ticks_us(), start_time) > timeout:
return None # Return None or some error indication on timeout
# Record the start time of the echo
signal_off = utime.ticks_us()
# Wait for the echo to end
while self.echo.value() == 1:
if utime.ticks_diff(utime.ticks_us(), signal_off) > timeout:
return None # Return None or some error indication on timeout
# Record the end time of the echo
signal_on = utime.ticks_us()
# Calculate the duration of the echo pulse
time_passed = utime.ticks_diff(signal_on, signal_off)
# Calculate the distance in centimeters
distance = (time_passed * 0.017)
self.distances.append(distance)
if len(self.distances) > self.num_samples:
self.distances.pop(0) # Maintain a fixed length of distances
[docs]
def get_distance(self):
"""Return the calculated distance based on current measurements."""
if not self.distances:
return None # Return None if no measurements have been made yet
# Check if filtering is enabled and there are enough samples to filter
if self.filter_enabled and len(self.distances) >= self.num_samples:
sorted_distances = sorted(self.distances)
# Consider only the middle samples if filtering is enabled
mid_index_start = len(sorted_distances) // 4
mid_index_end = len(sorted_distances) * 3 // 4
mid_distances = sorted_distances[mid_index_start:mid_index_end]
if not mid_distances: # Check if the slice results in an empty list
return None # Return None or an appropriate default value to prevent division by zero
calculated_distance = sum(mid_distances) / len(mid_distances)
else:
# Use the latest measurement if no filtering or not enough samples for filtering
calculated_distance = self.distances[-1] if self.distances else None
if calculated_distance is None:
return None # Safeguard against no available data to process
# Compare against the detection threshold
if calculated_distance > self.detection_threshold:
return False # Indicate no object detected within the threshold
return calculated_distance
[docs]
def set_detection_threshold(self, distance):
"""Set the maximum distance beyond which no object is considered detected."""
self.detection_threshold = distance
[docs]
def enable_filter(self, num_samples=20):
"""Enable filtering of measurements and set number of samples for filtering."""
if num_samples > 0:
self.num_samples = num_samples
self.filter_enabled = True
else:
raise ValueError("Number of samples must be greater than 0")
[docs]
def disable_filter(self):
"""Disable filtering of measurements and reset number of samples to 1."""
self.filter_enabled = False
self.num_samples = 1 # Reset to default sample count
gc.collect()