import time,machine,micropython,network,neopixel,dht
from neopixel import NeoPixel
from machine import Pin,PWM,I2C
from umqtt.simple import MQTTClient
from time import sleep,sleep_ms
from lcd_i2c import LCD

#Indicamos red WIFI y clave
ssid = 'Wokwi-GUEST'
wifipassword = ''
#Datos Server MQTT (Broker)
#Indicamos datos MQTT Broker (server y puerto)
mqtt_server = 'io.adafruit.com'
port = 1883
user = 'joelrueda' #definido en adafruit
password = 'aio_ypOr55ZrhhoAsRiXhJvgZRkbbL9H' #key adafruit
#Indicamos ID(unico) y topicos
client_id = 'MiSensor'
topic_LEDTEMPERATURA = 'joelrueda/feeds/LedTemperatura'
topic_SENSOR = 'joelrueda/feeds/Sensor'
topic_Humedad = 'joelrueda/feeds/Humedad'
#Usamos una variable para definir si los sensores están activos.
sensorTempActivo =False
alarmaHumedadActiva = False
displayEstado = False
#Definimos modo Station (conectarse a Access Point remoto)
sta_if = network.WLAN(network.STA_IF)
sta_if.active(True)
#Conectamos al wifi
sta_if.connect(ssid, wifipassword)
print("Conectando")
while not sta_if.isconnected():
  print(".", end="")
  sleep(0.1)
print("Conectado a Wifi!")
#Vemos cuales son las IP
print(sta_if.ifconfig())

#Antes de conectarnos al broker, vamos a definir una funcion que sera llamada cada vez que se produzca un publish sobre un topico donde estamos suscriptos
def callback_sensor(topic, msg):
    global sensorTempActivo,alarmaHumedadActiva,displayEstado
    #Cuando se ejecuta esta funcion quere decir que hubo un mensaje nuevo en algun topico, verificamos esto dado que lo que llega viene en UTF-8, lo decodificamos para que sea una cadena de texto regular
    dato = msg.decode('utf-8')
    topicrec = topic.decode('utf-8')
    print("Cambio en: "+topicrec+":"+dato)
    #Nos fijamos si es el topico esperado y el valor del dato
    if topicrec == topic_SENSOR:
        if "OFF" in dato:
            sensorTempActivo = False
            alarmaHumedadActiva = False
            #Apago el Display
            displayEstado = False
            lcd.set_backlight(displayOFF)#=0
            lcd.no_display()
        else: #"ON"
            sensorTempActivo = True
            alarmaHumedadActiva = True
            #Enciendo el Display
            displayEstado = True
            lcd.set_backlight(displayON)#=1
            lcd.display()

#Intentamos conectarnos al broker MQTT
try:
    conexionMQTT = MQTTClient(client_id, mqtt_server,user=user,password=password,port=int(port))
    conexionMQTT.set_callback(callback_sensor)
    conexionMQTT.connect()
    conexionMQTT.subscribe(topic_SENSOR)
    print("Conectado con Broker MQTT")
except OSError as e:
    #Si fallo la conexion, reiniciamos todo
    print("Fallo la conexion al Broker, reiniciando...")
    sleep(5)
    machine.reset()

#LED Variables
cantLeds = 16
npTemperatura = NeoPixel(Pin(2), cantLeds)
ledCalor = (255,0,0)
ledNormal = (0,255,0)
ledFrio = (0,0,255)
ledApagado = (0,0,0)

#Sensor de Humedad Variables
SensorDHT22 = dht.DHT22(Pin(14))
SensorDHT22.measure()
nuevaHumedad = SensorDHT22.humidity()
hume = SensorDHT22.humidity()
freqLow = 400
freqHigh = 550

#Sensor de temperatura Variables
alarma = PWM(Pin(5), freq=500, duty_u16=32768)
minT = -40 #Temperatura Minima
maxT = 80 #Temperatura Maxima
temp = SensorDHT22.temperature()

#Humedad y Temperatura ideales:
minTempNormal = 20
maxTempNormal = 28
limEstadosTemp= [[minT,minTempNormal-0.0001],[minTempNormal,maxTempNormal],[maxTempNormal+0.0001,maxT]]
#                        FRIO                          NORMAL                     CALOR

minHumNormal = 80
maxHumNormal = 95
limHumedad = [minHumNormal,maxHumNormal]
#                 LimInf      LimSup

