// Incluir la biblioteca necesaria para manejar la conexión WiFi en ESP
#include <WiFi.h> 
#include <LiquidCrystal.h>

// Incluir la biblioteca para el protocolo MQTT, que permite enviar y recibir mensajes
#include <PubSubClient.h>

// Incluir la biblioteca para el sensor DHT (temperatura y humedad)
#include <DHTesp.h>

// Definir el pin al cual está conectado el sensor DHT
const int DHT_PIN = 15;  

// Crear una instancia del sensor DHT
DHTesp dht; 
LiquidCrystal lcd(23,22,21,19,18,5); 

// Datos para la conexión WiFi: SSID (nombre de la red) y contraseña
const char* ssid = "Wokwi-GUEST"; 
const char* password = "";

// Dirección del servidor MQTT al que se conectará
const char* mqtt_server = "test.mosquitto.org";

// Crear un cliente WiFi para el ESP, necesario para la conexión MQTT
WiFiClient espClient;

// Crear un cliente MQTT utilizando el cliente WiFi anterior
PubSubClient client(espClient);

// Almacenar el momento (en milisegundos) de la última vez que se envió un mensaje
unsigned long lastMsg = 0;

// Variables para almacenar los valores de temperatura y humedad leídos del sensor DHT
float temp = 0;
float hum = 0;


// Función para establecer conexión con la red WiFi
void setup_wifi() { 
  // Pequeño retraso al comienzo
  delay(10);

  // Imprimir en el monitor serial el inicio de conexión
  Serial.println();
  Serial.print("Connecting to ");
  Serial.println(ssid);

  // Establecer el modo WiFi del ESP en modo estación (se conectará a un punto de acceso)
  WiFi.mode(WIFI_STA); 
  // Iniciar conexión con el SSID y contraseña proporcionados
  WiFi.begin(ssid, password);

  // Esperar hasta que el estado de la conexión sea "Conectado"
  while (WiFi.status() != WL_CONNECTED) { 
    // Retraso y punto para mostrar progreso en el monitor serial
    delay(500);
    Serial.print(".");
  }

  // Inicializar el generador de números aleatorios
  randomSeed(micros());

  // Imprimir en el monitor serial que la conexión fue exitosa y mostrar la IP asignada
  Serial.println("");
  Serial.println("WiFi connected");
  Serial.println("IP address: ");
  Serial.println(WiFi.localIP());
}

// Función callback para manejar mensajes entrantes de MQTT
void callback(char* topic, byte* payload, unsigned int length) { 
  // Imprimir el tema del mensaje en el monitor serial
  Serial.print("Message arrived [");
  Serial.print(topic);
  Serial.print("] ");
  // Imprimir el contenido del mensaje
  for (int i = 0; i < length; i++) { 
    Serial.print((char)payload[i]);
  }
}

// Función para (re)conectar al servidor MQTT
void reconnect() { 
  // Intentar reconectar mientras no haya conexión
  while (!client.connected()) {
    // Informar el intento de conexión en el monitor serial
    Serial.print("Attempting MQTT connection...");
    // Crear un ID de cliente único
    String clientId = "ESP32Client-";
    clientId += String(random(0xffff), HEX);

    // Intentar conectar al servidor MQTT
    if (client.connect(clientId.c_str())) {
      // Informar la conexión exitosa
      Serial.println("Connected");
    } else {
      // Informar el fallo en la conexión y el código de estado
      Serial.print("failed, rc=");
      Serial.print(client.state());
      Serial.println(" try again in 5 seconds");
      // Esperar 5 segundos antes de intentar nuevamente
      delay(5000);
    }
  }
}

// Función setup() que se ejecuta una vez al inicio del programa
void setup() {
  // Configurar el pin digital 2 como salida
  pinMode(2, OUTPUT);     

  // Iniciar la comunicación serial a una velocidad de 115200 baudios
  Serial.begin(115200);

  // Llamar a la función para establecer conexión WiFi
  setup_wifi(); 

  // Configurar el servidor MQTT y el puerto (1883 es el puerto estándar para MQTT)
  client.setServer(mqtt_server, 1883);

  // Establecer la función callback para manejar los mensajes entrantes de MQTT
  client.setCallback(callback); 

  // Iniciar el sensor DHT22 en el pin especificado
  dht.setup(DHT_PIN, DHTesp::DHT22);
  lcd.begin(16,2);
}

// Función loop() que se ejecuta continuamente después de setup()
void loop() {
  // Si el cliente MQTT no está conectado, intentar reconectar
  if (!client.connected()) {
    reconnect();
  }
  
  // Mantener la conexión MQTT y procesar mensajes entrantes y salientes
  client.loop();

  // Obtener el tiempo actual en milisegundos desde que se inició el programa
  unsigned long now = millis();

  // Verificar si han pasado más de 2 segundos desde la última vez que se publicaron datos
  if (now - lastMsg > 2000) { 
    // Actualizar el momento de la última publicación
    lastMsg = now;

    // Leer datos de temperatura y humedad del sensor DHT22
    TempAndHumidity  data = dht.getTempAndHumidity();

    // Convertir la temperatura a una cadena y publicarla en el servidor MQTT
    String temp = String(data.temperature, 2);
    client.publish("GALDO_temp", temp.c_str()); 

    // Convertir la humedad a una cadena y publicarla en el servidor MQTT
    String hum = String(data.humidity, 1); 
    client.publish("GALDO_hum", hum.c_str()); 

    // Imprimir los valores de temperatura y humedad en el monitor serial
    Serial.print("Temperature: ");
    Serial.println(temp);
    lcd.setCursor(0,0); 
    lcd.print("Temp="); 
    lcd.print(temp); 
    lcd.print(" C    "); 

    Serial.print("Humidity: ");
    Serial.println(hum);
    lcd.setCursor(0,1); 
    lcd.print("Hum="); 
    lcd.print(hum); 
    lcd.print(" %   "); 
  }
}