import time
import ujson
import network
import json
from time import sleep
from pump import PUMP
from environment_sensor import ENVIRONMENT_SENSOR
from cistern import CISTERN
from machine import Pin, PWM
from oled import OLED
from ldr import LDR
from fan import FAN
from door import DOOR
from ground_sensor import GROUND_SENSOR
from mqtt_client import MQTT

"""PARAMETERS DEFINITION"""
#WI-FI params
WIFI_SSID = 'Simone'
WIFI_PASSWORD = 'simonefaraulo'

# MQTT Server parameters
MQTT_CLIENT_ID = "gruppo09"
MQTT_BROKER    = "test.mosquitto.org"
MQTT_USER      = ""
MQTT_PASSWORD  = ""

#MQTT topic to witch the client must publish informations
MQTT_GROUND_HUMIDITY = "SmartGreenhouse/Ground/Humidity"
MQTT_ENVIRONMENT_TEMP = "SmartGreenhouse/Environment/Temp"
MQTT_ENVIRONMENT_HUMIDITY = "SmartGreenhouse/Environment/Humidity"
MQTT_CISTERN_LEVEL = "SmartGreenhouse/Irrigation/Cistern"
MQTT_OUTSIDE_LUX = "SmartGreenhouse/External/Lum"
MQTT_FAN_STATE = "SmartGreenhouse/Ventilation/Fan"
MQTT_PUMP_STATE = "SmartGreenhouse/Irrigation/Pump"
MQTT_LED_STATE = "SmartGreenhouse/Lights/LED"
MQTT_DOOR_STATE = "SmartGreenhouse/DoorState"

#MQTT topic to which the client must subscribe
MQTT_SUBSCRIBE_TOPIC =  { 
    "MQTT_IRRIGATION_MANUALCONTROL" : b'SmartGreenhouse/Irrigation/ManualState',
    "MQTT_IRRIGATION_MANUALPUMP" : b'SmartGreenhouse/Irrigation/ManualPump',
    "MQTT_IRRIGATION_HUMIDITY" : b'SmartGreenhouse/Irrigation/Humidity',
    "MQTT_FAN_MANUALSTATE" : b'SmartGreenhouse/Ventilation/ManualFan',
    "MQTT_FAN_MANUALCONTROL" : b'SmartGreenhouse/Ventilation/ManualState',
    "MQTT_FAN_ACTIVATIONTEMP" : b'SmartGreenhouse/Ventilation/Temp',
    "MQTT_LIGHTS_MANUALCONTROL" : b'SmartGreenhouse/Lights/ManualState',
    "MQTT_LIGHTS_MANUALLED" : b'SmartGreenhouse/Lights/ManualLED',
    "MQTT_LIGHTS_LEVEL" : b'SmartGreenhouse/Lights/Lum',
    "MQTT_RESET_SYSTEM" : b'SmartGreenhouse/Restart'
}

#dict of value that comes from sensors and actuators state  to publish on the respective topic: k = topic, v = value to publish on the topic 
MQTT_PUBLISH_VALUE = {
    MQTT_GROUND_HUMIDITY :  None,
    MQTT_ENVIRONMENT_TEMP :  None,
    MQTT_ENVIRONMENT_HUMIDITY :  None,
    MQTT_CISTERN_LEVEL:  None,
    MQTT_OUTSIDE_LUX :  None,
    MQTT_FAN_STATE :  None,
    MQTT_PUMP_STATE : None,
    MQTT_LED_STATE : None,
    MQTT_DOOR_STATE : None
}

SENSORS_ACTUAL_VALUE = {
    MQTT_GROUND_HUMIDITY :  None,
    MQTT_ENVIRONMENT_TEMP :  None,
    MQTT_ENVIRONMENT_HUMIDITY :  None,
    MQTT_CISTERN_LEVEL:  None,
    MQTT_OUTSIDE_LUX :  None,
}

#dict of parameter that are setted based on the informations that comes from the broker and from the system computation
SYSTEM_PARAMS = {
    "BOOT" : True,
    "DOOR_OPEN": False,
    "PRINT_SYS_STATE" : True,
    "CRITICAL_CISTERN_LEVEL" : 10,
    "HUMIDITY_CRITICAL_LEVEL" : 10,
    "MANUAL_IRRIGATION_CONTROL" : False,
    "MANUAL_IRRIGATION_PUMP": False,
    "MANUAL_FAN_CONTROL" : False,
    "MANUAL_FAN_STATE": False,
    "MANUAL_LIGHTS_CONTROL" : False,
    "MANUAL_LIGHTS_LEVEL": 0,
    "LIGHTS_OFF_LEVEL": 20,
    "BLOCK_IRRIGATION_FOR_CRITICAL_CISTERN_LEVEL": False,
    "RESTART_SYSTEM": False,
    "FAN_ACTIVATION_TEMP": 30,
    "PUMP_CICLE": False
}

#irq_lock setted true when the system is restarted
IRQ_LOCK = False

#CISTERN parameters
CISTERN_TRIGGER_PIN = 26
CISTERN_ECHO_PIN = 25
CISTERN_HEIGHT = 30

#PUMP parameters
PUMP_PIN = 2

#ENVIRONMENT_SENSOR parameters
ENVIRONMENT_SENSOR_PIN = 4

#PHOTO_RESISTOR parameters
PHOTO_RESISTOR_PIN = 35
PHOTO_RESISTOR_MIN_VALUE = 0
PHOTO_RESISTOR_MAX_VALUE = 100

#LED_ZONE_1 parameters 
LED1_PIN = 13
LED1_FREQ = 50

#LED_ZONE_2 parameters 
LED2_PIN = 19
LED2_FREQ = 50

#FAN parameters
FAN_PIN = 14

#GROUND_SENSOR parameters
GROUND_SENSOR_PIN = 33
GROUND_SENSOR_MAX_READ = 2800
GROUND_SENSOR_MIN_READ = 500
GROUND_SENSOR_MIN_VALUE = 0
GROUND_SENSOR_MAX_VALUE = 100

