360 lines
13 KiB
C++
360 lines
13 KiB
C++
#include <WiFi.h>
|
|
#include <WiFiManager.h>
|
|
#include <Firebase_ESP_Client.h>
|
|
#include <ESP32Servo.h>
|
|
#include <LiquidCrystal_I2C.h>
|
|
#include <Wire.h>
|
|
#include <HTTPClient.h>
|
|
|
|
// === 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
|
|
} |