#include <WiFi.h>
#include "PubSubClient.h"
#include "DHTesp.h"
#include <LiquidCrystal_I2C.h>
#include <ESP32Servo.h>
#include <ArduinoJson.h>

//Kênh gửi
const char * MQTTServer = "broker.emqx.io";
const char* data_ldr_topic = "iot50percent_ldr"; //LDR
const char* data_gas_topic = "iot50percent_gas"; //GAS
const char* data_pir_topic = "iot50percent_pir"; //PIR
const char* data_ultra_topic = "iot50percent_ultrasonic"; //ultrasonic
const char* data_rain_topic = "iot50percent_rain"; //rain
const char* data_dht_topic = "iot50percent_dht"; //TEMP & HUMI

// Kênh nhận
const char* rem_cua_topic = "iot50percent/remcua";
const char* rem_cua_topic_response = "iot50percent/remcua/response";

const char* cua_so_topic = "iot50percent/cuaso";
const char* cua_so_topic_response = "iot50percent/cuaso/response";

const char* cua_nha_topic = "iot50percent/cuanha";
const char* cua_nha_topic_response = "iot50percent/cuanha/response";

const char* gian_phoi_topic = "iot50percent/gianphoi";
const char* gian_phoi_topic_response = "iot50percent/gianphoi/response";

const char* den_wc_topic = "iot50percent/denwc";
const char* den_wc_topic_response = "iot50percent/denwc/response";

const char* den_war_topic = "iot50percent/denwar";
const char* den_war_topic_response = "iot50percent/denwar/response";

const char* den_nha_topic = "iot50percent/dennha";
const char* den_nha_topic_response = "iot50percent/dennha/response";

const char* dieu_hoa_topic = "iot50percent/dieuhoa";
const char* dieu_hoa_topic_response = "iot50percent/dieuhoa/response";

// Tạo ID ngẫu nhiên tại: https://www.guidgen.com/
const char * MQTT_ID = "c5afa5ae-d3a0-48b7-b850-92015a281909";
int Port = 1883;

WiFiClient espClient;
PubSubClient client(espClient);

//............................................................................................................
LiquidCrystal_I2C lcd(0x27, 20, 4);

//Các đèn led báo hiệu
const int ledAir = 16; //led xanh lam : máy điều hòa
const int ledHome = 17; //led trắng
const int ledWar = 4; //led đỏ
const int ledWC = 32; //led cam

//DHT22
const int DHT_PIN = 23;
DHTesp dht;

//Buzzer
const int buzzer = 14;

//Gas Sensor không có cảm biến này nên lấy "wokwi-slide-switch Reference" tượng trưng cho 2 mức cao thấp tương ứng với việc
//có gas hoặc không có gas
const int gasPin = 12;

//PIR
const int pir = 25;

//LDR
const int LDR_PIN = 34;
int ldrVal = 0;      // Value of LDR
int ledPin = 2;     // Build in LED pin
// LDR Characteristics
const float GAMMA = 0.7;
const float RL10 = 50;

//Rain Sensor
const int RAIN_ANALOG = 18;
const int RAIN_DIGITAL = 19;

//Ultrasonic
const int TRIG_PIN = 27;
const int ECHO_PIN = 26;
float duration_us, distance_cm;
bool ultrasonicPersonDetected = false; // Trạng thái để theo dõi nếu có người được phát hiện liên tục
unsigned long ultrasonicDetectionStartTime = 0; // Thời gian khi người được phát hiện lần đầu

//Servo
const int servoRemCua = 15;
Servo remCua; //Rèm cửa

const int servoCuaSo = 13;
Servo cuaSo; //Cửa sổ

const int servoCuaNha = 2;
Servo cuaNha; //Cửa nhà

const int servoGianPhoi = 5;
Servo gianPhoi; //Giàn phơi đồ

bool gasDetected = false;
bool rainDetected = false;


void WIFIConnect() {
  Serial.println("Connecting to SSID: Wokwi-GUEST");
  WiFi.begin("Wokwi-GUEST", "");
  while (WiFi.status() != WL_CONNECTED) {
    delay(500);
    Serial.print(".");
  }
  Serial.println("");
  Serial.print("WiFi connected");
  Serial.print(", IP address: ");
  Serial.println(WiFi.localIP());
}