#OLED_DISPLAY parameters
OLED_WIDTH = 128
OLED_HEIGHT = 64
OLED_SLC_PIN = 22
OLED_SDA_PIN = 32
OLED_FILL = 0
OLED_COLOR = 1

#DOOR parameters
DOOR_PIN = 23
DOOR_CLOSE_ANGLE = 180
DOOR_OPEN_ANGLE = 110

#RESET_BTN parameters
RESET_BTN_PIN = 15

#DOOR_BTN parameters
DOOR_BTN_PIN = 34

#BOUNCING BTNs VAR
LAST_RESET = 0
DELTA_MAX_RESET = 300
LAST_DOOR = 0
DELTA_MAX_DOOR = 1000

#DASHBOARD max and min value sent and recived on all topics
DASH_MAX_VAL = 100
DASH_MIN_VAL = 0 

"""IMPLEMENTATION OF SYSTEM LOGIC"""
#INSTANTIATION OF THE SYSTEM OBJECTS
reset_btn = Pin(RESET_BTN_PIN, Pin.IN, Pin.PULL_DOWN)
door_btn = Pin(DOOR_BTN_PIN, Pin.IN, Pin.PULL_DOWN)
cistern = CISTERN(CISTERN_TRIGGER_PIN, CISTERN_ECHO_PIN, CISTERN_HEIGHT)
pump = PUMP(PUMP_PIN)
environment_sensor = ENVIRONMENT_SENSOR(ENVIRONMENT_SENSOR_PIN)
photo_resistor = LDR(PHOTO_RESISTOR_PIN, PHOTO_RESISTOR_MIN_VALUE, PHOTO_RESISTOR_MAX_VALUE)
ground_sensor = GROUND_SENSOR(GROUND_SENSOR_PIN, GROUND_SENSOR_MIN_VALUE, GROUND_SENSOR_MAX_VALUE, GROUND_SENSOR_MAX_READ, GROUND_SENSOR_MIN_READ)
client = MQTT(MQTT_CLIENT_ID, MQTT_BROKER)
oled = OLED(OLED_WIDTH, OLED_HEIGHT, OLED_SLC_PIN, OLED_SDA_PIN)
fan = FAN(FAN_PIN)
door = DOOR(DOOR_PIN, DOOR_CLOSE_ANGLE, DOOR_OPEN_ANGLE)
led1 = PWM(Pin(LED1_PIN, Pin.OUT), freq=LED1_FREQ)
led2 = PWM(Pin(LED2_PIN, Pin.OUT), freq=LED2_FREQ)
led1.duty(0)
led2.duty(0)

#START THE CONNECTIVITY PROCEDURE
oled.print_booting_info(OLED_FILL, OLED_COLOR)

#IMPORTED IN THE BOOT FILE FOR THE WI-FI CONNECTION
def wifi_connect():

    global oled
    global OLED_COLOR

    try:
        shift_point = 0
        print("Connecting to WiFi", end="")
        oled.print_line("Connecting", 0, 1, OLED_COLOR)
        oled.print_line_no_fill("to Wifi", 0, 10, OLED_COLOR)
        sta_if = network.WLAN(network.STA_IF)
        sta_if.active(True)
        sta_if.connect(WIFI_SSID, WIFI_PASSWORD)
        while not sta_if.isconnected():
            print(".", end="")
            oled.print_line_no_fill(".", 53 + shift_point, 10, OLED_COLOR)
            time.sleep(0.1)
            shift_point = shift_point + 6
        print(" Connected!")
        oled.print_line_no_fill("Connected!", 0, 20, OLED_COLOR)
    except OSError as ex:
        print("Failure connecting to WiFi!!")
        oled.print_line("Not Connected!", 0, 1, OLED_COLOR)
        for i in range(10, 0, -1):
            oled.fill_rect(60, 10, 80, 10, OLED_FILL)
            oled.print_line_no_fill("Retrying in " + str(i) + "s", 0, 10, OLED_COLOR)
            time.sleep(1)
        wifi_connect()
        

#DEFINING ALL THE PROCEDURES
def restart_procedure():

    global MQTT_PUBLISH_VALUE, SYSTEM_PARAMS, SENSORS_ACTUAL_VALUE
    global MQTT_GROUND_HUMIDITY
    global MQTT_ENVIRONMENT_TEMP
    global MQTT_ENVIRONMENT_HUMIDITY
    global MQTT_CISTERN_LEVEL
    global MQTT_OUTSIDE_LUX
    global MQTT_FAN_STATE
    global MQTT_DOOR_STATE
    global MQTT_PUMP_STATE
    global OLED_COLOR
    global led, pump, fan, oled, client, door

    oled.print_line("Restarting the", 0, 1, OLED_COLOR)
    oled.print_line_no_fill("system", 0, 10, OLED_COLOR)
    shift_point = 0

    pump.stop_pump()
    led.duty(0)
    fan.stop_fan()
    door.close_door()

    try:
        client.publish(MQTT_GROUND_HUMIDITY, ujson.dumps({"humidity" : 0}))
        client.publish(MQTT_ENVIRONMENT_TEMP, ujson.dumps({"temp" : 0}))
        client.publish(MQTT_ENVIRONMENT_HUMIDITY, ujson.dumps({"humidity" : 0}))
        client.publish(MQTT_CISTERN_LEVEL, ujson.dumps({"cistern_level" : 0}))
        client.publish(MQTT_OUTSIDE_LUX, ujson.dumps({"light" : 0}))
        client.publish(MQTT_FAN_STATE, ujson.dumps({"fan" : 0}))
        client.publish(MQTT_PUMP_STATE,  ujson.dumps({"pump" : 0}))
        client.publish(MQTT_LED_STATE, ujson.dumps({"light" : 0}))
        client.publish(MQTT_DOOR_STATE, ujson.dumps({"door" : 0}))
    except OSError as e:
        print("Error during the sensors data pubblication: ", e)

    while shift_point < 28:
        oled.print_line_no_fill(".", 45 + shift_point, 10, OLED_COLOR)
        time.sleep(0.1)
        shift_point = shift_point + 6

    SYSTEM_PARAMS = {
        "BOOT" : True,
        "DOOR_OPEN": False,
        "PRINT_SYS_STATE" : True,
        "CRITICAL_CISTERN_LEVEL" : 10,
        "HUMIDITY_CRITICAL_LEVEL" : 10,
        "MANUAL_IRRIGATION_CONTROL" : False,
        "MANUAL_IRRIGATION_PUMP": False,
        "MANUAL_FAN_CONTROL" : False,
        "MANUAL_FAN_STATE": False,
        "MANUAL_LIGHTS_CONTROL" : False,
        "MANUAL_LIGHTS_LEVEL": 0,
        "LIGHTS_OFF_LEVEL": 20,
        "BLOCK_IRRIGATION_FOR_CRITICAL_CISTERN_LEVEL": False,
        "RESTART_SYSTEM": False,
        "FAN_ACTIVATION_TEMP": 30,
        "PUMP_CICLE": False
    }

    MQTT_PUBLISH_VALUE = {
        MQTT_GROUND_HUMIDITY :  None,
        MQTT_ENVIRONMENT_TEMP :  None,
        MQTT_ENVIRONMENT_HUMIDITY :  None,
        MQTT_CISTERN_LEVEL:  None,
        MQTT_OUTSIDE_LUX :  None,
        MQTT_FAN_STATE :  None,
        MQTT_PUMP_STATE : None,
        MQTT_LED_STATE : None,
        MQTT_DOOR_STATE : None
    }

    SENSORS_ACTUAL_VALUE = {
        MQTT_GROUND_HUMIDITY :  None,
        MQTT_ENVIRONMENT_TEMP :  None,
        MQTT_ENVIRONMENT_HUMIDITY :  None,
        MQTT_CISTERN_LEVEL:  None,
        MQTT_OUTSIDE_LUX :  None,
    }