#Estados de la Temperatura
frio = 0
normal = 1
calor = 2
apagado = 3 #Para el Led

#Estados de la Humedad
humBaja = 0
humNormal = 1
humAlta = 2

#Display Variables
I2C_ADDR = 0x27     # DEC 39, HEX 0x27
i2c = I2C(0, scl=Pin(18), sda=Pin(19), freq=800000)
lcd = LCD(addr=I2C_ADDR, cols=20, rows=4, i2c=i2c)

#Estados del Display
displayON = 1
displayOFF = 0

limInf = 0 #Para acceder al
limSup = 1 #array limEstadosTemp y limHumedad

print("Comenzando monitoreo del sensor")
alarma.duty(0)
alarmaHumedadSonando = False
estActualTemp = apagado
estActualHum = apagado
lcd.begin()
lcd.set_backlight(displayOFF)
while True:
    try:
    #Tenemos que verificar si hay mensajes nuevos publicados por el broker
        conexionMQTT.check_msg()
        sleep_ms(1000)
        #Actualizamos los valores del sensor
        SensorDHT22.measure()
        nuevaHumedad = SensorDHT22.humidity()
        nuevaTemp = SensorDHT22.temperature()
        #Si el sensor esta activo ...
        #Temperatura:
        if sensorTempActivo:
            if nuevaTemp <= limEstadosTemp[frio][limSup] and estActualTemp != frio:
                npTemperatura.fill(ledFrio)
                conexionMQTT.publish(topic_LEDTEMPERATURA,str(frio))
                estActualTemp = frio
            elif limEstadosTemp[normal][limInf] <= nuevaTemp <= limEstadosTemp[normal][limSup] and estActualTemp != normal:
                npTemperatura.fill(ledNormal)
                conexionMQTT.publish(topic_LEDTEMPERATURA,str(normal))
                estActualTemp = normal
            elif limEstadosTemp[calor][limInf] <= nuevaTemp and estActualTemp != calor:
                npTemperatura.fill(ledCalor)
                conexionMQTT.publish(topic_LEDTEMPERATURA,str(calor))
                estActualTemp = calor
        else:
            if estActualTemp != apagado:
                npTemperatura.fill(ledApagado)
                conexionMQTT.publish(topic_LEDTEMPERATURA,str(apagado))
                estActualTemp = apagado
        npTemperatura.write()
        #Humedad:
        if alarmaHumedadActiva:                
            if not alarmaHumedadSonando: #Alarma no suena
                if nuevaHumedad < limHumedad[limInf] and estActualHum != humBaja:
                    alarma.duty(512)
                    alarma.freq(freqLow)
                    alarmaHumedadSonando = True
                    conexionMQTT.publish(topic_Humedad,str(humBaja))
                    estActualHum = humBaja
                elif nuevaHumedad > limHumedad[limSup] and estActualHum != humAlta:
                    alarma.duty(512)
                    alarma.freq(freqHigh)
                    alarmaHumedadSonando = True
                    conexionMQTT.publish(topic_Humedad,str(humAlta))
                    estActualHum = humAlta
                elif limHumedad[limInf] <= nuevaHumedad <= limHumedad[limSup] and estActualHum == apagado:
                    estActualHum = normal
                    conexionMQTT.publish(topic_Humedad,str(humNormal))
            else: #Alarma suena
                if limHumedad[limInf] <= nuevaHumedad <= limHumedad[limSup] and estActualHum != humNormal:
                    alarma.duty(0)
                    alarmaHumedadSonando = False
                    conexionMQTT.publish(topic_Humedad,str(humNormal))
                    estActualHum = normal
        else:
            if estActualHum != apagado:
                if alarmaHumedadSonando:
                    alarma.duty(0)
                    alarmaHumedadSonando = False
                conexionMQTT.publish(topic_Humedad,str(apagado))
                estActualHum = apagado

                
        #Display:
        if displayEstado:
            if nuevaHumedad != hume or nuevaTemp != temp:
                lcd.clear()
            else:
                lcd.home()
            lcd.print("Temperatura: {:.2f}".format(nuevaTemp))
            lcd.set_cursor(col=0 ,row=1 )
            lcd.print("Humedad: {:.2f}%".format(nuevaHumedad))

        temp = nuevaTemp
        hume = nuevaHumedad
    except OSError as e:
        print("Error ",e)
        sleep(5)
        machine.reset()