#include <WiFi.h>                   // Include WiFi library for connecting to WiFi
#include <PubSubClient.h>           // Include MQTT library for communicating with an MQTT broker
#include <Wire.h>                   // Include Wire library for I2C communication
#include <Adafruit_Sensor.h>        // Include Adafruit Sensor library
#include <DHT.h>                    // Include DHT library for DHT sensors
#include <LiquidCrystal_I2C.h>      // Include library for the I2C LCD

// Define pins for sensors and LEDs
#define DHT_PIN 4                   // DHT22 sensor is connected to pin 4
#define LDR_PIN 34                  // LDR sensor is connected to pin 34
#define SDA_PIN 22                  // I2C SDA pin
#define SCL_PIN 21                  // I2C SCL pin
#define BLUE_LED_PIN 26             // Blue LED is connected to pin 26
#define GREEN_LED_PIN 27            // Green LED is connected to pin 27
#define SPEAKER_PIN 13              // Speaker is connected to pin 13

// LDR sensor characteristics
const float GAMMA = 0.7;            // Gamma value for LDR calculation
const float RL10 = 50;              // Resistance value for LDR calculation

// LCD settings
#define I2C_ADDR 0x27               // I2C address of the LCD
#define LCD_COLUMNS 16              // Number of columns on the LCD
#define LCD_LINES 2                 // Number of lines on the LCD

// Wi-Fi and MQTT broker settings
const char* ssid = "Wokwi-GUEST";   // WiFi network name
const char* password = "";          // WiFi password (empty for Wokwi-GUEST)
const char* mqtt_server = "public.mqtthq.com"; // MQTT broker address
const int mqtt_port = 1883;         // MQTT broker port
const char* mqtt_topic = "/mqtt";   // MQTT topic to publish data

// Create WiFi and MQTT clients
WiFiClient espClient;
PubSubClient client(espClient);

// Create objects for DHT sensor and LCD
DHT dht(DHT_PIN, DHT22);
LiquidCrystal_I2C lcd(I2C_ADDR, LCD_COLUMNS, LCD_LINES);

// Function to connect to Wi-Fi
void setup_wifi() {
  delay(10);
  Serial.println();
  Serial.print("Connecting to ");
  Serial.println(ssid);
  // Display Wi-Fi connection status on the LCD
  lcd.setCursor(0, 0);
  lcd.print("Wi-Fi Connecting");
  // Start connecting to Wi-Fi
  WiFi.begin(ssid, password);
  // Blink blue LED while connecting
  while (WiFi.status() != WL_CONNECTED) {
    digitalWrite(BLUE_LED_PIN, HIGH);
    delay(500);
    digitalWrite(BLUE_LED_PIN, LOW);
    delay(500);
    Serial.print(".");
  }
  // Once connected, display status and IP address
  Serial.println("");
  Serial.println("WiFi connected");
  lcd.clear();
  lcd.setCursor(0, 0);
  lcd.print("Wi-Fi Connected");
  Serial.println("IP address: ");
  Serial.println(WiFi.localIP());
  digitalWrite(BLUE_LED_PIN, HIGH); // Turn on blue LED to indicate Wi-Fi connection
}

// Function to reconnect to MQTT broker if connection is lost
void reconnect() {
  while (!client.connected()) {
    // Display MQTT connection attempt on LCD
    lcd.clear();
    lcd.setCursor(0, 0);
    lcd.print("Attempting MQTT");
    lcd.setCursor(0, 1);
    lcd.print("connection...");
    Serial.print("Attempting MQTT connection...");

    // Attempt to connect to MQTT broker
    if (client.connect("ESP32Client")) {
      Serial.println("connected");
      digitalWrite(GREEN_LED_PIN, HIGH); // Turn on green LED to indicate MQTT connection
    } else {
      Serial.print("failed, rc=");
      Serial.print(client.state());
      Serial.println(" try again in 5 seconds");
      digitalWrite(GREEN_LED_PIN, LOW); // Turn off green LED if connection fails
      delay(5000);
    }
  }
}

void setup() {
  Serial.begin(115200);             // Start serial communication at 115200 baud
  Serial.println("");
  dht.begin();                      // Initialize DHT sensor
  lcd.init();                       // Initialize the LCD
  lcd.backlight();                  // Turn on the LCD backlight
  Wire.begin(SDA_PIN, SCL_PIN);     // Initialize I2C communication
  pinMode(BLUE_LED_PIN, OUTPUT);    // Set blue LED pin as output
  pinMode(GREEN_LED_PIN, OUTPUT);   // Set green LED pin as output
  pinMode(SPEAKER_PIN, OUTPUT);     // Set speaker pin as output
  setup_wifi();                     // Connect to Wi-Fi
  client.setServer(mqtt_server, mqtt_port); // Set MQTT server and port
}

void loop() {
  if (!client.connected()) {
    reconnect(); // Reconnect to MQTT broker if not connected
  }
  client.loop(); // Keep the MQTT connection alive

  // Read DHT22 sensor values
  float humidity = dht.readHumidity();
  float temperature = dht.readTemperature();

  // Read LDR sensor value and convert to lux
  int ldrValue = analogRead(LDR_PIN);
  float voltage = ldrValue / 4095.0 * 3.3; // Adjust voltage calculation for ESP32
  float resistance = 2000.0 * voltage / (1 - voltage / 3.3);
  float lux = pow(RL10 * 1e3 * pow(10, GAMMA) / resistance, (1 / GAMMA));
 
  // Check if the DHT sensor readings are valid
  if (isnan(humidity) || isnan(temperature)) {
    Serial.println("Failed to read from DHT sensor!");
    return; // Exit the loop if reading fails
  }

  // Sound the alarm if temperature is 30°C or higher
  if (temperature >= 30) {
    digitalWrite(SPEAKER_PIN, HIGH);
    delay(1000); // Sound the alarm for 1 second
    digitalWrite(SPEAKER_PIN, LOW);
  }

  // Create a message string with the sensor values
  String message = "Temperature: " + String(temperature) + "C, Humidity: " + String(humidity) + "%, LDR Value: " + String(ldrValue);
  Serial.println("Publishing message...");
  client.publish(mqtt_topic, message.c_str()); // Publish the message to the MQTT broker

  // Display sensor values on the LCD
  lcd.clear();
  lcd.setCursor(0, 0);
  lcd.print("T:"); // Print temperature
  lcd.print(temperature);
  lcd.print("C H: "); // Print humidity
  lcd.print(humidity);
  lcd.setCursor(0, 1);
  lcd.print("LDR: "); // Print LDR value
  lcd.print(lux);

  // Blink blue LED to indicate data is being sent
  digitalWrite(BLUE_LED_PIN, HIGH);
  delay(1000); // Keep LED on for 1 second
  digitalWrite(BLUE_LED_PIN, LOW);
  delay(2000); // Wait for 2 seconds before the next loop
}