def auto_fan_control():
    
    global SYSTEM_PARAMS, MQTT_PUBLISH_VALUE, MQTT_FAN_STATE
    global OLED_COLOR, OLED_FILL
    global fan, oled, client

    oled.fill_rect(35, 20, 30, 10, OLED_FILL)
    oled.print_line_no_fill("AUT=>", 35, 20, OLED_COLOR)

    actual_temp = environment_sensor.get_last_temperature()
    if SYSTEM_PARAMS["FAN_ACTIVATION_TEMP"] != None and actual_temp > SYSTEM_PARAMS["FAN_ACTIVATION_TEMP"]:
        
        fan.start_fan()
        
        oled.fill_rect(80, 20, 60, 10, OLED_FILL)
        oled.print_line_no_fill("ON", 80, 20, OLED_COLOR)
        
        MQTT_PUBLISH_VALUE[MQTT_FAN_STATE] = ujson.dumps({"fan" : 1})
        try:
            client.publish(MQTT_FAN_STATE, MQTT_PUBLISH_VALUE[MQTT_FAN_STATE])
            oled.fill_rect(55, 10, 80, 10, OLED_FILL)
            oled.print_line_no_fill("Published", 55, 10, OLED_COLOR)
        except OSError as e:
            print("Error during the sensors data pubblication: ", e)
            oled.fill_rect(55, 10, 80, 10, OLED_FILL)
            oled.print_line_no_fill("Error", 55, 10, OLED_COLOR)
    else:
        
        fan.stop_fan()

        oled.fill_rect(80, 20, 60, 10, OLED_FILL)
        oled.print_line_no_fill("OFF", 80, 20, OLED_COLOR)
        
        MQTT_PUBLISH_VALUE[MQTT_FAN_STATE] = ujson.dumps({"fan": 0})
        try:
            client.publish(MQTT_FAN_STATE, MQTT_PUBLISH_VALUE[MQTT_FAN_STATE])
            oled.fill_rect(55, 10, 80, 10, OLED_FILL)
            oled.print_line_no_fill("Published", 55, 10, OLED_COLOR)
        except OSError as e:
            print("Error during the sensors data pubblication: ", e)
            oled.fill_rect(55, 10, 80, 10, OLED_FILL)
            oled.print_line_no_fill("Error", 55, 10, OLED_COLOR)

def auto_irrigation_control():

    global SYSTEM_PARAMS, MQTT_PUBLISH_VALUE, MQTT_PUMP_STATE
    global OLED_COLOR, OLED_FILL
    global oled, pump
    
    oled.fill_rect(40, 30, 30, 10, OLED_FILL)
    oled.print_line_no_fill("AUT=>", 40, 30, OLED_COLOR)
    
    actual_ground_humidity = ground_sensor.get_last_value()

    if actual_ground_humidity < SYSTEM_PARAMS["HUMIDITY_CRITICAL_LEVEL"]:
        print("Starting pump...", end="")
        pump.start_pump()

        oled.fill_rect(80, 30, 30, 10, OLED_FILL)
        oled.print_line_no_fill("ON", 80, 30, OLED_COLOR)

        MQTT_PUBLISH_VALUE[MQTT_PUMP_STATE] = ujson.dumps({"pump": 1})
        try:
            client.publish(MQTT_PUMP_STATE, MQTT_PUBLISH_VALUE[MQTT_PUMP_STATE])
            oled.fill_rect(55, 10, 80, 10, OLED_FILL)
            oled.print_line_no_fill("Published", 55, 10, OLED_COLOR)
        except OSError as e:
            print("Error during the sensors data pubblication: ", e)
            ooled.fill_rect(55, 10, 80, 10, OLED_FILL)
            oled.print_line_no_fill("Error", 55, 10, OLED_COLOR)
        
    else:
        pump.stop_pump()

        oled.fill_rect(80, 30, 30, 10, OLED_FILL)
        oled.print_line_no_fill("OFF", 80, 30, OLED_COLOR)

        MQTT_PUBLISH_VALUE[MQTT_PUMP_STATE] = ujson.dumps({"pump": 0})
        try:
            client.publish(MQTT_PUMP_STATE, MQTT_PUBLISH_VALUE[MQTT_PUMP_STATE])
            oled.fill_rect(55, 10, 80, 10, OLED_FILL)
            oled.print_line_no_fill("Published", 55, 10, OLED_COLOR)
        except OSError as e:
            print("Error during the sensors data pubblication: ", e)
            oled.fill_rect(55, 10, 80, 10, OLED_FILL)
            oled.print_line_no_fill("Error", 55, 10, OLED_COLOR)
        
