"""
ULTRASONIC DISTANCE SENSOR WITH RASPBERRY PI PICO
==================================================
Hardware:
- Raspberry Pi Pico
- HC-SR04 Ultrasonic Sensor
- 16x2 I2C LCD Display
- RGB LED (Common Cathode)
- Active Buzzer
- 3x 220Ω Resistors
Features:
- Real-time distance measurement (2-400 cm)
- LCD display showing distance in cm and inches
- RGB LED color changes based on distance
- Buzzer alerts for proximity
- Serial monitor data logging
- Auto-ranging display
Author: IoT Project Tutorial
Date: 2024
Version: 1.0
"""
from machine import Pin, I2C, PWM
import utime
from lcd_api import LcdApi
from pic_i2c_api import I2cLcd
# ============================================
# PIN CONFIGURATION
# ============================================
TRIG_PIN = 2 # Ultrasonic trigger pin
ECHO_PIN = 3 # Ultrasonic echo pin
RGB_RED = 13 # RGB LED Red pin
RGB_GREEN = 14 # RGB LED Green pin
RGB_BLUE = 15 # RGB LED Blue pin
BUZZER_PIN = 16 # Buzzer pin
LCD_SDA = 0 # I2C SDA pin
LCD_SCL = 1 # I2C SCL pin
# ============================================
# DISTANCE THRESHOLDS (in cm)
# ============================================
VERY_CLOSE = 10 # < 10cm - Critical proximity
CLOSE = 30 # < 30cm - Warning zone
MEDIUM = 100 # < 100cm - Caution zone
FAR = 200 # < 200cm - Safe zone
# ============================================
# LCD CONFIGURATION
# ============================================
I2C_ADDR = 0x27 # I2C address (try 0x3F if this doesn't work)
I2C_NUM_ROWS = 2
I2C_NUM_COLS = 16
# ============================================
# INITIALIZE HARDWARE
# ============================================
# Ultrasonic sensor pins
trigger = Pin(TRIG_PIN, Pin.OUT)
echo = Pin(ECHO_PIN, Pin.IN)
# RGB LED pins (PWM for brightness control)
red_led = PWM(Pin(RGB_RED))
green_led = PWM(Pin(RGB_GREEN))
blue_led = PWM(Pin(RGB_BLUE))
# Set PWM frequency
red_led.freq(1000)
green_led.freq(1000)
blue_led.freq(1000)
# Buzzer pin
buzzer = Pin(BUZZER_PIN, Pin.OUT)
# I2C for LCD
i2c = I2C(0, sda=Pin(LCD_SDA), scl=Pin(LCD_SCL), freq=400000)
lcd = I2cLcd(i2c, I2C_ADDR, I2C_NUM_ROWS, I2C_NUM_COLS)
# ============================================
# GLOBAL VARIABLES
# ============================================
distance_cm = 0
distance_inch = 0
measurement_count = 0
# ============================================
# FUNCTION: SET RGB LED COLOR
# ============================================
def set_rgb_color(r, g, b):
"""
Set RGB LED color using PWM
Parameters: r, g, b (0-65535 for PWM duty cycle)
"""
red_led.duty_u16(r)
green_led.duty_u16(g)
blue_led.duty_u16(b)
# ============================================
# FUNCTION: PLAY BUZZER PATTERN
# ============================================
def play_buzzer(beeps, duration=100):
"""
Play buzzer alert
Parameters:
beeps: number of beeps
duration: beep duration in ms
"""
for i in range(beeps):
buzzer.value(1)
utime.sleep_ms(duration)
buzzer.value(0)
utime.sleep_ms(duration)
# ============================================
# FUNCTION: MEASURE DISTANCE
# ============================================
def measure_distance():
"""
Measure distance using HC-SR04 ultrasonic sensor
Returns: distance in centimeters
"""
# Ensure trigger is low
trigger.low()
utime.sleep_us(2)
# Send 10us pulse to trigger
trigger.high()
utime.sleep_us(10)
trigger.low()
# Wait for echo to go high (start of pulse)
timeout_start = utime.ticks_us()
while echo.value() == 0:
signal_off = utime.ticks_us()
# Timeout after 30ms
if utime.ticks_diff(signal_off, timeout_start) > 30000:
return -1 # Timeout error
# Measure pulse width (echo high time)
timeout_start = utime.ticks_us()
while echo.value() == 1:
signal_on = utime.ticks_us()
# Timeout after 30ms
if utime.ticks_diff(signal_on, timeout_start) > 30000:
return -1 # Timeout error
# Calculate time difference
time_passed = utime.ticks_diff(signal_on, signal_off)
# Calculate distance in cm
# Speed of sound = 343 m/s = 0.0343 cm/us
# Distance = (Time * Speed) / 2 (round trip)
distance = (time_passed * 0.0343) / 2
return distance
# ============================================
# FUNCTION: UPDATE LCD DISPLAY
# ============================================
def update_lcd(distance_cm, distance_inch):
"""
Update LCD with distance measurements
"""
lcd.clear()
if distance_cm < 0:
# Error condition
lcd.putstr(" SENSOR ERROR ")
lcd.move_to(0, 1)
lcd.putstr(" Out of Range ")
elif distance_cm > 400:
# Out of range
lcd.putstr("Distance: OUT ")
lcd.move_to(0, 1)
lcd.putstr("OF RANGE (>4m) ")
else:
# Normal reading
lcd.putstr(f"Dist: {distance_cm:.1f} cm")
lcd.move_to(0, 1)
lcd.putstr(f" {distance_inch:.1f} in")
# ============================================
# FUNCTION: PROCESS DISTANCE ALERTS
# ============================================
def process_alerts(distance):
"""
Set LED color and buzzer based on distance
"""
if distance < 0:
# Error - Red LED, 3 beeps
set_rgb_color(65535, 0, 0)
play_buzzer(3, 50)
elif distance < VERY_CLOSE:
# Very close - Red LED, rapid beeping
set_rgb_color(65535, 0, 0) # Red
play_buzzer(3, 100)
print("⚠️ CRITICAL: Object very close!")
elif distance < CLOSE:
# Close - Orange LED, 2 beeps
set_rgb_color(65535, 16384, 0) # Orange
play_buzzer(2, 150)
print("⚠️ WARNING: Object close!")
elif distance < MEDIUM:
# Medium distance - Yellow LED, 1 beep
set_rgb_color(65535, 65535, 0) # Yellow
play_buzzer(1, 100)
print("⚠️ CAUTION: Object in range")
elif distance < FAR:
# Far - Green LED, no beep
set_rgb_color(0, 65535, 0) # Green
print("✅ SAFE: Object detected at safe distance")
else:
# Very far or no object - Blue LED
set_rgb_color(0, 0, 65535) # Blue
print("🔵 CLEAR: No object in range")
# ============================================
# FUNCTION: PRINT SERIAL DATA
# ============================================
def print_serial_data(distance_cm, distance_inch, count):
"""
Print formatted data to serial monitor
"""
print(f"#{count:04d} | {distance_cm:6.2f} cm | {distance_inch:6.2f} in | ", end="")
# ============================================
# STARTUP SEQUENCE
# ============================================
def startup_sequence():
"""
Display startup animation and test components
"""
print("\n╔═══════════════════════════════════════════════╗")
print("║ RASPBERRY PI PICO - ULTRASONIC DISTANCE ║")
print("║ MEASUREMENT SYSTEM ║")
print("╚═══════════════════════════════════════════════╝\n")
# Welcome message on LCD
lcd.clear()
lcd.putstr(" ULTRASONIC RNG ")
lcd.move_to(0, 1)
lcd.putstr(" SYSTEM v1.0 ")
# LED test sequence
print("🔧 Testing components...")
print(" - Red LED...", end="")
set_rgb_color(65535, 0, 0)
utime.sleep_ms(300)
print(" OK")
print(" - Green LED...", end="")
set_rgb_color(0, 65535, 0)
utime.sleep_ms(300)
print(" OK")
print(" - Blue LED...", end="")
set_rgb_color(0, 0, 65535)
utime.sleep_ms(300)
print(" OK")
set_rgb_color(0, 0, 0)
# Buzzer test
print(" - Buzzer...", end="")
play_buzzer(2, 100)
print(" OK")
utime.sleep(1)
lcd.clear()
lcd.putstr(" SYSTEM READY ")
lcd.move_to(0, 1)
lcd.putstr(" Starting... ")
print("\n✅ All systems operational!")
print("📊 Beginning measurements...\n")
print("Count | Distance (cm) | Distance (in) | Status")
print("─" * 60)
utime.sleep(2)
# ============================================
# MAIN PROGRAM
# ============================================
def main():
"""
Main program loop
"""
global distance_cm, distance_inch, measurement_count
# Run startup sequence
startup_sequence()
# Main measurement loop
while True:
try:
# Measure distance
distance_cm = measure_distance()
# Convert to inches (1 inch = 2.54 cm)
distance_inch = distance_cm / 2.54
# Increment counter
measurement_count += 1
# Update LCD display
update_lcd(distance_cm, distance_inch)
# Print to serial
print_serial_data(distance_cm, distance_inch, measurement_count)
# Process alerts (LED and buzzer)
process_alerts(distance_cm)
# Delay between measurements (adjust as needed)
utime.sleep_ms(500)
except KeyboardInterrupt:
print("\n\n⛔ Program stopped by user")
lcd.clear()
lcd.putstr(" STOPPED! ")
set_rgb_color(0, 0, 0)
buzzer.value(0)
break
except Exception as e:
print(f"\n❌ Error: {e}")
lcd.clear()
lcd.putstr(" SYSTEM ERROR ")
lcd.move_to(0, 1)
lcd.putstr(" Check Serial ")
set_rgb_color(65535, 0, 0)
play_buzzer(5, 100)
utime.sleep(5)
# ============================================
# RUN MAIN PROGRAM
# ============================================
if __name__ == "__main__":
main()