217 lines
5.8 KiB
C++
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;
|
|
}
|
|
}
|
|
}
|