import machine
from machine import Pin, I2C, ADC
import time
import network
import urequests
import dht
from ssd1306 import SSD1306_I2C
# ============================================================================
# WIFI Y TELEGRAM
# ============================================================================
SSID_RED = "AbdielF"
PASSWORD_RED = "Abdiel11."
TOKEN_TELEGRAM = "8336729586:AAF4yE6mdwJ3pdrfdHMSYiPZZpLErUkF9aA"
CHAT_ID = "8537656432"
def conectar_wifi():
wlan = network.WLAN(network.STA_IF)
wlan.active(True)
if not wlan.isconnected():
print("Conectando a la red WiFi:", SSID_RED)
wlan.connect(SSID_RED, PASSWORD_RED)
timeout = 15
while not wlan.isconnected() and timeout > 0:
print(".", end="")
time.sleep(1)
timeout -= 1
if wlan.isconnected():
print("\n[ OK ] Conectado a WiFi. IP:", wlan.ifconfig()[0])
return True
else:
print("\n[ERROR] No se pudo conectar al WiFi. Verifique credenciales.")
return False
def enviar_telegram(mensaje):
try:
msg_url = mensaje.replace(' ', '%20').replace('\n', '%0A').replace('|', '%7C')
url = "https://api.telegram.org/bot" + TOKEN_TELEGRAM + "/sendMessage?chat_id=" + CHAT_ID + "&text=" + msg_url
respuesta = urequests.get(url)
respuesta.close()
print("[TELEGRAM] Reporte enviado correctamente.")
except Exception as e:
print("[TELEGRAM ERROR] No se pudo enviar el mensaje:", e)
# ============================================================================
# HARDWARE
# ============================================================================
i2c = I2C(0, sda=Pin(0), scl=Pin(1), freq=400000)
oled_detectada = False
try:
dispositivos_i2c = i2c.scan()
if 0x3c in dispositivos_i2c:
oled_detectada = True
print("[ OK ] Pantalla OLED detectada.")
except:
pass
if oled_detectada:
oled = SSD1306_I2C(128, 64, i2c)
else:
class MockOLED:
def fill(self, c): pass
def text(self, t, x, y): pass
def show(self): pass
oled = MockOLED()
try: sensor_dht1 = dht.DHT22(Pin(14))
except: pass
try: sensor_dht2 = dht.DHT22(Pin(15))
except: pass
flotador1 = Pin(11, Pin.IN, Pin.PULL_UP)
flotador2 = Pin(12, Pin.IN, Pin.PULL_UP)
adc_c1 = ADC(Pin(26))
adc_c2 = ADC(Pin(27))
adc_c3 = ADC(Pin(28))
buzzer = Pin(16, Pin.OUT, value=0)
rojo = Pin(20, Pin.OUT)
verde = Pin(19, Pin.OUT)
azul = Pin(18, Pin.OUT)
bomba = Pin(13, Pin.OUT, value=1)
# TECLADO
pines_filas = [9, 8, 7, 6]
pines_columnas = [5, 4, 3, 2]
filas = [Pin(pin, Pin.OUT) for pin in pines_filas]
for fila in filas: fila.value(0)
columnas = [Pin(pin, Pin.IN, Pin.PULL_DOWN) for pin in pines_columnas]
teclas = [
['1', '2', '3', 'A'],
['4', '5', '6', 'B'],
['7', '8', '9', 'C'],
['*', '0', '#', 'D']
]
# ============================================================================
# VARIABLES
# ============================================================================
SP_SUELO_MIN = 30.0
SP_SUELO_MAX = 60.0
SP_AGUA_FRIA = 16.0
SP_AGUA_CALIENTE = 24.0
pantalla_actual = 0
modo_auto = True
bomba_encendida = False
y_scroll = 0
color_actual_rgb = "apagado"
h_tierra1, h_tierra2, h_tierra3, h_tierra_prom = 0.0, 0.0, 0.0, 0.0
t_agua = 20.0
t_amb_dht1, h_amb_dht1 = 24.0, 45.0
t_amb_dht2, h_amb_dht2 = 24.0, 45.0
status_c1, status_c2, status_c3 = "OK", "OK", "OK"
status_dht1, status_dht2 = "OK", "OK"
status_deposito_agua = "NIVEL OPTIMO"
txt_flotador1 = "VACIO"
txt_flotador2 = "VACIO"
estado_parpadeo = False
ciclo_alternancia = 0
ultimo_refresco_consola = 0
ultimo_dht = 0
ultimo_oled = 0
ultimo_tick_led = 0
ultimo_tick_alternancia = 0
ultimo_envio_telegram = 0
texto_guia = [
" MANUAL DE COLORES ",
"----------------",
"Amarillo: Error",
"Verde P: Suelo OK",
"Rojo F: Suelo Seco",
"Azul F: Suelo Inund",
"Cian P: Agua Fria",
"Magenta P:Ag.Calient",
"Blanco P: Agua Optim",
"----------------",
"Usa A para subir",
"Usa B para bajar",
"Presione 0 p/ salir"
]
# ============================================================================
# FUNCIONES
# ============================================================================
def leer_tecla():
"""Escanea el teclado con un sistema anti-rebote fuerte."""
for i, fila in enumerate(filas):
fila.value(1)
time.sleep_us(50)
for j, columna in enumerate(columnas):
if columna.value() == 1:
tecla_presionada = teclas[i][j]
timeout = 0
while columna.value() == 1 and timeout < 100:
time.sleep_ms(10)
timeout += 1
fila.value(0)
return tecla_presionada
fila.value(0)
return None
def controlar_rgb(color):
global color_actual_rgb
color_actual_rgb = color
if color == 'rojo': rojo.on(); verde.off(); azul.off()
elif color == 'verde': rojo.off(); verde.on(); azul.off()
elif color == 'azul': rojo.off(); verde.off(); azul.on()
elif color == 'amarillo': rojo.on(); verde.on(); azul.off()
elif color == 'cian': rojo.off(); verde.on(); azul.on()
elif color == 'magenta': rojo.on(); verde.off(); azul.on()
elif color == 'blanco': rojo.on(); verde.on(); azul.on()
else: rojo.off(); verde.off(); azul.off(); color_actual_rgb = "apagado"
def obtener_humedad_porcentaje(lector_adc):
suma_cruda = 0
for _ in range(5): suma_cruda += lector_adc.read_u16()
lectura_cruda = suma_cruda / 5
if lectura_cruda < 8000: return -1.0
seco, agua = 52000, 25000
porcentaje = ((seco - lectura_cruda) / (seco - agua)) * 100.0
if porcentaje < 0: porcentaje = 0.0
if porcentaje > 100: porcentaje = 100.0
return porcentaje
def procesar_sistema():
global h_tierra1, h_tierra2, h_tierra3, h_tierra_prom
global bomba_encendida, estado_parpadeo, ciclo_alternancia
global t_amb_dht1, h_amb_dht1, t_amb_dht2, h_amb_dht2
global status_c1, status_c2, status_c3, status_dht1, status_dht2
global status_deposito_agua, txt_flotador1, txt_flotador2
global ultimo_dht, ultimo_tick_led, ultimo_tick_alternancia
ahora = time.ticks_ms()
# --- SENSORES ---
if time.ticks_diff(ahora, ultimo_dht) >= 2000:
try:
sensor_dht1.measure()
t_amb_dht1, h_amb_dht1 = sensor_dht1.temperature(), sensor_dht1.humidity()
status_dht1 = "OK"
except: status_dht1 = "ERROR"
try:
sensor_dht2.measure()
t_amb_dht2, h_amb_dht2 = sensor_dht2.temperature(), sensor_dht2.humidity()
status_dht2 = "OK"
except: status_dht2 = "ERROR"
ultimo_dht = ahora
val_c1, val_c2, val_c3 = obtener_humedad_porcentaje(adc_c1), obtener_humedad_porcentaje(adc_c2), obtener_humedad_porcentaje(adc_c3)
status_c1 = "OK" if val_c1 >= 0 else "ERROR"
status_c2 = "OK" if val_c2 >= 0 else "ERROR"
status_c3 = "OK" if val_c3 >= 0 else "ERROR"
h_tierra1 = val_c1 if status_c1 == "OK" else 0.0
h_tierra2 = val_c2 if status_c2 == "OK" else 0.0
h_tierra3 = val_c3 if status_c3 == "OK" else 0.0
conectados = sum([1 for s in [status_c1, status_c2, status_c3] if s == "OK"])
h_tierra_prom = (sum([h for h, s in zip([h_tierra1, h_tierra2, h_tierra3], [status_c1, status_c2, status_c3]) if s == "OK"]) / conectados) if conectados > 0 else 0.0
val_f1, val_f2 = flotador1.value(), flotador2.value()
txt_flotador1 = "DETECTADO" if val_f1 == 0 else "VACIO"
txt_flotador2 = "DETECTADO" if val_f2 == 0 else "VACIO"
if val_f1 == 1 and val_f2 == 1: status_deposito_agua = "AGUA BAJA"
elif val_f1 == 0 and val_f2 == 0: status_deposito_agua = "DEMASIADA AGUA"
elif val_f1 == 0 and val_f2 == 1: status_deposito_agua = "NIVEL OPTIMO"
else: status_deposito_agua = "ERROR DE SENSORES"
# --- CONTROL BOMBA ---
if modo_auto:
# Sistema decide
if conectados > 0:
if h_tierra_prom < SP_SUELO_MIN: bomba_encendida = True
elif h_tierra_prom >= SP_SUELO_MAX: bomba_encendida = False
bomba.value(0 if bomba_encendida else 1)
else:
bomba_encendida = False
bomba.value(1)
else:
# Modo manual
bomba.value(0 if bomba_encendida else 1)
# --- LEDS Y ALERTAS ---
if time.ticks_diff(ahora, ultimo_tick_led) >= 300:
estado_parpadeo = not estado_parpadeo
ultimo_tick_led = ahora
if time.ticks_diff(ahora, ultimo_tick_alternancia) >= 50:
ciclo_alternancia = (ciclo_alternancia + 1) % 60
ultimo_tick_alternancia = ahora
color_suelo = 'verde'
if conectados > 0 and h_tierra_prom < SP_SUELO_MIN: color_suelo = 'rojo'
elif conectados > 0 and h_tierra_prom > 70.0: color_suelo = 'azul'
color_agua = 'blanco'
if t_agua <= SP_AGUA_FRIA: color_agua = 'cian'
elif t_agua >= SP_AGUA_CALIENTE: color_agua = 'magenta'
if "ERROR" in [status_c1, status_c2, status_c3] or status_deposito_agua == "ERROR DE SENSORES":
controlar_rgb('amarillo')
buzzer.value(0)
else:
if color_suelo in ['rojo', 'azul'] or color_agua in ['cian', 'magenta']:
buzzer.value(1 if color_suelo in ['rojo', 'azul'] else (1 if estado_parpadeo else 0))
else:
buzzer.value(0)
if ciclo_alternancia < 30:
controlar_rgb('verde' if estado_parpadeo else 'apagado') if color_suelo == 'verde' else controlar_rgb(color_suelo)
else:
controlar_rgb(color_agua if estado_parpadeo else 'apagado')
# ============================================================================
# INTERFAZ
# ============================================================================
def actualizar_pantalla_y_consola():
txt_bomba = "APAGADA" if bomba.value() == 0 else "ENCENDIDO"
txt_modo = "AUTO" if modo_auto else "MANUAL"
txt_buzzer = "ACTIVO" if buzzer.value() == 1 else "SILENCIO"
if oled_detectada:
oled.fill(0)
if pantalla_actual == 0:
oled.text("== MONITOREO ==", 4, 0)
oled.text("Bomba: " + txt_bomba, 0, 18)
oled.text("Modo: " + txt_modo, 0, 30)
oled.text("Agua: " + status_deposito_agua, 0, 42)
oled.text("Buzz: " + txt_buzzer, 0, 54)
elif pantalla_actual == 1:
oled.text("= SUELO NIVEL 1 =", 0, 0)
oled.text("Humedad: {:.1f}%".format(h_tierra1), 0, 25)
oled.text("Status: " + status_c1, 0, 45)
elif pantalla_actual == 2:
oled.text("= SUELO NIVEL 2 =", 0, 0)
oled.text("Humedad: {:.1f}%".format(h_tierra2), 0, 25)
oled.text("Status: " + status_c2, 0, 45)
elif pantalla_actual == 3:
oled.text("= SUELO NIVEL 3 =", 0, 0)
oled.text("Humedad: {:.1f}%".format(h_tierra3), 0, 25)
oled.text("Status: " + status_c3, 0, 45)
elif pantalla_actual == 4:
txt_diag = "FALLA" if h_tierra_prom == 0 else "SECO" if h_tierra_prom < SP_SUELO_MIN else "INUNDADO" if h_tierra_prom > 70 else "OPTIMO"
oled.text("= PROM. SUELO =", 4, 0)
oled.text("Prom: {:.1f}%".format(h_tierra_prom), 0, 25)
oled.text("Edo: " + txt_diag, 0, 45)
elif pantalla_actual == 5:
txt_tag_agua = "FRIA" if t_agua <= SP_AGUA_FRIA else "CALIENTE" if t_agua >= SP_AGUA_CALIENTE else "OPTIMA"
oled.text("= TEMP. AGUA =", 8, 0)
oled.text("Temp: {:.1f} C".format(t_agua), 0, 25)
oled.text("Condic: " + txt_tag_agua, 0, 45)
elif pantalla_actual == 6:
oled.text("== AMBIENTE ==", 16, 0)
oled.text("D1 T:{:.1f} H:{:.0f}%".format(t_amb_dht1, h_amb_dht1), 0, 20)
oled.text("D2 T:{:.1f} H:{:.0f}%".format(t_amb_dht2, h_amb_dht2), 0, 35)
oled.text("D1:{} D2:{}".format(status_dht1, status_dht2), 0, 50)
elif pantalla_actual == 7:
oled.text("= DIAGNOSTICO =", 4, 0)
oled.text("C1:{} C2:{}".format(status_c1, status_c2), 0, 20)
oled.text("C3:{} D1:{}".format(status_c3, status_dht1), 0, 35)
oled.text("D2:{} OLED:OK".format(status_dht2), 0, 50)
elif pantalla_actual == 8:
for i in range(len(texto_guia)):
pos_y = (i * 12) - y_scroll
if pos_y >= -10 and pos_y <= 55:
oled.text(texto_guia[i], 0, pos_y)
oled.show()
# ============================================================================
# PRINCIPAL
# ============================================================================
print(">> Iniciando Sistema de Riego IoT <<")
wifi_conectado = conectar_wifi()
ultimo_envio_telegram = time.ticks_ms()
while True:
procesar_sistema()
ahora_actual = time.ticks_ms()
# --- TELEGRAM ---
if wifi_conectado and time.ticks_diff(ahora_actual, ultimo_envio_telegram) >= 10000:
txt_bomba = "APAGADA" if bomba.value() == 0 else "ENCENDIDA"
txt_modo = "AUTO" if modo_auto else "MANUAL"
mensaje_completo = (
" REPORTE DEL SISTEMA \n\n"
f" SUELO Prom: {h_tierra_prom:.1f}%\n"
f" AMB: {t_amb_dht1:.1f}C, {h_amb_dht1:.1f}%\n"
f" AGUA: {status_deposito_agua}\n"
f" MODO: {txt_modo}\n"
f"BOMBA: {txt_bomba}"
)
enviar_telegram(mensaje_completo)
ultimo_envio_telegram = ahora_actual
# --- ACTUALIZAR PANTALLA ---
if time.ticks_diff(ahora_actual, ultimo_oled) >= 200:
actualizar_pantalla_y_consola()
ultimo_oled = ahora_actual
# --- LEER TECLADO ---
tecla = leer_tecla()
if tecla:
# AUTO/MANUAL Y BOMBA
if tecla == 'C':
modo_auto = not modo_auto
if wifi_conectado: enviar_telegram("⚙️ Modo cambiado a: " + ("AUTO" if modo_auto else "MANUAL"))
elif tecla == '*' and not modo_auto:
bomba_encendida = True
elif tecla == '#' and not modo_auto:
bomba_encendida = False
# CAMBIO DE PANTALLA
elif tecla in ['0', '1', '2', '3', '4', '5', '6', '7']:
pantalla_actual = int(tecla)
y_scroll = 0
elif tecla == 'D':
pantalla_actual = 8
y_scroll = 0
# SCROLL
elif pantalla_actual == 8:
if tecla == 'A':
y_scroll = max(0, y_scroll - 12)
elif tecla == 'B':
y_scroll = min((len(texto_guia) * 12) - 50, y_scroll + 12)
time.sleep_ms(5)