def auto_light_control():
    
    global MQTT_PUBLISH_VALUE, MQTT_LED_STATE
    global OLED_COLOR, OLED_FILL
    global led, client

    actual_external_lum = photo_resistor.get_last_value()

    oled.fill_rect(49, 40, 41, 10, OLED_FILL)
    oled.print_line_no_fill("AUT=>", 49, 40, OLED_COLOR)

    if actual_external_lum < SYSTEM_PARAMS["LIGHTS_OFF_LEVEL"]:

        actual_external_lum_converted = PHOTO_RESISTOR_MAX_VALUE - actual_external_lum

        duty_value = int(1023 * actual_external_lum_converted / (PHOTO_RESISTOR_MAX_VALUE - PHOTO_RESISTOR_MIN_VALUE))

        led1.duty(duty_value)
        led2.duty(duty_value)

        oled.fill_rect(90, 40, 60, 10, OLED_FILL)
        oled.print_line_no_fill(str(int(actual_external_lum_converted)) + "%", 90, 40, OLED_COLOR)

        MQTT_PUBLISH_VALUE[MQTT_LED_STATE] = ujson.dumps({"light" : 1})
        try:
            client.publish(MQTT_LED_STATE, MQTT_PUBLISH_VALUE[MQTT_LED_STATE])
            oled.fill_rect(55, 10, 80, 10, OLED_FILL)
            oled.print_line_no_fill("Published", 55, 10, OLED_COLOR)
        except OSError as e:
            print("Error during the sensors data pubblication: ", e)
            oled.fill_rect(55, 10, 80, 10, OLED_FILL)
            oled.print_line_no_fill("Error", 55, 10, OLED_COLOR)
       
    else:
        led1.duty(0)
        led2.duty(0)

        oled.fill_rect(90, 40, 60, 10, OLED_FILL)
        oled.print_line_no_fill("0%", 90, 40, OLED_COLOR)

        MQTT_PUBLISH_VALUE[MQTT_LED_STATE] = ujson.dumps({"light" : 0})
        try:
            client.publish(MQTT_LED_STATE, MQTT_PUBLISH_VALUE[MQTT_LED_STATE])
            oled.fill_rect(55, 10, 80, 10, OLED_FILL)
            oled.print_line_no_fill("Published", 55, 10, OLED_COLOR)
        except OSError as e:
            print("Error during the sensors data pubblication: ", e)
            oled.fill_rect(55, 10, 80, 10, OLED_FILL)
            oled.print_line_no_fill("Error", 55, 10, OLED_COLOR)
        
def read_sensor_value():

    global SENSORS_ACTUAL_VALUE, MQTT_ENVIRONMENT_TEMP, MQTT_ENVIRONMENT_HUMIDITY, MQTT_OUTSIDE_LUX
    global MQTT_CISTERN_LEVEL, MQTT_GROUND_HUMIDITY
    global OLED_COLOR, OLED_FILL
    global oled, environment_sensor, photo_resistor, cistern, ground_sensor

    print("Reading sensors values...")
    oled.fill_rect(70, 0, 60, 10, OLED_FILL)
    oled.print_line_no_fill("Reading", 65, 1, OLED_COLOR)
    environment_sensor.measure()
    SENSORS_ACTUAL_VALUE[MQTT_ENVIRONMENT_TEMP] = environment_sensor.get_ujson_temp()
    SENSORS_ACTUAL_VALUE[MQTT_OUTSIDE_LUX] = photo_resistor.get_ujson_value()
    SENSORS_ACTUAL_VALUE[MQTT_CISTERN_LEVEL] = cistern.get_ujson_level()
    SENSORS_ACTUAL_VALUE[MQTT_GROUND_HUMIDITY] = ground_sensor.get_ujson_value()
    SENSORS_ACTUAL_VALUE[MQTT_ENVIRONMENT_HUMIDITY] = environment_sensor.get_ujson_humidity()

    oled.fill_rect(70, 0, 60, 10, OLED_FILL)
    oled.print_line_no_fill("Read", 65, 1, OLED_COLOR)

