#define BLYNK_TEMPLATE_ID "TMPL6YU7qPUiH"
#define BLYNK_TEMPLATE_NAME "nongtraiCopyCopy"
#define BLYNK_AUTH_TOKEN "4A8ryPnQdxTvHZgz4H-FtZm419Zpjh2d"
#define DOAMDAT A0

#include <DHT.h>
#include <Wire.h>
#include <WiFi.h>
#include <WiFiUdp.h>
#include <NTPClient.h>
#include <HTTPClient.h>
#include <WiFiClient.h>
#include <Adafruit_GFX.h>
#include <BlynkSimpleEsp32.h>
#include <Adafruit_SSD1306.h>

char auth[] = "4A8ryPnQdxTvHZgz4H-FtZm419Zpjh2d";
char ssid[] = "Wokwi-GUEST";
char pass[] = "";

String Web_App_URL = "https://script.google.com/macros/s/AKfycby8Uv2cdQhvYDqSvpAYLSCRQN1vFz_th9fXyT7hqHSJ5Dzj-G6wEsBcOwgdR9R7Af5SjQ/execs";

#define On_Board_LED_PIN  2
#define DHTPIN 4          
#define DHTTYPE DHT11     // DHT 11
#define DOAMDAT 36        // Cảm biến độ ẩm đất LM393

DHT dht(DHTPIN, DHTTYPE);

#define SCREEN_WIDTH 128 // OLED display width
#define SCREEN_HEIGHT 64 // OLED display height
#define OLED_RESET -1 // Reset pin # (or -1 if sharing Arduino reset pin)

Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RESET);

int button_chedo = 26; 
int button_bomtuoi = 27; 
int button_den = 14;
int button_ra = 12;
int button_vao = 13;

int relay_bomtuoi = 19;
int relay_den = 18; 
int relay_ra = 5;  
int relay_vao = 15;

#define NHIETDO         V0
#define DOAM            V1
#define CHEDO           V2
#define RELAY           V3
#define DOAM_BAT        V4
#define DOAM_TAT        V5
#define RELAY_RA        V6
#define RELAY_VAO       V7
#define DEN             V9

WiFiUDP ntpUDP;
NTPClient timeClient(ntpUDP, "pool.ntp.org", 7 * 60 * 60);  // UTC+7, giờ Việt Nam

int autoOnHour = 18;
int autoOnMinute = 0;
int autoOffHour = 10;
int autoOffMinute = 30;

float tempHigh = 36.0; // Giá trị nhiệt độ cao để bật relay_ra
float tempLow = 33.0; // Giá trị nhiệt độ thấp để bật relay_vao

int doam_bat = 30; // Giá trị độ ẩm đất để bắt đầu tưới
int doam_tat = 80; // Giá trị độ ẩm đất để dừng tưới

boolean chedo_hoatdong = false;
int autoModeState = 0;
int relay_bomtuoi_State = LOW;
int relay_ra_State = LOW; // Trạng thái ban đầu của relay_ra
int relay_vao_State = LOW; // Trạng thái ban đầu của relay_vao
int relay_den_State = LOW;
String Status_Read_Sensor = "";
String Maiche_State = "";
String Den_State = "";
String Bom_State = "";
float Temp;
int Humd;

BLYNK_WRITE(V9) {
  if (chedo_hoatdong) {
    relay_den_State = param.asInt();
    digitalWrite(relay_den, relay_den_State);
    Serial.println("Chế độ Thủ công: Đang " + String(relay_den_State ? "BẬT" : "TẮT") + " đèn");
  }
}

BLYNK_WRITE(V7) {
  if (chedo_hoatdong) {
    relay_vao_State = param.asInt();
    if (relay_vao_State == HIGH) { // Khi relay_vao bật thì relay_ra sẽ tắt
      relay_ra_State = LOW;
      digitalWrite(relay_ra, relay_ra_State);
      Blynk.virtualWrite(V6, relay_ra_State); // Đồng bộ trạng thái của relay_ra với V6 trên Blynk
    }
    digitalWrite(relay_vao, relay_vao_State);
    Serial.println("Chế độ Thủ công: Đang " + String(relay_vao_State ? "BẬT" : "TẮT") + " relay_vao");
  }
}

