# main.py
# Water-level style monitor: HC-SR04 -> Pico
# SAFE if distance <= 200 cm -> green LED ON and "SAFE" on LCD
# DANGER if distance > 200 cm -> red LED blinks and "DANGER" on LCD
from machine import Pin, I2C
import utime
from i2c_lcd import I2cLcd
# ----- CONFIGURE PINS (change these if your wiring differs) -----
TRIG_PIN = 3 # GP3 on Pico
ECHO_PIN = 2 # GP2 on Pico
SDA_PIN = 4 # GP4 (I2C SDA)
SCL_PIN = 5 # GP5 (I2C SCL)
GREEN_LED = 14 # GP14
RED_LED = 15 # GP15
# ----- SETUP HARDWARE -----
TRIG = Pin(TRIG_PIN, Pin.OUT)
ECHO = Pin(ECHO_PIN, Pin.IN)
green = Pin(GREEN_LED, Pin.OUT)
red = Pin(RED_LED, Pin.OUT)
# Setup I2C and LCD (if available)
i2c = I2C(0, scl=Pin(SCL_PIN), sda=Pin(SDA_PIN), freq=100000)
devices = i2c.scan()
lcd = None
if len(devices) == 0:
# no I2C device found
print("No I2C device found. LCD will be disabled.")
else:
addr = devices[0]
print("I2C device(s) found:", [hex(a) for a in devices], "-> using", hex(addr))
lcd = I2cLcd(i2c, addr, 2, 16)
# ----- ULTRASONIC HELPER (with timeouts) -----
def get_distance(timeout_us=30000):
"""
Returns distance in cm or None if timeout / no echo.
timeout_us default 30000 (~30 ms) which corresponds to ~5 meters return time safe limit.
"""
TRIG.value(0)
utime.sleep_us(2)
TRIG.value(1)
utime.sleep_us(10)
TRIG.value(0)
start = utime.ticks_us()
# wait for ECHO to go high
while ECHO.value() == 0:
if utime.ticks_diff(utime.ticks_us(), start) > timeout_us:
return None
t1 = utime.ticks_us()
# wait for ECHO to go low
while ECHO.value() == 1:
if utime.ticks_diff(utime.ticks_us(), t1) > timeout_us:
return None
t2 = utime.ticks_us()
dt = utime.ticks_diff(t2, t1) # microseconds
# sound speed 343 m/s => 0.0343 cm/us ; distance = (dt * 0.0343) / 2
distance_cm = (dt * 0.0343) / 2.0
return distance_cm
# ----- MAIN LOOP -----
THRESHOLD = 200.0 # cm
def show_on_lcd(line1, line2=""):
if lcd:
lcd.move_to(0,0)
lcd.putstr(line1.ljust(16)[:16])
lcd.move_to(0,1)
lcd.putstr(line2.ljust(16)[:16])
# initial state
green.value(0)
red.value(0)
utime.sleep(0.1)
while True:
dist = get_distance()
if dist is None:
# no echo / sensor error
print("No echo (timeout). Check wiring / power.")
show_on_lcd("Distance: --.-- cm", "No Echo")
green.value(0)
red.value(0)
utime.sleep(1.0)
continue
# round for display
dstr = "{:6.1f} cm".format(dist)
print("Distance:", dstr)
if dist <= THRESHOLD:
# SAFE
green.value(1)
red.value(0)
show_on_lcd("Distance: " + dstr, "SAFE")
utime.sleep(0.8) # slower update while safe
else:
# DANGER: blink red
green.value(0)
show_on_lcd("Distance: " + dstr, "DANGER")
# blink once
red.value(1)
utime.sleep(0.35)
red.value(0)
utime.sleep(0.35)