def check_cistern():

    global SENSORS_ACTUAL_VALUE, SYSTEM_PARAMS, MQTT_PUMP_STATE, MQTT_CISTERN_LEVEL
    global OLED_COLOR, OLED_FILL
    global oled, pump, cistern
    
    if cistern.get_last_level() < SYSTEM_PARAMS["CRITICAL_CISTERN_LEVEL"]:
        print("Livello di cisterna critico, ricarica il serbatoio per proseguire l'irrigazione")
       
        pump.stop_pump()
        
        if SYSTEM_PARAMS["MANUAL_IRRIGATION_CONTROL"] == False:
            oled.fill_rect(40, 30, 30, 10, OLED_FILL)
            oled.print_line_no_fill("AUT=>", 40, 30, OLED_COLOR)

        oled.fill_rect(80, 30, 30, 10, OLED_FILL)
        oled.print_line_no_fill("OFF", 80, 30, OLED_COLOR)

        oled.fill_rect(70, 50, 60, 10, OLED_FILL)
        oled.print_line_no_fill("Critic!!", 70, 50, OLED_COLOR)

        SYSTEM_PARAMS["BLOCK_IRRIGATION_FOR_CRITICAL_CISTERN_LEVEL"] = True
        
        MQTT_PUBLISH_VALUE[MQTT_PUMP_STATE] = ujson.dumps({"pump": 0})
        try:
            client.publish(MQTT_PUMP_STATE, MQTT_PUBLISH_VALUE[MQTT_PUMP_STATE])
            oled.fill_rect(55, 10, 80, 10, OLED_FILL)
            oled.print_line_no_fill("Published", 55, 10, OLED_COLOR)
        except OSError as e:
            print("Error during the sensors data pubblication: ", e)
            oled.fill_rect(55, 10, 80, 10, OLED_FILL)
            oled.print_line_no_fill("Error", 55, 10, OLED_COLOR)

    
    elif cistern.get_last_level() > SYSTEM_PARAMS["CRITICAL_CISTERN_LEVEL"] and SYSTEM_PARAMS["BLOCK_IRRIGATION_FOR_CRITICAL_CISTERN_LEVEL"]:
        print("Sistema di irrigazione ripristinato")
        SYSTEM_PARAMS["BLOCK_IRRIGATION_FOR_CRITICAL_CISTERN_LEVEL"] = False
        
        oled.fill_rect(70, 50, 60, 10, OLED_FILL)
        oled.print_line_no_fill("Filled!!", 70, 50, OLED_COLOR)

        if SYSTEM_PARAMS["MANUAL_IRRIGATION_PUMP"] == True:
            print("Starting pump...", end="")
            pump.start_pump()

            oled.fill_rect(80, 30, 30, 10, OLED_FILL)
            oled.print_line_no_fill("ON", 80, 30, OLED_COLOR)

            MQTT_PUBLISH_VALUE[MQTT_PUMP_STATE] = ujson.dumps({"pump": 1})
            try:
                client.publish(MQTT_PUMP_STATE, MQTT_PUBLISH_VALUE[MQTT_PUMP_STATE])
                oled.fill_rect(55, 10, 80, 10, OLED_FILL)
                oled.print_line_no_fill("Published", 55, 10, OLED_COLOR)
            except OSError as e:
                print("Error during the sensors data pubblication: ", e)
                ooled.fill_rect(55, 10, 80, 10, OLED_FILL)
                oled.print_line_no_fill("Error", 55, 10, OLED_COLOR)
    
    else:
        oled.fill_rect(70, 50, 60, 10, OLED_FILL)
        oled.print_line_no_fill(str(int(cistern.get_last_level())) + "%", 70, 50, OLED_COLOR)
           
def manual_fan_control():

    global MQTT_PUBLISH_VALUE, MQTT_FAN_STATE
    global OLED_COLOR, OLED_FILL
    global oled, fan, client

    oled.fill_rect(35, 20, 30, 10, OLED_FILL)
    oled.print_line_no_fill("MAN=>", 35, 20, OLED_COLOR)

    fan.stop_fan()

    oled.fill_rect(80, 20, 60, 10, OLED_FILL)
    oled.print_line_no_fill("OFF", 80, 20, OLED_COLOR)

    MQTT_PUBLISH_VALUE[MQTT_FAN_STATE] = ujson.dumps({"fan" : 0 })
    try:
        client.publish(MQTT_FAN_STATE, MQTT_PUBLISH_VALUE[MQTT_FAN_STATE])
        oled.fill_rect(55, 10, 80, 10, OLED_FILL)
        oled.print_line_no_fill("Published", 55, 10, OLED_COLOR)
    except OSError as e:
        print("Error during the sensors data pubblication: ", e)
        oled.fill_rect(55, 10, 80, 10, OLED_FILL)
        oled.print_line_no_fill("Error", 55, 10, OLED_COLOR)

def manual_fan_on():

    global SYSTEM_PARAMS, MQTT_PUBLISH_VALUE, DASH_MAX_VAL, MQTT_FAN_STATE
    global OLED_COLOR, OLED_FILL
    global fan, oled, client

    fan.start_fan()

    oled.fill_rect(80, 20, 60, 10, OLED_FILL)
    oled.print_line_no_fill("ON", 80, 20, OLED_COLOR)

    MQTT_PUBLISH_VALUE[MQTT_FAN_STATE] = ujson.dumps({"fan" : 1})
    try:
        client.publish(MQTT_FAN_STATE, MQTT_PUBLISH_VALUE[MQTT_FAN_STATE])
        oled.fill_rect(55, 10, 80, 10, OLED_FILL)
        oled.print_line_no_fill("Published", 55, 10, OLED_COLOR)
    except OSError as e:
        print("Error during the sensors data pubblication: ", e)
        oled.fill_rect(55, 10, 80, 10, OLED_FILL)
        oled.print_line_no_fill("Error", 55, 10, OLED_COLOR)

def manual_fan_off():
    global SYSTEM_PARAMS, MQTT_PUBLISH_VALUE, DASH_MAX_VAL, MQTT_FAN_STATE
    global OLED_COLOR, OLED_FILL
    global fan, oled, client

    fan.stop_fan()

    oled.fill_rect(80, 20, 60, 10, OLED_FILL)
    oled.print_line_no_fill("OFF", 80, 20, OLED_COLOR)

    MQTT_PUBLISH_VALUE[MQTT_FAN_STATE] = ujson.dumps({"fan" : 0})
    try:
        client.publish(MQTT_FAN_STATE, MQTT_PUBLISH_VALUE[MQTT_FAN_STATE])
        oled.fill_rect(55, 10, 80, 10, OLED_FILL)
        oled.print_line_no_fill("Published", 55, 10, OLED_COLOR)
    except OSError as e:
        print("Error during the sensors data pubblication: ", e)
        oled.fill_rect(55, 10, 80, 10, OLED_FILL)
        oled.print_line_no_fill("Error", 55, 10, OLED_COLOR)