BLYNK_WRITE(V6) {
  if (chedo_hoatdong) {
    relay_ra_State = param.asInt();
    if (relay_ra_State == HIGH) { // Khi relay_ra bật thì relay_vao sẽ tắt
      relay_vao_State = LOW;
      digitalWrite(relay_vao, relay_vao_State);
      Blynk.virtualWrite(V7, relay_vao_State); // Đồng bộ trạng thái của relay_vao với V7 trên Blynk
    }
    digitalWrite(relay_ra, relay_ra_State);
    Serial.println("Chế độ Thủ công: Đang " + String(relay_ra_State ? "BẬT" : "TẮT") + " relay_ra");
  }
}

BLYNK_WRITE(V3) {
  if (chedo_hoatdong) {
    relay_bomtuoi_State = param.asInt();
    digitalWrite(relay_bomtuoi, relay_bomtuoi_State);
    Serial.println("Chế độ Thủ công: Đang " + String(relay_bomtuoi_State ? "BẬT" : "TẮT") + " bom tưới");
  } else {
    Blynk.virtualWrite(V3, relay_bomtuoi_State); // Cập nhật trạng thái của relay_bomtuoi trên Blynk
  }
}

BLYNK_WRITE(V2) {
  autoModeState = param.asInt();
  Blynk.virtualWrite(CHEDO, autoModeState);  // Cập nhật trạng thái của nút chế độ trên Blynk
  Serial.println("Nút V2 được nhấn! Chế độ Tự động: " + String(autoModeState));
  if (autoModeState == 0) {
    chedo_hoatdong = false;
    Serial.println("Chế độ hoạt động: Tự động");
  } else {
    chedo_hoatdong = true;
    Serial.println("Chế độ hoạt động: Thủ công");
    Serial.println("Trạng thái đèn: " + String(relay_den_State ? "BẬT" : "TẮT")); // Hiển thị trạng thái đèn
  }
}

BLYNK_WRITE(DOAM_BAT){
  doam_bat = param.asInt();
  Serial.println("Độ ẩm MIN: " + String(doam_bat) + "%");
}

BLYNK_WRITE(DOAM_TAT){
  doam_tat = param.asInt();
  Serial.println("Độ ẩm MAX: " + String(doam_tat) + "%");
}

BLYNK_CONNECTED() {
  Blynk.virtualWrite(V2, autoModeState);
  Blynk.virtualWrite(V3, relay_bomtuoi_State); 
  Blynk.virtualWrite(V6, relay_ra_State);
  Blynk.virtualWrite(V7, relay_vao_State);
  Blynk.virtualWrite(V9, relay_den_State);
}

void setup() {
  Serial.begin(115200);
  Serial.println();
  delay(1000);
  Blynk.begin(auth, ssid, pass);
  //----------------------------------------Set Wifi to STA mode
  Serial.println();
  Serial.println("-------------");
  Serial.println("WIFI mode : STA");
  WiFi.mode(WIFI_STA);
  Serial.println("-------------");
  Serial.println();
  Serial.println("------------");
  Serial.print("Connecting to ");
  Serial.println(ssid);
  WiFi.begin(ssid, pass);

  int connecting_process_timed_out = 20; //--> 20 = 20 seconds.
  connecting_process_timed_out = connecting_process_timed_out * 2;
  while (WiFi.status() != WL_CONNECTED) {
    Serial.print(".");
    digitalWrite(On_Board_LED_PIN, HIGH);
    delay(250);
    digitalWrite(On_Board_LED_PIN, LOW);
    delay(250);
    if (connecting_process_timed_out > 0) connecting_process_timed_out--;
    if (connecting_process_timed_out == 0) {
      delay(1000);
      ESP.restart();
    }
  }

  digitalWrite(On_Board_LED_PIN, LOW);
  
  Serial.println();
  Serial.println("WiFi connected");
  Serial.println("------------");
  delay(100);
  
  pinMode(DOAMDAT, INPUT); // Khai báo DOAMDAT là INPUT
  pinMode(relay_bomtuoi, OUTPUT); // Khai báo relay_bomtuoi là OUTPUT
  pinMode(relay_ra, OUTPUT);  // Khai báo relay_ra là OUTPUT
  pinMode(relay_vao, OUTPUT);  // Khai báo relay_vao là OUTPUT
  pinMode(relay_den, OUTPUT);
  pinMode(On_Board_LED_PIN, OUTPUT);

  pinMode(button_chedo, INPUT_PULLUP);
  pinMode(button_bomtuoi, INPUT_PULLUP); // Khai báo button_bomtuoi là nút nhấn
  pinMode(button_ra, INPUT_PULLUP);  // Khai báo button_ra là nút nhấn
  pinMode(button_vao, INPUT_PULLUP);  // Khai báo button_vao là nút nhấn
  pinMode(button_den, INPUT_PULLUP);  // Khai báo button_den là nút nhấn

  // Bắt đầu ở chế độ auto
  chedo_hoatdong = false;
  Blynk.virtualWrite(CHEDO, autoModeState);  // Cập nhật trạng thái của nút chế độ trên Blynk
  Blynk.virtualWrite(V2, autoModeState);     // Cập nhật trạng thái chế độ trên Blynk
  Blynk.virtualWrite(V3, relay_bomtuoi_State); // Cập nhật trạng thái của relay_bomtuoi trên Blynk
  timeClient.begin();

  // Khởi tạo màn hình OLED
  if(!display.begin(SSD1306_SWITCHCAPVCC, 0x3C)) { // Địa chỉ I2C 0x3C
    Serial.println(F("SH1106 allocation failed"));
    for(;;);
  }
  delay(1000);
  display.clearDisplay();
  display.setTextSize(1);
  display.setTextColor(WHITE);
}

