#include #include // Gunakan library "Firebase ESP32 Client" by Mobizt #define RXD2 16 // RX pin ESP32 #define TXD2 17 // TX pin ESP32 // WiFi credentials #define WIFI_SSID "hydrop" #define WIFI_PASSWORD "123123123" // Firebase config #define FIREBASE_HOST "https://hydrop-a1e64-default-rtdb.firebaseio.com" #define FIREBASE_AUTH "4lJpDHPo9LGh6WCYpjs1aRB6ABJZwHLOXkaCoLAV" // Firebase objects FirebaseData fbdo; FirebaseAuth auth; FirebaseConfig config; struct SensorData { float temperature; float humidity; float ph; float tds; bool tempValid; bool humValid; bool phValid; bool tdsValid; unsigned long lastUpdate; } sensorData; struct CommStats { unsigned long totalReceived; unsigned long validData; unsigned long errorData; unsigned long lastReceived; } commStats = {0, 0, 0, 0}; const unsigned long DATA_TIMEOUT = 10000; const unsigned long STATS_INTERVAL = 30000; void setup() { Serial.begin(115200); Serial2.begin(9600, SERIAL_8N1, RXD2, TXD2); // Koneksi WiFi WiFi.begin(WIFI_SSID, WIFI_PASSWORD); Serial.print("Connecting to WiFi"); while (WiFi.status() != WL_CONNECTED) { Serial.print("."); delay(500); } Serial.println("\nWiFi connected!"); // Konfigurasi Firebase config.database_url = FIREBASE_HOST; config.signer.tokens.legacy_token = FIREBASE_AUTH; Firebase.begin(&config, &auth); Firebase.reconnectWiFi(true); Serial.println("Firebase initialized."); sensorData = {0, 0, 0, 0, false, false, false, false, 0}; Serial.println("=== ESP32 Hydroponic Receiver ==="); Serial.println("Menunggu data dari Arduino..."); } void loop() { if (Serial2.available()) { String rawData = Serial2.readStringUntil('\n'); rawData.trim(); commStats.totalReceived++; commStats.lastReceived = millis(); if (parseAndValidateData(rawData)) { commStats.validData++; sensorData.lastUpdate = millis(); displaySensorData(); sendToFirebase(); } else { commStats.errorData++; Serial.print("ERROR: Invalid data format: "); Serial.println(rawData); } } checkDataTimeout(); displayStats(); delay(100); } bool parseAndValidateData(String data) { if (data.length() < 5) return false; int idx1 = data.indexOf(','); int idx2 = data.indexOf(',', idx1 + 1); int idx3 = data.indexOf(',', idx2 + 1); if (idx1 <= 0 || idx2 <= 0 || idx3 <= 0) return false; String tempStr = data.substring(0, idx1); String humStr = data.substring(idx1 + 1, idx2); String phStr = data.substring(idx2 + 1, idx3); String tdsStr = data.substring(idx3 + 1); float temp = tempStr.toFloat(); float hum = humStr.toFloat(); float ph = phStr.toFloat(); float tds = tdsStr.toFloat(); sensorData.temperature = temp; sensorData.humidity = hum; sensorData.ph = ph; sensorData.tds = tds; sensorData.tempValid = validateTemperature(temp); sensorData.humValid = validateHumidity(hum); sensorData.phValid = validatePH(ph); sensorData.tdsValid = validateTDS(tds); return (sensorData.tempValid || sensorData.humValid || sensorData.phValid || sensorData.tdsValid); } void sendToFirebase() { if (WiFi.status() != WL_CONNECTED) { Serial.println("WiFi disconnected, skipping Firebase upload."); return; } FirebaseJson json; json.set("temperature", sensorData.tempValid ? sensorData.temperature : -999); json.set("humidity", sensorData.humValid ? sensorData.humidity : -999); json.set("ph", sensorData.phValid ? sensorData.ph : -999); json.set("tds", sensorData.tdsValid ? sensorData.tds : -999); json.set("timestamp", millis()); if (Firebase.updateNode(fbdo, "/sensorData", json)) { Serial.println("Data sent to Firebase."); } else { Serial.print("Firebase failed: "); Serial.println(fbdo.errorReason()); } } bool validateTemperature(float temp) { return temp != -999 && temp >= -10 && temp <= 60; } bool validateHumidity(float hum) { return hum != -999 && hum >= 0 && hum <= 100; } bool validatePH(float ph) { return ph != -999 && ph >= 0 && ph <= 14; } bool validateTDS(float tds) { return tds != -999 && tds >= 0 && tds <= 3000; } void displaySensorData() { Serial.println("=== SENSOR DATA RECEIVED ==="); Serial.print("Temperature: "); Serial.println(sensorData.tempValid ? String(sensorData.temperature, 2) + " °C" : "INVALID"); Serial.print("Humidity: "); Serial.println(sensorData.humValid ? String(sensorData.humidity, 2) + " %" : "INVALID"); Serial.print("pH: "); Serial.println(sensorData.phValid ? String(sensorData.ph, 2) : "INVALID"); Serial.print("TDS: "); Serial.println(sensorData.tdsValid ? String(sensorData.tds, 0) + " ppm" : "INVALID"); Serial.println("==========================="); } void checkDataTimeout() { static unsigned long lastTimeoutCheck = 0; if (millis() - lastTimeoutCheck >= 5000) { if (sensorData.lastUpdate > 0 && (millis() - sensorData.lastUpdate) > DATA_TIMEOUT) { Serial.println("⚠️ WARNING: No valid data for 10 seconds!"); sensorData.tempValid = false; sensorData.humValid = false; sensorData.phValid = false; sensorData.tdsValid = false; } lastTimeoutCheck = millis(); } } void displayStats() { static unsigned long lastStatsDisplay = 0; if (millis() - lastStatsDisplay >= STATS_INTERVAL) { Serial.println("=== STATISTIK KOMUNIKASI ==="); Serial.print("Total diterima: "); Serial.println(commStats.totalReceived); Serial.print("Valid: "); Serial.println(commStats.validData); Serial.print("Error: "); Serial.println(commStats.errorData); if (commStats.totalReceived > 0) { float success = (float)commStats.validData / commStats.totalReceived * 100; Serial.print("Success rate: "); Serial.print(success, 1); Serial.println(" %"); } Serial.print("Terakhir diterima: "); Serial.println((millis() - commStats.lastReceived) / 1000); Serial.println("============================="); lastStatsDisplay = millis(); } }