def manual_irrigation_pump_on():

    global SYSTEM_PARAMS, MQTT_PUBLISH_VALUE, MQTT_PUMP_STATE, MQTT_CISTERN_LEVEL
    global OLED_COLOR, OLED_FILL
    global oled, client, pump 

    if SYSTEM_PARAMS["BLOCK_IRRIGATION_FOR_CRITICAL_CISTERN_LEVEL"] == False:
        print("Starting pump...", end="")
        pump.start_pump()

        oled.fill_rect(80, 30, 30, 10, OLED_FILL)
        oled.print_line_no_fill("ON", 80, 30, OLED_COLOR)

        MQTT_PUBLISH_VALUE[MQTT_PUMP_STATE] = ujson.dumps({"pump": 1})
        try:
            client.publish(MQTT_PUMP_STATE, MQTT_PUBLISH_VALUE[MQTT_PUMP_STATE])
            oled.fill_rect(55, 10, 80, 10, OLED_FILL)
            oled.print_line_no_fill("Published", 55, 10, OLED_COLOR)
        except OSError as e:
            print("Error during the sensors data pubblication: ", e)
            oled.fill_rect(55, 10, 80, 10, OLED_FILL)
            oled.print_line_no_fill("Error", 55, 10, OLED_COLOR)
    else:
        print("Livello di cisterna critico, ricarica il serbatoio per proseguire l'irrigazione")
        pump.stop_pump()

        oled.fill_rect(80, 30, 30, 10, OLED_FILL)
        oled.print_line_no_fill("OFF", 80, 30, OLED_COLOR)

        MQTT_PUBLISH_VALUE[MQTT_PUMP_STATE] = ujson.dumps({"pump": 0})
        try:
            client.publish(MQTT_PUMP_STATE, MQTT_PUBLISH_VALUE[MQTT_PUMP_STATE])
            oled.fill_rect(55, 10, 80, 10, OLED_FILL)
            oled.print_line_no_fill("Published", 55, 10, OLED_COLOR)
        except OSError as e:
            print("Error during the sensors data pubblication: ", e)
            oled.fill_rect(55, 10, 80, 10, OLED_FILL)
            oled.print_line_no_fill("Error", 55, 10, OLED_COLOR)

def manual_irrigation_pump_off():

    global MQTT_PUBLISH_VALUE, MQTT_PUMP_STATE
    global OLED_COLOR, OLED_FILL
    global oled, client, pump

    pump.stop_pump()

    oled.fill_rect(80, 30, 30, 10, OLED_FILL)
    oled.print_line_no_fill("OFF", 80, 30, OLED_COLOR)

    MQTT_PUBLISH_VALUE[MQTT_PUMP_STATE] = ujson.dumps({"pump": 0})
    try:
        client.publish(MQTT_PUMP_STATE, MQTT_PUBLISH_VALUE[MQTT_PUMP_STATE])
        oled.fill_rect(55, 10, 80, 10, OLED_FILL)
        oled.print_line_no_fill("Published", 55, 10, OLED_COLOR)
    except OSError as e:
        print("Error during the sensors data pubblication: ", e)
        oled.fill_rect(55, 10, 80, 10, OLED_FILL)
        oled.print_line_no_fill("Error", 55, 10, OLED_COLOR)

#DA AGGIUSTARE IL DUTY VALUE
def manual_light_control():

    global MQTT_PUBLISH_VALUE, MQTT_LED_STATE, SYSTEM_PARAMS
    global OLED_COLOR, OLED_FILL
    global client, led

    led1.duty(0)
    led2.duty(0)

    oled.fill_rect(49, 40, 41, 10, OLED_FILL)
    oled.print_line_no_fill("MAN=>", 49, 40, OLED_COLOR)

    SYSTEM_PARAMS["MANUAL_LIGHTS_LEVEL"] = 0

    oled.fill_rect(90, 40, 60, 10, OLED_FILL)
    oled.print_line_no_fill(str(SYSTEM_PARAMS["MANUAL_LIGHTS_LEVEL"]) + "%", 90, 40, OLED_COLOR)

    MQTT_PUBLISH_VALUE[MQTT_LED_STATE] = ujson.dumps({"light" : 0 })

    try:
        client.publish(MQTT_LED_STATE, MQTT_PUBLISH_VALUE[MQTT_LED_STATE])
        oled.fill_rect(55, 10, 80, 10, OLED_FILL)
        oled.print_line_no_fill("Published", 55, 10, OLED_COLOR)
    except OSError as e:
        print("Error during the sensors data pubblication: ", e)
        oled.fill_rect(55, 10, 80, 10, OLED_FILL)
        oled.print_line_no_fill("Error", 55, 10, OLED_COLOR)
    
def manual_light_level():

    global SYSTEM_PARAMS, DASH_MAX_VAL, DASH_MIN_VAL, MQTT_LED_STATE, MQTT_PUBLISH_VALUE
    global OLED_COLOR, OLED_FILL
    global led, client

    duty_value = 1023 * SYSTEM_PARAMS["MANUAL_LIGHTS_LEVEL"] / (DASH_MAX_VAL - DASH_MIN_VAL)
    
    led1.duty(int(duty_value))
    led2.duty(int(duty_value))

    oled.fill_rect(90, 40, 60, 10, OLED_FILL)
    oled.print_line_no_fill(str(SYSTEM_PARAMS["MANUAL_LIGHTS_LEVEL"]) + "%", 90, 40, OLED_COLOR)

    MQTT_PUBLISH_VALUE[MQTT_LED_STATE] = ujson.dumps({"light" : 1})
    try:
        client.publish(MQTT_LED_STATE, MQTT_PUBLISH_VALUE[MQTT_LED_STATE])
        oled.fill_rect(55, 10, 80, 10, OLED_FILL)
        oled.print_line_no_fill("Published", 55, 10, OLED_COLOR)
    except OSError as e:
        print("Error during the sensors data pubblication: ", e)
        oled.fill_rect(55, 10, 80, 10, OLED_FILL)
        oled.print_line_no_fill("Error", 55, 10, OLED_COLOR)

