#include #include #include #include "addons/TokenHelper.h" #include "addons/RTDBHelper.h" #include // ====== KONFIGURASI WIFI ====== #define WIFI_SSID "sandata" #define WIFI_PASSWORD "gnulibre567" // ====== KONFIGURASI FIREBASE ====== #define API_KEY "AIzaSyAO4ja4G-y20Qv20pe_kU7-vItW908nec4" #define DATABASE_URL "https://penyiramanotomatis-3f962-default-rtdb.asia-southeast1.firebasedatabase.app/" // ====== PIN SENSOR DAN RELAY ====== #define DHTPIN 4 #define DHTTYPE DHT11 #define SOIL_PIN 36 #define RELAY_PIN 5 DHT dht(DHTPIN, DHTTYPE); FirebaseData fbdo; FirebaseAuth auth; FirebaseConfig config; unsigned long sendDataPrevMillis = 0; bool signupOK = false; bool modeOtomatis = true; int moistureThreshold = 30; // default kalau Firebase gagal unsigned long lastFetchThreshold = 0; const unsigned long LOG_INTERVAL = 60UL * 60UL * 1000UL; // 1 jam (ms) unsigned long lastLogTime = 0; void fetchThresholdFromFirebase() { if (Firebase.RTDB.getInt(&fbdo, "controls/pump/moisture_threshold")) { moistureThreshold = fbdo.intData(); Serial.println("Threshold diperbarui: " + String(moistureThreshold) + "%"); } else { Serial.println("Gagal ambil threshold: " + fbdo.errorReason()); } } void loadLastLogTime() { if (Firebase.RTDB.getInt(&fbdo, "/monitoring/meta/lastLogTime")) { lastLogTime = fbdo.intData(); Serial.println("LastLogTime loaded"); } } void saveLastLogTime(unsigned long ts) { Firebase.RTDB.setInt( &fbdo, "/monitoring/meta/lastLogTime", ts ); } unsigned long getTimestamp() { time_t now; time(&now); return (unsigned long)now * 1000; } String getDateKey() { struct tm timeinfo; if (!getLocalTime(&timeinfo)) return "unknown"; char buf[11]; strftime(buf, sizeof(buf), "%Y-%m-%d", &timeinfo); return String(buf); } String getDateOffset(int days) { time_t now; time(&now); now += days * 86400; struct tm timeinfo; localtime_r(&now, &timeinfo); char buf[11]; strftime(buf, sizeof(buf), "%Y-%m-%d", &timeinfo); return String(buf); } void sendHourlyLog( unsigned long ts, float suhu, float humidity, int soilMoisture ) { String dateKey = getDateKey(); FirebaseJson json; json.set("timestamp", ts); json.set("suhu", suhu); json.set("humidity", humidity); json.set("soilMoisture", soilMoisture); // push ke logs/tanggal String path = "/monitoring/logs/" + dateKey; Firebase.RTDB.pushJSON(&fbdo, path, &json); Serial.println("Log tersimpan: " + path); } void deleteOldDate() { String oldDate = getDateOffset(-2); // 2 hari lalu String path = "/monitoring/logs/" + oldDate; Firebase.RTDB.deleteNode(&fbdo, path); Serial.println("Hapus data: " + path); } void setup() { Serial.begin(115200); dht.begin(); pinMode(SOIL_PIN, INPUT); pinMode(RELAY_PIN, OUTPUT); digitalWrite(RELAY_PIN, LOW); // ====== KONEKSI WIFI ====== WiFi.begin(WIFI_SSID, WIFI_PASSWORD); Serial.print("Menghubungkan WiFi"); while (WiFi.status() != WL_CONNECTED) { Serial.print("."); delay(300); } Serial.println("\nTerhubung ke WiFi!"); // NTP untuk timestamp configTime(7 * 3600, 0, "pool.ntp.org", "time.nist.gov"); // ====== KONFIGURASI FIREBASE ====== config.api_key = API_KEY; config.database_url = DATABASE_URL; if (Firebase.signUp(&config, &auth, "", "")) { Serial.println("Firebase SignUp OK"); signupOK = true; loadLastLogTime(); } else { Serial.printf("SignUp Error: %s\n", config.signer.signupError.message.c_str()); } Firebase.begin(&config, &auth); Firebase.reconnectWiFi(true); } void loop() { if (!Firebase.ready() || !signupOK) return; // === BACA SENSOR === float suhu = dht.readTemperature(); float kelembapanUdara = dht.readHumidity(); int soilValue = analogRead(SOIL_PIN); float kelembapanTanah = map(soilValue, 4095, 0, 0, 100); Serial.println("==========================="); Serial.println("Suhu: " + String(suhu) + "°C"); Serial.println("Kelembapan Udara: " + String(kelembapanUdara) + "%"); Serial.println("Kelembapan Tanah: " + String(kelembapanTanah) + "%"); // === BACA MODE DARI FIREBASE === if (Firebase.RTDB.getString(&fbdo, "mode")) { String mode = fbdo.stringData(); modeOtomatis = (mode == "otomatis"); } fetchThresholdFromFirebase(); unsigned long nowTs = getTimestamp(); // log 1 JAM SEKALI if (nowTs - lastLogTime >= LOG_INTERVAL) { lastLogTime = nowTs; sendHourlyLog(nowTs, suhu, kelembapanUdara, kelembapanTanah); saveLastLogTime(nowTs); // hapus data 2 hari lalu (jaga 24 jam) deleteOldDate(); } // === KONTROL POMPA === if (modeOtomatis) { if (kelembapanTanah < moistureThreshold) { digitalWrite(RELAY_PIN, LOW); // pompa menyala Firebase.RTDB.setString(&fbdo, "status/pompa", "ON"); Serial.println("Pompa ON (otomatis - tanah kering)"); } else { digitalWrite(RELAY_PIN, HIGH); // pompa mati Firebase.RTDB.setString(&fbdo, "status/pompa", "OFF"); Serial.println("Pompa OFF (otomatis - tanah lembap)"); } } else { // === MODE MANUAL === if (Firebase.RTDB.getString(&fbdo, "manual_control")) { String control = fbdo.stringData(); if (control == "ON") { digitalWrite(RELAY_PIN, HIGH); Serial.println("Pompa ON (manual)"); } else { digitalWrite(RELAY_PIN, LOW); Serial.println("Pompa OFF (manual)"); } } } // === KIRIM DATA SENSOR SETIAP 3 DETIK === if (millis() - sendDataPrevMillis > 3000) { sendDataPrevMillis = millis(); Firebase.RTDB.setFloat(&fbdo, "sensor/suhu", suhu); Firebase.RTDB.setFloat(&fbdo, "sensor/kelembapan_udara", kelembapanUdara); Firebase.RTDB.setFloat(&fbdo, "sensor/kelembapan_tanah", kelembapanTanah); Serial.println("Data sensor dikirim ke Firebase!"); } delay(3000); }