import machine
import utime
import sys
import uselect
import onewire
import ds18x20
# =========================================================
# LCD 16x2 I2C (PCF8574) SIN LIBRERIA EXTERNA
# =========================================================
class LcdI2C:
LCD_CLR = 0x01
LCD_HOME = 0x02
LCD_ENTRY_MODE = 0x04
LCD_DISPLAY_CTRL = 0x08
LCD_FUNCTION = 0x20
LCD_SET_DDRAM = 0x80
LCD_ENTRY_INC = 0x02
LCD_DISPLAY_ON = 0x04
LCD_2LINE = 0x08
LCD_4BIT = 0x00
LCD_5X8DOTS = 0x00
MASK_RS = 0x01
MASK_E = 0x04
MASK_BL = 0x08
def __init__(self, i2c, addr=0x27, cols=16, rows=2):
self.i2c = i2c
self.addr = addr
self.cols = cols
self.rows = rows
self.backlight = self.MASK_BL
utime.sleep_ms(50)
self._write_init_nibble(0x03)
utime.sleep_ms(5)
self._write_init_nibble(0x03)
utime.sleep_ms(5)
self._write_init_nibble(0x03)
utime.sleep_ms(1)
self._write_init_nibble(0x02)
utime.sleep_ms(1)
self.command(self.LCD_FUNCTION | self.LCD_2LINE | self.LCD_4BIT | self.LCD_5X8DOTS)
self.command(self.LCD_DISPLAY_CTRL | self.LCD_DISPLAY_ON)
self.command(self.LCD_ENTRY_MODE | self.LCD_ENTRY_INC)
self.clear()
def _write_byte_raw(self, data):
self.i2c.writeto(self.addr, bytes([data | self.backlight]))
def _pulse(self, data):
self._write_byte_raw(data | self.MASK_E)
utime.sleep_us(1)
self._write_byte_raw(data & ~self.MASK_E)
utime.sleep_us(50)
def _write4bits(self, nibble, rs=0):
data = ((nibble & 0x0F) << 4) | (self.MASK_RS if rs else 0)
self._pulse(data)
def _write_init_nibble(self, nibble):
data = ((nibble & 0x0F) << 4)
self._pulse(data)
def send(self, value, rs=0):
self._write4bits((value >> 4) & 0x0F, rs)
self._write4bits(value & 0x0F, rs)
def command(self, cmd):
self.send(cmd, 0)
if cmd == self.LCD_CLR or cmd == self.LCD_HOME:
utime.sleep_ms(2)
def write_char(self, ch):
self.send(ord(ch), 1)
def putstr(self, text):
for ch in text:
self.write_char(ch)
def clear(self):
self.command(self.LCD_CLR)
utime.sleep_ms(2)
def move_to(self, col, row):
row_offsets = [0x00, 0x40, 0x14, 0x54]
self.command(self.LCD_SET_DDRAM | (col + row_offsets[row]))
def backlight_on(self):
self.backlight = self.MASK_BL
self._write_byte_raw(0)
def backlight_off(self):
self.backlight = 0
self._write_byte_raw(0)
# =========================================================
# RTC DS1307
# =========================================================
class DS1307:
def __init__(self, i2c, addr=0x68):
self.i2c = i2c
self.addr = addr
def _bcd_to_dec(self, bcd):
return ((bcd >> 4) * 10) + (bcd & 0x0F)
def _dec_to_bcd(self, dec):
return ((dec // 10) << 4) | (dec % 10)
def obtener_tiempo(self):
try:
data = self.i2c.readfrom_mem(self.addr, 0x00, 7)
segundo = self._bcd_to_dec(data[0] & 0x7F)
minuto = self._bcd_to_dec(data[1])
hora = self._bcd_to_dec(data[2] & 0x3F)
dia = self._bcd_to_dec(data[4])
mes = self._bcd_to_dec(data[5])
anio = self._bcd_to_dec(data[6]) + 2000
return anio, mes, dia, hora, minuto, segundo
except:
return 2026, 5, 26, 12, 0, 0
def ajustar_tiempo(self, anio, mes, dia, hora, minuto, segundo):
try:
data = bytearray(7)
data[0] = self._dec_to_bcd(segundo)
data[1] = self._dec_to_bcd(minuto)
data[2] = self._dec_to_bcd(hora)
data[3] = self._dec_to_bcd(1)
data[4] = self._dec_to_bcd(dia)
data[5] = self._dec_to_bcd(mes)
data[6] = self._dec_to_bcd(anio - 2000)
self.i2c.writeto_mem(self.addr, 0x00, data)
return True
except:
return False
# =========================================================
# HARDWARE
# =========================================================
i2c0 = machine.I2C(0, sda=machine.Pin(20), scl=machine.Pin(21), freq=100000)
lcd_disponible = False
lcd = None
try:
dispositivos = i2c0.scan()
print("I2C detectados:", [hex(x) for x in dispositivos])
lcd_addr = None
if 0x27 in dispositivos:
lcd_addr = 0x27
elif 0x3F in dispositivos:
lcd_addr = 0x3F
if lcd_addr is not None:
lcd = LcdI2C(i2c0, addr=lcd_addr, cols=16, rows=2)
lcd.backlight_on()
lcd_disponible = True
print("LCD I2C detectado en:", hex(lcd_addr))
else:
print("LCD I2C no detectado. Revisa direccion 0x27/0x3F y cableado.")
except Exception as e:
print("Error LCD I2C:", e)
rtc = DS1307(i2c0)
pin_ds = machine.Pin(26)
ow = onewire.OneWire(pin_ds)
ds_sensor = ds18x20.DS18X20(ow)
led_rojo = machine.Pin(6, machine.Pin.OUT)
led_verde = machine.Pin(7, machine.Pin.OUT)
led_azul = machine.Pin(8, machine.Pin.OUT)
leds = [led_rojo, led_verde, led_azul]
motor_pwm = machine.PWM(machine.Pin(13))
motor_pwm.freq(1000)
motor_pwm.duty_u16(0)
trigger = machine.Pin(14, machine.Pin.OUT)
echo = machine.Pin(15, machine.Pin.IN)
trigger.value(0)
servo = machine.PWM(machine.Pin(16))
servo.freq(50)
buzzer = machine.Pin(17, machine.Pin.OUT)
buzzer.value(0)
poller = uselect.poll()
poller.register(sys.stdin, uselect.POLLIN)
# =========================================================
# AUXILIARES
# =========================================================
def _lcd_fit(txt):
txt = str(txt)
if len(txt) > 16:
return txt[:16]
return txt + (" " * (16 - len(txt)))
def lcd_texto(linea1, linea2=""):
if not lcd_disponible:
return
try:
lcd.clear()
lcd.move_to(0, 0)
lcd.putstr(_lcd_fit(linea1))
lcd.move_to(0, 1)
lcd.putstr(_lcd_fit(linea2))
except Exception as e:
print("LCD write error:", e)
def responder(comando, accion):
lcd_texto(comando, accion)
print(accion)
def comando_no_reconocido():
lcd_texto("comando no", "reconocido")
print("comando no reconocido")
def leer_temperatura_ds18b20():
try:
roms = ds_sensor.scan()
if not roms:
return None
ds_sensor.convert_temp()
utime.sleep_ms(750)
temp_c = ds_sensor.read_temp(roms[0])
return temp_c
except Exception as e:
print("Error lectura DS18B20:", e)
return None
def set_servo_angle(angle):
angle = max(0, min(180, angle))
min_duty = 1638
max_duty = 8192
duty = int(min_duty + (angle / 180.0) * (max_duty - min_duty))
servo.duty_u16(duty)
def medir_distancia_cm():
try:
trigger.value(0)
utime.sleep_us(5)
trigger.value(1)
utime.sleep_us(10)
trigger.value(0)
duracion = machine.time_pulse_us(echo, 1, 30000)
if duracion < 0:
return -1
return (duracion * 0.0343) / 2
except:
return -1
def leds_todos(valor):
for led in leds:
led.value(valor)
def secuencia_chase():
for _ in range(3):
for led in leds:
leds_todos(0)
led.value(1)
utime.sleep_ms(150)
leds_todos(0)
def secuencia_blink():
for _ in range(5):
leds_todos(1)
utime.sleep_ms(150)
leds_todos(0)
utime.sleep_ms(150)
def secuencia_alterno():
for _ in range(6):
led_rojo.value(1)
led_verde.value(0)
led_azul.value(1)
utime.sleep_ms(120)
led_rojo.value(0)
led_verde.value(1)
led_azul.value(0)
utime.sleep_ms(120)
leds_todos(0)
def reiniciar_sistema():
motor_pwm.duty_u16(0)
leds_todos(0)
buzzer.value(0)
set_servo_angle(0)
# =========================================================
# COMANDOS
# =========================================================
def procesar_comando(entrada):
cmd = entrada.strip().lower()
if not cmd:
return
try:
if cmd == "temperatura":
temp_c = leer_temperatura_ds18b20()
if temp_c is None:
lcd_texto("Error Temp", "Sensor offline")
print("Error: No se detecto DS18B20")
else:
lcd_texto("temperatura", "T={:.1f}C".format(temp_c))
print("Temperatura DS18B20: {:.2f} C".format(temp_c))
elif cmd.startswith("pwm="):
valor = int(cmd.split("=")[1])
valor = max(0, min(65535, valor))
motor_pwm.duty_u16(valor)
responder(cmd, "PWM={}".format(valor))
elif cmd == "motor on":
motor_pwm.duty_u16(65535)
responder("motor on", "motor encendido")
elif cmd == "motor off":
motor_pwm.duty_u16(0)
responder("motor off", "motor apagado")
elif cmd == "leds on":
leds_todos(1)
responder("leds on", "leds encendidos")
elif cmd == "leds off":
leds_todos(0)
responder("leds off", "leds apagados")
elif cmd == "hora":
anio, mes, dia, hora, minuto, segundo = rtc.obtener_tiempo()
responder("hora", "{:02d}:{:02d}:{:02d}".format(hora, minuto, segundo))
elif cmd == "fecha":
anio, mes, dia, hora, minuto, segundo = rtc.obtener_tiempo()
responder("fecha", "{:02d}/{:02d}/{:04d}".format(dia, mes, anio))
elif cmd.startswith("set hora="):
dato = cmd.split("=", 1)[1].strip()
partes = dato.split(":")
if len(partes) != 3:
raise ValueError("Formato incorrecto")
hh = int(partes[0])
mm = int(partes[1])
ss = int(partes[2])
anio, mes, dia, _, _, _ = rtc.obtener_tiempo()
rtc.ajustar_tiempo(anio, mes, dia, hh, mm, ss)
responder("set hora", "{:02d}:{:02d}:{:02d}".format(hh, mm, ss))
elif cmd.startswith("set fecha="):
dato = cmd.split("=", 1)[1].strip()
partes = dato.split("/")
if len(partes) != 3:
raise ValueError("Formato incorrecto")
dd = int(partes[0])
mm = int(partes[1])
aa = int(partes[2])
_, _, _, hora, minuto, segundo = rtc.obtener_tiempo()
rtc.ajustar_tiempo(aa, mm, dd, hora, minuto, segundo)
responder("set fecha", "{:02d}/{:02d}/{:04d}".format(dd, mm, aa))
elif cmd == "distancia":
d = medir_distancia_cm()
if d < 0:
responder("distancia", "sin medicion")
else:
responder("distancia", "{:.1f} cm".format(d))
elif cmd.startswith("servo="):
ang = int(cmd.split("=")[1])
ang = max(0, min(180, ang))
set_servo_angle(ang)
responder("servo", "{} grados".format(ang))
elif cmd == "buzzer on":
buzzer.value(1)
responder("buzzer on", "buzzer activo")
elif cmd == "buzzer off":
buzzer.value(0)
responder("buzzer off", "buzzer apagado")
elif cmd == "buzzer beep":
for _ in range(3):
buzzer.value(1)
utime.sleep_ms(100)
buzzer.value(0)
utime.sleep_ms(100)
responder("buzzer beep", "3 beeps")
elif cmd == "led rojo on":
led_rojo.value(1)
responder("led rojo on", "rojo encendido")
elif cmd == "led rojo off":
led_rojo.value(0)
responder("led rojo off", "rojo apagado")
elif cmd == "led verde on":
led_verde.value(1)
responder("led verde on", "verde encendido")
elif cmd == "led verde off":
led_verde.value(0)
responder("led verde off", "verde apagado")
elif cmd == "led azul on":
led_azul.value(1)
responder("led azul on", "azul encendido")
elif cmd == "led azul off":
led_azul.value(0)
responder("led azul off", "azul apagado")
elif cmd == "motor medio":
motor_pwm.duty_u16(32768)
responder("motor medio", "motor 50%")
elif cmd == "reset":
reiniciar_sistema()
responder("reset", "sistema listo")
elif cmd == "secuencia chase":
lcd_texto("secuencia chase", "ejecutando")
print("Secuencia chase ejecutada")
secuencia_chase()
lcd_texto("secuencia chase", "completada")
elif cmd == "secuencia blink":
lcd_texto("secuencia blink", "ejecutando")
print("Secuencia blink ejecutada")
secuencia_blink()
lcd_texto("secuencia blink", "completada")
elif cmd == "secuencia alterno":
lcd_texto("secuencia alterno", "ejecutando")
print("Secuencia alterno ejecutada")
secuencia_alterno()
lcd_texto("secuencia alterno", "completada")
elif cmd == "help":
lcd_texto("help", "ver terminal")
print("Comandos disponibles:")
print("temperatura")
print("pwm=<0-65535>")
print("motor on")
print("motor off")
print("leds on")
print("leds off")
print("hora")
print("fecha")
print("set hora=HH:MM:SS")
print("set fecha=DD/MM/AAAA")
print("distancia")
print("servo=<0-180>")
print("buzzer on")
print("buzzer off")
print("buzzer beep")
print("led rojo on / off")
print("led verde on / off")
print("led azul on / off")
print("motor medio")
print("reset")
print("secuencia chase")
print("secuencia blink")
print("secuencia alterno")
print("Si no se reconoce el comando: comando no reconocido")
else:
comando_no_reconocido()
except Exception as e:
lcd_texto("error comando", "ver terminal")
print("Error procesando comando '{}': {}".format(cmd, e))
# =========================================================
# INICIO
# =========================================================
reiniciar_sistema()
if lcd_disponible:
lcd_texto("Sistema listo", "esperando cmd")
print("Interprete listo")
print("Proyecto con LCD I2C, DS18B20, DS1307, motor, servo, buzzer y HC-SR04")
print("Escribe 'help' para ver comandos")
buffer_serial = ""
while True:
try:
if poller.poll(0):
ch = sys.stdin.read(1)
if ch in ['\n', '\r']:
if buffer_serial.strip():
procesar_comando(buffer_serial)
buffer_serial = ""
else:
buffer_serial += ch
utime.sleep_ms(10)
except Exception as e:
print("Error en loop principal:", e)
buffer_serial = ""
utime.sleep_ms(50)