Skip to content

Commit c5a4f26

Browse files
fix: improve ultrasonic sensor reliability with retry logic and timing optimizations
- Add retry logic: sensor attempts reading twice with 20ms delay on failure - Add echo pin pre-check to detect stuck-high wiring issues early - Move eventlog calls after timing-sensitive measurements to prevent interference - Update distance calculation to use integer math (duration * 100 // 582) - Increase timeout from 14.5ms to 30ms for better sensor tolerance - Optimize trigger pulse timing (5μs stabilization, removed 30ms pre-delay) - Correct GPIO pin assignments and documentation (trig=GP6, echo=GP7) - Add diagnostic test script for ultrasonic sensor debugging - Fix Assembly_Instructions.md test script to call main() These changes address intermittent sensor readings while maintaining student-friendly debugging features.
1 parent acd44a2 commit c5a4f26

5 files changed

Lines changed: 108 additions & 38 deletions

File tree

_Firmware/AI_Driver_RP2040.uf2

512 Bytes
Binary file not shown.

_Firmware/build_info.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
Build Date: Thu Feb 12 06:29:17 UTC 2026
1+
Build Date: Fri Feb 20 05:35:39 UTC 2026
22
MicroPython Version: v1.28.0-preview-62-g0fd084363 (0fd084363, 2026-01-09)
33
Custom Modules: 4 files
44
Build Host: codespaces-35b014

docs/Assembly_Instructions.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -136,4 +136,8 @@ def main():
136136
hold_state(0.5)
137137

138138
print("All hardware tests completed.")
139+
140+
141+
if __name__ == "__main__":
142+
main()
139143
```

project/lib/aidriver.py

Lines changed: 59 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -290,10 +290,9 @@ def __init__(self, trig_pin, echo_pin):
290290
self.trig_pin.off()
291291

292292
# Sensor configuration
293-
self.sound_speed_mm_us = 0.343 # Speed of sound in mm/μs
294293
self.max_distance_mm = 2000 # Max sensor range in mm
295-
# Calculate timeout based on max distance
296-
self.timeout_us = int(self.max_distance_mm * 2.5 / self.sound_speed_mm_us)
294+
# Timeout: 30,000μs allows ~2x longer echo wait (500 * 2 * 30)
295+
self.timeout_us = 30000
297296

298297
def read_distance_mm(self):
299298
"""
@@ -302,46 +301,69 @@ def read_distance_mm(self):
302301
Returns:
303302
int: Distance in millimeters, or -1 if the reading is out of range or fails.
304303
"""
305-
# Add a delay to allow the sensor to settle between readings.
306-
sleep_ms(30)
304+
# Pre-check: ensure echo pin is LOW (not stuck high from wiring issue)
305+
if self.echo_pin.value() != 0:
306+
_ultrasonic_warn_inline("Echo pin stuck HIGH – check wiring")
307+
if _ultrasonic_fail_count <= 3 and eventlog is not None:
308+
try:
309+
eventlog.log_event("ultrasonic echo pin stuck high")
310+
except Exception:
311+
pass
312+
return -1
307313

308-
# Send a 10μs trigger pulse
314+
# Send a 10μs trigger pulse with 5μs stabilization
309315
self.trig_pin.off()
310-
sleep_us(2)
316+
sleep_us(5)
311317
self.trig_pin.on()
312318
sleep_us(10)
313319
self.trig_pin.off()
314320

315321
try:
316-
# Measure the duration of the echo pulse
322+
# Measure the duration of the echo pulse (with retry on failure)
317323
duration = time_pulse_us(self.echo_pin, 1, self.timeout_us)
318324

319325
# time_pulse_us returns -1 on timeout and -2 on invalid state
320326
if duration < 0:
321-
_ultrasonic_warn_inline("Sensor error – check wiring")
322-
# Only log to eventlog on first few failures to avoid log spam
323-
if _ultrasonic_fail_count <= 3 and eventlog is not None:
324-
try:
325-
eventlog.log_event("ultrasonic timeout or invalid echo")
326-
except Exception:
327-
pass
328-
return -1
329-
330-
# Calculate distance in mm: (duration * speed_of_sound) / 2
331-
distance_mm = (duration * self.sound_speed_mm_us) / 2
327+
# Retry once after brief delay to handle transient issues
328+
sleep_ms(20) # Let sensor settle
329+
self.trig_pin.off()
330+
sleep_us(5)
331+
self.trig_pin.on()
332+
sleep_us(10)
333+
self.trig_pin.off()
334+
duration = time_pulse_us(self.echo_pin, 1, self.timeout_us)
335+
336+
# If still failing after retry, report error
337+
if duration < 0:
338+
_ultrasonic_warn_inline("Sensor error – check wiring")
339+
# Only log to eventlog on first few failures to avoid log spam
340+
if _ultrasonic_fail_count <= 3 and eventlog is not None:
341+
try:
342+
eventlog.log_event("ultrasonic timeout or invalid echo")
343+
except Exception:
344+
pass
345+
return -1
346+
347+
# Calculate distance in mm using integer math (avoids floating point)
348+
# Sound speed: 343.2 m/s = 0.3432 mm/μs
349+
# distance = (time * speed) / 2, so: time * 100 // 582
350+
distance_mm = duration * 100 // 582
332351

333352
# Check if the reading is within the valid range (20mm to 2000mm)
334353
if 20 <= distance_mm <= self.max_distance_mm:
335354
# Clear any inline warning since we got a good reading
336355
_ultrasonic_warn_clear()
356+
result = int(distance_mm)
357+
358+
# Log AFTER timing-sensitive measurement is complete
337359
if eventlog is not None:
338360
try:
339361
eventlog.log_event(
340-
"distance reading: {} mm".format(int(distance_mm))
362+
"distance reading: {} mm".format(result)
341363
)
342364
except Exception:
343365
pass
344-
return int(distance_mm)
366+
return result
345367

346368
# Out of range – likely too close, too far, or pointing into open space
347369
_ultrasonic_warn_inline("Out of range ({}mm)".format(int(distance_mm)))
@@ -467,26 +489,26 @@ class AIDriver:
467489

468490
def __init__(
469491
self,
470-
right_speed_pin=3, # GP2 (PWM capable)
471-
left_speed_pin=11, # GP3 (PWM capable)
472-
right_dir_pin=12, # GP4
473-
right_brake_pin=9, # GP5
474-
left_dir_pin=13, # GP6
475-
left_brake_pin=8, # GP7
476-
trig_pin=6, # GP8
477-
echo_pin=7, # GP9
492+
right_speed_pin=3, # GP3 (PWM capable)
493+
left_speed_pin=11, # GP11 (PWM capable)
494+
right_dir_pin=12, # GP12
495+
right_brake_pin=9, # GP9
496+
left_dir_pin=13, # GP13
497+
left_brake_pin=8, # GP8
498+
trig_pin=6, # GP6
499+
echo_pin=7, # GP7
478500
):
479501
"""Initialize RP2040 based AIDriver differential drive robot.
480502
481503
Args:
482-
right_speed_pin: PWM pin for right motor speed (default GP2)
483-
left_speed_pin: PWM pin for left motor speed (default GP3)
484-
right_dir_pin: Digital pin for right motor direction (default GP4)
485-
right_brake_pin: Digital pin for right motor brake (default GP5)
486-
left_dir_pin: Digital pin for left motor direction (default GP6)
487-
left_brake_pin: Digital pin for left motor brake (default GP7)
488-
trig_pin: Ultrasonic sensor trigger pin (default GP8)
489-
echo_pin: Ultrasonic sensor echo pin (default GP9)
504+
right_speed_pin: PWM pin for right motor speed (default GP3)
505+
left_speed_pin: PWM pin for left motor speed (default GP11)
506+
right_dir_pin: Digital pin for right motor direction (default GP12)
507+
right_brake_pin: Digital pin for right motor brake (default GP9)
508+
left_dir_pin: Digital pin for left motor direction (default GP13)
509+
left_brake_pin: Digital pin for left motor brake (default GP8)
510+
trig_pin: Ultrasonic sensor trigger pin (default GP6)
511+
echo_pin: Ultrasonic sensor echo pin (default GP7)
490512
"""
491513

492514
# Library-side preflight: log pin config and attempt a quick sensor ping
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
"""
2+
Raw ultrasonic sensor diagnostic test
3+
Upload this to your Pico and run it to diagnose the sensor readings.
4+
"""
5+
6+
from machine import Pin, time_pulse_us
7+
from time import sleep_us, sleep_ms
8+
9+
print("\n=== Ultrasonic Sensor Raw Test ===")
10+
print("Testing with current pin configuration:")
11+
print(" Trigger: GPIO 6")
12+
print(" Echo: GPIO 7")
13+
print()
14+
15+
echo = Pin(7, Pin.IN) # Echo on GPIO 7
16+
trig = Pin(6, Pin.OUT) # Trigger on GPIO 6
17+
18+
# Initial state check
19+
print(f"Initial echo pin state: {echo.value()} (should be 0)")
20+
print()
21+
22+
# Take 5 raw readings
23+
print("Taking 5 readings...")
24+
for i in range(5):
25+
trig.off()
26+
sleep_us(5)
27+
trig.on()
28+
sleep_us(10)
29+
trig.off()
30+
31+
duration = time_pulse_us(echo, 1, 30000)
32+
33+
if duration < 0:
34+
print(f"Reading {i+1}: TIMEOUT (duration={duration})")
35+
else:
36+
distance = duration * 100 // 582
37+
print(f"Reading {i+1}: duration={duration:5d} μs, distance={distance:4d} mm")
38+
39+
sleep_ms(100) # 100ms between readings
40+
41+
print()
42+
print("=== Test Complete ===")
43+
print("If all readings show same value, try moving obstacle closer/farther")
44+
print("If all show TIMEOUT, check wiring")

0 commit comments

Comments
 (0)