void MQTT_Reconnect() {
  while (!client.connected()) {
    if (client.connect(MQTT_ID)) {
      Serial.print("MQTT Topic: ");
      client.subscribe(data_ldr_topic);
      client.subscribe(data_gas_topic);
      client.subscribe(data_pir_topic);
      client.subscribe(data_ultra_topic);
      client.subscribe(data_rain_topic);
      client.subscribe(data_dht_topic);
      client.subscribe(rem_cua_topic);
      client.subscribe(rem_cua_topic_response);
      client.subscribe(cua_so_topic);
      client.subscribe(cua_so_topic_response);
      client.subscribe(cua_nha_topic);
      client.subscribe(cua_nha_topic_response);
      client.subscribe(gian_phoi_topic);
      client.subscribe(gian_phoi_topic_response);
      Serial.println(" connected");
      Serial.println("");
    } else {
      Serial.print("failed, rc = ");
      Serial.print(client.state());
      Serial.println(" try again in 5 seconds");
      delay(5000);
    }
  }
}

void callback(char* topic, byte* message, unsigned int length) {
  Serial.print("Message arrived on topic: ");
  Serial.println(topic);
  Serial.print("Message: ");
  String receivedMessage;
  for (int i = 0; i < length; i++) {
    Serial.print((char)message[i]);
    receivedMessage += (char)message[i];
  }

  // Kiểm tra và xử lý các thông điệp điều khiển từ web

}

void setup() {
  Serial.begin(115200);
  //LCD
  lcd.init();
  lcd.backlight();
  noTone(buzzer);


  //Rain Sensor
  pinMode(RAIN_ANALOG, INPUT);
  pinMode(RAIN_DIGITAL, INPUT);
  analogReadResolution(10);

  //Ultrasonic
  pinMode(TRIG_PIN, OUTPUT);
  pinMode(ECHO_PIN, INPUT);

  //DHT22
  dht.setup(DHT_PIN, DHTesp::DHT22);

  //Led và buzzer
  pinMode(ledAir, OUTPUT); //led xanh lam: máy điều hòa
  pinMode(ledHome, OUTPUT); // led trắng: đèn ở khu vực luôn sáng khi trời tối như cửa ngoài, cổng...
  pinMode(ledWar, OUTPUT); //led đỏ: cảnh báo có khí gas
  pinMode(ledWC, OUTPUT); //led cam: đèn cửa trong nhà, nhà vệ sinh, khu ít người qua lại

  digitalWrite(ledAir, LOW);
  digitalWrite(ledHome, LOW);
  digitalWrite(ledWar, LOW);
  digitalWrite(ledWC, LOW);

  //Servo
  remCua.attach(15);

  cuaSo.attach(13);

  gianPhoi.attach(5);

  cuaNha.attach(2);

  //Buzzer
  pinMode(buzzer, OUTPUT);

  //Gas Sensor
  pinMode(gasPin, INPUT);

  //LDR
  pinMode(LDR_PIN, INPUT);

  //PIR
  pinMode(pir, INPUT);

  WIFIConnect();
  client.setServer(MQTTServer, Port);
  client.setCallback(callback);
  remCua.write(90);
  cuaSo.write(90);
  gianPhoi.write(90);
  cuaNha.write(90);
}

//LDR sensor
void ldrSensor() {
  ldrVal = analogRead(LDR_PIN); // Read the analog value of the LDR
  float voltage = ldrVal / 1024. * 5;
  float resistance = 2000 * voltage / (1 - voltage / 5);
  float lux = pow(RL10 * 1e3 * pow(10, GAMMA) / resistance, (1 / GAMMA));

  if (lux > 1000) {
    remCua.write(180); // mở rèm cửa
    lcd.setCursor(0, 0);
    lcd.print("RemCua: Mo  ");
    digitalWrite(ledHome, LOW); // tắt đèn trong nhà
    client.publish(rem_cua_topic, "Đang mở rèm");
    client.publish(den_nha_topic, "Đang bật");

  } else {
    remCua.write(90); // đóng rèm cửa
    lcd.setCursor(0, 0);
    lcd.print("RemCua: Dong");
    digitalWrite(ledHome, HIGH); // bật đèn trong nhà
    client.publish(rem_cua_topic, "Đang đóng rèm");
    client.publish(den_nha_topic, "Đang tắt");
  }
  Serial.println(lux);
  // Chuyển đổi giá trị lux thành chuỗi và gửi qua MQTT
  String luxStr = String(lux);
  client.publish(data_ldr_topic, luxStr.c_str());
  delay(1000);
}

//GAS sensor
void gasSensor() {
  int gasVal = digitalRead(gasPin);
  if (gasVal == HIGH) { // Phát hiện khí gas
    gasDetected = true;
    tone(buzzer, 1000); // Bật còi báo động
    digitalWrite(ledWar, HIGH); // Bật đèn cảnh báo
    lcd.setCursor(0, 1);
    lcd.print("Co khi Gas      ");
    delay(1000);
    client.publish(data_gas_topic, "Phát hiện có khí Gas");
    client.publish(den_war_topic, "Đang bật");
  } else {
    gasDetected = false;
    noTone(buzzer); // Tắt còi báo động
    digitalWrite(ledWar, LOW); // Tắt đèn cảnh báo
    lcd.setCursor(0, 1);
    lcd.print("Khong co khi Gas");
    client.publish(data_gas_topic, "Không phát hiện có khí Gas");
    client.publish(den_war_topic, "Đang tắt");
  }
  Serial.print("Giá trị của gas: ");
  Serial.println(gasVal);
  delay(1000);
}

