Files
esp32-jemuran-pintar/ESP32_JemuranPintar.ino
2025-07-10 16:45:39 +07:00

217 lines
5.8 KiB
C++

#include <WiFi.h>
#include <Firebase_ESP_Client.h>
#include <NTPClient.h>
#include <WiFiUdp.h>
#include <ESP32Servo.h>
#include <LiquidCrystal_I2C.h>
// 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;
}
}
}