#DEFINE THE CALLBACK PROCEDURE FOR THE RECIVED MSG FROM THE BROKER
def subCallback(topic, msg):

    global SYSTEM_PARAMS, MQTT_SUBSCRIBE_TOPIC
    global oled
    
    print(topic, msg)
    if topic == MQTT_SUBSCRIBE_TOPIC["MQTT_RESET_SYSTEM"]:
        if msg == b'true':
            SYSTEM_PARAMS["RESTART_SYSTEM"] = True 
        elif msg == b'false':
            SYSTEM_PARAMS["RESTART_SYSTEM"] = False 

    if topic == MQTT_SUBSCRIBE_TOPIC["MQTT_IRRIGATION_MANUALCONTROL"]:
        if msg == b'true':
            oled.fill_rect(40, 30, 30, 10, OLED_FILL)
            oled.print_line_no_fill("MAN=>", 40, 30, OLED_COLOR)
            SYSTEM_PARAMS["MANUAL_IRRIGATION_CONTROL"] = True
        elif msg == b'false':
            oled.fill_rect(40, 30, 30, 10, OLED_FILL)
            oled.print_line_no_fill("AUT=>", 40, 30, OLED_COLOR)
            SYSTEM_PARAMS["MANUAL_IRRIGATION_CONTROL"] = False
    
    if topic == MQTT_SUBSCRIBE_TOPIC["MQTT_IRRIGATION_MANUALPUMP"]:
        if msg == b'true':
            SYSTEM_PARAMS["MANUAL_IRRIGATION_PUMP"] = True 
            manual_irrigation_pump_on()
        elif msg == b'false':
            SYSTEM_PARAMS["MANUAL_IRRIGATION_PUMP"] = False
            manual_irrigation_pump_off()
    
    if topic == MQTT_SUBSCRIBE_TOPIC["MQTT_IRRIGATION_HUMIDITY"]:
        SYSTEM_PARAMS["HUMIDITY_CRITICAL_LEVEL"] = int(msg)

    if topic == MQTT_SUBSCRIBE_TOPIC["MQTT_FAN_MANUALCONTROL"]:
        if msg == b'true':
            SYSTEM_PARAMS["MANUAL_FAN_CONTROL"] = True
            manual_fan_control()
        elif msg == b'false':
            SYSTEM_PARAMS["MANUAL_FAN_CONTROL"] = False

    if topic == MQTT_SUBSCRIBE_TOPIC["MQTT_FAN_MANUALSTATE"]:
        if msg == b'true':
            SYSTEM_PARAMS["MANUAL_FAN_STATE"] = True
            manual_fan_on()
        elif msg == b'false':
            SYSTEM_PARAMS["MANUAL_FAN_STATE"] = False
            manual_fan_off()

    if topic == MQTT_SUBSCRIBE_TOPIC["MQTT_FAN_ACTIVATIONTEMP"]:
        SYSTEM_PARAMS["FAN_ACTIVATION_TEMP"] = int(msg)
    
    if topic == MQTT_SUBSCRIBE_TOPIC["MQTT_LIGHTS_MANUALCONTROL"]:
        if msg == b'true':
            SYSTEM_PARAMS["MANUAL_LIGHTS_CONTROL"] = True
            manual_light_control()
        elif msg == b'false':
            SYSTEM_PARAMS["MANUAL_LIGHTS_CONTROL"] = False
    
    if topic == MQTT_SUBSCRIBE_TOPIC["MQTT_LIGHTS_MANUALLED"]:
        SYSTEM_PARAMS["MANUAL_LIGHTS_LEVEL"] = int(msg)
        manual_light_level()
    
    if topic == MQTT_SUBSCRIBE_TOPIC["MQTT_LIGHTS_LEVEL"]:
        SYSTEM_PARAMS["LIGHTS_OFF_LEVEL"] = int(msg)

# PROCEDURE OF CONNECTION TO THE MQTT SERVER
def mqtt_connection():

    global client, oled
    global OLED_COLOR

    print("Connecting to MQTT server", end="")
    shift_point = 0
    oled.print_line("Connecting to", 0, 1, OLED_COLOR)
    oled.print_line_no_fill("MQTT server", 0, 10, OLED_COLOR)
    client.connect(MQTT_SUBSCRIBE_TOPIC, subCallback)
    while shift_point < 28:
        print(".", end="")
        oled.print_line_no_fill(".", 85 + shift_point, 10, OLED_COLOR)
        time.sleep(0.1)
        shift_point = shift_point + 6
    if client.get_conn_state():
        print(" Connected")
        oled.print_line_no_fill("Connected!", 0, 20, OLED_COLOR)
        oled.fill_clr()
    else:
        print("Failure connecting to MQTT server!!")
        oled.print_line_no_fill("Not Connected!", 0, 20, OLED_COLOR)
        oled.fill_clr()
        raise OSException

#UTILITIES FUNCTIONS USED FOR THE TESTS
def print_dict(dic):
    for k, v in dic.items():
        if v != None:
            print(k, ": ", v)

#DEFINING THE IRQ PROCEDURE FOR THE RESET BTN
def hard_reset(btn_reset):
    global LAST_RESET, SYSTEM_PARAMS, DELTA_MAX_RESET

    current = time.ticks_ms()
    delta = time.ticks_diff(current, LAST_RESET)
    
    if delta < DELTA_MAX_RESET:
        return
    
    LAST_RESET = current
    SYSTEM_PARAMS["RESTART_SYSTEM"] = True 

reset_btn.irq(trigger=Pin.IRQ_RISING, handler=hard_reset)

#DEFINING THE IRQ PROCEDURE FOR THE DOOR BTN

