#if defined(ESP32)
  #include <WiFi.h>
#elif defined(ESP8266)
  #include <ESP8266WiFi.h>
#endif
#include <Arduino.h>
#include <Wire.h> 
#include <LiquidCrystal_I2C.h>
LiquidCrystal_I2C lcd(0x27,16,2); 
#include <DHT.h>
Servo myServo;
#include <ThingsBoard.h>
#include <PubSubClient.h>

/*// Khai báo các thông số kết nối WiFi và MQTT
#define WIFI_SSID "Khang Hy"
#define WIFI_PASSWORD "0947781757"
#define MQTT_SERVER "thingsboard.cloud"
#define MQTT_PORT 1883
#define MQTT_TOKEN "0ToL8YvSIBuNR8zcfeRv"
WiFiClient wifiClient;
PubSubClient mqttClient(wifiClient);
*/
// GPIO
#define DHTPIN 32
#define DHTTYPE DHT11
#define sensor  27
#define led 5

SemaphoreHandle_t xBinarySemaphore;//khởi tạo 
QueueHandle_t xQueueTemp, xQueueHumidity, xQueueRain, xQueueStateBTN, xQueueCheckMode;//khởi tạo

//--------mutex đi kèm với semapho------//
portMUX_TYPE timerMux = portMUX_INITIALIZER_UNLOCKED;
SemaphoreHandle_t xMutex;

TaskHandle_t xHandle1;//khai báo có tác dụng điều chỉnh task còn lại trong task của chính mình
TaskHandle_t xHandle2;
TaskHandle_t xHandle3;

static int BTN_STATE;

void readSensor(void *params);// *params là 1 giá trị được truyền vào hàm
void taskChooseMode(void *params);//chọn chế độ hoạt động
void taskAutoMode(void *params);
void taskManualMode(void *params);
void rtos_delay(uint32_t delay_in_ms);

