TKK_E32222868/TA_MEJAv2_FIREBASE/TA_MEJAv2_FIREBASE.ino

394 lines
12 KiB
C++

#include <Wire.h>
#include <LiquidCrystal_I2C.h>
#include <ESP8266WiFi.h>
#include <Firebase_ESP_Client.h>
#include <ArduinoJson.h>
// --- Konfigurasi WiFi ---
#define WIFI_SSID "LMAO"
#define WIFI_PASSWORD "awokawok"
// --- Konfigurasi Firebase ---
#define FIREBASE_HOST "https://meja-iotv0-default-rtdb.firebaseio.com/"
#define FIREBASE_AUTH "AIzaSyC9YQJWMSajJQTlh1tGLRcIeui4rqGvl8E"
FirebaseData firebaseData;
FirebaseConfig config;
FirebaseAuth auth;
LiquidCrystal_I2C lcd(0x27, 16, 2);
// --- Definisi Pin ---
#define TRIG_PIN D5
int echoPins[4] = {D6, D7, D8, D4};
#define BUZZER_PIN D0
#define FORCE_BUTTON_PIN D3
float distances[4];
float lastValidDistances[4];
float lastSensorDistances[4];
// --- Sistem Reservasi ---
bool tableActivationSensorActive = false;
bool tableReserved = false;
bool tableOccupied = false;
bool refreshTriggered = false;
unsigned long reservationSessionStartOrRefreshTime = 0;
unsigned long lastPresenceTime = 0;
String tempLine1 = "";
String tempLine2 = "";
unsigned long messageStartTime = 0;
unsigned long messageDuration = 2000;
bool showTempMessage = false;
String lastLine1 = "";
String lastLine2 = "";
unsigned long lastLCDUpdate = 0;
const unsigned long LCD_UPDATE_INTERVAL = 1000;
const unsigned long RESERVATION_ACTIVE_DURATION = 30 * 60 * 1000;
const unsigned long IDLE_OCCUPIED_TIMEOUT = 10 * 60 * 1000;
const unsigned long WARNING_TIME_10_MIN = 10 * 60 * 1000;
const unsigned long WARNING_TIME_5_MIN = 5 * 60 * 1000;
bool warning10MinTriggered = false;
bool warning5MinTriggered = false;
const float PRESENCE_MIN = 50.0;
const float PRESENCE_MAX = 80.0;
const float MOTION_THRESHOLD = 5.0;
bool presenceDetected = false;
String customerName = "N/A";
bool forceShutdownRequested = false;
unsigned long lastButtonPress = 0;
const unsigned long BUTTON_DEBOUNCE = 200;
unsigned long lastFirebaseUpdate = 0;
const unsigned long FIREBASE_UPDATE_INTERVAL = 5000;
#define TABLE_ID "meja_001"
String deviceID = TABLE_ID;
void showTemporaryMessage(String line1, String line2, int duration = 2000) {
tempLine1 = line1;
tempLine2 = line2;
messageDuration = duration;
showTempMessage = true;
messageStartTime = millis();
lcd.clear();
lcd.setCursor(0, 0); lcd.print(tempLine1);
lcd.setCursor(0, 1); lcd.print(tempLine2);
lastLCDUpdate = millis(); // supaya tidak update terlalu cepat
}
// ==== SETUP ====
void setup() {
Serial.begin(115200);
WiFi.mode(WIFI_STA);
lcd.init(); lcd.backlight();
pinMode(TRIG_PIN, OUTPUT);
pinMode(BUZZER_PIN, OUTPUT);
pinMode(FORCE_BUTTON_PIN, INPUT_PULLUP);
for (int i = 0; i < 4; i++) pinMode(echoPins[i], INPUT);
connectWiFi();
if (WiFi.status() == WL_CONNECTED) initFirebase();
showTemporaryMessage("Sistem Siap!", deviceID);
}
// ==== LOOP ====
void loop() {
if (WiFi.status() != WL_CONNECTED) connectWiFi();
if (Firebase.ready() && millis() - lastFirebaseUpdate > FIREBASE_UPDATE_INTERVAL) {
updateFromFirebase();
updateToFirebase();
lastFirebaseUpdate = millis();
}
if (forceShutdownRequested) {
forceShutdown();
if (Firebase.ready()) {
Firebase.RTDB.setInt(&firebaseData, "/" + deviceID + "/force_shutdown", 0);
}
forceShutdownRequested = false;
}
if (digitalRead(FORCE_BUTTON_PIN) == LOW && millis() - lastButtonPress > BUTTON_DEBOUNCE) {
forceShutdown(); lastButtonPress = millis();
}
readSensors();
analyzeActivity();
if (tableActivationSensorActive) {
if (!tableReserved) startReservation();
if (tableReserved) {
checkSessionTimeout();
checkIdle();
checkWarnings();
}
} else if (tableReserved) {
endReservation();
}
displayLCD();
printSerial();
delay(500);
}
// ==== SENSOR & AKTIVITAS ====
float readDistance(int pin) {
float sum = 0; int valid = 0;
for (int i = 0; i < 3; i++) {
digitalWrite(TRIG_PIN, LOW); delayMicroseconds(2);
digitalWrite(TRIG_PIN, HIGH); delayMicroseconds(10);
digitalWrite(TRIG_PIN, LOW);
long d = pulseIn(pin, HIGH, 30000);
float dist = d * 0.034 / 2;
if (dist > 2 && dist < 400) { sum += dist; valid++; }
delay(10);
}
return (valid == 0) ? 0 : sum / valid;
}
void readSensors() {
for (int i = 0; i < 4; i++) {
float d = readDistance(echoPins[i]);
if (d > 0) lastValidDistances[i] = d;
distances[i] = lastValidDistances[i];
}
}
void analyzeActivity() {
unsigned long now = millis();
bool motion = false;
for (int i = 0; i < 4; i++) {
float delta = abs(distances[i] - lastSensorDistances[i]);
if (lastSensorDistances[i] > 0 && delta >= MOTION_THRESHOLD) motion = true;
lastSensorDistances[i] = distances[i];
}
if (now - reservationSessionStartOrRefreshTime > 10000) refreshTriggered = false;
if (tableReserved && motion && !refreshTriggered) {
reservationSessionStartOrRefreshTime = now;
warning10MinTriggered = warning5MinTriggered = false;
refreshTriggered = true;
Serial.println("Sesi diperpanjang karena gerakan.");
}
presenceDetected = false;
for (int i = 0; i < 4; i++) {
if (distances[i] >= PRESENCE_MIN && distances[i] <= PRESENCE_MAX) {
presenceDetected = true;
break;
}
}
if (presenceDetected) {
lastPresenceTime = now;
tableOccupied = true;
} else if (now - lastPresenceTime > IDLE_OCCUPIED_TIMEOUT) {
tableOccupied = false;
}
// Tambahkan warning jika terlalu dekat
static unsigned long lastWarningTime = 0;
for (int i = 0; i < 4; i++) {
if (distances[i] > 0 && distances[i] < 10.0 && millis() - lastWarningTime > 5000) {
showTooCloseWarning();
lastWarningTime = millis();
break;
}
}
}
void showTooCloseWarning() {
showTemporaryMessage("!! TERLALU DEKAT !!", "Jarak < 10 cm");
}
// ==== WIFI & FIREBASE ====
void connectWiFi() {
lcd.clear(); lcd.setCursor(0, 0); lcd.print("Koneksi WiFi...");
WiFi.begin(WIFI_SSID, WIFI_PASSWORD);
for (int i = 0; i < 30 && WiFi.status() != WL_CONNECTED; i++) {
delay(500); Serial.print(".");
}
lcd.clear();
if (WiFi.status() == WL_CONNECTED) {
showTemporaryMessage("WiFi Terhubung!", WiFi.localIP().toString());
Serial.println("WiFi IP: " + WiFi.localIP().toString());
} else {
showTemporaryMessage("Gagal WiFi!", "Coba lagi...");
}
}
void initFirebase() {
config.host = FIREBASE_HOST;
config.signer.tokens.legacy_token = FIREBASE_AUTH;
Firebase.begin(&config, &auth);
Firebase.reconnectWiFi(true);
Serial.println(Firebase.ready() ? "Firebase Terhubung!" : "Gagal Firebase!");
}
void updateFromFirebase() {
String base = "/" + deviceID;
if (Firebase.RTDB.getInt(&firebaseData, base + "/sensors/table_activation_sensor_active"))
tableActivationSensorActive = firebaseData.intData() == 1;
else
tableActivationSensorActive = false;
if (Firebase.RTDB.getString(&firebaseData, base + "/reserved_by")) {
customerName = firebaseData.stringData();
if (customerName.length() > 8) customerName = customerName.substring(0, 8);
} else customerName = "N/A";
if (Firebase.RTDB.getInt(&firebaseData, base + "/force_shutdown")) {
forceShutdownRequested = firebaseData.intData() == 1;
} else {
forceShutdownRequested = false;
}
}
void updateToFirebase() {
if (!Firebase.ready()) return;
String path = "/" + deviceID;
FirebaseJson json;
json.set("device_id", deviceID);
json.set("mac_address", WiFi.macAddress());
json.set("last_update", millis() / 1000);
json.set("table_occupied", presenceDetected ? 1 : 0);
json.set("time_remaining_minutes", (int)(getRemainingSessionTime() / 60000));
for (int i = 0; i < 4; i++) json.set("sensors/s" + String(i + 1), (int)distances[i]);
json.set("sensors/table_activation_sensor_active", tableActivationSensorActive ? 1 : 0);
Firebase.RTDB.updateNode(&firebaseData, path, &json);
}
// ==== RESERVASI ====
void resetFlags() {
for (int i = 0; i < 4; i++) lastSensorDistances[i] = 0;
warning10MinTriggered = warning5MinTriggered = refreshTriggered = false;
}
void startReservation() {
tableReserved = true;
reservationSessionStartOrRefreshTime = millis();
lastPresenceTime = millis();
resetFlags();
buzzerAlert(2, 500, 150);
Serial.println("Reservasi dimulai oleh " + customerName);
showTemporaryMessage("MEJA " + deviceID.substring(5), "RVSP: " + customerName);
}
void endReservation() {
tableReserved = false;
tableOccupied = false;
resetFlags();
showTemporaryMessage("RESERVASI", "BERAKHIR");
}
void forceShutdown() {
tableActivationSensorActive = false;
endReservation();
Firebase.RTDB.setInt(&firebaseData, "/" + deviceID + "/sensors/table_activation_sensor_active", 0);
Firebase.RTDB.setString(&firebaseData, "/" + deviceID + "/reserved_by", "N/A");
customerName = "N/A";
buzzerAlert(5, 100, 50);
lcd.clear(); lcd.setCursor(0, 0); lcd.print("TERIMA KASIH");
lcd.setCursor(0, 1); lcd.print("SUDAH DATANG!"); delay(3000);
}
unsigned long getRemainingSessionTime() {
if (!tableReserved) return 0;
unsigned long elapsed = millis() - reservationSessionStartOrRefreshTime;
return (elapsed >= RESERVATION_ACTIVE_DURATION) ? 0 : RESERVATION_ACTIVE_DURATION - elapsed;
}
void checkSessionTimeout() {
if (getRemainingSessionTime() == 0) {
tableActivationSensorActive = false;
endReservation();
Firebase.RTDB.setInt(&firebaseData, "/" + deviceID + "/sensors/table_activation_sensor_active", 0);
Firebase.RTDB.setString(&firebaseData, "/" + deviceID + "/reserved_by", "N/A");
}
}
void checkIdle() {
if (millis() - lastPresenceTime >= IDLE_OCCUPIED_TIMEOUT) tableOccupied = false;
else tableOccupied = true;
}
void checkWarnings() {
unsigned long remain = getRemainingSessionTime();
if (remain <= WARNING_TIME_5_MIN && !warning5MinTriggered) {
warning5MinTriggered = true;
buzzerAlert(5, 200, 80);
showWarningLCD("< 5 menit!");
} else if (remain <= WARNING_TIME_10_MIN && !warning10MinTriggered) {
warning10MinTriggered = true;
buzzerAlert(3, 300, 100);
showWarningLCD("< 10 menit!");
}
}
void showWarningLCD(String text) {
showTemporaryMessage("** PERINGATAN **", text);
}
// ==== TAMPILAN ====
void displayLCD() {
if (millis() - lastLCDUpdate < LCD_UPDATE_INTERVAL) return;
if (showTempMessage) {
if (millis() - messageStartTime >= messageDuration) {
showTempMessage = false;
lastLine1 = ""; // paksa refresh ke tampilan normal
lastLine2 = "";
}
return;
}
String line1 = "MEJA " + deviceID.substring(5);
String line2;
if (WiFi.status() != WL_CONNECTED) line2 = "WiFi terputus";
else if (!tableReserved) line2 = "TERSEDIA";
else line2 = "RVSP: " + customerName;
if (line1 != lastLine1 || line2 != lastLine2) {
lcd.clear();
lcd.setCursor(0, 0); lcd.print(line1);
lcd.setCursor(0, 1); lcd.print(line2);
lastLine1 = line1;
lastLine2 = line2;
lastLCDUpdate = millis();
}
}
void printSerial() {
Serial.println("\n=== STATUS " + deviceID + " ===");
Serial.println("WiFi: " + String(WiFi.status() == WL_CONNECTED ? "YA" : "TIDAK"));
Serial.println("Firebase: " + String(Firebase.ready() ? "YA" : "TIDAK"));
Serial.print("Sensor: ");
for (int i = 0; i < 4; i++) Serial.print("S" + String(i+1) + ":" + String(distances[i]) + " ");
Serial.println();
Serial.println("Aktif: " + String(tableActivationSensorActive ? "YA" : "TIDAK"));
Serial.println("Reserved: " + String(tableReserved ? "YA" : "TIDAK"));
Serial.println("Occupied: " + String(tableOccupied ? "YA" : "TIDAK"));
Serial.println("Sisa Waktu: " + String(getRemainingSessionTime() / 60000) + " menit");
Serial.println("Pemesan: " + customerName);
Serial.println("Warning10: " + String(warning10MinTriggered));
Serial.println("Warning5: " + String(warning5MinTriggered));
}
// ==== BUZZER ====
void buzzerAlert(int count, int onTime, int offTime) {
for (int i = 0; i < count; i++) {
digitalWrite(BUZZER_PIN, HIGH); delay(onTime);
digitalWrite(BUZZER_PIN, LOW); delay(offTime);
}
}