def change_door_state_reprint():
    global SYSTEM_PARAMS
    global oled
    
    if SYSTEM_PARAMS["MANUAL_IRRIGATION_CONTROL"] == True:
        oled.fill_rect(40, 30, 30, 10, OLED_FILL)
        oled.print_line_no_fill("MAN=>", 40, 30, OLED_COLOR)
        if SYSTEM_PARAMS["MANUAL_IRRIGATION_PUMP"] == True:
            oled.fill_rect(80, 30, 30, 10, OLED_FILL)
            oled.print_line_no_fill("ON", 80, 30, OLED_COLOR)
        else:
            oled.fill_rect(80, 30, 30, 10, OLED_FILL)
            oled.print_line_no_fill("OFF", 80, 30, OLED_COLOR)
    
    if SYSTEM_PARAMS["MANUAL_FAN_CONTROL"] == True:
        oled.fill_rect(35, 20, 30, 10, OLED_FILL)
        oled.print_line_no_fill("MAN=>", 35, 20, OLED_COLOR)
        
        if SYSTEM_PARAMS["MANUAL_FAN_STATE"] == True:
            oled.fill_rect(80, 20, 60, 10, OLED_FILL)
            oled.print_line_no_fill("ON", 80, 20, OLED_COLOR)
        else:
            oled.fill_rect(80, 20, 60, 10, OLED_FILL)
            oled.print_line_no_fill("OFF", 80, 20, OLED_COLOR)
    
    if SYSTEM_PARAMS["MANUAL_LIGHTS_CONTROL"] == True:
        oled.fill_rect(49, 40, 41, 10, OLED_FILL)
        oled.print_line_no_fill("MAN=>", 49, 40, OLED_COLOR)

        oled.fill_rect(90, 40, 60, 10, OLED_FILL)
        oled.print_line_no_fill(str(SYSTEM_PARAMS["MANUAL_LIGHTS_LEVEL"]) + "%", 90, 40, OLED_COLOR)




def change_door_state(btn_door):
    global LAST_DOOR, SYSTEM_PARAMS, DELTA_MAX_DOOR, IRQ_LOCK, MQTT_DOOR_STATE
    global door, oled, client
    global OLED_FILL, OLED_COLOR

    if IRQ_LOCK == False:

        current = time.ticks_ms()
        delta = time.ticks_diff(current, LAST_DOOR)
        
        if delta < DELTA_MAX_DOOR:
            return
        
        LAST_DOOR = current

        SYSTEM_PARAMS["DOOR_OPEN"] = not SYSTEM_PARAMS["DOOR_OPEN"]

        if SYSTEM_PARAMS["DOOR_OPEN"] == True:
            door.open_door()
            
            oled.print_door_state(SYSTEM_PARAMS["DOOR_OPEN"], OLED_FILL, OLED_COLOR)
            
            MQTT_PUBLISH_VALUE[MQTT_DOOR_STATE] = ujson.dumps({"door" : 1})
            try:
                client.publish(MQTT_DOOR_STATE, MQTT_PUBLISH_VALUE[MQTT_DOOR_STATE])
                oled.fill_rect(55, 10, 80, 10, OLED_FILL)
                oled.print_line_no_fill("Published", 55, 10, OLED_COLOR)
            except OSError as e:
                print("Error during the sensors data pubblication: ", e)
                oled.fill_rect(55, 10, 80, 10, OLED_FILL)
                oled.print_line_no_fill("Error", 55, 10, OLED_COLOR)
            
        elif SYSTEM_PARAMS["DOOR_OPEN"] == False:
            door.close_door()
            oled.print_door_state(SYSTEM_PARAMS["DOOR_OPEN"], OLED_FILL, OLED_COLOR)
            
            MQTT_PUBLISH_VALUE[MQTT_DOOR_STATE] = ujson.dumps({"door" : 0})
            
            try:
                client.publish(MQTT_DOOR_STATE, MQTT_PUBLISH_VALUE[MQTT_DOOR_STATE])
                oled.fill_rect(55, 10, 80, 10, OLED_FILL)
                oled.print_line_no_fill("Published", 55, 10, OLED_COLOR)
            except OSError as e:
                print("Error during the sensors data pubblication: ", e)
                oled.fill_rect(55, 10, 80, 10, OLED_FILL)
                oled.print_line_no_fill("Error", 55, 10, OLED_COLOR)
        
        oled.print_system_state(OLED_FILL,OLED_COLOR)
        change_door_state_reprint()

door_btn.irq(trigger=Pin.IRQ_RISING, handler=change_door_state)

#MAIN LOOP
while True:
    if SYSTEM_PARAMS["BOOT"] == True:
        IRQ_LOCK = True
        wifi_connect()
        mqtt_connection()
        oled.print_system_state(OLED_FILL,OLED_COLOR)
        SYSTEM_PARAMS["BOOT"] = False
        IRQ_LOCK = False
    
    if SYSTEM_PARAMS["PRINT_SYS_STATE"] == True:
        oled.print_system_state(OLED_FILL,OLED_COLOR)
        SYSTEM_PARAMS["PRINT_SYS_STATE"] = False

    if SYSTEM_PARAMS["RESTART_SYSTEM"] == False:
        try:
            read_sensor_value()
        except OSError as e:
            print("Error while reading the sensors values:", e)
        
        try:
            MQTT_PUBLISH_VALUE = client.publish_new_sensor_value(MQTT_PUBLISH_VALUE, SENSORS_ACTUAL_VALUE)
        except OSError as e:
            print("Error during the sensors data pubblication: ", e)
        
        check_cistern()

        try:
            print("Checking messages from the broker...")
            client.check_msg()
            oled.fill_rect(55, 10, 80, 10, OLED_FILL)
            oled.print_line_no_fill("Checked", 55, 10, OLED_COLOR)
        except OSError as e:
            oled.fill_rect(55, 10, 80, 10, OLED_FILL)
            oled.print_line_no_fill("Error", 55, 10, OLED_COLOR)
            print("Checking masseges from the broker fail: ", e)

        if SYSTEM_PARAMS["MANUAL_FAN_CONTROL"] == False:
            auto_fan_control()
        
        if SYSTEM_PARAMS["MANUAL_IRRIGATION_CONTROL"] == False and SYSTEM_PARAMS["BLOCK_IRRIGATION_FOR_CRITICAL_CISTERN_LEVEL"] == False:
            auto_irrigation_control()

        if SYSTEM_PARAMS["MANUAL_LIGHTS_CONTROL"] == False:
            auto_light_control()

    else:
        IRQ_LOCK = True
        restart_procedure()
        IRQ_LOCK = False



$abcdeabcde151015202530354045505560fghijfghij