From 6d94ef6230d2dc24b32c429593a83b865a364be1 Mon Sep 17 00:00:00 2001 From: Akbar_Maulana_Agritanto Date: Wed, 6 Aug 2025 14:10:42 +0700 Subject: [PATCH] Upload files to "/" --- server.js | 52 ++++++ sketch_esp1.ino | 412 ++++++++++++++++++++++++++++++++++++++++++++++++ sketch_esp2.ino | 360 ++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 824 insertions(+) create mode 100644 server.js create mode 100644 sketch_esp1.ino create mode 100644 sketch_esp2.ino diff --git a/server.js b/server.js new file mode 100644 index 0000000..6dae39c --- /dev/null +++ b/server.js @@ -0,0 +1,52 @@ +const { Client, LocalAuth } = require('whatsapp-web.js'); +const qrcode = require('qrcode-terminal'); +const express = require('express'); +const bodyParser = require('body-parser'); + +const app = express(); +const port = 3000; + +// Middleware untuk meng-parse JSON request body +app.use(bodyParser.json()); + +// Inisialisasi client WhatsApp +const client = new Client({ + authStrategy: new LocalAuth(), +}); + +// Menampilkan QR Code saat pertama kali login +client.on('qr', (qr) => { + qrcode.generate(qr, { small: true }); +}); + +// Setelah terhubung +client.on('ready', () => { + console.log('WhatsApp Web is ready'); +}); + +// Service untuk mengirim pesan +app.post('/send-message', (req, res) => { + const { phone, message } = req.body; + + if (!phone || !message) { + return res.status(400).send('Phone number and message are required!'); + } + + // Mengirim pesan + client.sendMessage(`${phone}@c.us`, message) + .then((response) => { + res.status(200).send('Message sent successfully!'); + }) + .catch((error) => { + console.error(error); + res.status(500).send('Failed to send message'); + }); +}); + +// Jalankan server +app.listen(port, () => { + console.log(`WhatsApp API service running on http://localhost:${port}`); +}); + +// Log in to WhatsApp Web +client.initialize(); diff --git a/sketch_esp1.ino b/sketch_esp1.ino new file mode 100644 index 0000000..016cabe --- /dev/null +++ b/sketch_esp1.ino @@ -0,0 +1,412 @@ +#include +#include +#include +#include +#include +#include +#include + +// === KREDENSIAL FIREBASE === +#define API_KEY "AIzaSyBRT9jVU0jbLLri5ZWFgELiP-MwdWqJqGY" +#define DATABASE_URL "https://palang-pintu-1ee1b-default-rtdb.asia-southeast1.firebasedatabase.app/" +#define LEGACY_TOKEN "ii4jFWAjUalW2zkYddwhzk8313hD5TLsewohcvdR" + +// === PIN SENSOR DAN KOMPONEN === +#define TRIG1 14 +#define ECHO1 27 +#define TRIG2 25 +#define ECHO2 26 +#define SERVO_PIN 18 +#define BUZZER_PIN 12 +#define LED_AMAN 32 +#define LED_BAHAYA 33 +#define PIEZO_PIN1 34 +#define BUTTON_UP 23 +#define BUTTON_DOWN 19 +#define RXD2 16 +#define TXD2 17 + +// === OBJEK & VARIABEL GLOBAL === +FirebaseData fbdo; +FirebaseAuth auth; +FirebaseConfig config; +Servo servoJalur1; +LiquidCrystal_I2C lcd(0x27, 16, 2); + +unsigned long lastUpdate = 0; +unsigned long lastSerialPrint = 0; +const unsigned long interval = 5000; +unsigned long intervalJeda = 5000; + +bool isOfflineMode = false; +bool keretaTerdeteksi = false; +bool keretaLewat = false; + +int upClickCount = 0; +int downClickCount = 0; +bool lastButtonUpState = HIGH; +bool lastButtonDownState = HIGH; +// Flag to track if the waiting message for Jalur 2 has been sent +bool waitingMessageSent = false; +// Flag to track if the "Kereta telah lewat di Jalur 1!" message has been sent +bool keretaLewatMessageSent = false; + +int tresholdUltrasonik = 50; // Default 50 cm +int tresholdPiezo = 300; // Default 300 +String phoneNumber = "6282221448622"; // Nomor telepon yang akan dihubungi +String serviceURL = "http://192.168.37.201:3000/send-message"; // Default service URL + + + +// Fungsi untuk mengambil status_kereta dari Firebase dan mengontrol palang +void fetchStatusKereta() { + // Coba ambil status kereta dari Firebase + if (Firebase.RTDB.getBool(&fbdo, "/status_kereta")) { + if (fbdo.dataType() == "boolean") { // Cek apakah tipe data adalah boolean + bool statusKereta = fbdo.boolData(); // Ambil nilai boolean dari Firebase + + // Hanya tampilkan status jika ada pembaruan + static bool lastStatusKereta = false; // Variabel untuk menyimpan status terakhir + if (statusKereta != lastStatusKereta) { // Cek apakah status berubah + Serial.print("[INFO] Status kereta dari Firebase: "); + Serial.println(statusKereta ? "DITUTUP" : "DIBUKA"); + lastStatusKereta = statusKereta; // Update status terakhir + } + + // Lakukan aksi berdasarkan status kereta + if (statusKereta) { + // Status kereta true, tutup palang + servoJalur1.write(90); // Tutup palang + digitalWrite(LED_BAHAYA, HIGH); // LED merah menyala + digitalWrite(LED_AMAN, LOW); + digitalWrite(BUZZER_PIN, HIGH); // Bunyi buzzer + lcd.clear(); lcd.setCursor(0, 0); lcd.print("PALANG DITUTUP"); + } else { + // Status kereta false, buka palang + servoJalur1.write(0); // Buka palang + digitalWrite(LED_BAHAYA, LOW); + digitalWrite(LED_AMAN, HIGH); + digitalWrite(BUZZER_PIN, LOW); // Matikan buzzer + lcd.clear(); lcd.setCursor(0, 0); lcd.print("PALANG DIBUKA"); + } + } else { + // Jika tipe data bukan boolean + Serial.print("[ERROR] Data bukan boolean: "); + Serial.println(fbdo.dataType()); // Tampilkan tipe data yang salah + } + } else { + // Jika gagal mendapatkan data dari Firebase + Serial.print("[ERROR] Gagal mendapatkan status kereta: "); + Serial.println(fbdo.errorReason()); // Menampilkan alasan error + } +} + + + + +// === AMBIL DATA THRESHOLD DARI FIREBASE === +void updateThresholdFromFirebase() { + if (Firebase.RTDB.getInt(&fbdo, "/treshold/ultrasonik") && fbdo.dataType() == "int") { + tresholdUltrasonik = fbdo.intData(); + Serial.print("Treshold Ultrasonik diperbarui: "); + Serial.println(tresholdUltrasonik); + } + + if (Firebase.RTDB.getInt(&fbdo, "/treshold/piezo") && fbdo.dataType() == "int") { + tresholdPiezo = fbdo.intData(); + Serial.print("Treshold Piezo diperbarui: "); + Serial.println(tresholdPiezo); + } + + if (Firebase.RTDB.getInt(&fbdo, "/treshold/interval_jeda") && fbdo.dataType() == "int") { + intervalJeda = fbdo.intData() * 1000; + Serial.print("Interval Jeda diperbarui: "); + Serial.println(intervalJeda); + } +} + +void sendWAAlert(String message) { + HTTPClient http; + http.begin(serviceURL); // Gunakan serviceURL yang telah disesuaikan + + String payload = "{\"phone\": \"" + phoneNumber + "\", \"message\": \"" + message + "\"}"; // Gunakan phoneNumber langsung + http.addHeader("Content-Type", "application/json"); + + // Kirim permintaan POST + int httpResponseCode = http.POST(payload); + + if (httpResponseCode > 0) { + // Menyediakan feedback sukses + Serial.println("Pesan WhatsApp terkirim ke " + phoneNumber + ": " + String(httpResponseCode)); + } else { + // Menyediakan feedback gagal + Serial.println("Gagal mengirim pesan WhatsApp ke " + phoneNumber + ": " + String(httpResponseCode)); + Serial.println("Error: " + http.errorToString(httpResponseCode)); + } + + http.end(); // Tutup permintaan HTTP +} + + + +// === SETUP === +void setup() { + Serial.begin(115200); + Serial2.begin(9600, SERIAL_8N1, RXD2, TXD2); // komunikasi ke ESP2 + + servoJalur1.setPeriodHertz(50); + servoJalur1.attach(SERVO_PIN); + + Wire.begin(22, 21); + lcd.init(); + lcd.backlight(); + + pinMode(TRIG1, OUTPUT); + pinMode(ECHO1, INPUT); + pinMode(TRIG2, OUTPUT); + pinMode(ECHO2, INPUT); + pinMode(PIEZO_PIN1, INPUT); + pinMode(BUZZER_PIN, OUTPUT); + pinMode(LED_AMAN, OUTPUT); + pinMode(LED_BAHAYA, OUTPUT); + pinMode(BUTTON_UP, INPUT_PULLUP); + pinMode(BUTTON_DOWN, INPUT_PULLUP); + + digitalWrite(LED_AMAN, HIGH); + digitalWrite(LED_BAHAYA, LOW); + + lcd.setCursor(0, 0); + lcd.print("Menghubungkan..."); + Serial.println("Menghubungkan ke WiFi..."); + + // WiFiManager untuk konfigurasi URL + WiFiManager wm; + WiFiManagerParameter custom_service_url("service_url", "Service URL", serviceURL.c_str(), 40); // Ukuran maksimal 40 karakter + wm.addParameter(&custom_service_url); // Menambahkan parameter + + wm.setTimeout(60); + if (!wm.autoConnect("PALANG 1-Setup")) { + Serial.println("Gagal konek WiFi. Mode offline."); + lcd.clear(); lcd.setCursor(0, 0); lcd.print("Mode Offline"); + isOfflineMode = true; // Set mode offline + delay(2000); + } else { + Serial.println("WiFi Terhubung!"); + lcd.clear(); lcd.setCursor(0, 0); lcd.print("WiFi Terhubung"); + delay(2000); + + // Ambil URL yang dimasukkan pengguna + serviceURL = custom_service_url.getValue(); // Ambil URL yang dikonfigurasi + Serial.println("Service URL: " + serviceURL); // Debug URL yang diambil + + // Konfigurasi Firebase jika diperlukan + config.api_key = API_KEY; + config.database_url = DATABASE_URL; + config.signer.tokens.legacy_token = LEGACY_TOKEN; + Firebase.begin(&config, &auth); + Firebase.reconnectWiFi(true); + updateThresholdFromFirebase(); // Update threshold from Firebase + } + + lcd.clear(); lcd.setCursor(0, 0); lcd.print("PALANG PINTAR"); + Serial.println("Sistem Siap: PALANG PINTAR"); +} + + + +// Fungsi utama yang berjalan di loop +void loop() { + // Baca sensor Ultrasonik 1 untuk mendeteksi kereta + digitalWrite(TRIG1, LOW); + delayMicroseconds(2); + digitalWrite(TRIG1, HIGH); + delayMicroseconds(10); + digitalWrite(TRIG1, LOW); + long duration1 = pulseIn(ECHO1, HIGH); + float distance1 = duration1 * 0.034 / 2; // Menghitung jarak + + // Baca sensor Ultrasonik 2 + digitalWrite(TRIG2, LOW); + delayMicroseconds(2); + digitalWrite(TRIG2, HIGH); + delayMicroseconds(10); + digitalWrite(TRIG2, LOW); + long duration2 = pulseIn(ECHO2, HIGH); + float distance2 = duration2 * 0.034 / 2; // Menghitung jarak + + // Baca sensor Piezo + int piezo1Val = analogRead(PIEZO_PIN1); + + // Menampilkan status sensor ke Serial Monitor setiap interval tertentu + if (millis() - lastSerialPrint >= interval) { + lastSerialPrint = millis(); + Serial.println("===== STATUS SENSOR ====="); + Serial.print("Ultrasonik 1: "); Serial.println(distance1); + Serial.print("Ultrasonik 2: "); Serial.println(distance2); + Serial.print("Piezo1: "); Serial.println(piezo1Val); + } + + // === Ambil Status Kereta secara Berkala dari Firebase === + fetchStatusKereta(); // Fungsi yang memeriksa status_kereta setiap interval dan mengontrol palang + + // === Deteksi Kereta Masuk (Jalur 1) === +if (distance1 < tresholdUltrasonik && piezo1Val > tresholdPiezo && !keretaTerdeteksi) { + // Periksa status_kereta di Firebase + if (Firebase.RTDB.getBool(&fbdo, "/status_kereta")) { + bool statusKereta = fbdo.boolData(); // status_kereta (perintah umum) + + if (!statusKereta) { // Hanya tutup palang jika status_kereta adalah false + digitalWrite(LED_BAHAYA, HIGH); + digitalWrite(LED_AMAN, LOW); + digitalWrite(BUZZER_PIN, HIGH); // Bunyi buzzer + servoJalur1.write(90); // Tutup palang Jalur 1 + + lcd.clear(); lcd.setCursor(0, 0); lcd.print("KERETA DATANG"); + sendWAAlert("Peringatan: Kereta datang di Jalur 1!"); // Kirim pesan WA + Serial.println("[INFO] Kereta terdeteksi di Jalur 1 - Palang DITUTUP"); + + // Update status_kereta di Firebase + Firebase.RTDB.setBool(&fbdo, "/status_kereta", true); // Update status kereta menjadi true + + // Kirim perintah ke ESP Jalur 2 untuk tutup palang jika dalam mode offline + if (isOfflineMode) { + Serial2.write('0'); // Perintah tutup ke ESP Jalur 2 + } + + // Update status_kereta_jalur1 di Firebase + Firebase.RTDB.setBool(&fbdo, "/status_kereta_jalur1", true); // Update status Jalur 1 + + keretaTerdeteksi = true; + keretaLewat = false; + } + } +} +// === Cek Kereta Lewat (Jalur 1) === + if (keretaTerdeteksi && distance2 < tresholdUltrasonik) { + keretaLewat = true; + } + + // Kirim pesan WA hanya sekali jika status_kereta_jalur2 TRUE + if (keretaTerdeteksi && keretaLewat && distance2 > tresholdUltrasonik) { + // Update status_kereta_jalur1 menjadi false setelah kereta lewat + Firebase.RTDB.setBool(&fbdo, "/status_kereta_jalur1", false); // Status Jalur 1 menjadi false + + // Kirim pesan "Kereta telah lewat di Jalur 1!" hanya sekali + if (!keretaLewatMessageSent) { + sendWAAlert("Peringatan: Kereta telah lewat di Jalur 1!"); // Kirim pesan WA + keretaLewatMessageSent = true; // Set flag to prevent sending multiple messages + Serial.println("[INFO] Kereta sudah lewat - Pesan telah dikirim."); + } + + // Periksa status_kereta_jalur2 + if (Firebase.RTDB.getBool(&fbdo, "/status_kereta_jalur2")) { + bool statusKeretaJalur2 = fbdo.boolData(); // Cek status_kereta_jalur2 + + if (statusKeretaJalur2 && !waitingMessageSent) { // Jika status_kereta_jalur2 adalah true, kirim pesan menunggu + sendWAAlert("Peringatan: Menunggu kereta 2 lewat!"); // Kirim pesan WA + Serial.println("[INFO] Menunggu kereta 2 lewat..."); + waitingMessageSent = true; // Set flag to true to prevent multiple messages + } + + // Cek jika status_kereta adalah false dan status_kereta_jalur2 juga false + if (!statusKeretaJalur2) { + bool statusKereta = fbdo.boolData(); // Cek status_kereta global + + if (!statusKereta) { // Hanya buka palang jika status_kereta adalah false + servoJalur1.write(0); // Buka palang Jalur 1 + digitalWrite(LED_BAHAYA, LOW); + digitalWrite(LED_AMAN, HIGH); + digitalWrite(BUZZER_PIN, LOW); // Matikan buzzer + + lcd.clear(); lcd.setCursor(0, 0); lcd.print("KERETA LEWAT"); + + Serial.println("[INFO] Kereta sudah lewat - Palang DIBUKA"); + + // Update status_kereta di Firebase + Firebase.RTDB.setBool(&fbdo, "/status_kereta", false); // Update status kereta menjadi false + + // Kirim perintah ke ESP Jalur 2 untuk membuka palang jika dalam mode offline + if (isOfflineMode) { + Serial2.write('1'); // Perintah buka ke ESP Jalur 2 + } + + keretaTerdeteksi = false; + keretaLewat = false; + } + } + } + } + + + // === Manual Control: Tombol UP === +bool currentUp = digitalRead(BUTTON_UP); +if (currentUp == LOW && lastButtonUpState == HIGH) { // Deteksi tombol ditekan + servoJalur1.write(0); // Buka palang + digitalWrite(LED_BAHAYA, LOW); + digitalWrite(LED_AMAN, HIGH); + digitalWrite(BUZZER_PIN, LOW); // Matikan buzzer + Serial.println("[MANUAL] Palang DITUTUP (1x klik)"); + + if (!isOfflineMode) { + Firebase.RTDB.setBool(&fbdo, "/status_kereta", false); // Update status ke Firebase + } + + // Kirim perintah ke ESP Jalur 2 jika dalam mode offline + if (isOfflineMode) { + Serial2.write('1'); // Perintah tutup ke ESP Jalur 2 + } + + // Reset hitungan setelah aksi + // No need for upClickCount here anymore +} +lastButtonUpState = currentUp; + +// === Manual Control: Tombol DOWN === +bool currentDown = digitalRead(BUTTON_DOWN); +if (currentDown == LOW && lastButtonDownState == HIGH) { // Deteksi tombol ditekan + servoJalur1.write(90); // Tutup palang + digitalWrite(LED_BAHAYA, HIGH); + digitalWrite(LED_AMAN, LOW); + digitalWrite(BUZZER_PIN, HIGH); // Bunyi buzzer + Serial.println("[MANUAL] Palang DIBUKA (1x klik)"); + + if (!isOfflineMode) { + Firebase.RTDB.setBool(&fbdo, "/status_kereta", true); // Update status ke Firebase + } + + // Kirim perintah ke ESP Jalur 2 jika dalam mode offline + if (isOfflineMode) { + Serial2.write('0'); // Perintah buka ke ESP Jalur 2 + } + + // Reset hitungan setelah aksi + // No need for downClickCount here anymore +} +lastButtonDownState = currentDown; + + + // === Terima sinyal dari ESP Jalur 2 === (Mode Offline) + if (isOfflineMode && Serial2.available() > 0) { + char incomingByte = Serial2.read(); + if (incomingByte == '0') { // Perintah tutup dari ESP Jalur 2 + servoJalur1.write(90); // Tutup palang + digitalWrite(LED_BAHAYA, HIGH); + digitalWrite(LED_AMAN, LOW); + digitalWrite(BUZZER_PIN, HIGH); + Serial.println("[ESP2] Sinyal diterima - Palang DITUTUP oleh ESP2"); + + lcd.clear(); lcd.setCursor(0, 0); lcd.print("ESP2: PALANG TUTUP"); + } + if (incomingByte == '1') { // Perintah buka dari ESP Jalur 2 + servoJalur1.write(0); // Buka palang + digitalWrite(LED_BAHAYA, LOW); + digitalWrite(LED_AMAN, HIGH); + digitalWrite(BUZZER_PIN, LOW); + Serial.println("[ESP2] Sinyal diterima - Palang DIBUKA oleh ESP2"); + + lcd.clear(); lcd.setCursor(0, 0); lcd.print("ESP2: PALANG BUKA"); + } + } + + delay(500); // jeda agar loop tidak terlalu cepat +} \ No newline at end of file diff --git a/sketch_esp2.ino b/sketch_esp2.ino new file mode 100644 index 0000000..e5a1885 --- /dev/null +++ b/sketch_esp2.ino @@ -0,0 +1,360 @@ +#include +#include +#include +#include +#include +#include +#include + +// === KREDENSIAL FIREBASE === +// === KREDENSIAL FIREBASE === +#define API_KEY "AIzaSyBRT9jVU0jbLLri5ZWFgELiP-MwdWqJqGY" +#define DATABASE_URL "https://palang-pintu-1ee1b-default-rtdb.asia-southeast1.firebasedatabase.app/" +#define LEGACY_TOKEN "ii4jFWAjUalW2zkYddwhzk8313hD5TLsewohcvdR" + +// === PIN SENSOR DAN KOMPONEN === +#define TRIG1 14 +#define ECHO1 27 +#define TRIG2 25 +#define ECHO2 26 +#define SERVO_PIN 18 +#define BUZZER_PIN 12 +#define LED_AMAN 23 +#define LED_BAHAYA 32 +#define PIEZO_PIN1 34 +#define RXD2 16 +#define TXD2 17 + +// === OBJEK & VARIABEL GLOBAL === +FirebaseData fbdo; +FirebaseAuth auth; +FirebaseConfig config; +Servo servoJalur1; +LiquidCrystal_I2C lcd(0x27, 16, 2); + +unsigned long lastUpdate = 0; +unsigned long lastSerialPrint = 0; +const unsigned long interval = 5000; +unsigned long intervalJeda = 5000; + +bool isOfflineMode = false; +bool keretaTerdeteksi = false; +bool keretaLewat = false; + +int upClickCount = 0; +int downClickCount = 0; +bool lastButtonUpState = HIGH; +bool lastButtonDownState = HIGH; +// Flag to track if the waiting message for Jalur 2 has been sent +bool waitingMessageSent = false; +// Flag to track if the "Kereta telah lewat di Jalur 1!" message has been sent +bool keretaLewatMessageSent = false; + +int tresholdUltrasonik = 50; // Default 50 cm +int tresholdPiezo = 300; // Default 300 +String phoneNumber = "6282221448622"; // Nomor telepon yang akan dihubungi +String serviceURL = "http://192.168.37.201:3000/send-message"; // Default service URL + +// Fungsi untuk mengambil status_kereta dari Firebase dan mengontrol palang +void fetchStatusKereta() { + // Coba ambil status kereta dari Firebase + if (Firebase.RTDB.getBool(&fbdo, "/status_kereta")) { + if (fbdo.dataType() == "boolean") { // Cek apakah tipe data adalah boolean + bool statusKereta = fbdo.boolData(); // Ambil nilai boolean dari Firebase + + // Hanya tampilkan status jika ada pembaruan + static bool lastStatusKereta = false; // Variabel untuk menyimpan status terakhir + if (statusKereta != lastStatusKereta) { // Cek apakah status berubah + Serial.print("[INFO] Status kereta dari Firebase: "); + Serial.println(statusKereta ? "DITUTUP" : "DIBUKA"); + lastStatusKereta = statusKereta; // Update status terakhir + } + + // Lakukan aksi berdasarkan status kereta + if (statusKereta) { + // Status kereta true, tutup palang + servoJalur1.write(90); // Tutup palang + digitalWrite(LED_BAHAYA, HIGH); // LED merah menyala + digitalWrite(LED_AMAN, LOW); + digitalWrite(BUZZER_PIN, HIGH); // Bunyi buzzer + lcd.clear(); lcd.setCursor(0, 0); lcd.print("PALANG DITUTUP"); + } else { + // Status kereta false, buka palang + servoJalur1.write(0); // Buka palang + digitalWrite(LED_BAHAYA, LOW); + digitalWrite(LED_AMAN, HIGH); + digitalWrite(BUZZER_PIN, LOW); // Matikan buzzer + lcd.clear(); lcd.setCursor(0, 0); lcd.print("PALANG DIBUKA"); + } + } else { + // Jika tipe data bukan boolean + Serial.print("[ERROR] Data bukan boolean: "); + Serial.println(fbdo.dataType()); // Tampilkan tipe data yang salah + } + } else { + // Jika gagal mendapatkan data dari Firebase + Serial.print("[ERROR] Gagal mendapatkan status kereta: "); + Serial.println(fbdo.errorReason()); // Menampilkan alasan error + } +} + + + + +// === AMBIL DATA THRESHOLD DARI FIREBASE === +void updateThresholdFromFirebase() { + if (Firebase.RTDB.getInt(&fbdo, "/treshold/ultrasonik") && fbdo.dataType() == "int") { + tresholdUltrasonik = fbdo.intData(); + Serial.print("Treshold Ultrasonik diperbarui: "); + Serial.println(tresholdUltrasonik); + } + + if (Firebase.RTDB.getInt(&fbdo, "/treshold/piezo") && fbdo.dataType() == "int") { + tresholdPiezo = fbdo.intData(); + Serial.print("Treshold Piezo diperbarui: "); + Serial.println(tresholdPiezo); + } + + if (Firebase.RTDB.getInt(&fbdo, "/treshold/interval_jeda") && fbdo.dataType() == "int") { + intervalJeda = fbdo.intData() * 1000; + Serial.print("Interval Jeda diperbarui: "); + Serial.println(intervalJeda); + } +} + +void sendWAAlert(String message) { + HTTPClient http; + http.begin(serviceURL); // Gunakan serviceURL yang telah disesuaikan + + String payload = "{\"phone\": \"" + phoneNumber + "\", \"message\": \"" + message + "\"}"; // Gunakan phoneNumber langsung + http.addHeader("Content-Type", "application/json"); + + // Kirim permintaan POST + int httpResponseCode = http.POST(payload); + + if (httpResponseCode > 0) { + // Menyediakan feedback sukses + Serial.println("Pesan WhatsApp terkirim ke " + phoneNumber + ": " + String(httpResponseCode)); + } else { + // Menyediakan feedback gagal + Serial.println("Gagal mengirim pesan WhatsApp ke " + phoneNumber + ": " + String(httpResponseCode)); + Serial.println("Error: " + http.errorToString(httpResponseCode)); + } + + http.end(); // Tutup permintaan HTTP +} + + +// === SETUP === +void setup() { + Serial.begin(115200); + Serial2.begin(9600, SERIAL_8N1, RXD2, TXD2); // komunikasi ke ESP1 + + servoJalur1.setPeriodHertz(50); + servoJalur1.attach(SERVO_PIN); + + Wire.begin(22, 21); + lcd.init(); + lcd.backlight(); + + pinMode(TRIG1, OUTPUT); + pinMode(ECHO1, INPUT); + pinMode(TRIG2, OUTPUT); + pinMode(ECHO2, INPUT); + pinMode(PIEZO_PIN1, INPUT); + pinMode(BUZZER_PIN, OUTPUT); + pinMode(LED_AMAN, OUTPUT); + pinMode(LED_BAHAYA, OUTPUT); + + digitalWrite(LED_AMAN, HIGH); + digitalWrite(LED_BAHAYA, LOW); + + lcd.setCursor(0, 0); + lcd.print("Menghubungkan..."); + Serial.println("Menghubungkan ke WiFi..."); + + // WiFiManager untuk konfigurasi URL + WiFiManager wm; + WiFiManagerParameter custom_service_url("service_url", "Service URL", serviceURL.c_str(), 40); // Ukuran maksimal 40 karakter + wm.addParameter(&custom_service_url); // Menambahkan parameter + + wm.setTimeout(60); + if (!wm.autoConnect("PALANG 2-Setup")) { + Serial.println("Gagal konek WiFi. Mode offline."); + lcd.clear(); lcd.setCursor(0, 0); lcd.print("Mode Offline"); + isOfflineMode = true; // Set mode offline + delay(2000); + } else { + Serial.println("WiFi Terhubung!"); + lcd.clear(); lcd.setCursor(0, 0); lcd.print("WiFi Terhubung"); + delay(2000); + + // Ambil URL yang dimasukkan pengguna + serviceURL = custom_service_url.getValue(); // Ambil URL yang dikonfigurasi + Serial.println("Service URL: " + serviceURL); // Debug URL yang diambil + + // Konfigurasi Firebase jika diperlukan + config.api_key = API_KEY; + config.database_url = DATABASE_URL; + config.signer.tokens.legacy_token = LEGACY_TOKEN; + Firebase.begin(&config, &auth); + Firebase.reconnectWiFi(true); + updateThresholdFromFirebase(); // Update threshold from Firebase + } + + lcd.clear(); lcd.setCursor(0, 0); lcd.print("PALANG PINTAR"); + Serial.println("Sistem Siap: PALANG PINTAR"); +} +// Fungsi utama yang berjalan di loop +void loop() { + // Baca sensor Ultrasonik 1 untuk mendeteksi kereta + digitalWrite(TRIG1, LOW); + delayMicroseconds(2); + digitalWrite(TRIG1, HIGH); + delayMicroseconds(10); + digitalWrite(TRIG1, LOW); + long duration1 = pulseIn(ECHO1, HIGH); + float distance1 = duration1 * 0.034 / 2; // Menghitung jarak + + // Baca sensor Ultrasonik 2 + digitalWrite(TRIG2, LOW); + delayMicroseconds(2); + digitalWrite(TRIG2, HIGH); + delayMicroseconds(10); + digitalWrite(TRIG2, LOW); + long duration2 = pulseIn(ECHO2, HIGH); + float distance2 = duration2 * 0.034 / 2; // Menghitung jarak + + // Baca sensor Piezo + int piezo1Val = analogRead(PIEZO_PIN1); + + // Menampilkan status sensor ke Serial Monitor setiap interval tertentu + if (millis() - lastSerialPrint >= interval) { + lastSerialPrint = millis(); + Serial.println("===== STATUS SENSOR ====="); + Serial.print("Ultrasonik 1: "); Serial.println(distance1); + Serial.print("Ultrasonik 2: "); Serial.println(distance2); + Serial.print("Piezo1: "); Serial.println(piezo1Val); + + // Jika nilai piezo 0, tampilkan status palang terbuka atau tidak ada kereta + if (piezo1Val == 0) { + Serial.println("[INFO] Palang terbuka - Tidak ada kereta"); + } + } + + // === Ambil Status Kereta secara Berkala dari Firebase === + fetchStatusKereta(); // Fungsi yang memeriksa status_kereta setiap interval dan mengontrol palang + + // === Deteksi Kereta Masuk (Jalur 2) === + if (distance1 < tresholdUltrasonik && piezo1Val > tresholdPiezo && !keretaTerdeteksi) { + // Periksa status_kereta di Firebase + if (Firebase.RTDB.getBool(&fbdo, "/status_kereta")) { + bool statusKereta = fbdo.boolData(); // status_kereta (perintah umum) + + if (!statusKereta) { // Hanya tutup palang jika status_kereta adalah false + digitalWrite(LED_BAHAYA, HIGH); + digitalWrite(LED_AMAN, LOW); + digitalWrite(BUZZER_PIN, HIGH); // Bunyi buzzer + servoJalur1.write(90); // Tutup palang Jalur 2 + + lcd.clear(); lcd.setCursor(0, 0); lcd.print("KERETA DATANG"); + sendWAAlert("Peringatan: Kereta datang di Jalur 2!"); // Kirim pesan WA + Serial.println("[INFO] Kereta terdeteksi di Jalur 2 - Palang DITUTUP"); + + // Update status_kereta di Firebase + Firebase.RTDB.setBool(&fbdo, "/status_kereta", true); // Update status kereta menjadi true + + // Kirim perintah ke ESP Jalur 1 untuk tutup palang jika dalam mode offline + if (isOfflineMode) { + Serial2.write('0'); // Perintah tutup ke ESP Jalur 1 + } + + // Update status_kereta_jalur2 di Firebase + Firebase.RTDB.setBool(&fbdo, "/status_kereta_jalur2", true); // Update status Jalur 2 + + keretaTerdeteksi = true; + keretaLewat = false; + } + } + } + + // === Cek Kereta Lewat (Jalur 2) === + if (keretaTerdeteksi && distance2 < tresholdUltrasonik) { + keretaLewat = true; + } + + if (keretaTerdeteksi && keretaLewat && distance2 > tresholdUltrasonik) { + // Update status_kereta_jalur2 menjadi false setelah kereta lewat + Firebase.RTDB.setBool(&fbdo, "/status_kereta_jalur2", false); // Status Jalur 2 menjadi false + + // Kirim pesan "Kereta telah lewat di Jalur 2!" hanya sekali + if (!keretaLewatMessageSent) { + sendWAAlert("Peringatan: Kereta telah lewat di Jalur 2!"); // Kirim pesan WA + keretaLewatMessageSent = true; // Set flag to prevent sending multiple messages + Serial.println("[INFO] Kereta sudah lewat - Pesan telah dikirim."); + } + + // Periksa status_kereta_jalur1 + if (Firebase.RTDB.getBool(&fbdo, "/status_kereta_jalur1")) { + bool statusKeretaJalur1 = fbdo.boolData(); // Cek status_kereta_jalur1 + + if (statusKeretaJalur1 && !waitingMessageSent) { // Jika status_kereta_jalur1 adalah true, kirim pesan menunggu + sendWAAlert("Peringatan: Menunggu kereta 1 lewat!"); // Kirim pesan WA + Serial.println("[INFO] Menunggu kereta 1 lewat..."); + waitingMessageSent = true; // Set flag to true to prevent multiple messages + } + + // Cek jika status_kereta adalah false dan status_kereta_jalur1 juga false + if (!statusKeretaJalur1) { + bool statusKereta = fbdo.boolData(); // Cek status_kereta global + + if (!statusKereta) { // Hanya buka palang jika status_kereta adalah false + servoJalur1.write(0); // Buka palang Jalur 2 + digitalWrite(LED_BAHAYA, LOW); + digitalWrite(LED_AMAN, HIGH); + digitalWrite(BUZZER_PIN, LOW); // Matikan buzzer + + lcd.clear(); lcd.setCursor(0, 0); lcd.print("KERETA LEWAT"); + + Serial.println("[INFO] Kereta sudah lewat - Palang DIBUKA"); + + // Update status_kereta di Firebase + Firebase.RTDB.setBool(&fbdo, "/status_kereta", false); // Update status kereta menjadi false + + // Kirim perintah ke ESP Jalur 1 untuk membuka palang jika dalam mode offline + if (isOfflineMode) { + Serial2.write('1'); // Perintah buka ke ESP Jalur 1 + } + + keretaTerdeteksi = false; + keretaLewat = false; + } + } + } + } + + // === Terima sinyal dari ESP Jalur 1 === (Mode Offline) + if (isOfflineMode && Serial2.available() > 0) { + char incomingByte = Serial2.read(); + if (incomingByte == '0') { // Perintah tutup dari ESP Jalur 1 + servoJalur1.write(90); // Tutup palang + digitalWrite(LED_BAHAYA, HIGH); + digitalWrite(LED_AMAN, LOW); + digitalWrite(BUZZER_PIN, HIGH); // Bunyi buzzer + Serial.println("[ESP1] Sinyal diterima - Palang DITUTUP oleh ESP1"); + + lcd.clear(); lcd.setCursor(0, 0); lcd.print("ESP2: PALANG TUTUP"); + } + if (incomingByte == '1') { // Perintah buka dari ESP Jalur 1 + servoJalur1.write(0); // Buka palang + digitalWrite(LED_BAHAYA, LOW); + digitalWrite(LED_AMAN, HIGH); + digitalWrite(BUZZER_PIN, LOW); + Serial.println("[ESP1] Sinyal diterima - Palang DIBUKA oleh ESP1"); + + lcd.clear(); lcd.setCursor(0, 0); lcd.print("ESP2: PALANG BUKA"); + } + } + + delay(500); // jeda agar loop tidak terlalu cepat +} \ No newline at end of file