#include #include "Adafruit_SHT31.h" #include #include #include // ================= WIFI ================= #define WIFI_SSID "Toge" #define WIFI_PASSWORD "123123123" // ================= FIREBASE ================= #define DATABASE_URL "https://smarttoge-default-rtdb.firebaseio.com/" #define DATABASE_SECRET "0CWsHxiRJar3qXSx4uGX385rf0VAnpKKOhQ1wbok" // ================= PIN ================= #define RELAY_PIN 5 // ================= OBJECT ================= Adafruit_SHT31 sht31 = Adafruit_SHT31(); RTC_DS3231 rtc; FirebaseData fbdo; FirebaseAuth auth; FirebaseConfig config; FirebaseJson json; FirebaseJsonData result; // ================= VAR GLOBAL ================= bool relayOn = false; bool pumpRunning = false; bool pumpManual = false; bool humidityNotifSent = false; bool wateringDoneNotifSent = false; String activeScheduleKey = ""; // TIMER unsigned long lastSensor = 0; unsigned long lastFirebase = 0; unsigned long lastSchedule = 0; unsigned long lastManual = 0; // DATA GLOBAL float suhu = 0; float kelembaban = 0; String rtcDate = ""; String rtcTime = ""; // ================= SET RTC DARI SERIAL ================= void setRTCFromSerial() { if (Serial.available()) { String input = Serial.readStringUntil('\n'); input.trim(); int year, month, day, hour, minute, second; if (sscanf(input.c_str(), "%d-%d-%d %d:%d:%d", &year, &month, &day, &hour, &minute, &second) == 6) { rtc.adjust(DateTime(year, month, day, hour, minute, second)); Serial.println("RTC berhasil di-set!"); } else { Serial.println("Format salah! Gunakan: YYYY-MM-DD HH:MM:SS"); } } } // ================= SETUP ================= void setup() { Serial.begin(115200); pinMode(RELAY_PIN, OUTPUT); digitalWrite(RELAY_PIN, HIGH); Wire.begin(21, 22); if (!sht31.begin(0x44)) { Serial.println("Sensor tidak ditemukan!"); while (1); } if (!rtc.begin()) { Serial.println("RTC tidak ditemukan!"); while (1); } // Set otomatis kalau RTC kehilangan daya if (rtc.lostPower()) { rtc.adjust(DateTime(F(__DATE__), F(__TIME__))); } Serial.println("Masukkan waktu RTC format:"); Serial.println("YYYY-MM-DD HH:MM:SS"); // ================= WIFI ================= WiFi.begin(WIFI_SSID, WIFI_PASSWORD); Serial.print("Connecting WiFi"); while (WiFi.status() != WL_CONNECTED) { Serial.print("."); delay(500); } Serial.println("\nWiFi Connected"); config.database_url = DATABASE_URL; config.signer.tokens.legacy_token = DATABASE_SECRET; Firebase.begin(&config, &auth); Firebase.reconnectWiFi(true); } // ================= LOOP ================= void loop() { setRTCFromSerial(); // 🔥 input waktu dari serial unsigned long nowMillis = millis(); // ================= SENSOR ================= if (nowMillis - lastSensor > 2000) { lastSensor = nowMillis; suhu = sht31.readTemperature(); kelembaban = sht31.readHumidity(); DateTime now = rtc.now(); char tgl[11]; sprintf(tgl, "%02d-%02d-%04d", now.day(), now.month(), now.year()); rtcDate = String(tgl); char jam[9]; sprintf(jam, "%02d:%02d:%02d", now.hour(), now.minute(), now.second()); rtcTime = String(jam); Serial.println("Sensor Update"); } // ================= MANUAL ================= if (nowMillis - lastManual > 2000) { lastManual = nowMillis; if (Firebase.RTDB.getBool(&fbdo, "/pump/status")) { pumpManual = fbdo.boolData(); } } // ================= SCHEDULE ================= if (nowMillis - lastSchedule > 15000) { lastSchedule = nowMillis; relayOn = false; if (Firebase.RTDB.getJSON(&fbdo, "/schedules")) { json = fbdo.jsonObject(); size_t len = json.iteratorBegin(); String key, value; int type; for (size_t i = 0; i < len; i++) { json.iteratorGet(i, type, key, value); String scheduleTime, scheduleDate, scheduleStatus; if (json.get(result, key + "/time")) scheduleTime = result.to(); if (json.get(result, key + "/date")) scheduleDate = result.to(); if (json.get(result, key + "/status")) scheduleStatus = result.to(); int schHour = 0, schMinute = 0; sscanf(scheduleTime.c_str(), "%d:%d", &schHour, &schMinute); DateTime now = rtc.now(); int hour = now.hour(); int minute = now.minute(); if (rtcDate == scheduleDate && (hour > schHour || (hour == schHour && minute >= schMinute)) && scheduleStatus == "idle" && kelembaban < 85) { relayOn = true; activeScheduleKey = key; Firebase.RTDB.setString( &fbdo, "/schedules/" + key + "/status", "watering"); Serial.println("TRIGGER SCHEDULE"); } else if (scheduleStatus == "watering" && !pumpRunning) { Firebase.RTDB.setString(&fbdo, "/schedules/" + key + "/status", "done"); } } json.iteratorEnd(); } } // ================= RELAY ================= if (pumpManual) { digitalWrite(RELAY_PIN, LOW); } else if (relayOn && !pumpRunning) { digitalWrite(RELAY_PIN, LOW); pumpRunning = true; Serial.println("POMPA ON"); } // ================= AUTO OFF SAAT KELEMBAPAN >= 85 ================= if (pumpRunning && kelembaban >= 85) { digitalWrite(RELAY_PIN, HIGH); pumpRunning = false; relayOn = false; Serial.println("POMPA OFF - KELEMBAPAN SUDAH 85%"); // UPDATE STATUS JADWAL MENJADI DONE if (activeScheduleKey != "") { Firebase.RTDB.setString( &fbdo, "/schedules/" + activeScheduleKey + "/status", "done"); activeScheduleKey = ""; } // NOTIFIKASI Firebase.RTDB.setString( &fbdo, "/notification/type", "watering_done"); Firebase.RTDB.setString( &fbdo, "/notification/message", "Penyiraman selesai. Kelembapan telah mencapai 85%"); Firebase.RTDB.setInt( &fbdo, "/notification/timestamp", millis()); wateringDoneNotifSent = true; } // ================= DEFAULT OFF ================= if (!relayOn && !pumpRunning && !pumpManual) { digitalWrite(RELAY_PIN, HIGH); } // ================= FIREBASE UPDATE ================= if (nowMillis - lastFirebase > 5000) { lastFirebase = nowMillis; Firebase.RTDB.setFloat(&fbdo, "/sht/temperature", suhu); Firebase.RTDB.setFloat(&fbdo, "/sht/humidity", kelembaban); Firebase.RTDB.setString(&fbdo, "/sht/time", rtcTime); Firebase.RTDB.setString(&fbdo, "/sht/date", rtcDate); // ================= NOTIFIKASI KELEMBAPAN ================= if (kelembaban < 70) { if (!humidityNotifSent) { Firebase.RTDB.setString( &fbdo, "/notification/type", "humidity_low"); Firebase.RTDB.setString( &fbdo, "/notification/message", "Kelembapan rendah, segera lakukan penyiraman"); Firebase.RTDB.setInt( &fbdo, "/notification/timestamp", millis()); humidityNotifSent = true; wateringDoneNotifSent = false; } } else { humidityNotifSent = false; } Serial.println("Firebase Update"); } }