void loop() {

   if (WiFi.status() != WL_CONNECTED) {
    Serial.println("Đang thử kết nối lại WiFi...");
    Blynk.connectWiFi(ssid, pass);
  }
  if (!Blynk.connected()) {
    Serial.println("Đang thử kết nối lại Blynk...");
    Blynk.connect();
  }
  Blynk.run();
  
  timeClient.update();

  float t = dht.readTemperature(); 
  Blynk.virtualWrite(V0, t); // Gửi giá trị nhiệt độ lên chân V0 trên Blynk
  Serial.println("Nhiệt độ hiện tại: " + String(t) + " °C"); // Hiển thị nhiệt độ lên Serial Monitor
  int doamdat = analogRead(DOAMDAT); // Đọc giá trị độ ẩm đất từ cảm biến LM393
  doamdat = constrain(doamdat, 0, 4095);
  int percent = map(doamdat, 0, 4095, 100, 0); // Chuyển đổi giá trị đọc được thành phần trăm
  Blynk.virtualWrite(V1, percent); // Gửi giá trị độ ẩm đất lên chân V1 trên Blynk
  Serial.println("Độ ẩm đất hiện tại: " + String(percent) + " %"); // Hiển thị độ ẩm đất lên Serial Monitor
  int hours = timeClient.getHours();
  int minutes = timeClient.getMinutes();
  int seconds = timeClient.getSeconds();
      Temp = digitalRead(t);
      Humd = digitalRead(percent);
//________________________________________________________________________________
if (isnan(percent) || isnan(t)) {
    Serial.println();
    Serial.println(F("Failed to read from DHT sensor!"));
    Serial.println();

    Status_Read_Sensor = "Failed";
    t = 0.00;
    percent = 0;
  } else {
    Status_Read_Sensor = "Success";
  }
 
  if (digitalRead(relay_bomtuoi_State) == LOW)  Bom_State = "Tắt";
  if (digitalRead(relay_bomtuoi_State) == HIGH) Bom_State = "Bật";
  if (digitalRead(relay_den_State) == LOW)  Den_State = "Tắt";
  if (digitalRead(relay_den_State) == HIGH) Den_State = "Bật";
  if (relay_ra_State == HIGH && relay_vao_State == LOW) {
    Maiche_State = "Ra";
  } else if (relay_ra_State == LOW && relay_vao_State == HIGH) {
    Maiche_State = "Vào"; 
  } else if (relay_ra_State == LOW && relay_vao_State == LOW) {
    Maiche_State = "Dừng";
  }
  
//________________________________________________________________________________


  if (WiFi.status() == WL_CONNECTED) {
    digitalWrite(On_Board_LED_PIN, HIGH);
  
    String Send_Data_URL = Web_App_URL + "?sts=write";
    Send_Data_URL += "&srs=" + Status_Read_Sensor;
    Send_Data_URL += "&temp=" + String(Temp); 
    Send_Data_URL += "&humd=" + String(Humd); 
    Send_Data_URL += "&bom=" + Bom_State; 
    Send_Data_URL += "&den=" + Den_State; 
    Send_Data_URL += "&maiche=" + Maiche_State;

    Serial.println();
    Serial.println("-------------");
    Serial.println("Send data to Google Spreadsheet...");
    Serial.print("URL : ");
    Serial.println(Send_Data_URL);
    delay(1000);

      HTTPClient http;
  
      http.begin(Send_Data_URL.c_str());
      http.setFollowRedirects(HTTPC_STRICT_FOLLOW_REDIRECTS);
  
      int httpCode = http.GET(); 
      Serial.print("HTTP Status Code : ");
      Serial.println(httpCode);
  
      String payload;
      if (httpCode > 0) {
        payload = http.getString();
        Serial.println("Payload : " + payload);    
      }
      
      http.end();
    
    digitalWrite(On_Board_LED_PIN, LOW);
    Serial.println("-------------");
  }
  //----------------------------------------

  delay(1000);

//________________________________________________________________________________
  display.clearDisplay();
  // Hiển thị thời gian thực
  display.setCursor(42, 0);
  char buffer[9];
  sprintf(buffer, "%02d:%02d:%02d", hours, minutes, seconds);
  display.println(buffer);
  // Hiển thị thông tin cá nhân
  display.setCursor(10, 9); // Đặt con trỏ tại vị trí ngay dưới thời gian
  display.println("PHAM NGOC TUAN K18");
  // Hiển thị trạng thái các relay
  display.setCursor(16, 21); // Đặt con trỏ tại vị trí dòng đầu tiên
  display.println("Che do: " + String(chedo_hoatdong ? "THU CONG" : "TU DONG"));
  display.setCursor(6, 32);
  relay_bomtuoi_State = digitalRead(relay_bomtuoi);
  display.println("Bom: " + String(relay_bomtuoi_State ? "ON" : "OFF"));
  display.setCursor(61, 32);
  relay_den_State = digitalRead(relay_den);
  display.println("- Den: " + String(relay_den_State ? "ON" : "OFF"));
  if (relay_ra_State == HIGH && relay_vao_State == LOW) {
    display.setCursor(17, 42);
    display.println(" Mai che [ RA ]");
  } else if (relay_ra_State == LOW && relay_vao_State == HIGH) {
    display.setCursor(17, 42);
    display.println(" Mai che [ VAO ]");
  } else if (relay_ra_State == LOW && relay_vao_State == LOW) {
    display.setCursor(15, 42);
    display.println("Mai che [ DUNG ]");
  }
  display.setCursor(0,54);
  display.println("T: " + String(t) + " *C");
  display.setCursor(68,54);
  display.println("- H: " + String(percent) + " %");
  display.display();

  if (digitalRead(button_chedo) == LOW) {
    changeMode();
  }
  if (chedo_hoatdong && digitalRead(button_den) == LOW) {
    relay_den_State = !relay_den_State;  // Đảo trạng thái của relay_den
    digitalWrite(relay_den, relay_den_State);  // Cập nhật trạng thái của relay_den
    Blynk.virtualWrite(V9, relay_den_State);  // Cập nhật trạng thái của V9 trên Blynk
 }
  if (chedo_hoatdong && digitalRead(button_bomtuoi) == LOW) {
  relay_bomtuoi_State = !relay_bomtuoi_State; // Đảo trạng thái của relay_bomtuoi
  digitalWrite(relay_bomtuoi, relay_bomtuoi_State); // Cập nhật trạng thái của relay_bomtuoi
  Blynk.virtualWrite(V3, relay_bomtuoi_State); // Cập nhật trạng thái của relay_bomtuoi trên Blynk
  }
  if (!chedo_hoatdong) {
    int currentHour = timeClient.getHours();
    int currentMinute = timeClient.getMinutes();
    /*if ((currentHour > autoOnHour || (currentHour == autoOnHour && currentMinute >= autoOnMinute)) &&
        (currentHour < autoOffHour || (currentHour == autoOffHour && currentMinute < autoOffMinute))) { // cùng giờ khác phút*/
      if ((currentHour >= autoOnHour && currentMinute >= autoOnMinute) || 
    (currentHour < autoOffHour || (currentHour == autoOffHour && currentMinute < autoOffMinute))) {// khác giờ khác phút
      digitalWrite(relay_den, HIGH);
      Blynk.virtualWrite(V9, 1);
   } else {
      digitalWrite(relay_den, LOW);
      Blynk.virtualWrite(V9, 0);
   }
  }
  if (chedo_hoatdong) {
    if (digitalRead(button_ra) == LOW) {
      relay_ra_State = !relay_ra_State;
      relay_vao_State = LOW;
      digitalWrite(relay_ra, relay_ra_State);
      digitalWrite(relay_vao, relay_vao_State);
      Blynk.virtualWrite(V6, relay_ra_State);
      Blynk.virtualWrite(V7, relay_vao_State);
   }
    if (digitalRead(button_vao) == LOW) {
      relay_vao_State = !relay_vao_State;
      relay_ra_State = LOW;
      digitalWrite(relay_vao, relay_vao_State);
      digitalWrite(relay_ra, relay_ra_State);
      Blynk.virtualWrite(V7, relay_vao_State);
      Blynk.virtualWrite(V6, relay_ra_State);
  }
  }
 // Kiểm tra nhiệt độ và điều khiển relay
  if (!chedo_hoatdong) { // Chỉ kiểm tra nhiệt độ khi ở chế độ tự động
    if (t > tempHigh) {
      relay_ra_State = HIGH; // Bật relay_ra
      relay_vao_State = LOW; // Tắt relay_vao
      digitalWrite(relay_ra, relay_ra_State); // Cập nhật trạng thái của relay_ra
      digitalWrite(relay_vao, relay_vao_State); // Cập nhật trạng thái của relay_vao
      Blynk.virtualWrite(V6, relay_ra_State); // Cập nhật trạng thái của relay_ra trên Blynk
      Blynk.virtualWrite(V7, relay_vao_State); // Cập nhật trạng thái của relay_vao trên Blynk
   } else if (t <= tempLow) {
      relay_ra_State = LOW; // Tắt relay_ra
      relay_vao_State = HIGH; // Bật relay_vao
      digitalWrite(relay_ra, relay_ra_State); // Cập nhật trạng thái của relay_ra
      digitalWrite(relay_vao, relay_vao_State); // Cập nhật trạng thái của relay_vao
      Blynk.virtualWrite(V6, relay_ra_State); // Cập nhật trạng thái của relay_ra trên Blynk
      Blynk.virtualWrite(V7, relay_vao_State); // Cập nhật trạng thái của relay_vao trên Blynk
      }
  }
   if (!chedo_hoatdong) { // Chỉ kiểm tra độ ẩm đất khi ở chế độ tự động
    if (percent <= doam_bat && ((hours >= 17 && minutes >= 0 && seconds >= 0) || (hours <= 8 && minutes <= 0 && seconds <= 0))) {
      relay_bomtuoi_State = HIGH; // Bật relay_bomtuoi
      digitalWrite(relay_bomtuoi, relay_bomtuoi_State); // Cập nhật trạng thái của relay_bomtuoi
      Blynk.virtualWrite(V3, relay_bomtuoi_State); // Cập nhật trạng thái của relay_bomtuoi trên Blynk

    } else if (percent >= doam_tat) {
      relay_bomtuoi_State = LOW; // Tắt relay_bomtuoi
      digitalWrite(relay_bomtuoi, relay_bomtuoi_State); // Cập nhật trạng thái của relay_bomtuoi
      Blynk.virtualWrite(V3, relay_bomtuoi_State); // Cập nhật trạng thái của relay_bomtuoi trên Blynk
  
    }
  }
}
void connectToWiFi() {
  while (WiFi.status() != WL_CONNECTED) {
    WiFi.begin(ssid, pass);
    Serial.print("Attempting to connect to WiFi...");
    while (WiFi.status() != WL_CONNECTED) {
      delay(500);
      Serial.print(".");
    }
    Serial.println("");
    Serial.println("WiFi connected");
  }
}
void checkWiFiConnection() {
  if (WiFi.status() != WL_CONNECTED) {
    Serial.println("WiFi connection lost. Reconnecting...");
    connectToWiFi(); // Gọi hàm kết nối Wi-Fi của bạn ở đây

    // Nếu sau một thời gian mà vẫn không kết nối được, thực hiện reset ESP32
    unsigned long currentMillis = millis();
    static unsigned long previousMillis = 0;
    if (currentMillis - previousMillis >= 30000) { // Reset sau mỗi 30 giây (có thể điều chỉnh)
      ESP.restart();
    }
  }
}
//________________________________________________________________________________

//________________________________________________________________________________

void checkBlynkConnection() {
  if (!Blynk.connected()) {
    Serial.println("Blynk connection lost. Reconnecting...");
    Blynk.connect(); // Cố gắng kết nối lại Blynk
  }
}
void changeMode() {
  chedo_hoatdong = !chedo_hoatdong;
  Blynk.virtualWrite(CHEDO, chedo_hoatdong ? 1 : 0);
  Blynk.virtualWrite(V2, chedo_hoatdong ? 1 : 0);
  Serial.println("Nút chế độ được nhấn! Chế độ hoạt động: " + String(chedo_hoatdong ? "Thủ công" : "Tự động"));
}
NOCOMNCVCCGNDINLED1PWRRelay Module
NOCOMNCVCCGNDINLED1PWRRelay Module
NOCOMNCVCCGNDINLED1PWRRelay Module
NOCOMNCVCCGNDINLED1PWRRelay Module