from controladorMotor import ControladorMotor
from controladorLed import ControladorLed
from controladorSensor import ControladorSensor
from controladorPantalla import ControladorPantalla
import utime
import _thread
import network
from umqtt.simple import MQTTClient
# ----------------------
# Configuración Wi-Fi
# ----------------------
NOMBRE_RED = "Wokwi-GUEST"
CONTRASEÑA = ""
# ----------------------
# Configuración MQTT
# ----------------------
BROKER = "test.mosquitto.org"
PUERTO = 1883
ID_CLIENTE = "bomba_agua_cliente"
# ----------------------
# Variables globales de estado
# ----------------------
alarma_activada = False # Se activa si la temperatura supera el umbral
bomba_encendida = False # Estado real de la bomba
tiempo_inicio_bomba = 0 # Momento en el que se activó la bomba
temperatura_anterior = None
humedad_anterior = None
UMBRAL_TEMP_ALARMA = 40
comando_manual = None # Para control manual del motor vía MQTT
cliente_mqtt = None
# Crear un bloqueo para evitar acceso concurrente al cliente MQTT
bloqueo_mqtt = _thread.allocate_lock()
# ----------------------
# Instanciación de dispositivos
# ----------------------
sensor_dht = ControladorSensor(pin=41)
pantalla_oled = ControladorPantalla(3, 46)
led_alarma = ControladorLed(pin=16)
motor_bomba = ControladorMotor(pin_ap=6, pin_am=7, pin_bp=5, pin_bm=4)
# ----------------------
# Función para conectar a Wi-Fi
# ----------------------
def conectar_wifi():
red = network.WLAN(network.STA_IF)
red.active(True)
red.connect(NOMBRE_RED, CONTRASEÑA)
while not red.isconnected():
utime.sleep(1)
print("Conectado a WiFi:", red.ifconfig())
# ----------------------
# Función para conectar al broker MQTT
# ----------------------
def conectar_mqtt():
global cliente_mqtt
intentos = 0
max_intentos = 3
while intentos < max_intentos:
try:
print(f"Intento de conexión MQTT {intentos+1}/{max_intentos}")
cliente_mqtt = MQTTClient(ID_CLIENTE, BROKER, PUERTO)
cliente_mqtt.set_callback(callback_mqtt)
cliente_mqtt.connect()
# Convertir tema a bytes
cliente_mqtt.subscribe(b"bomba_agua_mqtt/control")
cliente_mqtt.subscribe(b"bomba_agua_mqtt/estado")
print("Conectado a MQTT Broker")
return True
except OSError as e:
print(f"Error conectando a MQTT: {e}")
intentos += 1
utime.sleep(2)
print("No se pudo conectar al broker MQTT")
return False
# ----------------------
# Callback para recibir mensajes MQTT
# ----------------------
def callback_mqtt(tema, mensaje):
global comando_manual
# Comparar temas como bytes
if tema == b"bomba_agua_mqtt/control":
comando = mensaje.decode().lower()
if comando == "on":
comando_manual = "on"
print("Comando ON recibido")
elif comando == "off":
comando_manual = "off"
print("Comando OFF recibido")
# ----------------------
# Hilo 1: Lectura del sensor, actualización del display y publicación MQTT
# ----------------------
def hilo_sensor_display():
global alarma_activada, temperatura_anterior, humedad_anterior
while True:
temperatura_leida, humedad_leida = sensor_dht.leer_datos()
# Evaluar si se debe activar la alarma
if temperatura_leida >= UMBRAL_TEMP_ALARMA:
alarma_activada = True
mensaje_linea_1 = "ALERTA TEMP!"
mensaje_linea_2 = "Bomba activada"
else:
alarma_activada = False
mensaje_linea_1 = "Sistema normal"
mensaje_linea_2 = ""
# Actualizar el display solo si hubo cambio en la lectura
if (temperatura_leida != temperatura_anterior) or (humedad_leida != humedad_anterior):
pantalla_oled.limpiar_pantalla()
pantalla_oled.escribir_texto(f"Temp: {temperatura_leida} C", 0, 0)
pantalla_oled.escribir_texto(f"Hum: {humedad_leida} %", 0, 10)
pantalla_oled.escribir_texto(mensaje_linea_1, 0, 20)
pantalla_oled.escribir_texto(mensaje_linea_2, 0, 30)
temperatura_anterior = temperatura_leida
humedad_anterior = humedad_leida
# Publicar valores en MQTT con temas en bytes
try:
with bloqueo_mqtt:
cliente_mqtt.publish(b"bomba_agua_mqtt/temperatura", str(temperatura_leida))
cliente_mqtt.publish(b"bomba_agua_mqtt/humedad", str(humedad_leida))
except OSError as e:
print(f"Error publicando datos del sensor: {e}")
utime.sleep(1)
# ----------------------
# Hilo 2: Control del motor de la bomba con registro de tiempo y control manual MQTT
# ----------------------
def hilo_motor():
global alarma_activada, bomba_encendida, tiempo_inicio_bomba, comando_manual
estado_anterior = False # Para detectar cambios en el estado de la bomba
while True:
# Si se recibe un comando manual, éste tiene prioridad
if comando_manual is not None:
if comando_manual == "on":
if not bomba_encendida:
tiempo_inicio_bomba = utime.time()
bomba_encendida = True
# Publicar estado "on" cuando se enciende manualmente - CON RETAIN
try:
with bloqueo_mqtt:
cliente_mqtt.publish(b"bomba_agua_mqtt/estado", b"on", qos=1, retain=True)
print("Estado publicado: ON (manual) [RETENIDO]")
except OSError as e:
print("Error publicando estado:", e)
motor_bomba.ejecutar_pasos()
elif comando_manual == "off":
if bomba_encendida:
tiempo_total = utime.time() - tiempo_inicio_bomba
print(f"La bomba estuvo activada por {tiempo_total:.2f} segundos.")
try:
with bloqueo_mqtt:
cliente_mqtt.publish(b"bomba_agua_mqtt/tiempo_motor", str(tiempo_total))
cliente_mqtt.publish(b"bomba_agua_mqtt/estado", b"off", qos=1, retain=True)
print("Estado publicado: OFF (manual) [RETENIDO]")
except OSError as e:
print("Error publicando datos:", e)
bomba_encendida = False
motor_bomba.apagar_motor()
comando_manual = None # Reiniciamos el comando manual después de procesarlo
else:
# Control automático según el sensor
if alarma_activada:
if not bomba_encendida:
tiempo_inicio_bomba = utime.time()
bomba_encendida = True
# Publicar estado "on" cuando se enciende automáticamente - CON RETAIN
try:
with bloqueo_mqtt:
cliente_mqtt.publish(b"bomba_agua_mqtt/estado", b"on", qos=1, retain=True)
print("Estado publicado: ON (automático) [RETENIDO]")
except OSError as e:
print("Error publicando estado:", e)
motor_bomba.ejecutar_pasos()
else:
if bomba_encendida:
tiempo_total = utime.time() - tiempo_inicio_bomba
print(f"La bomba estuvo activada por {tiempo_total:.2f} segundos.")
try:
with bloqueo_mqtt:
cliente_mqtt.publish(b"bomba_agua_mqtt/tiempo_motor", str(tiempo_total))
cliente_mqtt.publish(b"bomba_agua_mqtt/estado", b"off", qos=1, retain=True)
print("Estado publicado: OFF (automático) [RETENIDO]")
except OSError as e:
print("Error publicando datos:", e)
bomba_encendida = False
motor_bomba.apagar_motor()
# Si hubo un cambio de estado que no hayamos capturado en los casos anteriores
if bomba_encendida != estado_anterior:
estado_anterior = bomba_encendida
utime.sleep(0.1)
# ----------------------
# Hilo 3: LED parpadeante
# ----------------------
def hilo_led():
global alarma_activada
estado_led = False
while True:
if alarma_activada:
estado_led = not estado_led
if estado_led:
led_alarma.encender_led()
else:
led_alarma.apagar_led()
utime.sleep(0.5)
else:
led_alarma.apagar_led()
utime.sleep(0.5)
# ----------------------
# Hilo 4: Bucle MQTT para recibir mensajes
# ----------------------
def hilo_mqtt_loop():
global cliente_mqtt
while True:
try:
if cliente_mqtt is not None:
with bloqueo_mqtt:
cliente_mqtt.check_msg()
except OSError as e:
print(f"Error en check_msg: {e}")
utime.sleep(2)
try:
if cliente_mqtt is not None:
cliente_mqtt.disconnect()
except Exception as ex:
print(f"Error al desconectar: {ex}")
# Intentar reconectar
conectar_mqtt()
utime.sleep(0.1)
# ----------------------
# Conectar a Wi-Fi y al broker MQTT
# ----------------------
try:
conectar_wifi()
if not conectar_mqtt():
print("Iniciando sin conexión MQTT")
except Exception as e:
print(f"Error en la configuración inicial: {e}")
# ----------------------
# Iniciar los hilos
# ----------------------
try:
_thread.start_new_thread(hilo_sensor_display, ())
_thread.start_new_thread(hilo_motor, ())
_thread.start_new_thread(hilo_led, ())
_thread.start_new_thread(hilo_mqtt_loop, ())
# Hilo principal: mantener el programa activo y manejar reconexiones MQTT
while True:
# Si no hay conexión MQTT, intentar reconectar periódicamente
if cliente_mqtt is None:
print("Intentando reconectar a MQTT...")
conectar_mqtt()
utime.sleep(10)
except Exception as e:
print(f"Error al iniciar hilos: {e}")