From 70cc09c8c45df4bb744d8bdf4e714658d4cc7270 Mon Sep 17 00:00:00 2001 From: furqon Date: Fri, 4 Jul 2025 11:09:07 +0700 Subject: [PATCH] Upload kode program ESP32 --- ESP32/revisi.ino | 658 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 658 insertions(+) create mode 100644 ESP32/revisi.ino diff --git a/ESP32/revisi.ino b/ESP32/revisi.ino new file mode 100644 index 0000000..77949b2 --- /dev/null +++ b/ESP32/revisi.ino @@ -0,0 +1,658 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +// Pin Definitions +#define SOIL_MOISTURE_PIN 34 +#define LDR_PIN 35 +#define DHT_PIN 4 +#define DHT_TYPE DHT22 +#define RELAY_WATER_PUMP 26 +#define RELAY_FERTILIZER_PUMP 27 +#define RTC_SDA 21 +#define RTC_SCL 22 + +// Network & Firebase Configuration +#define API_KEY "AIzaSyCY_b7OHt64p1z_Ytna1YsFIIJ1jPRc2r4" +#define DATABASE_URL "tess-2d281-default-rtdb.firebaseio.com" +#define WIFI_SSID "Redmi" +#define WIFI_PASSWORD "qwerasdf" + +// System Settings +#define SENSING_INTERVAL 5000 +#define CHECK_PUMP_INTERVAL 1000 +#define SOIL_THRESHOLD 2.0 +#define LIGHT_THRESHOLD 5.0 +#define TEMP_THRESHOLD 0.5 +#define HUMID_THRESHOLD 1.0 +#define LCD_UPDATE_INTERVAL 1000 +#define WIFI_TIMEOUT 20000 // 20 seconds timeout +#define FIREBASE_RETRY_DELAY 5000 +#define DHT_READ_INTERVAL 2000 +#define DHT_RETRY_DELAY 500 + +// Global Objects +FirebaseData fbdo; +FirebaseAuth auth; +FirebaseConfig config; +DHT dht(DHT_PIN, DHT_TYPE); +RTC_DS3231 rtc; +LiquidCrystal_I2C lcd(0x27, 16, 2); + +// Global Variables +float soilMoisture, lightIntensity, temperature, humidity; +float lastSoilMoisture, lastLightIntensity, lastTemperature, lastHumidity; +int soilRawValue; +String currentTime; +bool signupOK = false; +bool initialReading = true; + +// Pump Control Variables +unsigned long lastSensorTime = 0; +unsigned long lastPumpCheckTime = 0; +unsigned long lastLCDUpdateTime = 0; +unsigned long lastTimeDisplayUpdate = 0; +bool waterPumpStatus = false; +bool fertilizerPumpStatus = false; +float minSoilMoisture = 60.0; +float maxSoilMoisture = 80.0; + +// Non-blocking WiFi +enum WiFiState { + WIFI_DISCONNECTED, + WIFI_CONNECTING, + WIFI_CONNECTED, + WIFI_FAILED +}; +WiFiState wifiState = WIFI_DISCONNECTED; +unsigned long wifiConnectStartTime = 0; +unsigned long lastWiFiRetryTime = 0; + +// Non-blocking Firebase +enum FirebaseState { + FIREBASE_NOT_INITIALIZED, + FIREBASE_INITIALIZING, + FIREBASE_READY, + FIREBASE_FAILED +}; +FirebaseState firebaseState = FIREBASE_NOT_INITIALIZED; +unsigned long lastFirebaseRetryTime = 0; + +// Non-blocking fertilizer pump +unsigned long fertilizerPumpStartTime = 0; +bool fertilizerPumpRunning = false; +static bool fertilizerPumpScheduled = false; + +// Non-blocking DHT +unsigned long lastDHTReadTime = 0; +unsigned long dhtReadAttemptTime = 0; +int dhtAttemptCount = 0; +bool dhtReading = false; + +// Non-blocking RTC +bool rtcSyncPending = false; +unsigned long lastRTCSyncAttempt = 0; +bool rtcSynced = false; // Track if RTC has been synchronized + +// Setup state tracking +enum SetupState { + SETUP_START, + SETUP_PINS, + SETUP_DHT, + SETUP_WIFI, + SETUP_RTC, + SETUP_LCD, + SETUP_FIREBASE, + SETUP_RELAY_TEST, + SETUP_COMPLETE +}; +SetupState setupState = SETUP_START; +unsigned long setupStateStartTime = 0; + +// LCD Display Functions +void setupLCD() { + lcd.init(); + lcd.backlight(); + lcd.setCursor(0, 0); + lcd.print("Smart Planting Pot"); + lcd.setCursor(0, 1); + lcd.print("By: Ahmad Furqon S"); +} + +void updateLCD() { + lcd.clear(); + lcd.setCursor(0, 0); + lcd.print("Smart Planting Pot"); + lcd.setCursor(0, 1); + lcd.print("By: Ahmad Furqon S"); +} + +// Control Pump Functions +void controlWaterPump(bool turnOn) { + if (turnOn) { + digitalWrite(RELAY_WATER_PUMP, LOW); + Serial.println("Water Pump: ON (pin=LOW)"); + } else { + digitalWrite(RELAY_WATER_PUMP, HIGH); + Serial.println("Water Pump: OFF (pin=HIGH)"); + } + waterPumpStatus = turnOn; +} + +void controlFertilizerPump(bool turnOn) { + if (turnOn) { + digitalWrite(RELAY_FERTILIZER_PUMP, LOW); + Serial.println("Fertilizer Pump: ON (pin=LOW)"); + } else { + digitalWrite(RELAY_FERTILIZER_PUMP, HIGH); + Serial.println("Fertilizer Pump: OFF (pin=HIGH)"); + } + fertilizerPumpStatus = turnOn; +} + +// Non-blocking WiFi Connection +void handleWiFiConnection() { + unsigned long currentMillis = millis(); + + switch (wifiState) { + case WIFI_DISCONNECTED: + if (currentMillis - lastWiFiRetryTime >= 5000) { // Retry every 5 seconds + Serial.println("Starting WiFi connection..."); + WiFi.begin(WIFI_SSID, WIFI_PASSWORD); + wifiConnectStartTime = currentMillis; + wifiState = WIFI_CONNECTING; + lastWiFiRetryTime = currentMillis; + } + break; + + case WIFI_CONNECTING: + if (WiFi.status() == WL_CONNECTED) { + wifiState = WIFI_CONNECTED; + Serial.println("WiFi Connected Successfully"); + Serial.print("IP Address: "); + Serial.println(WiFi.localIP()); + configTime(7 * 3600, 0, "pool.ntp.org"); + rtcSyncPending = true; + } else if (currentMillis - wifiConnectStartTime >= WIFI_TIMEOUT) { + wifiState = WIFI_FAILED; + Serial.println("WiFi Connection Timeout"); + } + break; + + case WIFI_CONNECTED: + if (WiFi.status() != WL_CONNECTED) { + wifiState = WIFI_DISCONNECTED; + Serial.println("WiFi Disconnected"); + firebaseState = FIREBASE_NOT_INITIALIZED; + signupOK = false; + rtcSynced = false; // Reset RTC sync status when WiFi disconnects + } + break; + + case WIFI_FAILED: + if (currentMillis - lastWiFiRetryTime >= 10000) { // Retry after 10 seconds + wifiState = WIFI_DISCONNECTED; + } + break; + } +} + +// Non-blocking Firebase Setup +void handleFirebaseConnection() { + unsigned long currentMillis = millis(); + + if (wifiState != WIFI_CONNECTED) return; + + switch (firebaseState) { + case FIREBASE_NOT_INITIALIZED: + if (currentMillis - lastFirebaseRetryTime >= FIREBASE_RETRY_DELAY) { + Serial.println("Initializing Firebase..."); + config.api_key = API_KEY; + config.database_url = DATABASE_URL; + firebaseState = FIREBASE_INITIALIZING; + lastFirebaseRetryTime = currentMillis; + } + break; + + case FIREBASE_INITIALIZING: + if (Firebase.signUp(&config, &auth, "", "")) { + firebaseState = FIREBASE_READY; + signupOK = true; + Serial.println("Firebase Authentication Successful"); + Firebase.begin(&config, &auth); + Firebase.reconnectWiFi(true); + fbdo.setResponseSize(4096); + + // Initialize pump states + Firebase.RTDB.setBool(&fbdo, "/pumps/waterPump", false); + Firebase.RTDB.setBool(&fbdo, "/pumps/fertilizerPump", false); + } else { + firebaseState = FIREBASE_FAILED; + Serial.println("Firebase Authentication Failed"); + } + break; + + case FIREBASE_FAILED: + if (currentMillis - lastFirebaseRetryTime >= FIREBASE_RETRY_DELAY) { + firebaseState = FIREBASE_NOT_INITIALIZED; + } + break; + + case FIREBASE_READY: + if (!Firebase.ready()) { + firebaseState = FIREBASE_NOT_INITIALIZED; + signupOK = false; + } + break; + } +} + +// Non-blocking RTC Sync +void handleRTCSync() { + unsigned long currentMillis = millis(); + + if (!rtcSyncPending || wifiState != WIFI_CONNECTED || rtcSynced) return; + + if (currentMillis - lastRTCSyncAttempt >= 2000) { // Try every 2 seconds + struct tm timeinfo; + if (getLocalTime(&timeinfo)) { + rtc.adjust(DateTime(timeinfo.tm_year + 1900, timeinfo.tm_mon + 1, + timeinfo.tm_mday, timeinfo.tm_hour, + timeinfo.tm_min, timeinfo.tm_sec)); + Serial.println("===== RTC SYNCHRONIZATION ====="); + Serial.println("RTC Successfully Synchronized with NTP Server"); + Serial.println("Time has been updated from internet"); + Serial.println("==============================="); + rtcSyncPending = false; + rtcSynced = true; + } else { + lastRTCSyncAttempt = currentMillis; + } + } +} + +// Non-blocking DHT Reading +void handleDHTReading() { + unsigned long currentMillis = millis(); + + if (!dhtReading && (currentMillis - lastDHTReadTime >= DHT_READ_INTERVAL)) { + dhtReading = true; + dhtAttemptCount = 0; + dhtReadAttemptTime = currentMillis; + lastDHTReadTime = currentMillis; + } + + if (dhtReading && (currentMillis - dhtReadAttemptTime >= DHT_RETRY_DELAY)) { + float h = dht.readHumidity(); + float t = dht.readTemperature(); + + if (!isnan(h) && !isnan(t)) { + humidity = h; + temperature = t; + dhtReading = false; + } else { + dhtAttemptCount++; + if (dhtAttemptCount >= 3) { + Serial.println("DHT reading failed after 3 attempts"); + dhtReading = false; + } else { + dhtReadAttemptTime = currentMillis; + } + } + } +} + +// Display Current Time from RTC (Real-time per second) +void displayCurrentTimeRealTime() { + DateTime now = rtc.now(); + + Serial.println("===== CURRENT TIME FROM RTC ====="); + Serial.printf("Date: %02d-%02d-%04d\n", now.day(), now.month(), now.year()); + Serial.printf("Time: %02d:%02d:%02d\n", now.hour(), now.minute(), now.second()); + Serial.println("================================="); +} + +// Sensor Reading Function +void readSensors() { + // Save last values + lastSoilMoisture = soilMoisture; + lastLightIntensity = lightIntensity; + lastTemperature = temperature; + lastHumidity = humidity; + + // Soil moisture + soilRawValue = analogRead(SOIL_MOISTURE_PIN); + soilMoisture = map(soilRawValue, 2700, 2250, 0, 100); + soilMoisture = constrain(soilMoisture, 0, 100); + + // Light intensity + int ldrValue = analogRead(LDR_PIN); + lightIntensity = map(ldrValue, 4095, 0, 0, 100); +} + +// Time Update Function +void updateCurrentTime() { + DateTime now = rtc.now(); + + char timeString[25]; + snprintf(timeString, sizeof(timeString), + "%04d-%02d-%02dT%02d:%02d:%02dZ", + now.year(), now.month(), now.day(), + now.hour(), now.minute(), now.second()); + + currentTime = String(timeString); +} + +// Firebase Upload Function +bool uploadToFirebase() { + if (firebaseState != FIREBASE_READY) return false; + + updateCurrentTime(); + + String path = "/sensors"; + bool success = true; + + success &= Firebase.RTDB.setFloat(&fbdo, path + "/humidity", humidity); + success &= Firebase.RTDB.setFloat(&fbdo, path + "/light", lightIntensity); + success &= Firebase.RTDB.setFloat(&fbdo, path + "/soilMoisture", soilMoisture); + success &= Firebase.RTDB.setFloat(&fbdo, path + "/temperature", temperature); + success &= Firebase.RTDB.setString(&fbdo, path + "/time", currentTime); + + return success; +} + +// Pump Settings Reading Function +void readPumpSettings() { + if (firebaseState != FIREBASE_READY) return; + + if (Firebase.RTDB.getString(&fbdo, "/pump_settings/waterPumpRange")) { + String rangeStr = fbdo.stringData(); + int delimiterPos = rangeStr.indexOf('-'); + + if (delimiterPos != -1) { + minSoilMoisture = rangeStr.substring(0, delimiterPos).toFloat(); + maxSoilMoisture = rangeStr.substring(delimiterPos + 1).toFloat(); + } + } +} + +// Automatic Watering Function +void checkAutomaticWatering() { + if (soilMoisture <= minSoilMoisture) { + if (!waterPumpStatus) { + controlWaterPump(true); + } + } + else if (soilMoisture >= maxSoilMoisture) { + if (waterPumpStatus) { + controlWaterPump(false); + } + } +} + +// Fertilizer Schedule Check Function +void checkFertilizerSchedule() { + if (firebaseState != FIREBASE_READY) return; + + String scheduleDate = ""; + String scheduleTime = ""; + + if (Firebase.RTDB.getString(&fbdo, "/pump_settings/fertilizerPumpDate")) { + scheduleDate = fbdo.stringData(); + scheduleDate.trim(); + } else { + return; + } + + if (Firebase.RTDB.getString(&fbdo, "/pump_settings/fertilizerPumpTime")) { + scheduleTime = fbdo.stringData(); + scheduleTime.trim(); + } else { + return; + } + + DateTime now = rtc.now(); + + char currentDateStr[11]; + snprintf(currentDateStr, sizeof(currentDateStr), + "%02d-%02d-%04d", + now.day(), now.month(), now.year()); + + char currentTimeStr[6]; + snprintf(currentTimeStr, sizeof(currentTimeStr), + "%02d:%02d", + now.hour(), now.minute()); + + if (scheduleDate == currentDateStr && + scheduleTime == currentTimeStr && + !fertilizerPumpScheduled && + !fertilizerPumpRunning) { + + controlFertilizerPump(true); + fertilizerPumpStartTime = millis(); + fertilizerPumpRunning = true; + fertilizerPumpScheduled = true; + + Firebase.RTDB.setTimestamp(&fbdo, "/lastFertilizerPumpTime"); + } + + if (now.minute() != atoi(scheduleTime.substring(3, 5).c_str())) { + fertilizerPumpScheduled = false; + } +} + +// Handle Fertilizer Pump Timer +void handleFertilizerPumpTimer() { + if (fertilizerPumpRunning && (millis() - fertilizerPumpStartTime >= 6000)) { + controlFertilizerPump(false); + fertilizerPumpRunning = false; + } +} + +// Pump Status Check Function +void checkPumpStatus() { + if (firebaseState != FIREBASE_READY) { + if (waterPumpStatus || fertilizerPumpStatus) { + controlWaterPump(false); + controlFertilizerPump(false); + } + return; + } + + bool success = Firebase.RTDB.getBool(&fbdo, "/pumps/waterPump"); + if (success) { + bool newWaterPumpStatus = fbdo.boolData(); + if (newWaterPumpStatus != waterPumpStatus) { + controlWaterPump(newWaterPumpStatus); + } + } + + success = Firebase.RTDB.getBool(&fbdo, "/pumps/fertilizerPump"); + if (success) { + bool newFertilizerPumpStatus = fbdo.boolData(); + if (newFertilizerPumpStatus != fertilizerPumpStatus && !fertilizerPumpRunning) { + controlFertilizerPump(newFertilizerPumpStatus); + } + } + + Firebase.RTDB.setBool(&fbdo, "/pumps/waterPump", waterPumpStatus); + Firebase.RTDB.setBool(&fbdo, "/pumps/fertilizerPump", fertilizerPumpStatus); +} + +// Change Detection Function +bool hasSignificantChange() { + if (initialReading) { + initialReading = false; + return true; + } + + return (abs(soilMoisture - lastSoilMoisture) >= SOIL_THRESHOLD || + abs(lightIntensity - lastLightIntensity) >= LIGHT_THRESHOLD || + abs(temperature - lastTemperature) >= TEMP_THRESHOLD || + abs(humidity - lastHumidity) >= HUMID_THRESHOLD); +} + +// Non-blocking Setup State Machine +void handleSetupStateMachine() { + unsigned long currentMillis = millis(); + + switch (setupState) { + case SETUP_START: + Serial.begin(115200); + Serial.println("\nESP32 Garden Monitoring System Initializing..."); + setupState = SETUP_PINS; + setupStateStartTime = currentMillis; + break; + + case SETUP_PINS: + if (currentMillis - setupStateStartTime >= 100) { + pinMode(SOIL_MOISTURE_PIN, INPUT); + pinMode(LDR_PIN, INPUT); + pinMode(DHT_PIN, INPUT); + pinMode(RELAY_WATER_PUMP, OUTPUT); + pinMode(RELAY_FERTILIZER_PUMP, OUTPUT); + digitalWrite(RELAY_WATER_PUMP, HIGH); + digitalWrite(RELAY_FERTILIZER_PUMP, HIGH); + setupState = SETUP_DHT; + setupStateStartTime = currentMillis; + } + break; + + case SETUP_DHT: + if (currentMillis - setupStateStartTime >= 100) { + dht.begin(); + setupState = SETUP_WIFI; + setupStateStartTime = currentMillis; + } + break; + + case SETUP_WIFI: + if (currentMillis - setupStateStartTime >= 500) { + // WiFi will be handled by handleWiFiConnection() + setupState = SETUP_RTC; + setupStateStartTime = currentMillis; + } + break; + + case SETUP_RTC: + if (currentMillis - setupStateStartTime >= 100) { + Wire.begin(RTC_SDA, RTC_SCL); + if (rtc.begin()) { + if (rtc.lostPower()) { + Serial.println("RTC Lost Power, will sync when WiFi connects"); + rtc.adjust(DateTime(F(__DATE__), F(__TIME__))); + } + setupState = SETUP_LCD; + } else { + Serial.println("RTC Not Detected!"); + // Continue anyway, system can work without RTC + setupState = SETUP_LCD; + } + setupStateStartTime = currentMillis; + } + break; + + case SETUP_LCD: + if (currentMillis - setupStateStartTime >= 100) { + setupLCD(); + setupState = SETUP_FIREBASE; + setupStateStartTime = currentMillis; + } + break; + + case SETUP_FIREBASE: + if (currentMillis - setupStateStartTime >= 500) { + // Firebase will be handled by handleFirebaseConnection() + setupState = SETUP_RELAY_TEST; + setupStateStartTime = currentMillis; + } + break; + + case SETUP_RELAY_TEST: + if (currentMillis - setupStateStartTime >= 1000) { + Serial.println("Performing Pump Relay Test..."); + controlWaterPump(false); + controlFertilizerPump(false); + setupState = SETUP_COMPLETE; + setupStateStartTime = currentMillis; + } + break; + + case SETUP_COMPLETE: + if (currentMillis - setupStateStartTime >= 1000) { + Serial.println("System Ready - Manual Pump Control via Firebase"); + // Setup is complete, normal operation begins + } + break; + } +} + +// Setup Function +void setup() { + // Setup will be handled by the state machine + setupState = SETUP_START; +} + +// Main Loop Function +void loop() { + unsigned long currentMillis = millis(); + + // Handle setup state machine + if (setupState != SETUP_COMPLETE) { + handleSetupStateMachine(); + } + + // Handle all non-blocking operations + handleWiFiConnection(); + handleFirebaseConnection(); + handleRTCSync(); + handleDHTReading(); + handleFertilizerPumpTimer(); + + // Only run main operations after setup is complete + if (setupState == SETUP_COMPLETE) { + // Real-time Clock Display (every second) + if (currentMillis - lastTimeDisplayUpdate >= 1000) { + lastTimeDisplayUpdate = currentMillis; + displayCurrentTimeRealTime(); + } + + // Sensor Reading and Firebase Upload + if (currentMillis - lastSensorTime >= SENSING_INTERVAL) { + lastSensorTime = currentMillis; + + readSensors(); + updateCurrentTime(); + + if (hasSignificantChange() && firebaseState == FIREBASE_READY) { + uploadToFirebase(); + } + + readPumpSettings(); + checkAutomaticWatering(); + } + + // Pump Status Check + if (currentMillis - lastPumpCheckTime >= CHECK_PUMP_INTERVAL) { + lastPumpCheckTime = currentMillis; + + if (firebaseState == FIREBASE_READY) { + checkPumpStatus(); + checkFertilizerSchedule(); + } + } + + // Update LCD Display + if (currentMillis - lastLCDUpdateTime >= LCD_UPDATE_INTERVAL) { + lastLCDUpdateTime = currentMillis; + updateLCD(); + } + } +} \ No newline at end of file