void setup(){
    Serial.begin(115200);

    lcd.init(); //Khởi động LCD                    
    lcd.backlight(); //Mở đèn
    connectWiFi();
    connectMQTT();
    pinMode(sensor, INPUT);
    pinMode(led, OUTPUT);
SemaphoreHandle_t xBinarySemaphore;//khởi tạo 
QueueHandle_t xQueueTemp, xQueueHumidity, xQueueRain, xQueueStateBTN, xQueueCheckMode;//khởi tạo

//--------mutex đi kèm với semapho------//
portMUX_TYPE timerMux = portMUX_INITIALIZER_UNLOCKED;
SemaphoreHandle_t xMutex;

TaskHandle_t xHandle1;//khai báo có tác dụng điều chỉnh task còn lại trong task của chính mình
TaskHandle_t xHandle2;
TaskHandle_t xHandle3;

static int BTN_STATE;

void readSensor(void *params);// *params là 1 giá trị được truyền vào hàm
void taskChooseMode(void *params);//chọn chế độ hoạt động
void taskAutoMode(void *params);
void taskManualMode(void *params);
void taskRoofOn(void *params);
void taskRoofOff(void *params);
void rtos_delay(uint32_t delay_in_ms);

// SET UP
void setup(){
    Serial.begin(115200);

    lcd.init(); //Khởi động LCD                    
    lcd.backlight(); //Mở đèn
    connectWiFi();
    connectMQTT();
    pinMode(SERVO_PIN, OUTPUT);
    pinMode(RAINSENSOR_PIN, INPUT);
    pinMode(AUTO_BUTTON, INPUT_PULLUP);
    pinMode(MANU_BUTTON, INPUT_PULLUP);
    myServo.attach(SERVO_PIN);

    xQueueTemp = xQueueCreate(1, sizeof(float));// ví dụ chức năng: sau khi task sensor đọc dữ liệu xong thì task auto sẽ đọc dữ liệu để xét điều kiện bật hay tắt mái hiên
    xQueueRain = xQueueCreate(1, sizeof(int));
    xQueueHumidity = xQueueCreate(1, sizeof(float));
    xQueueStateBTN = xQueueCreate(1, sizeof(int));

    vSemaphoreCreateBinary(xBinarySemaphore);//khởi tạo
    xMutex = xSemaphoreCreateMutex();

    // ESP32 has 526KB SRAM
    xTaskCreatePinnedToCore(readSensor, "ReadSensor", 10000, NULL, 3, &xHandle3, ARDUINO_RUNNING_CORE);// khởi tạo readsensor
    xTaskCreatePinnedToCore(taskChooseMode, "ChooseModeUsing", 10000, NULL, 3, NULL, ARDUINO_RUNNING_CORE);// khởi tạo chosemode
}

void connectMQTT() {
  Serial.print("Connecting to MQTT server: ");
  Serial.println(MQTT_SERVER);
 
  mqttClient.setServer(MQTT_SERVER, MQTT_PORT);

  while (!mqttClient.connected()) {
    Serial.print("Attempting MQTT connection...");
    if (mqttClient.connect("ESP32Client", MQTT_TOKEN, NULL)) {
      Serial.println("connected");
    } else {
      Serial.print("failed, rc=");
      Serial.print(mqttClient.state());
      Serial.println(" try again in 5 seconds");
      delay(5000);
    }
  }
}
void connectWiFi() {
  Serial.print("Connecting to ");
  Serial.println(WIFI_SSID);

  WiFi.begin(WIFI_SSID, WIFI_PASSWORD);

  while (WiFi.status() != WL_CONNECTED) {
    delay(500);
    Serial.print(".");
  }

  Serial.println("");
  Serial.println("WiFi connected");
  Serial.println("IP address: ");
  Serial.println(WiFi.localIP());
}

void loop(){
    vTaskDelete(NULL);//dùng để xóa dữ liệu trong loop
}

//-------task readsensor-------///
void readSensor(void *params){
  float oldtemp = 0;// lưu trữ gí trị nhiệt độ cũ nếu xảy ra vấn đề thì sẽ truyền dữ liệu sang 2 biến h và t
  float oldhumidity =0;
  int r;
  float t;
float h;
  DHT dht(DHTPIN, DHTTYPE);
  dht.begin();
  xSemaphoreTake(xMutex, portMAX_DELAY); 

  for(;;){
    vTaskDelay(1000 / portTICK_PERIOD_MS);

    t = dht.readTemperature();
    h = dht.readHumidity();
     if (isnan(t)){
      Serial.println("Faild to read from DHT11 sensor!");
      t= oldtemp;
      r= oldrain;
      h= oldhumidity;
    } else{
      oldtemp = t;
      oldhumidity = h;
    }


  lcd.setCursor(6,1); //Con trở ở vị trí 5, hiện ô 6
  lcd.print(h);
  lcd.setCursor(0,0);
  lcd.print(t);

   // Tạo chuỗi JSON chứa dữ liệu nhiệt độ và độ ẩm
  String payload = "{";
  payload += "\"t\":"; payload += t; payload += ",";
  payload += "\"h\":"; payload += h; payload += ","; 
  payload += "\"r\":"; payload += r; payload +=  "}"; 
  Serial.print("Sending data to ThingsBoard: ");
  Serial.println(payload);
  mqttClient.publish("v1/devices/me/telemetry", payload.c_str());

    xSemaphoreGive(xMutex);
    xQueueSendToBack(xQueueTemp,&t,0);//gửi giá trị t vào queue

    //xQueueSendToBack(xQueueHumidity,&t,0);

    Serial.print("Nhiet do moi truong: ");
    Serial.println(t);
    Serial.print(F("do am: "));
    Serial.println(h);  

    lcd.display();
    //lcd.clear();  // Clear the LCD display
    rtos_delay(1000);
    taskYIELD();       
  }
}
/*void taskChooseMode(void * params) {
  static int temp ;//khởi tạo 
  for(;;) {
    xQueueReceive(xQueueStateBTN,&temp,portMAX_DELAY);
    Serial.print(temp);
    if (temp == 0){
      vTaskDelete(xHandle2);// xóa task manual
      xTaskCreatePinnedToCore(taskAutoMode,"AutoMode",12288,NULL,2,&xHandle1,ARDUINO_RUNNING_CORE);
      Serial.println(F("AUTO"));
    }else if(temp == 1){
      vTaskDelete(xHandle1);// xóa task auto
      xTaskCreatePinnedToCore(taskManualMode,"ManualMode",12288,NULL,2,&xHandle2,ARDUINO_RUNNING_CORE);
      Serial.println(F("MANUAL"));
    }
    rtos_delay(200);
    taskYIELD();
  }
}
void taskAutoMode(void * params) {
  vTaskResume(xHandle3);//dùng taskreadsensor để đọc cảm biến
  xSemaphoreTake(xMutex, portMAX_DELAY);
  float Buff;
  lcd.clear();            // Clear the LCD display
  for(;;){ //chạy vòng 
    xQueueReceive(xQueueRain,&Buff,portMAX_DELAY);//lấy giá trị lượng mưa cho vào biến buff
    Serial.println(F("Hello im auto"));// F có tác dụng lưu dữ liệu sẽ không bị mất khi tắt hoặc reset
    if (Buff == 1){
      xTaskCreatePinnedToCore(taskRoofOff,"TurnOFF",4096,NULL,4,NULL,0);
    }else{
      xTaskCreatePinnedToCore(taskRoofOn,"TurnON",4096,NULL,4,NULL,0);
    }
    xSemaphoreGive(xMutex);
    rtos_delay(4000);
    taskYIELD();
  }
}*/