Skip to content

Commit 8ebee7a

Browse files
committed
Add Ultrasonic sensor module
1 parent 5981284 commit 8ebee7a

4 files changed

Lines changed: 212 additions & 0 deletions

File tree

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
# FILE: UltrasonicSensor-readDistanceNative.py
2+
# AUTHOR: Josip Šimun Kuči @ Soldered
3+
# BRIEF: An example showing how to read the distance of the Native Ultrasonic sensor
4+
# WORKS WITH: Ultrasonic module HC-SR04: www.solde.red/101202
5+
# LAST UPDATED: 2025-06-12
6+
7+
from machine import I2C, Pin
8+
from UltrasonicSensor import UltrasonicSensor
9+
import time
10+
11+
# Create sensor instance
12+
sensor = UltrasonicSensor()
13+
14+
while True:
15+
sensor.takeMeasure()
16+
time.sleep(0.1) # Small delay for measurement
17+
18+
#The sensor sends a pulse through the trigger pin, which is deflected
19+
#By a surface in front of it and picked up by the echo pin, we then use the
20+
#time it took the impulse to reflect to calculate the distance
21+
distance = sensor.getDistance()
22+
23+
#How long did it take for the pulse to reflect
24+
duration = sensor.getDuration()
25+
26+
#Print out the distance and duration to the user
27+
print(f"Distance: {distance:.2f} cm")
28+
print(f"Time it took: {duration:.2f} us\n")
29+
30+
31+
time.sleep(0.25) # Delay between readings
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
# FILE: UltrasonicSensor-readDistanceQwiic.py
2+
# AUTHOR: Josip Šimun Kuči @ Soldered
3+
# BRIEF: An example showing how to read the distance of the Qwiic compatible
4+
# Ultrasonic sensor from an object
5+
# WORKS WITH: Ultrasonic sensor with Qwiic: www.solde.red/333001
6+
# LAST UPDATED: 2025-06-12
7+
8+
from machine import I2C, Pin
9+
from UltrasonicSensor import UltrasonicSensor
10+
import time
11+
12+
# Create sensor instance
13+
sensor = UltrasonicSensor()
14+
15+
while True:
16+
sensor.takeMeasure()
17+
time.sleep(0.1) # Small delay for measurement
18+
19+
#The sensor sends a pulse through the trigger pin, which is deflected
20+
#By a surface in front of it and picked up by the echo pin, we then use the
21+
#time it took the impulse to reflect to calculate the distance
22+
distance = sensor.getDistance()
23+
24+
#How long did it take for the pulse to reflect
25+
duration = sensor.getDuration()
26+
27+
#Print out the distance and duration to the user
28+
print(f"Distance: {distance:.2f} cm")
29+
print(f"Time it took: {duration:.2f} us\n")
30+
31+
32+
time.sleep(0.25) # Delay between readings
Lines changed: 127 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,127 @@
1+
# FILE: UltrasonicSensor.py
2+
# AUTHOR: Josip Šimun Kuči @ Soldered
3+
# BRIEF: A MicroPython module for the HC-SR04 UltraSonic sensor. Supports both the Native and Qwiic version
4+
# LAST UPDATED: 2025-06-12
5+
6+
7+
# Import Qwiic base class for I2C devices
8+
from Qwiic import Qwiic
9+
# Import Pin and I2C classes for GPIO and I2C operations
10+
from machine import I2C, Pin
11+
# Used to detect the board type (ESP32, ESP8266, etc.)
12+
from os import uname
13+
# Time module used for precise delays and measuring pulse duration
14+
import time
15+
16+
# Register addresses for I2C communication with a supported ultrasonic module
17+
TAKE_MEAS_REG = 0 # Command register to start measurement
18+
DISTANCE_REG = 1 # Register where distance in cm is stored (from external sensor)
19+
DURATION_REG = 2 # Register where echo pulse duration is stored (from external sensor)
20+
21+
class UltrasonicSensor(Qwiic):
22+
"""
23+
Ultrasonic sensor class that supports both native (GPIO) and I2C-based operation.
24+
In native mode, it uses GPIO pins to trigger and read echo.
25+
In I2C mode, it uses a pre-programmed I2C-based ultrasonic sensor.
26+
"""
27+
def __init__(self, i2c=None, address=0x30, echo_pin=None, trig_pin=None):
28+
"""
29+
Initializes the sensor either in native GPIO mode or I2C mode.
30+
- If trig_pin and echo_pin are given: use GPIO mode.
31+
- Otherwise, fall back to I2C mode.
32+
"""
33+
if trig_pin is not None and echo_pin is not None:
34+
# Native GPIO mode: setup trig and echo pins
35+
self.trig_pin = Pin(trig_pin, Pin.OUT)
36+
self.echo_pin = Pin(echo_pin, Pin.IN)
37+
self.native = True
38+
else:
39+
# I2C mode
40+
if i2c is not None:
41+
# Use provided I2C instance
42+
i2c = i2c
43+
else:
44+
# Auto-select Qwiic for CONNECT boards
45+
if uname().sysname in ("esp32", "esp8266"):
46+
i2c = I2C(0, scl=Pin(22), sda=Pin(21))
47+
else:
48+
raise Exception("Board not recognized, enter I2C pins manually")
49+
# Call the Qwiic base class constructor
50+
super().__init__(i2c=i2c, address=address, native=False)
51+
52+
def takeMeasure(self):
53+
"""
54+
For I2C mode: Sends a command to start measurement on the I2C sensor.
55+
"""
56+
return self.send_address(TAKE_MEAS_REG)
57+
58+
def _measure_pulse(self, timeout_us=50000):
59+
"""
60+
Manual pulse-in implementation to measure echo pulse width in microseconds.
61+
Waits for the echo pin to go high, then times how long it stays high.
62+
If the pulse is not received within timeout, returns 0.
63+
"""
64+
start = time.ticks_us()
65+
66+
# Wait for echo pin to go high (start of echo)
67+
while self.echo_pin.value() == 0:
68+
if time.ticks_diff(time.ticks_us(), start) > timeout_us:
69+
return 0 # Timeout occurred
70+
71+
pulse_start = time.ticks_us()
72+
73+
# Wait for echo pin to go low (end of echo)
74+
while self.echo_pin.value() == 1:
75+
if time.ticks_diff(time.ticks_us(), pulse_start) > timeout_us:
76+
return 0 # Timeout occurred
77+
78+
pulse_end = time.ticks_us()
79+
duration = time.ticks_diff(pulse_end, pulse_start)
80+
return duration # Return pulse width in microseconds
81+
82+
def getDuration(self):
83+
"""
84+
Returns the duration (in microseconds) of the echo signal.
85+
- In native mode: triggers a measurement and measures echo duration using GPIO.
86+
- In I2C mode: reads duration from external sensor over I2C.
87+
"""
88+
if self.native:
89+
# Trigger a short pulse to start measurement
90+
self.trig_pin.value(0)
91+
time.sleep_us(5)
92+
self.trig_pin.value(1)
93+
time.sleep_us(20)
94+
self.trig_pin.value(0)
95+
96+
# Measure echo pulse width
97+
return self._measure_pulse()
98+
else:
99+
# Read duration from I2C register
100+
data = self.read_register(DURATION_REG, 2)
101+
return data[1] << 8 | data[0]
102+
103+
def getDistance(self):
104+
"""
105+
Returns the distance to an object in centimeters.
106+
- In native mode: measures pulse duration and calculates distance using speed of sound.
107+
- In I2C mode: reads precomputed distance from external sensor over I2C.
108+
"""
109+
if self.native:
110+
# Trigger pulse
111+
self.trig_pin.value(0)
112+
time.sleep_us(5)
113+
self.trig_pin.value(1)
114+
time.sleep_us(20)
115+
self.trig_pin.value(0)
116+
117+
# Measure pulse width
118+
duration = self._measure_pulse()
119+
120+
# Convert duration to distance (speed of sound = 0.034 cm/us)
121+
distance_cm = duration * 0.034 / 2.0 # divide by 2 (round trip)
122+
return distance_cm
123+
else:
124+
# Read distance from I2C register
125+
data = self.read_register(DISTANCE_REG, 2)
126+
return data[1] << 8 | data[0]
127+

UltrasonicSensor/package.json

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
{
2+
"urls": [
3+
[
4+
"UltrasonicSensor.py",
5+
"github:SolderedElectronics/Soldered-MicroPython-Modules/UltrasonicSensor/UltrasonicSensor/UltrasonicSensor.py"
6+
],
7+
[
8+
"Examples/UltrasonicSensor-readDistanceNative.py",
9+
"github:SolderedElectronics/Soldered-MicroPython-Modules/UltrasonicSensor/UltrasonicSensor/Examples/UltrasonicSensor-readDistanceNative.py"
10+
],
11+
[
12+
"Examples/UltrasonicSensor-readDistanceQwiic.py",
13+
"github:SolderedElectronics/Soldered-MicroPython-Modules/UltrasonicSensor/UltrasonicSensor/Examples/UltrasonicSensor-readDistanceQwiic.py"
14+
],
15+
[
16+
"Qwiic.py",
17+
"github:SolderedElectronics/Soldered-MicroPython-Modules/Qwiic/Qwiic.py"
18+
]
19+
],
20+
"deps": [],
21+
"version": "0.2"
22+
}

0 commit comments

Comments
 (0)