#include #include #include #include #include #include // LCD configuration int lcdColumns = 16; int lcdRows = 2; LiquidCrystal_I2C lcd(0x27, lcdColumns, lcdRows); // WiFi configuration #define WIFI_SSID "Nofal" #define WIFI_PASSWORD "123456789" // Firebase configuration #define API_KEY "AIzaSyCncK2P7cCxBlwIikX-cxcQ8lneUalADVw" #define DATABASE_URL "https://esp32-86fe1-default-rtdb.asia-southeast1.firebasedatabase.app/" #define USER_EMAIL "lutfi@gmail.com" #define USER_PASSWORD "1234567" // Pin configuration #define LDR_PIN 13 #define RD_PIN 35 static const int servoPin = 14; Servo myServo; // Firebase & NTP objects FirebaseData fbdo; FirebaseAuth auth; FirebaseConfig config; WiFiUDP ntpUDP; NTPClient timeClient(ntpUDP, "pool.ntp.org", 25200, 60000); // GMT+7 unsigned long sendDataPrevMillis = 0; int lastRainState = -1; int lastLightValue = -1; int lastPositionValue = -1; // LCD scroll text variables String scrollText = ""; int scrollIndex = 0; unsigned long lastScrollTime = 0; const int scrollDelay = 300; String getFormattedDate() { time_t rawTime = timeClient.getEpochTime(); struct tm *timeInfo = localtime(&rawTime); char buffer[11]; strftime(buffer, sizeof(buffer), "%Y-%m-%d", timeInfo); return String(buffer); } // Update LCD scroll text void updateScrollText(int posValue, int rain, int light) { String posText = (posValue == 0) ? "Jemuran di luar. " : "Jemuran di dalam. "; String lightText, weatherText; if (rain == 1 && light == 1) { lightText = "Light: overcast. "; weatherText = "Weather: Cloudy. "; } else if (rain == 0 && light == 0) { lightText = "Light: Good. "; weatherText = "Weather: Rainy. "; } else if (rain == 0 && light == 1) { lightText = "Light: Overcast. "; weatherText = "Weather: Rainy. "; } else { lightText = "Light: Good. "; weatherText = "Weather: Sunny. "; } scrollText = posText + lightText + weatherText; } // Display scroll text + WiFi status void scrollTextToLCD(bool isOnline) { if (millis() - lastScrollTime >= scrollDelay) { lastScrollTime = millis(); lcd.clear(); lcd.setCursor(0, 0); int len = scrollText.length(); String toPrint = scrollText.substring(scrollIndex, scrollIndex + lcdColumns); lcd.print(toPrint); lcd.setCursor(0, 1); lcd.print(isOnline ? "Status: Online " : "Status:Offline"); scrollIndex++; if (scrollIndex > len) scrollIndex = 0; } } void setup() { pinMode(LDR_PIN, INPUT); pinMode(RD_PIN, INPUT); myServo.attach(servoPin); Serial.begin(9600); WiFi.begin(WIFI_SSID, WIFI_PASSWORD); Serial.print("Connecting to Wi-Fi"); while (WiFi.status() != WL_CONNECTED) { Serial.print("."); delay(300); } Serial.println("\nConnected with IP: " + WiFi.localIP().toString()); config.api_key = API_KEY; auth.user.email = USER_EMAIL; auth.user.password = USER_PASSWORD; config.database_url = DATABASE_URL; Firebase.reconnectNetwork(true); fbdo.setBSSLBufferSize(4096, 1024); fbdo.setResponseSize(2048); Firebase.begin(&config, &auth); Firebase.setDoubleDigits(5); config.timeout.serverResponse = 10 * 1000; lcd.init(); lcd.backlight(); timeClient.begin(); } void loop() { timeClient.update(); // Baca sensor int rainStateNow = digitalRead(RD_PIN); int lightValueNow = digitalRead(LDR_PIN); int currentPosition = lastPositionValue; // Update LCD lokal updateScrollText(currentPosition, rainStateNow, lightValueNow); bool isOnline = WiFi.status() == WL_CONNECTED; scrollTextToLCD(isOnline); // Jika Wi-Fi dan Firebase ready if (isOnline && Firebase.ready() && (millis() - sendDataPrevMillis > 1000 || sendDataPrevMillis == 0)) { sendDataPrevMillis = millis(); String status; if (Firebase.RTDB.getString(&fbdo, "/status/value")) { status = fbdo.stringData(); Serial.println("Status: " + status); } else { Serial.print("Error fetching status: "); Serial.println(fbdo.errorReason().c_str()); return; } if (status == "Off") { int positionValue = -1; if (Firebase.RTDB.getInt(&fbdo, "/position/value")) { positionValue = fbdo.intData(); } if (positionValue == 1) { lastPositionValue = 1; return; } myServo.write(55); Firebase.RTDB.setInt(&fbdo, "/position/value", 1); lastPositionValue = 1; return; } // Logika otomatis saat online int newPositionValue = (rainStateNow == 1 && lightValueNow == 0) ? 0 : 1; if (rainStateNow != lastRainState || lightValueNow != lastLightValue) { if (Firebase.RTDB.setInt(&fbdo, "/sensor/ldr", lightValueNow) && Firebase.RTDB.setInt(&fbdo, "/sensor/rd", rainStateNow)) { Serial.println("Sensor updated."); } lastRainState = rainStateNow; lastLightValue = lightValueNow; FirebaseJson json; json.set("tanggal", getFormattedDate()); json.set("jam", timeClient.getFormattedTime()); json.set("ldr", lightValueNow); json.set("rd", rainStateNow); Firebase.RTDB.pushJSON(&fbdo, "/weather", &json); } if (newPositionValue != lastPositionValue) { if (Firebase.RTDB.setInt(&fbdo, "/position/value", newPositionValue)) { if (newPositionValue == 0) { myServo.write(110); } else { myServo.write(55); } } lastPositionValue = newPositionValue; } } else { // Mode offline: kontrol lokal Serial.println("Firebase not ready or Wi-Fi lost. Running offline mode."); int newPositionValue = (rainStateNow == 1 && lightValueNow == 0) ? 0 : 1; if (newPositionValue != lastPositionValue) { if (newPositionValue == 0) { myServo.write(110); // Jemuran masuk } else { myServo.write(55); // Jemuran keluar } lastPositionValue = newPositionValue; } } }