from machine import Pin, PWM
import time
import json
import network
from umqtt.simple import MQTTClient
import tm1637
# SECCION 1. Pines y tiempos
PIN_VEH_ROJO = 25
PIN_VEH_AMARILLO = 26
PIN_VEH_VERDE = 27
PIN_PEA_ROJO = 32
PIN_PEA_VERDE = 33
PIN_BOTON = 14
PIN_BUZZER = 13
PIN_CLK = 18
PIN_DIO = 19
T_AMARILLO = 3000
T_TODO_ROJO = 1000
T_CRUCE = 20000
T_DESPEJE = 2000
T_ANTIRREBOTE = 200
FREC_PITIDO = 2000
DUR_PITIDO = 60
# SECCION 1B. Red y MQTT
ID_ESTUDIANTE = "calvinmiro"
WIFI_SSID = "Wokwi-GUEST"
WIFI_CLAVE = ""
BROKER = "broker.hivemq.com"
#BROKER = "broker.noexiste.utp"
PUERTO = 1883
CLIENTE_ID = "semaforo-" + ID_ESTUDIANTE
BASE = "utp/ds8/" + ID_ESTUDIANTE + "/"
TOPICO_ESTADO = (BASE + "estado").encode()
TOPICO_CMD = (BASE + "cmd").encode()
TOPICO_CONEXION = (BASE + "conexion").encode()
T_WIFI = 15000
T_PING = 30000
T_REINTENTO = 10000
# SECCION 2. Objetos de hardware y funciones de salida
veh_rojo = Pin(PIN_VEH_ROJO, Pin.OUT)
veh_amarillo = Pin(PIN_VEH_AMARILLO, Pin.OUT)
veh_verde = Pin(PIN_VEH_VERDE, Pin.OUT)
pea_rojo = Pin(PIN_PEA_ROJO, Pin.OUT)
pea_verde = Pin(PIN_PEA_VERDE, Pin.OUT)
boton = Pin(PIN_BOTON, Pin.IN, Pin.PULL_UP)
buzzer = PWM(Pin(PIN_BUZZER), freq=FREC_PITIDO, duty_u16=0)
pantalla = tm1637.TM1637(clk=Pin(PIN_CLK), dio=Pin(PIN_DIO))
def luces_vehiculo(rojo, amarillo, verde):
veh_rojo.value(rojo)
veh_amarillo.value(amarillo)
veh_verde.value(verde)
def luces_peaton(rojo, verde):
pea_rojo.value(rojo)
pea_verde.value(verde)
def sonar(activo):
if activo:
buzzer.duty_u16(32768)
else:
buzzer.duty_u16(0)
# SECCION 3. Boton con interrupcion y antirrebote
solicitud = False
ultimo_toque = 0
def al_presionar(pin):
global solicitud, ultimo_toque
ahora = time.ticks_ms()
if time.ticks_diff(ahora, ultimo_toque) > T_ANTIRREBOTE:
solicitud = True
ultimo_toque = ahora
boton.irq(trigger=Pin.IRQ_FALLING, handler=al_presionar)
# SECCION 4. Ritmo del pitido segun el tiempo restante
def intervalo_pitido(restante_ms):
if restante_ms > 10000:
return 1000
elif restante_ms > 5000:
return 500
elif restante_ms > 3000:
return 250
else:
return 120
# SECCION 5. Estados del semaforo
VEH_VERDE = "VEHICULOS EN VERDE"
VEH_AMARILLO = "VEHICULOS EN AMARILLO"
TODO_ROJO = "TODO EN ROJO"
CRUCE = "CRUCE PEATONAL"
DESPEJE = "DESPEJE"
estado = VEH_VERDE
inicio_estado = 0
def cambiar_estado(nuevo):
global estado, inicio_estado
estado = nuevo
inicio_estado = time.ticks_ms()
print("Estado:", nuevo)
if nuevo == VEH_VERDE:
luces_vehiculo(0, 0, 1)
luces_peaton(1, 0)
pantalla.guiones()
elif nuevo == VEH_AMARILLO:
luces_vehiculo(0, 1, 0)
luces_peaton(1, 0)
elif nuevo == TODO_ROJO:
luces_vehiculo(1, 0, 0)
luces_peaton(1, 0)
elif nuevo == CRUCE:
luces_vehiculo(1, 0, 0)
luces_peaton(0, 1)
elif nuevo == DESPEJE:
luces_vehiculo(1, 0, 0)
luces_peaton(1, 0)
sonar(False)
pantalla.numero(0)
if nuevo != CRUCE:
publicar_estado()
# SECCION 5B. Conexion WiFi
wlan = network.WLAN(network.STA_IF)
def conectar_wifi():
wlan.active(True)
if wlan.isconnected():
return True
print("Conectando a WiFi", WIFI_SSID, end="")
wlan.connect(WIFI_SSID, WIFI_CLAVE)
inicio = time.ticks_ms()
while not wlan.isconnected():
if time.ticks_diff(time.ticks_ms(), inicio) > T_WIFI:
print(" sin respuesta")
return False
print(".", end="")
time.sleep_ms(250)
print(" conectado. IP:", wlan.ifconfig()[0])
return True
# SECCION 5C. MQTT: publicar, recibir y reconectar
cliente = None
mqtt_ok = False
ultimo_ping = 0
ultimo_intento = 0
def publicar(topico, texto, retener=False):
global mqtt_ok
if not mqtt_ok:
return
try:
cliente.publish(topico, texto.encode(), retener)
except OSError as e:
print("Error al publicar:", e)
mqtt_ok = False
def publicar_estado():
restante = ultimo_segundo if estado == CRUCE else 0
datos = json.dumps({"estado": estado, "restante": restante})
publicar(TOPICO_ESTADO, datos)
def al_mensaje(topico, msg):
global solicitud
orden = msg.decode().strip().upper()
print("MQTT recibido:", orden)
if orden == "CRUZAR":
solicitud = True
elif orden == "ESTADO":
publicar_estado()
else:
print("Comando desconocido")
def conectar_mqtt():
global cliente, mqtt_ok, ultimo_ping
try:
cliente = MQTTClient(CLIENTE_ID, BROKER, port=PUERTO, keepalive=60)
cliente.set_callback(al_mensaje)
cliente.set_last_will(TOPICO_CONEXION, b"offline", retain=True)
cliente.connect()
cliente.subscribe(TOPICO_CMD)
mqtt_ok = True
ultimo_ping = time.ticks_ms()
publicar(TOPICO_CONEXION, "online", True)
publicar_estado()
print("MQTT conectado a", BROKER)
print("Comandos en:", TOPICO_CMD.decode())
except OSError as e:
print("No se pudo conectar al broker:", e)
mqtt_ok = False
def atender_red():
global mqtt_ok, ultimo_ping, ultimo_intento
ahora = time.ticks_ms()
if mqtt_ok:
try:
cliente.check_msg()
if time.ticks_diff(ahora, ultimo_ping) >= T_PING:
cliente.ping()
ultimo_ping = ahora
except OSError as e:
print("Conexion MQTT perdida:", e)
mqtt_ok = False
ultimo_intento = ahora
elif estado == VEH_VERDE:
if time.ticks_diff(ahora, ultimo_intento) >= T_REINTENTO:
ultimo_intento = ahora
if wlan.isconnected():
print("Reintentando MQTT...")
conectar_mqtt()
else:
print("Sin WiFi: se reintenta en 10 s")
try:
wlan.connect(WIFI_SSID, WIFI_CLAVE)
except OSError:
pass
# SECCION 6. Bucle principal
ultimo_pitido = 0
ultimo_segundo = -1
cambiar_estado(VEH_VERDE)
if conectar_wifi():
conectar_mqtt()
else:
print("Sin red: el semaforo funciona solo con el boton")
while True:
ahora = time.ticks_ms()
transcurrido = time.ticks_diff(ahora, inicio_estado)
if estado == VEH_VERDE:
if solicitud:
solicitud = False
print("Solicitud de cruce aceptada")
cambiar_estado(VEH_AMARILLO)
elif estado == VEH_AMARILLO:
if transcurrido >= T_AMARILLO:
cambiar_estado(TODO_ROJO)
elif estado == TODO_ROJO:
if transcurrido >= T_TODO_ROJO:
cambiar_estado(CRUCE)
ultimo_segundo = -1
ultimo_pitido = time.ticks_ms()
sonar(True)
elif estado == CRUCE:
restante = T_CRUCE - transcurrido
if restante <= 0:
cambiar_estado(DESPEJE)
else:
segundos = (restante + 999) // 1000
if segundos != ultimo_segundo:
ultimo_segundo = segundos
pantalla.numero(segundos)
print("Tiempo para cruzar:", segundos, "s")
publicar_estado()
desde_pitido = time.ticks_diff(ahora, ultimo_pitido)
if desde_pitido >= intervalo_pitido(restante):
ultimo_pitido = ahora
sonar(True)
elif desde_pitido > DUR_PITIDO:
sonar(False)
elif estado == DESPEJE:
if transcurrido >= T_DESPEJE:
solicitud = False
cambiar_estado(VEH_VERDE)
atender_red()
time.sleep_ms(10)