"""
Página oficial: https://micropython.org/
Referencia del lenguaje: https://docs.micropython.org/en/latest/
Referencia al lenguaje propio del ESP32: https://docs.micropython.org/en/latest/esp32/quickref.html
Tutorials https://www.youtube.com/@Piogram
Ejemplo del uso del FOR: https://www.youtube.com/watch?v=qt9lWtvvi1A
Para la programación, debo ponerlo como AP (AccessPoint). De esta manera un dispositivo se puede conectar
directo al ESP32 y ahí leveanto una WEB pidiendo los datos a configurar en duro.
Video Ejemplo: https://www.youtube.com/watch?v=laHXQoIRiUw&list=PLJ9xqaywaFW6cTQIchNmjKRiPMdveiJjV&index=4$0
A useful function for connecting to your local WiFi network is:
def do_connect():
import network
wlan = network.WLAN(network.STA_IF)
wlan.active(True)
if not wlan.isconnected():
print('connecting to network...')
wlan.connect('ssid', 'key')
while not wlan.isconnected():
pass
print('network config:', wlan.ipconfig('addr4'))
"""
"""
import time
time.sleep(1) # sleep for 1 second
time.sleep_ms(500) # sleep for 500 milliseconds
time.sleep_us(10) # sleep for 10 microseconds
start = time.ticks_ms() # get value of millisecond counter
delta = time.ticks_diff(time.ticks_ms(), start) # compute time difference
"""
"""
Ver "class RTC - real time clock" que permite programar alarmas y manejar un IRQ cuando se dispara la alarma.
Puedo usarlo para el senseo de temperatura cada x tiempo y enviar un mensaje por MQTT o verificar si no pasa de determinado
valor para enceder automaticamente el extractor.
"""
"""
DEBOUNCE de botones
URL: https://randomnerdtutorials.com/micropython-timer-interrupts-ep32-esp8266/$0
Punto 3
"""
# Importa librerias necesarias
import machine
from machine import Pin
from machine import Timer
import time
from dht import DHT22
## Declaración de Variables ##
# Defino los Timers que usare para ejecuciones programadas
Timer_ApagadoAutomatico = Timer(0)
Timer_LedParpadea = Timer(1)
Timer_Sensor = Timer(3)
Timer_Debounce = None
# Variables Generales
BotonPresionado = None # Clave del Diccionario Boton_Pins que identifica el botón presionado.
TemporizadorApagado = 5000 # Tiempo de espera para el apagdo por Temporizador (5000ms = 5s)
TemporizadorLedParpadeaApagado = 175 # Período para el parpadeo de los leds cuando se activa el Temporizador de Apagado (175ms)
DuracionPresionApagado = 1500 # Duración de la Presión del botón para activar Temporizador de Apagado (1500ms = 1,5s)
TemporizadorClima = 1000 # Período para la medición de condiciones climáticas (Temperatura y humedad) (1000ns = 1s)
TemporizadorDebounce = 10 # Período para el debounce de los botones
#debounce_counter = 0. # Despues de probar eliminar
# Listas donde se almacenan los valores de los pines a conectar los botones, los reles y los leds indicadores
Boton_Pins = {"Luz" : {"Pin" : Pin(26, Pin.IN, Pin.PULL_DOWN), "NroPin" : 26, "Desc" : "Btn Luz", "EstadoFisico" : 0, "EstadoLogico" : 0, "PresIni" : 0, "PresFin" : 0, "PresDura" : 0},
"Vel1" : {"Pin" : Pin(25, Pin.IN, Pin.PULL_DOWN), "NroPin" : 25, "Desc" : "Btn Vel1", "EstadoFisico" : 0, "EstadoLogico" : 0, "PresIni" : 0, "PresFin" : 0, "PresDura" : 0},
"Vel2" : {"Pin" : Pin(33, Pin.IN, Pin.PULL_DOWN), "NroPin" : 33, "Desc" : "Btn Vel2", "EstadoFisico" : 0, "EstadoLogico" : 0, "PresIni" : 0, "PresFin" : 0, "PresDura" : 0},
"Vel3" : {"Pin" : Pin(32, Pin.IN, Pin.PULL_DOWN), "NroPin" : 32, "Desc" : "Btn Vel3", "EstadoFisico" : 0, "EstadoLogico" : 0, "PresIni" : 0, "PresFin" : 0, "PresDura" : 0}
}
Rele_Pins = {"Luz" : {"Pin" : Pin(15, Pin.OUT, value=0), "NroPin" : 15, "Desc" : "Btn Luz", "Estado" : 0},
"Vel1" : {"Pin" : Pin(4, Pin.OUT, value=0), "NroPin" : 4, "Desc" : "Btn Vel1", "Estado" : 0},
"Vel2" : {"Pin" : Pin(18, Pin.OUT, value=0), "NroPin" : 18, "Desc" : "Btn Vel2", "Estado" : 0},
"Vel3" : {"Pin" : Pin(19, Pin.OUT, value=0), "NroPin" : 19, "Desc" : "Btn Vel3", "Estado" : 0}
}
Leds_Pins = {"Luz" : {"Pin" : Pin(27, Pin.OUT, value=0), "NroPin" : 27, "Desc" : "Btn Luz", "Estado" : 0},
"Vel1" : {"Pin" : Pin(14, Pin.OUT, value=0), "NroPin" : 14, "Desc" : "Btn Vel1", "Estado" : 0},
"Vel2" : {"Pin" : Pin(12, Pin.OUT, value=0), "NroPin" : 12, "Desc" : "Btn Vel2", "Estado" : 0},
"Vel3" : {"Pin" : Pin(13, Pin.OUT, value=0), "NroPin" : 13, "Desc" : "Btn Vel3", "Estado" : 0},
}
# Asignación del pin y creación de variable del sensor de temperatura y humedad
sensorDHT = DHT22(Pin(23))
## >>> Finaliza declaración de Variables <<< ##
## <<< Inicia declaración de Funciones >>> ##
# Funcion que atiende las INTERRUPCIONES generadas al presionar o soltar uno de los 4 botones (Luz, Vel1, Vel2 o Vel3).
# Registra el momento en el cual se presiona o suelta.
# Al soltar se calcula la duración de la presión (tiempo al momento de soltar menos el tiempo al momento de presionar) y se marca el "Estado Físico" del botón en 1.
# La información la guarda en el diccionaro "Boton_Pins"
def irqBotonPresionado(Pin):
global BotonPresionado
global Timer_Debounce
#global debounce_counter
if Timer_Debounce is None:
RegistroTiempo = time.ticks_ms()
# debounce_counter sirve para ver cuantas veces se presiona el boton.
#debounce_counter += 1
#print("Button Pressed! Count: ", debounce_counter)
#print(f"Inicia interrupcion: {Pin} - Valor {Pin()}")
#print(list(Boton_Pins["Luz"].keys()))
# Recorre el diccionario de Botones hasta identificar que botón se presionó o soltó.
for Boton in Boton_Pins:
if Boton_Pins[Boton]["Pin"] == Pin:
if Pin() == 1 and Boton_Pins[Boton]["PresIni"] == 0:
# Si el botón se presionó y no hay registro de inicio de presión, registra el inicio de presión
Boton_Pins[Boton]["PresIni"] = RegistroTiempo
elif Pin() == 0 and Boton_Pins[Boton]["PresFin"] == 0 and Boton_Pins[Boton]["PresIni"] > 0:
# Si el botón se soltó, no hay registro de fin de presión y si hay registro de inciio de presión, se registra el fin de presión, calucla la duración y establece el "EstadoFísico" a Encendido (1)
Boton_Pins[Boton]["EstadoFisico"] = 1
Boton_Pins[Boton]["PresFin"] = RegistroTiempo
Boton_Pins[Boton]["PresDura"] = time.ticks_diff(Boton_Pins[Boton]["PresFin"], Boton_Pins[Boton]["PresIni"])
BotonPresionado = Boton
#print(list(Boton_Pins[Boton].values()))
# Activa el temporizador para realizar el debounce de los botones y evitar replicas.
Timer_Debounce = Timer(2)
Timer_Debounce.init(mode=Timer.ONE_SHOT, period=TemporizadorDebounce, callback=Debounce_irqBotonPresionado)
# Fin Fx "irqBotonPresionado"
# Funcion que atiende el periodo de debounce de los botones. Cuand se cumple el termporizador, simplemente lo elimina.
def Debounce_irqBotonPresionado(timer):
global Timer_Debounce
Timer_Debounce = None
# Fin Fx "irqBotonPresionado"
# Si algún botón ha sido presionado realiza las siguientes acciones:
# 1) Si el estado anterior del boton presionado es apagado:
# A) Si el botón presionado no es el botón de LUZ, cambia el estado lógico a los botones de Velocidad a apagados. Con esto se garantiza que solo una velocidad esté activa.
# B) Establece el estado lógico del botón precinado a encendido.
# 2) Si el estado anterior del botón presionado es encendido:
# A) Si la duración de presión del botón es menor a la "DuracionPresionApagado" o es el botón de LUZ, marca el estado lógico del botón como apagado.
# B) Si la duración de presión del botón es mayor o igual a la "DuracionPresionApagado", programa el apagado automático.
# Restablece los parámetros de tiempo del botón presionado a cero
# Ejecuta la función que activa o desactiva las velocidades y la luz
def Botonera ():
global BotonPresionado
if BotonPresionado != None:
# Boton presionado
#mensaje = "Boton presionado {} - Estado Actual: {}".format(str(BotonPresionado), str(Boton_Pins[BotonPresionado]["EstadoLogico"]))
# Si el estado anterior del botón es apagado, entonces cambia el estado a encendido, de lo contario lo cambia a apagado.
if Boton_Pins[BotonPresionado]["EstadoLogico"] == 0: # El botón presionado estaba apagado.
if Boton_Pins[BotonPresionado]["Desc"] != "Btn Luz":
# Si presiono un boton de Velocidad, marcar el estado de todos los botones de Velocidad como apagado para garantizar que solo uno esté encendido.
Boton_Pins["Vel1"]["EstadoLogico"] = 0
Boton_Pins["Vel2"]["EstadoLogico"] = 0
Boton_Pins["Vel3"]["EstadoLogico"] = 0
# Marca el estado del botón presionado como encendido.
Boton_Pins[BotonPresionado]["EstadoLogico"] = 1
else: # El botón presionado estaba encendido.
# Cambia el estado del boton presionado a apagado
if Boton_Pins[BotonPresionado]["PresDura"] < DuracionPresionApagado or Boton_Pins[BotonPresionado]["Desc"] == "Btn Luz":
# la duración de presión del botón es menor a la "DuracionPresionApagado" o es el botón de LUZ, por lo tanto cambia el estado del botón a apagado.
Boton_Pins[BotonPresionado]["EstadoLogico"] = 0
else:
# la duración de presión del botón es mayor o igual a la "DuracionPresionApagado", por lo tanto:
# 1) apaga la luz
# 2) programa el apagado automático.
# 3) programa el parpadeo de los leds.
Boton_Pins["Luz"]["EstadoLogico"] = 0
Timer_ApagadoAutomatico.init(mode=Timer.ONE_SHOT, period=TemporizadorApagado, callback=ApagadoAutomatico)
Timer_LedParpadea.init(mode=Timer.PERIODIC, period=TemporizadorLedParpadeaApagado, callback=LedParpadea)
#print("Activa Temporizador")
#mensaje = mensaje + " - Nuevo Estado: {} - Duracion Presion: {}".format(str(Boton_Pins[BotonPresionado]["EstadoLogico"]), str(Boton_Pins[BotonPresionado]["PresDura"]))
# Reinicializa los parametros para detección de botón presionado.
Boton_Pins[BotonPresionado]["PresIni"] = 0
Boton_Pins[BotonPresionado]["PresFin"] = 0
Boton_Pins[BotonPresionado]["PresDura"] = 0
Boton_Pins[BotonPresionado]["Estado"] = 0
# Ejecuta la función que activa o desactiva las velocidades y la luz
ActivarCampana()
BotonPresionado = None
#print(mensaje)
# Fin Fx "Botonera"
# Recorre la lista de Estado y asigna el valor del estado al Rele y al Led Indicador.
# Esto implica que enciendo o apaga según corresponda al valor del Estado.
def ActivarCampana():
for Boton in Boton_Pins:
# Activa el Rele correspondiente
Rele_Pins[Boton]["Pin"].value(Boton_Pins[Boton]["EstadoLogico"])
# Activa el Led testigo correspondiente
Leds_Pins[Boton]["Pin"].value(Boton_Pins[Boton]["EstadoLogico"])
# Fin Fx "ActivarCampana"
# Si si ejecuta el temporizador de Apagado Automático, llama a esta función que actualiza el estado de todos los botones a apagado y luego ejecuta el apagado de los mismos.
def ApagadoAutomatico(Timer):
for Boton in Boton_Pins:
Boton_Pins[Boton]["EstadoLogico"] = 0
Timer_LedParpadea.deinit()
ActivarCampana()
# Fin Fx "ApagadoAutomatico"
# Si se ejecuta el temporizador de parpadeo de los Les, llama a esta funcion cada 175ms que enciende o apaga los leds del Boton activado.
def LedParpadea(Timer):
for Boton in Boton_Pins:
if Boton_Pins[Boton]["EstadoLogico"] == 1:
Leds_Pins[Boton]["Pin"].value(not Leds_Pins[Boton]["Pin"].value())
# Fin Fx "LedParpadea"
# Se ejecuta automaticamente cada 1s. Sensa temperatura y humedad y envía un mensaje MQTT (pendiente desarrollar)
def SensarTemperatura(Timer):
sensorDHT.measure()
temperature = sensorDHT.temperature()
humedad = sensorDHT.humidity()
print ("temp: {}, humedad: {}".format(temperature, humedad))
# Fin Fx "SensarTemperatura"
## >>> Finaliza declaración de Funciones <<< ##
## <<< Inicia configuración inicial >>> ##
# Inicia Configuracion de Interrupciones
Boton_Pins["Luz"]["Pin"].irq(handler = irqBotonPresionado, trigger = Pin.IRQ_FALLING | Pin.IRQ_RISING)#, priority=1, wake=None, hard=False)
Boton_Pins["Vel1"]["Pin"].irq(handler = irqBotonPresionado, trigger = Pin.IRQ_FALLING | Pin.IRQ_RISING)#, priority=1, wake=None, hard=False)
Boton_Pins["Vel2"]["Pin"].irq(handler = irqBotonPresionado, trigger = Pin.IRQ_FALLING | Pin.IRQ_RISING)#, priority=1, wake=None, hard=False)
Boton_Pins["Vel3"]["Pin"].irq(handler = irqBotonPresionado, trigger = Pin.IRQ_FALLING | Pin.IRQ_RISING)#, priority=1, wake=None, hard=False)
# Inicializa el Timer del sensor de Temperatura y Humedad.
Timer_Sensor.init(mode=Timer.PERIODIC, period=TemporizadorClima, callback=SensarTemperatura)
## >>> Finaliza configuración inicial <<< ##
## <<< Inicia Bucle Principal >>> ##
print("Inicia Bucle")
while True:
Botonera()
## >>> Finaliza Bucle Principal <<< ##FPS: 0
Power: 0.00W
Power: 0.00W