#include #include #include #include #include // --- 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); } }