#include #include #include #include #include #include #include #include // [EEPROM] // WiFi Credentials const char* ssid = "Test123"; const char* password = "a?=31@#daE313-=;l."; // Firebase credentials #define API_KEY "AIzaSyBCe-30GPJRp0p7psDH4t2D01WHPcSNIqQ" #define DATABASE_URL "https://apkrafi-default-rtdb.firebaseio.com/" // Firebase objects FirebaseData fbdo; FirebaseAuth auth; FirebaseConfig config; // Relay pins for pumps const int pumpRelayPins[4] = {D4, D5, D6, D7}; // I2C pins for DS3231 RTC #define SDA_PIN D2 #define SCL_PIN D1 // DHT sensor setup #define DHTPIN D3 #define DHTTYPE DHT11 DHT dht(DHTPIN, DHTTYPE); // Soil moisture sensor #define SOIL_MOISTURE_PIN A0 int soilMoisturePercent = 0; int rawMoisture = 0; bool pumpShouldRun[4] = {false, false, false, false}; unsigned long lastRTCSync = 0; const unsigned long rtcSyncInterval = 3600000; bool pump3Enabled = true; WiFiUDP ntpUDP; NTPClient timeClient(ntpUDP, "time.google.com", 7 * 3600, 60000); RTC_DS3231 rtc; bool wifiConnected = false; unsigned long lastReconnectAttempt = 0; const unsigned long reconnectInterval = 10000; // [MOD] Firebase-controlled schedule variables uint8_t fbPumpStartHour2 = 22, fbPumpStartMinute2 = 3, fbPumpEndHour2 = 22, fbPumpEndMinute2 = 5; uint8_t fbPumpStartHour3 = 2, fbPumpStartMinute3 = 15, fbPumpEndHour3 = 2, fbPumpEndMinute3 = 50; uint8_t fbPumpStartHour4 = 2, fbPumpStartMinute4 = 15, fbPumpEndHour4 = 2, fbPumpEndMinute4 = 50; bool fbPump3Enabled = true; unsigned long lastFirebaseSync = 0; const unsigned long firebaseSyncInterval = 2000; bool firebaseInitialized = false; // [EEPROM] EEPROM setup #define EEPROM_SIZE 64 void saveScheduleToEEPROM() { EEPROM.write(0, fbPumpStartHour2); EEPROM.write(1, fbPumpStartMinute2); EEPROM.write(2, fbPumpEndHour2); EEPROM.write(3, fbPumpEndMinute2); EEPROM.write(4, fbPumpStartHour3); EEPROM.write(5, fbPumpStartMinute3); EEPROM.write(6, fbPumpEndHour3); EEPROM.write(7, fbPumpEndMinute3); EEPROM.write(8, fbPumpStartHour4); EEPROM.write(9, fbPumpStartMinute4); EEPROM.write(10, fbPumpEndHour4); EEPROM.write(11, fbPumpEndMinute4); EEPROM.write(12, fbPump3Enabled); EEPROM.commit(); } void loadScheduleFromEEPROM() { fbPumpStartHour2 = EEPROM.read(0); fbPumpStartMinute2 = EEPROM.read(1); fbPumpEndHour2 = EEPROM.read(2); fbPumpEndMinute2 = EEPROM.read(3); fbPumpStartHour3 = EEPROM.read(4); fbPumpStartMinute3 = EEPROM.read(5); fbPumpEndHour3 = EEPROM.read(6); fbPumpEndMinute3 = EEPROM.read(7); fbPumpStartHour4 = EEPROM.read(8); fbPumpStartMinute4 = EEPROM.read(9); fbPumpEndHour4 = EEPROM.read(10); fbPumpEndMinute4 = EEPROM.read(11); fbPump3Enabled = EEPROM.read(12); } void connectToWiFi() { Serial.println("Connecting to WiFi..."); WiFi.begin(ssid, password); int attempt = 0; while (WiFi.status() != WL_CONNECTED && attempt < 20) { delay(500); Serial.print("."); attempt++; } wifiConnected = WiFi.status() == WL_CONNECTED; Serial.println(wifiConnected ? "\nWiFi connected." : "\nFailed to connect to WiFi."); } void syncRTCTime() { if (wifiConnected) { timeClient.update(); DateTime now = DateTime(timeClient.getEpochTime()); rtc.adjust(now); Serial.print("RTC synced with NTP time: "); Serial.println(now.timestamp()); } } void fetchScheduleFromFirebase() { if (!Firebase.ready()) return; if (Firebase.RTDB.getInt(&fbdo, "/schedule/pump2/startHour")) fbPumpStartHour2 = fbdo.to(); if (Firebase.RTDB.getInt(&fbdo, "/schedule/pump2/startMinute")) fbPumpStartMinute2 = fbdo.to(); if (Firebase.RTDB.getInt(&fbdo, "/schedule/pump2/endHour")) fbPumpEndHour2 = fbdo.to(); if (Firebase.RTDB.getInt(&fbdo, "/schedule/pump2/endMinute")) fbPumpEndMinute2 = fbdo.to(); if (Firebase.RTDB.getInt(&fbdo, "/schedule/pump3/startHour")) fbPumpStartHour3 = fbdo.to(); if (Firebase.RTDB.getInt(&fbdo, "/schedule/pump3/startMinute")) fbPumpStartMinute3 = fbdo.to(); if (Firebase.RTDB.getInt(&fbdo, "/schedule/pump3/endHour")) fbPumpEndHour3 = fbdo.to(); if (Firebase.RTDB.getInt(&fbdo, "/schedule/pump3/endMinute")) fbPumpEndMinute3 = fbdo.to(); if (Firebase.RTDB.getInt(&fbdo, "/schedule/pump4/startHour")) fbPumpStartHour4 = fbdo.to(); if (Firebase.RTDB.getInt(&fbdo, "/schedule/pump4/startMinute")) fbPumpStartMinute4 = fbdo.to(); if (Firebase.RTDB.getInt(&fbdo, "/schedule/pump4/endHour")) fbPumpEndHour4 = fbdo.to(); if (Firebase.RTDB.getInt(&fbdo, "/schedule/pump4/endMinute")) fbPumpEndMinute4 = fbdo.to(); if (Firebase.RTDB.getBool(&fbdo, "/schedule/pump3Enabled")) fbPump3Enabled = fbdo.to(); saveScheduleToEEPROM(); } void setup() { Serial.begin(115200); delay(1000); EEPROM.begin(EEPROM_SIZE); loadScheduleFromEEPROM(); for (int i = 0; i < 4; i++) { pinMode(pumpRelayPins[i], OUTPUT); digitalWrite(pumpRelayPins[i], HIGH); } Wire.begin(SDA_PIN, SCL_PIN); if (!rtc.begin()) Serial.println("Couldn't find RTC"); dht.begin(); connectToWiFi(); if (wifiConnected) { syncRTCTime(); lastRTCSync = millis(); } config.api_key = API_KEY; config.database_url = DATABASE_URL; if (wifiConnected) { Firebase.begin(&config, &auth); Firebase.reconnectWiFi(true); if (!Firebase.signUp(&config, &auth, "", "")) { Serial.printf("Firebase signUp failed: %s\n", config.signer.signupError.message.c_str()); } else { Serial.println("Anonymous sign-in successful."); } unsigned long timeout = millis(); while (!Firebase.ready() && millis() - timeout < 10000) { delay(100); } if (Firebase.ready()) { Serial.println("Firebase is ready."); } else { Serial.println("Failed to connect to Firebase."); } } else { Serial.println("Skipped Firebase initialization (offline mode)."); } } void loop() { static bool initialRTCSynced = false; if (!initialRTCSynced && wifiConnected) { syncRTCTime(); initialRTCSynced = true; } if (millis() - lastFirebaseSync > firebaseSyncInterval) { fetchScheduleFromFirebase(); lastFirebaseSync = millis(); } if (WiFi.status() != WL_CONNECTED) { if (millis() - lastReconnectAttempt > reconnectInterval) { Serial.println("WiFi lost. Reconnecting..."); WiFi.disconnect(); WiFi.begin(ssid, password); lastReconnectAttempt = millis(); } wifiConnected = false; } else { if (!wifiConnected) { Serial.println("WiFi reconnected."); wifiConnected = true; } } if (wifiConnected && !firebaseInitialized) { Serial.println("Initializing Firebase..."); Firebase.begin(&config, &auth); Firebase.reconnectWiFi(true); if (!Firebase.signUp(&config, &auth, "", "")) { Serial.printf("Firebase signUp failed: %s\n", config.signer.signupError.message.c_str()); } else { Serial.println("Anonymous sign-in successful."); } unsigned long timeout = millis(); while (!Firebase.ready() && millis() - timeout < 10000) { delay(100); } if (Firebase.ready()) { Serial.println("Firebase is ready."); firebaseInitialized = true; } else { Serial.println("Failed to connect to Firebase."); } } if (wifiConnected && millis() - lastRTCSync > rtcSyncInterval) { syncRTCTime(); lastRTCSync = millis(); } DateTime nowTime; int currentHour, currentMinute, currentSecond; int currentMinutes; if (wifiConnected) { timeClient.update(); currentHour = timeClient.getHours(); currentMinute = timeClient.getMinutes(); currentSecond = timeClient.getSeconds(); } else { nowTime = rtc.now(); currentHour = nowTime.hour(); currentMinute = nowTime.minute(); currentSecond = nowTime.second(); } currentMinutes = currentHour * 60 + currentMinute; Serial.printf("Time: %02d:%02d:%02d\n", currentHour, currentMinute, currentSecond); float temperature = dht.readTemperature(); float humidity = dht.readHumidity(); if (!isnan(temperature) && !isnan(humidity)) { Serial.printf("Temperature: %.1f °C, Humidity: %.1f %%\n", temperature, humidity); } else { Serial.println("Failed to read from DHT sensor!"); } rawMoisture = analogRead(SOIL_MOISTURE_PIN); const int WET_VALUE = 450; const int DRY_VALUE = 1024; soilMoisturePercent = map(rawMoisture, DRY_VALUE, WET_VALUE, 0, 100); soilMoisturePercent = constrain(soilMoisturePercent, 0, 100); Serial.printf("Soil Moisture: %d %% (Raw: %d)\n", soilMoisturePercent, rawMoisture); if (Firebase.ready()) { Firebase.RTDB.setFloat(&fbdo, "/sensors/temperature", temperature); Firebase.RTDB.setFloat(&fbdo, "/sensors/humidity", humidity); Firebase.RTDB.setInt(&fbdo, "/sensors/soilMoisture", soilMoisturePercent); } pumpShouldRun[0] = (!isnan(temperature) && temperature >= 31.0); static bool pump2Running = false; static bool pump2SkippedDueToMoisture = false; const int soilMoistureThreshold = 60; int start2 = fbPumpStartHour2 * 60 + fbPumpStartMinute2; int end2 = fbPumpEndHour2 * 60 + fbPumpEndMinute2; if (currentMinutes == start2) { if (soilMoisturePercent <= soilMoistureThreshold) { pump2Running = true; pump2SkippedDueToMoisture = false; Serial.println("Pump 2 scheduled to start: soil is dry enough."); } else { pump2Running = false; pump2SkippedDueToMoisture = true; Serial.println("Pump 2 skipped due to sufficient soil moisture."); } } // Keep running only while time range AND moisture is still low if (currentMinutes >= start2 && currentMinutes < end2) { if (pump2Running && soilMoisturePercent > soilMoistureThreshold) { pump2Running = false; // stop immediately Serial.println("Pump 2 stopped early due to sufficient soil moisture."); } pumpShouldRun[1] = pump2Running && !pump2SkippedDueToMoisture; } else { pumpShouldRun[1] = false; pump2Running = false; pump2SkippedDueToMoisture = false; } // ------------------------------------------ int start3 = fbPumpStartHour3 * 60 + fbPumpStartMinute3; int end3 = fbPumpEndHour3 * 60 + fbPumpEndMinute3; int start4 = fbPumpStartHour4 * 60 + fbPumpStartMinute4; int end4 = fbPumpEndHour4 * 60 + fbPumpEndMinute4; bool pump3Time = (currentMinutes >= start3 && currentMinutes < end3); bool pump4Time = (currentMinutes >= start4 && currentMinutes < end4); pumpShouldRun[2] = pump3Time && fbPump3Enabled; pumpShouldRun[3] = pump4Time && !fbPump3Enabled; Serial.printf("Scheduled pump: %s\n", fbPump3Enabled ? "Pump 3" : "Pump 4"); for (int i = 0; i < 4; i++) { bool isOn = digitalRead(pumpRelayPins[i]) == LOW; if (pumpShouldRun[i] && !isOn) digitalWrite(pumpRelayPins[i], LOW); else if (!pumpShouldRun[i] && isOn) digitalWrite(pumpRelayPins[i], HIGH); } for (int i = 0; i < 4; i++) { bool isOn = digitalRead(pumpRelayPins[i]) == LOW; const char* statusText = isOn ? "ON" : "OFF"; Serial.printf("Pump %d: %s\n", i + 1, statusText); } Serial.println("--------------------------"); delay(1000); }