#define BLYNK_TEMPLATE_ID "TMPL6G1uKAeN1" #define BLYNK_TEMPLATE_NAME "testingCopy" #define BLYNK_AUTH_TOKEN "0QsM_PxN7blv9Zn1Rnroft8uxdJ7zCHR" #define relay 4 #define soilMoisture 34 #define floot_water 25 #define buzzer 26 #include #include #include #include #include #include #include #include #include #include #include #include #include // Define constants #define BLYNK_TEMPLATE_ID_LEN 20 #define BLYNK_TEMPLATE_NAME_LEN 40 #define BLYNK_AUTH_TOKEN_LEN 40 #define MAX_LEN 40 LiquidCrystal_I2C lcd(0X27, 16, 2); WiFiClient client; MySQL_Connection conn((Client *)&client); bool relayState = false; bool relayTurnedOnToday = false; bool ManualButtonCondition = false; bool AutoButtonCondition = false; bool buzzerEnabled = true; bool buzzerState = false; int moistureLevelLo = 0; int moistureLevelHi = 0; float lastSoilMoisture = 0.0; char ssid[64]; char pass[64]; char kebun_id[MAX_LEN]; char blynk_template_id[BLYNK_TEMPLATE_ID_LEN]; char blynk_template_name[BLYNK_TEMPLATE_NAME_LEN]; char blynk_auth_token[BLYNK_AUTH_TOKEN_LEN]; char created_at[50]; const char* ntpServer = "pool.ntp.org"; const long gmtOffset_sec = 25200; const int daylightOffset_sec = 0; const char* otaPassword = "admin123"; // Kata sandi OTA const char* otaHostname = "ESP32"; // Hostname OTA float soilMoistureValue = analogRead(soilMoisture); float calibratedMoisture = map(soilMoistureValue, 4095, 533, 0, 100); unsigned long lastBuzzerStartTime = 0; const unsigned long buzzerDuration = 300000; const unsigned long buzzerOffDuration = 60000; unsigned long lastBuzzerOffTime = 0; unsigned long lastPumpStartTime = 0; const unsigned long pumpRunDuration = 1 * 60 * 1000; unsigned long lastHttpPostTime = 0; unsigned long httpPostInterval = 60 * 1000; unsigned long pumpManualOnTime = 0; void setup() { WiFi.mode(WIFI_STA); Serial.begin(115200); EEPROM.begin(512); pinMode(floot_water, INPUT_PULLUP); pinMode(buzzer, OUTPUT); digitalWrite(relay, LOW); pinMode(relay, OUTPUT); lcd.init(); lcd.backlight(); // Membaca data dari EEPROM String read_kebun_id = EEPROM.readString(0); String read_blynk_template_id = EEPROM.readString(20); String read_blynk_template_name = EEPROM.readString(60); String read_blynk_auth_token = EEPROM.readString(100); // Menampilkan data yang dibaca di Serial Monitor Serial.println("Reading from EEPROM:"); Serial.println("kebun_id: " + read_kebun_id); Serial.println("blynk_template_id: " + read_blynk_template_id); Serial.println("blynk_template_name: " + read_blynk_template_name); Serial.println("blynk_auth_token: " + read_blynk_auth_token); // Inisialisasi WiFiManager WiFiManager wifiManager; bool needConfiguration = false; // Memeriksa apakah data EEPROM valid if (read_kebun_id.length() > 0 && read_blynk_template_id.length() > 0 && read_blynk_template_name.length() > 0 && read_blynk_auth_token.length() > 0) { // Data valid, gunakan untuk konfigurasi Blynk strncpy(kebun_id, read_kebun_id.c_str(), sizeof(kebun_id)); strncpy(blynk_template_id, read_blynk_template_id.c_str(), sizeof(blynk_template_id)); strncpy(blynk_template_name, read_blynk_template_name.c_str(), sizeof(blynk_template_name)); strncpy(blynk_auth_token, read_blynk_auth_token.c_str(), sizeof(blynk_auth_token)); Serial.println("Using saved data from EEPROM:"); Serial.println("kebun_id: " + String(kebun_id)); Serial.println("blynk_template_id: " + String(blynk_template_id)); Serial.println("blynk_template_name: " + String(blynk_template_name)); Serial.println("blynk_auth_token: " + String(blynk_auth_token)); // Coba hubungkan ke WiFi dengan SSID yang disimpan lcd.clear(); lcd.setCursor(0, 0); lcd.print("Connecting to"); lcd.setCursor(0, 1); lcd.print("WiFi........."); delay(5000); WiFi.begin(ssid, pass); if (WiFi.waitForConnectResult() != WL_CONNECTED) { Serial.println("Failed to connect to WiFi with saved credentials. Opening configuration portal..."); needConfiguration = true; } else { Serial.println("Connected to WiFi with saved credentials"); // Periksa apakah SSID telah berubah if (String(WiFi.SSID()) != String(ssid)) { Serial.println("SSID has changed, opening configuration portal..."); needConfiguration = true; } } } else { // Data tidak valid atau kosong, buka portal konfigurasi Serial.println("No saved data in EEPROM or data invalid. Opening configuration portal..."); needConfiguration = true; } if (needConfiguration) { WiFiManagerParameter custom_kebun_id("kebun_id", "Kebun ID", kebun_id, MAX_LEN); WiFiManagerParameter custom_blynk_template_id("blynk_template_id", "Blynk Template ID", blynk_template_id, BLYNK_TEMPLATE_ID_LEN); WiFiManagerParameter custom_blynk_template_name("blynk_template_name", "Blynk Template Name", blynk_template_name, BLYNK_TEMPLATE_NAME_LEN); WiFiManagerParameter custom_blynk_auth_token("blynk_auth_token", "Blynk Auth Token", blynk_auth_token, BLYNK_AUTH_TOKEN_LEN); wifiManager.addParameter(&custom_kebun_id); wifiManager.addParameter(&custom_blynk_template_id); wifiManager.addParameter(&custom_blynk_template_name); wifiManager.addParameter(&custom_blynk_auth_token); if (!wifiManager.autoConnect("TerraTech", "password")) { Serial.println("Failed to connect to WiFi and hit timeout"); ESP.restart(); delay(1000); } // Simpan parameter kustom ke variabel strncpy(ssid, WiFi.SSID().c_str(), sizeof(ssid)); strncpy(pass, WiFi.psk().c_str(), sizeof(pass)); strncpy(kebun_id, custom_kebun_id.getValue(), sizeof(kebun_id)); strncpy(blynk_template_id, custom_blynk_template_id.getValue(), sizeof(blynk_template_id)); strncpy(blynk_template_name, custom_blynk_template_name.getValue(), sizeof(blynk_template_name)); strncpy(blynk_auth_token, custom_blynk_auth_token.getValue(), sizeof(blynk_auth_token)); // Simpan parameter kustom ke EEPROM EEPROM.writeString(0, kebun_id); EEPROM.writeString(20, blynk_template_id); EEPROM.writeString(60, blynk_template_name); EEPROM.writeString(100, blynk_auth_token); EEPROM.commit(); // Tampilkan data yang ditulis ke EEPROM di Serial Monitor Serial.println("Saved new data to EEPROM:"); Serial.println("kebun_id: " + String(kebun_id)); Serial.println("blynk_template_id: " + String(blynk_template_id)); Serial.println("blynk_template_name: " + String(blynk_template_name)); Serial.println("blynk_auth_token: " + String(blynk_auth_token)); } // Initialize Blynk with obtained credentials Blynk.begin(blynk_auth_token, ssid, pass); ArduinoOTA.setPassword(otaPassword); ArduinoOTA.setHostname(otaHostname); ArduinoOTA.begin(); // Menampilkan data yang digunakan ke Serial Monitor Serial.println("Final data used:"); Serial.println("kebun_id: " + String(kebun_id)); Serial.println("blynk_template_id: " + String(blynk_template_id)); Serial.println("blynk_template_name: " + String(blynk_template_name)); Serial.println("blynk_auth_token: " + String(blynk_auth_token)); buzzTwice(); uploadWireless(); lcd.clear(); lcd.setCursor(0, 0); lcd.print("SSID: "); lcd.print(WiFi.SSID()); lcd.setCursor(0, 1); lcd.print("IP: "); lcd.print(WiFi.localIP()); delay(5000); lcd.clear(); lcd.setCursor(0, 0); lcd.print(" Penyiram"); lcd.setCursor(0, 1); lcd.print(" Otomatis"); delay(5000); lcd.clear(); for (int i = 0; i < 5; i++) { sendSoilMoistureData(); delay(500); } configTime(gmtOffset_sec, daylightOffset_sec, ntpServer); // Ensure time is properly set up // Wait for time to be set struct tm timeinfo; if (!getLocalTime(&timeinfo)) { Serial.println("Failed to obtain time"); return; } else { Serial.println("Time obtained successfully"); Serial.print("Current time: "); Serial.printf("%02d-%02d-%04d %02d:%02d:%02d\n", timeinfo.tm_mday, timeinfo.tm_mon + 1, timeinfo.tm_year + 1900, timeinfo.tm_hour, timeinfo.tm_min, timeinfo.tm_sec); lcd.setCursor(0, 0); lcd.printf("%02d-%02d-%04d %02d:%02d:%02d", timeinfo.tm_mday, timeinfo.tm_mon + 1, timeinfo.tm_year + 1900, timeinfo.tm_hour, timeinfo.tm_min, timeinfo.tm_sec); } } void loop() { ArduinoOTA.handle(); Blynk.run(); getDateTime(); checkWiFiConnection(); unsigned long currentMillis = millis(); // Kirim data ke server web setiap 1 menit if (currentMillis - lastHttpPostTime >= httpPostInterval) { int kebun_id_int = atoi(kebun_id); sendHttpPost(calibratedMoisture, buzzerState, relayState, kebun_id_int, created_at); lastHttpPostTime = currentMillis; } // Kirim data ke Blynk setiap 2 detik static unsigned long lastBlynkUpdateTime = 0; const unsigned long blynkUpdateInterval = 2000; // Waktu interval dalam milidetik (2 detik) if (currentMillis - lastBlynkUpdateTime >= blynkUpdateInterval) { sendSoilMoistureData(); // Memanggil fungsi untuk mengirim data ke Blynk sendRelayStatusToBlynk(); sendBuzzerStatusToBlynk(); lastBlynkUpdateTime = currentMillis; } // Diperbarui nilai kelembaban tanah soilMoistureValue = analogRead(soilMoisture); calibratedMoisture = map(soilMoistureValue, 4095, 533, 0, 100); calibratedMoisture = constrain(calibratedMoisture, 0, 100); bool isDry = (calibratedMoisture < moistureLevelLo); bool isPumpOn = relayState; displayLCD(isDry, calibratedMoisture, isPumpOn); // Cek jadwal pompa dan kontrol pompa berdasarkan jadwal checkPumpSchedule(); handleFlootWater(); // Kontrol relay berdasarkan kelembaban tanah controlRelayBasedOnSoilMoisture(); delay(1000); // Delay singkat untuk menjaga agar loop tidak terlalu cepat } void checkWiFiConnection() { if (WiFi.status() != WL_CONNECTED) { Serial.println("WiFi connection lost."); lcd.clear(); lcd.setCursor(0, 0); lcd.print("WiFi connection lost."); lcd.setCursor(0, 1); lcd.print("Reconnecting...."); // Start WiFi Manager again for reconnection attempt WiFiManager wifiManager; // Custom parameters to configure in WiFi Manager WiFiManagerParameter custom_kebun_id("kebun_id", "Kebun ID", kebun_id, MAX_LEN); WiFiManagerParameter custom_blynk_template_id("blynk_template_id", "Blynk Template ID", blynk_template_id, BLYNK_TEMPLATE_ID_LEN); WiFiManagerParameter custom_blynk_template_name("blynk_template_name", "Blynk Template Name", blynk_template_name, BLYNK_TEMPLATE_NAME_LEN); WiFiManagerParameter custom_blynk_auth_token("blynk_auth_token", "Blynk Auth Token", blynk_auth_token, BLYNK_AUTH_TOKEN_LEN); wifiManager.addParameter(&custom_kebun_id); wifiManager.addParameter(&custom_blynk_template_id); wifiManager.addParameter(&custom_blynk_template_name); wifiManager.addParameter(&custom_blynk_auth_token); if (!wifiManager.autoConnect("TerraTech", "password")) { Serial.println("Failed to connect and hit timeout"); // Handle failure (e.g., restart) ESP.restart(); } // Check if connected after trying to connect if (WiFi.status() == WL_CONNECTED) { Serial.println("\nWiFi reconnected"); lcd.clear(); lcd.setCursor(0, 0); lcd.print("Connected to"); lcd.setCursor(0, 1); lcd.print(WiFi.SSID()); // Update credentials strncpy(ssid, WiFi.SSID().c_str(), sizeof(ssid)); strncpy(pass, WiFi.psk().c_str(), sizeof(pass)); strncpy(kebun_id, custom_kebun_id.getValue(), sizeof(kebun_id)); strncpy(blynk_template_id, custom_blynk_template_id.getValue(), sizeof(blynk_template_id)); strncpy(blynk_template_name, custom_blynk_template_name.getValue(), sizeof(blynk_template_name)); strncpy(blynk_auth_token, custom_blynk_auth_token.getValue(), sizeof(blynk_auth_token)); EEPROM.writeString(0, kebun_id); EEPROM.writeString(20, blynk_template_id); EEPROM.writeString(60, blynk_template_name); EEPROM.writeString(100, blynk_auth_token); EEPROM.commit(); Serial.println("Saved new data to EEPROM:"); Serial.println("kebun_id: " + String(kebun_id)); Serial.println("blynk_template_id: " + String(blynk_template_id)); Serial.println("blynk_template_name: " + String(blynk_template_name)); Serial.println("blynk_auth_token: " + String(blynk_auth_token)); // Reinitialize Blynk with new credentials Blynk.begin(blynk_auth_token, ssid, pass); } else { Serial.println("\nFailed to reconnect WiFi. Restarting..."); ESP.restart(); } } } void handleFlootWater() { unsigned long currentMillis = millis(); if (buzzerEnabled) { // Only handle buzzer if it's enabled if (digitalRead(floot_water) == HIGH && (currentMillis - lastBuzzerOffTime >= buzzerOffDuration)) { // Floot water up if (!buzzerState) { digitalWrite(buzzer, HIGH); buzzerState = true; lastBuzzerStartTime = millis(); } } else { // Floot water down if (buzzerState) { digitalWrite(buzzer, LOW); buzzerState = false; lastBuzzerOffTime = millis(); Blynk.virtualWrite(V10, "Hidupkan Mode Otomatis atau Manual"); } } // Turn off the buzzer after the specified duration if floot water stays high if (buzzerState && (currentMillis - lastBuzzerStartTime >= buzzerDuration)) { digitalWrite(buzzer, LOW); buzzerState = false; lastBuzzerOffTime = millis(); // Record the time when the buzzer was turned off } } else { digitalWrite(buzzer, LOW); buzzerState = false; } } void sendHttpPost(float calibratedMoisture, bool buzzerState, bool relayState, int kebun_id, const char* created_at) { HTTPClient http; Serial.println("Starting HTTP POST..."); http.begin("https://kantorku.cloud/terratech/upload/upload.php"); http.addHeader("Content-Type", "application/json"); StaticJsonDocument<200> jsonDoc; jsonDoc["soil_moisture"] = calibratedMoisture; jsonDoc["buzzer_state"] = buzzerState; jsonDoc["relay_state"] = relayState; jsonDoc["kebun_id"] = kebun_id; jsonDoc["created_at"] = created_at; String jsonString; serializeJson(jsonDoc, jsonString); // Send POST request with JSON payload int httpResponseCode = http.POST(jsonString); if (httpResponseCode > 0) { Serial.print("HTTP Response code: "); Serial.println(httpResponseCode); String response = http.getString(); Serial.println(response); Serial.println("---------------------------------"); } else { Serial.print("Error code: "); Serial.println(httpResponseCode); Serial.println("---------------------------------"); } http.end(); } void uploadWireless() { ArduinoOTA.setHostname(otaHostname); // Hostname OTA ArduinoOTA.setPassword(otaPassword); // Kata sandi OTA ArduinoOTA.onStart([]() { String type; if (ArduinoOTA.getCommand() == U_FLASH) type = "sketch"; else // U_SPIFFS type = "filesystem"; Serial.println("Start updating " + type); }) .onEnd([]() { Serial.println("\nEnd"); }) .onProgress([](unsigned int progress, unsigned int total) { Serial.printf("Progress: %u%%\r", (progress / (total / 100))); }) .onError([](ota_error_t error) { Serial.printf("Error[%u]: ", error); if (error == OTA_AUTH_ERROR) Serial.println("Auth Failed"); else if (error == OTA_BEGIN_ERROR) Serial.println("Begin Failed"); else if (error == OTA_CONNECT_ERROR) Serial.println("Connect Failed"); if (error == OTA_RECEIVE_ERROR) Serial.println("Receive Failed"); else if (error == OTA_END_ERROR) Serial.println("End Failed"); }); ArduinoOTA.begin(); Serial.println("Ready"); Serial.print("IP address: "); Serial.println(WiFi.localIP()); } void displayLCD(bool isDry, float moistureValue, bool isPumpOn) { lcd.clear(); char moistureStr[6]; // Menyimpan string untuk kelembaban tanah // Format nilai kelembaban ke dalam format dua digit dengan angka nol di depan jika diperlukan sprintf(moistureStr, "%05.2f", moistureValue); if (isDry) { lcd.setCursor(0, 0); lcd.print("T Kering: "); lcd.print(moistureStr); lcd.print("%"); lcd.setCursor(0, 1); lcd.print("Pompa : "); lcd.print(isPumpOn ? "Hidup" : "Mati"); } else { lcd.setCursor(0, 0); lcd.print("T Basah: "); lcd.print(moistureStr); lcd.print("%"); lcd.setCursor(0, 1); lcd.print("Pompa : "); lcd.print(isPumpOn ? "Hidup" : "Mati"); } } void sendRelayStatusToBlynk() { // Mengirim status relay ke pin virtual 14 di Blynk Blynk.virtualWrite(V14, relayState ? "Hidup" : "Mati"); } void sendBuzzerStatusToBlynk() { // Mengirim status buzzer ke pin virtual 15 di Blynk Blynk.virtualWrite(V15, buzzerState ? "Habis" : "Penuh"); } void getDateTime() { struct tm timeinfo; if (!getLocalTime(&timeinfo)) { Serial.println("Failed to obtain time"); snprintf(created_at, sizeof(created_at), "0000-00-00 00:00:00"); } else { snprintf(created_at, sizeof(created_at), "%04d-%02d-%02d %02d:%02d:%02d", timeinfo.tm_year + 1900, timeinfo.tm_mon + 1, timeinfo.tm_mday, timeinfo.tm_hour, timeinfo.tm_min, timeinfo.tm_sec); } } void controlRelayBasedOnSoilMoisture() { static unsigned long lastTurnOnTime = 0; static unsigned long lastTurnOffTime = 0; unsigned long currentTime = millis(); if (digitalRead(floot_water) == HIGH) { // Check if floot water is HIGH // If flood water is low, turn off the relay digitalWrite(relay, LOW); // Matikan pompa relayState = false; Serial.println("Pompa dimatikan karena floot water rendah"); Blynk.virtualWrite(V10, "Air Habis Penyiraman Nonaktif"); handleFlootWater(); return; // Exit the function } if (AutoButtonCondition) { // Mode otomatis aktif if (calibratedMoisture < moistureLevelLo && !relayState) { digitalWrite(relay, HIGH); // Nyalakan pompa relayState = true; lastTurnOnTime = currentTime; Serial.println("Pompa dinyalakan otomatis karena kelembaban rendah"); } else if (calibratedMoisture >= moistureLevelHi && relayState) { digitalWrite(relay, LOW); // Matikan pompa relayState = false; lastTurnOffTime = currentTime; Serial.println("Pompa dimatikan otomatis karena kelembaban cukup tinggi"); } } else if (ManualButtonCondition) { // Mode manual aktif digitalWrite(relay, HIGH); // Nyalakan pompa relayState = true; pumpManualOnTime = currentTime; // Set the manual on time Serial.println("Pompa dinyalakan secara manual"); } else { // Matikan pompa jika tidak dalam mode otomatis atau manual digitalWrite(relay, LOW); // Matikan pompa relayState = false; Serial.println("Pompa dimatikan karena tidak dalam mode otomatis atau manual"); } } void sendSoilMoistureData() { if (calibratedMoisture != lastSoilMoisture) { lastSoilMoisture = calibratedMoisture; Blynk.virtualWrite(V11, calibratedMoisture); Serial.print("Soil Moisture: "); Serial.println(soilMoistureValue); Serial.print("kalibrasi: "); Serial.println(calibratedMoisture); Serial.println("Relay: " + (String)(relayState ? "Hidup" : "Mati")); Serial.println("Moisture Lo: " + (String)moistureLevelLo); Serial.println("Moisture Hi: " + (String)moistureLevelHi); Serial.println("buzzer: " + (String)(buzzerState ? "Hidup" : "Mati")); Serial.println("Level Air: " + (String)floot_water); Serial.println("Soil Moisture data sent to Blynk"); Serial.println("---------------------------------"); } } void buzzTwice() { for (int i = 0; i < 2; i++) { digitalWrite(buzzer, HIGH); delay(100); // Buzz duration digitalWrite(buzzer, LOW); delay(100); // Pause between buzzes } } void checkPumpSchedule() { unsigned long currentMillis = millis(); struct tm timeinfo; // Check if the current time is 9 AM if (getLocalTime(&timeinfo)) { if (timeinfo.tm_hour == 9 && timeinfo.tm_min == 0 && !relayTurnedOnToday) { digitalWrite(relay, HIGH); // Turn on the pump relayState = true; lastPumpStartTime = currentMillis; relayTurnedOnToday = true; Serial.println("Pompa dinyalakan pada pukul 9 pagi"); } } // Turn off the pump after the run duration if (relayTurnedOnToday && (currentMillis - lastPumpStartTime >= pumpRunDuration)) { digitalWrite(relay, LOW); // Turn off the pump relayState = false; relayTurnedOnToday = false; // Reset for the next day Serial.println("Pompa dimatikan setelah 1 menit"); } } BLYNK_WRITE(V8) { moistureLevelLo = param.asInt(); Serial.print("Set low moisture level: "); Serial.println(moistureLevelLo); } BLYNK_WRITE(V9) { moistureLevelHi = param.asInt(); Serial.print("Set high moisture level: "); Serial.println(moistureLevelHi); } BLYNK_WRITE(V6) { AutoButtonCondition = param.asInt(); if (AutoButtonCondition == 1) { Serial.println("Automatic mode activated"); Blynk.virtualWrite(V10, "Penyiraman Otomatis Hidup"); } else { Serial.println("Automatic mode deactivated"); Blynk.virtualWrite(V10, "Penyiraman Mati"); } } BLYNK_WRITE(V7) { relayState = param.asInt() == 1; if (relayState == 1) { digitalWrite(relay, HIGH); ManualButtonCondition = true; Blynk.virtualWrite(V8, created_at); Blynk.virtualWrite(V10, "Penyiraman Manual Hidup"); } else { digitalWrite(relay, LOW); ManualButtonCondition = false; Blynk.virtualWrite(V10, "Penyiraman Mati"); } } BLYNK_WRITE(V13) { // New Blynk virtual pin for buzzer control buzzerEnabled = param.asInt() == 1; if (buzzerEnabled) { Serial.println("Buzzer enabled");; } else { Serial.println("Buzzer disabled"); digitalWrite(buzzer, LOW); buzzerState = false; } }