//RAIN sensor
void rainSensor() {
  int rainAnalog = analogRead(RAIN_ANALOG);
  int rainDigital = digitalRead(RAIN_DIGITAL);
  if (rainDigital == 1) { // Phát hiện mưa
    gianPhoi.write(90); // Thu giàn phơi đồ vào
    lcd.setCursor(0, 2);
    lcd.print("Co mua:Thu gian phoi");
    client.publish(data_rain_topic, "Có mưa");
    client.publish(gian_phoi_topic, "Thu giàn phơi");
  } else { // Không mưa
    gianPhoi.write(180); // Kéo giàn phơi đồ ra
    lcd.setCursor(0, 2);
    lcd.print("Ko mua:Keo gian phoi");
    client.publish(data_rain_topic, "Không có mưa");
    client.publish(gian_phoi_topic, "Kéo giàn phơi");
  }
  Serial.print("Gia tri mua ");
  Serial.println(rainDigital);
  delay(1000);
}

//PIR sensor
void pirSensor() {
  int pirVal = digitalRead(pir);
  if (pirVal == HIGH) { // Phát hiện chuyển động
    digitalWrite(ledWC, HIGH); // Bật đèn nhà vệ sinh
    client.publish(data_pir_topic, "Có chuyển động");
    client.publish(den_wc_topic, "Đang bật");
  } else { // Không phát hiện chuyển động
    digitalWrite(ledWC, LOW); // Tắt đèn nhà vệ sinh
    client.publish(data_pir_topic, "Không có chuyển động");
    client.publish(den_wc_topic, "Đang tắt");

  }
  delay(1000);
}

//ULTRA sensor
void ultrasonicSensor() {
  digitalWrite(TRIG_PIN, LOW);
  delayMicroseconds(2);
  digitalWrite(TRIG_PIN, HIGH);
  delayMicroseconds(10);
  digitalWrite(TRIG_PIN, LOW);
  duration_us = pulseIn(ECHO_PIN, HIGH);
  distance_cm = 0.017 * duration_us;

  if (distance_cm < 200) {
    if (!ultrasonicPersonDetected) { // Nếu vừa bắt đầu phát hiện người
      ultrasonicDetectionStartTime = millis();
      ultrasonicPersonDetected = true;
    } else {
      if ((millis() - ultrasonicDetectionStartTime) >= 3000) { // 3 giây phát hiện liên tục
        tone(buzzer, 1000); // Bật còi báo động
        lcd.setCursor(0, 3);
        lcd.print("Co nguoi truoc nha");
        client.publish(data_ultra_topic, "Có người đứng gần cửa nhà!");
      }
    }
  } else {
    ultrasonicPersonDetected = false;
    lcd.setCursor(0, 3);
    noTone(buzzer); // Tắt còi báo động
    lcd.print("...               ");
    client.publish(data_ultra_topic, ". . .");
  }
  delay(1000);
  Serial.print("khoangcach: ");
  Serial.println(distance_cm);
}

//DHT sensor
void dhtSensor() {
  TempAndHumidity data = dht.getTempAndHumidity();
  if (data.temperature > 38 && data.humidity > 70) {
    digitalWrite(ledAir, HIGH); // Bật điều hòa
    client.publish(dieu_hoa_topic, "Đang bật");
  } else {
    digitalWrite(ledAir, LOW); // Tắt điều hòa
    client.publish(dieu_hoa_topic, "Đang tắt");
  }
  delay(1000);
  Serial.print("Nhiet do/Do am ");
  Serial.print(data.temperature);
  Serial.print(" / ");
  Serial.println(data.humidity);

  //Gửi dữ liệu sang web
  DynamicJsonDocument doc(128);
  doc["nhietDo"] = data.temperature;
  doc["doAm"] = data.humidity;

  String jsonString;
  serializeJson(doc, jsonString);

  client.publish(data_dht_topic, jsonString.c_str());
}

//--------------------------------------------------------
void process() {
  pirSensor();
  ldrSensor();
  gasSensor();
  ultrasonicSensor();
  rainSensor();
  dhtSensor();
}

void loop() {
  if (!client.connected()) {
    MQTT_Reconnect();
  }
  client.loop();
  process();
  delay(1000);
}
rain-sensorBreakout