add new fitur register cards and change configuration

This commit is contained in:
Vckynando12 2025-05-02 06:53:08 +07:00
parent 837112d41e
commit 8e48853055
10 changed files with 27996 additions and 199 deletions

View File

@ -0,0 +1,351 @@
#include <Wire.h>
#include <ESP8266WiFi.h>
#include <FirebaseESP8266.h>
#include <DHT.h>
// Konfigurasi WiFi
#define WIFI_SSID "smartcab"
#define WIFI_PASSWORD "123123123"
// Konfigurasi Firebase
#define FIREBASE_HOST "smartcab-8bb42-default-rtdb.firebaseio.com"
#define FIREBASE_AUTH "kiiQoFa6Ckp7bL2oRLbaTSGQth9z0PgN64Ybv8dw"
FirebaseData firebaseData;
FirebaseConfig firebaseConfig;
FirebaseAuth firebaseAuth;
// MPU6050 I2C address
#define MPU6050_ADDR 0x68
const int relayMPUPin = D6; // Relay untuk MPU6050
const int relayKipasPin = D5; // Relay untuk kipas
const int dhtPin = D3; // DHT11 sensor
#define DHTTYPE DHT11
DHT dht(dhtPin, DHTTYPE);
float threshold = 0.05;
float baseAccelX, baseAccelY, baseAccelZ;
bool relayActive = false;
bool mpuEnabled = true;
unsigned long relayStartTime = 0;
unsigned long lastUpdateTime = 0;
unsigned long lastI2CCheckTime = 0;
const unsigned long restartInterval = 6 * 60 * 60 * 1000; // Restart otomatis 6 jam
unsigned long firebaseRetryCount = 0; // Menghitung percobaan koneksi ke Firebase
// Tambahkan variabel global untuk timestamp
unsigned long lastHeartbeatTime = 0;
const unsigned long heartbeatInterval = 60000; // 60 detik = 1 menit
unsigned long lastFirebaseRetryTime = 0;
const unsigned long firebaseRetryInterval = 5000; // 5 detik antara percobaan
// Fungsi untuk membaca data dari MPU6050
void readMPU6050(int16_t* ax, int16_t* ay, int16_t* az) {
Wire.beginTransmission(MPU6050_ADDR);
Wire.write(0x3B); // starting with register 0x3B (ACCEL_XOUT_H)
Wire.endTransmission(false);
Wire.requestFrom(MPU6050_ADDR, 6, true); // request a total of 6 registers
*ax = Wire.read() << 8 | Wire.read(); // 0x3B (ACCEL_XOUT_H) & 0x3C (ACCEL_XOUT_L)
*ay = Wire.read() << 8 | Wire.read(); // 0x3D (ACCEL_YOUT_H) & 0x3E (ACCEL_YOUT_L)
*az = Wire.read() << 8 | Wire.read(); // 0x3F (ACCEL_ZOUT_H) & 0x40 (ACCEL_ZOUT_L)
}
// Fungsi untuk menginisialisasi MPU6050
bool initMPU6050() {
Wire.beginTransmission(MPU6050_ADDR);
Wire.write(0x6B); // PWR_MGMT_1 register
Wire.write(0); // set to zero (wakes up the MPU-6050)
return Wire.endTransmission(true) == 0;
}
void setup() {
Serial.begin(115200);
Wire.begin(D2, D1); // SDA = D2, SCL = D1
// Tambahkan pinMode untuk relay
pinMode(relayMPUPin, OUTPUT);
pinMode(relayKipasPin, OUTPUT);
// Matikan semua relay saat startup
digitalWrite(relayMPUPin, HIGH); // Relay MPU OFF
digitalWrite(relayKipasPin, HIGH); // Relay Kipas OFF
// Koneksi ke WiFi dulu
WiFi.begin(WIFI_SSID, WIFI_PASSWORD);
Serial.print("Menghubungkan ke WiFi");
while (WiFi.status() != WL_CONNECTED) {
Serial.print(".");
delay(1000);
}
Serial.println("\nTerhubung ke WiFi");
// Konfigurasi Firebase
firebaseConfig.host = FIREBASE_HOST;
firebaseConfig.signer.tokens.legacy_token = FIREBASE_AUTH;
Firebase.begin(&firebaseConfig, &firebaseAuth);
Firebase.reconnectWiFi(true);
bool firebaseConnected = false;
for (int i = 0; i < 3; i++) { // Coba 3 kali
if (Firebase.setString(firebaseData, "/logs/systemESP", "Device online")) {
firebaseConnected = true;
Serial.println("Firebase terhubung");
break;
} else {
Serial.println("Gagal menghubungkan ke Firebase, mencoba lagi...");
delay(1000);
}
}
if (!firebaseConnected) {
Serial.println("Tidak dapat terhubung ke Firebase. Sistem keamanan dimatikan.");
mpuEnabled = false; // Matikan keamanan jika Firebase tidak terhubung
}
// Inisialisasi sensor
if (!initMPU6050()) {
Serial.println("MPU6050 tidak terhubung!");
if (firebaseConnected) {
Firebase.setString(firebaseData, "/logs/mpu/status", "disconnected");
}
mpuEnabled = false;
} else {
if (firebaseConnected) {
Firebase.setString(firebaseData, "/logs/mpu/status", "connected");
}
calibrateSensor();
}
dht.begin();
// Cek DHT11
float testReading = dht.readTemperature();
if (isnan(testReading)) {
Serial.println("DHT11 tidak terhubung atau error!");
if (firebaseConnected) {
Firebase.setString(firebaseData, "/logs/dht/status", "disconnected");
}
} else {
if (firebaseConnected) {
Firebase.setString(firebaseData, "/logs/dht/status", "connected");
}
}
// Konfigurasi NTP
configTime(7 * 3600, 0, "pool.ntp.org");
Serial.println("Waiting for time sync");
while (time(nullptr) < 1000000000) {
Serial.print(".");
delay(100);
}
Serial.println("\nTime synchronized");
}
void loop() {
// Cek koneksi WiFi
if (WiFi.status() != WL_CONNECTED) {
Serial.println("WiFi terputus, mencoba menyambung kembali...");
WiFi.begin(WIFI_SSID, WIFI_PASSWORD);
delay(5000); // Tunggu 5 detik
// Jika masih tidak terhubung, matikan keamanan
if (WiFi.status() != WL_CONNECTED) {
mpuEnabled = false;
digitalWrite(relayMPUPin, HIGH); // Pastikan relay OFF
}
return; // Keluar dari loop dan coba lagi
}
// Update lastActive dengan unix timestamp
if (millis() - lastHeartbeatTime >= heartbeatInterval) {
lastHeartbeatTime = millis();
unsigned long epochTime = time(nullptr);
bool heartbeatSent = false;
if (Firebase.setInt(firebaseData, "/device/lastActive", epochTime)) {
Serial.println("Heartbeat sent: " + String(epochTime));
// Tambahkan update status Device online saat heartbeat berhasil
Firebase.setString(firebaseData, "/logs/systemESP", "Device online");
Serial.println("Device status updated: Online");
heartbeatSent = true;
firebaseRetryCount = 0; // Reset counter jika berhasil
} else {
Serial.println("Failed to send heartbeat: " + firebaseData.errorReason());
// Jika gagal mengirim heartbeat, set status offline
firebaseRetryCount++;
if (firebaseRetryCount >= 3) { // Setelah 3 kali gagal
Serial.println("Firebase tidak dapat diakses setelah beberapa percobaan, mematikan sistem keamanan");
mpuEnabled = false; // Matikan keamanan
digitalWrite(relayMPUPin, HIGH); // Pastikan relay OFF
Firebase.setString(firebaseData, "/security/status", "off"); // Coba set status off
Firebase.setString(firebaseData, "/logs/systemESP", "Device offline");
}
}
}
// Cek jika tidak ada perubahan dalam 70 detik
if (millis() - lastHeartbeatTime > 70000) {
Firebase.setString(firebaseData, "/logs/systemESP", "Device offline");
}
// Tambahkan pengecekan status restart di awal loop
if (Firebase.getString(firebaseData, "/control/restartESP")) {
String restartStatus = firebaseData.stringData();
if (restartStatus == "true") {
Serial.println("Perintah restart diterima dari Firebase");
// Reset status restart di Firebase ke false
Firebase.setString(firebaseData, "/control/restartESP", "false");
// Kirim log sebelum restart
Firebase.setString(firebaseData, "/logs/systemESP", "Device restarting by command...");
delay(1000); // Tunggu sebentar agar data terkirim
ESP.restart();
}
} else {
Serial.println("Gagal membaca status restart: " + firebaseData.errorReason());
// Jika gagal membaca dari Firebase, tambahkan counter
firebaseRetryCount++;
if (firebaseRetryCount >= 3) { // Setelah 3 kali gagal
Serial.println("Tidak dapat mengakses Firebase, mematikan sistem keamanan");
mpuEnabled = false; // Matikan keamanan
digitalWrite(relayMPUPin, HIGH); // Pastikan relay OFF
// Masih coba set status meski kemungkinan gagal
Firebase.setString(firebaseData, "/security/status", "off");
}
}
// Cek auto-restart berdasarkan interval
if (millis() > restartInterval) {
Serial.println("Auto-restart setelah " + String(restartInterval/3600000) + " jam");
Firebase.setString(firebaseData, "/logs/systemESP", "Device auto-restarting...");
delay(1000);
ESP.restart();
}
// **Cek status MPU6050 setiap 30 detik**
if (millis() - lastI2CCheckTime >= 30000) {
lastI2CCheckTime = millis();
if (!initMPU6050()) {
Serial.println("MPU6050 tidak merespons! Reset I2C...");
Firebase.setString(firebaseData, "/logs/mpu/status", "error");
Wire.begin(D2, D1);
if (initMPU6050()) {
Firebase.setString(firebaseData, "/logs/mpu/status", "connected");
}
}
}
// **Baca status MPU dari Firebase**
bool readSuccess = false;
if (Firebase.getString(firebaseData, "/security/status")) {
String mpuStatus = firebaseData.stringData();
Serial.print("Status MPU dari Firebase: ");
Serial.println(mpuStatus);
mpuEnabled = (mpuStatus == "on");
readSuccess = true;
firebaseRetryCount = 0; // Reset counter jika berhasil
} else {
Serial.println("Gagal membaca data dari Firebase: " + firebaseData.errorReason());
Firebase.setString(firebaseData, "/logs/error", firebaseData.errorReason());
// Jika gagal memperoleh data dari Firebase, tambahkan counter
firebaseRetryCount++;
if (firebaseRetryCount >= 3) { // Setelah 3 kali gagal
// Cek jika waktu cukup berlalu untuk percobaan berikutnya
if (millis() - lastFirebaseRetryTime >= firebaseRetryInterval) {
lastFirebaseRetryTime = millis();
Serial.println("Tidak dapat membaca status dari Firebase, mematikan sistem keamanan");
mpuEnabled = false; // Matikan keamanan
digitalWrite(relayMPUPin, HIGH); // Pastikan relay OFF
// Masih coba set status meski kemungkinan gagal
Firebase.setString(firebaseData, "/security/status", "off");
}
}
}
// **Proses MPU6050**
if (mpuEnabled) {
if (millis() - lastUpdateTime >= 10) {
lastUpdateTime = millis();
int16_t ax, ay, az;
readMPU6050(&ax, &ay, &az);
float accelX = ax / 16384.0;
float accelY = ay / 16384.0;
float accelZ = az / 16384.0;
float deltaAccel = sqrt(pow(accelX - baseAccelX, 2) + pow(accelY - baseAccelY, 2) + pow(accelZ - baseAccelZ, 2));
if (deltaAccel > threshold && !relayActive) {
Serial.println("Getaran terdeteksi! Relay ON.");
digitalWrite(relayMPUPin, LOW);
relayActive = true;
relayStartTime = millis();
Firebase.setString(firebaseData, "/security/motion", "detected");
}
if (relayActive && millis() - relayStartTime >= 8000) {
Serial.println("Relay mati, kalibrasi ulang.");
digitalWrite(relayMPUPin, HIGH);
relayActive = false;
calibrateSensor();
Firebase.setString(firebaseData, "/security/motion", "clear");
}
}
} else {
digitalWrite(relayMPUPin, HIGH); // Pastikan relay OFF saat disabled
relayActive = false;
Firebase.setString(firebaseData, "/security/motion", "disabled");
}
// **Baca suhu dan kelembaban dari DHT11**
float suhu = dht.readTemperature();
float humidity = dht.readHumidity();
if (isnan(suhu) || isnan(humidity)) {
Serial.println("Gagal membaca data dari DHT11!");
Firebase.setString(firebaseData, "/logs/dht/status", "error");
} else {
Firebase.setString(firebaseData, "/logs/dht/status", "connected");
Serial.print("Suhu: ");
Serial.print(suhu);
Serial.println(" °C");
Serial.print("Kelembaban: ");
Serial.print(humidity);
Serial.println(" %");
Firebase.setFloat(firebaseData, "/dht11/temperature", suhu);
Firebase.setFloat(firebaseData, "/dht11/humidity", humidity);
}
// **Kendalikan relay kipas**
if (suhu > 40) {
Serial.println("Suhu tinggi! Kipas ON.");
digitalWrite(relayKipasPin, LOW);
Firebase.setString(firebaseData, "/security/fan", "ON");
} else {
digitalWrite(relayKipasPin, HIGH);
Firebase.setString(firebaseData, "/security/fan", "OFF");
}
yield();
}
void calibrateSensor() {
int16_t ax, ay, az;
readMPU6050(&ax, &ay, &az);
baseAccelX = ax / 16384.0;
baseAccelY = ay / 16384.0;
baseAccelZ = az / 16384.0;
Serial.println("Kalibrasi selesai!");
}

View File

@ -36,6 +36,10 @@ const unsigned long restartInterval = 6 * 60 * 60 * 1000; // Restart otomatis 6
unsigned long lastHeartbeatTime = 0;
const unsigned long heartbeatInterval = 60000; // 60 detik = 1 menit
// Tambahkan variabel untuk pengecekan koneksi WiFi
unsigned long lastWiFiCheckTime = 0;
const unsigned long wifiCheckInterval = 10000; // 10 detik
void setup() {
Serial.begin(115200);
Wire.begin(D2, D1); // SDA = D2, SCL = D1
@ -105,29 +109,67 @@ void setup() {
}
void loop() {
// Periksa koneksi WiFi dan Firebase
if (millis() - lastWiFiCheckTime >= wifiCheckInterval) {
lastWiFiCheckTime = millis();
// Cek koneksi WiFi
if (WiFi.status() != WL_CONNECTED) {
Serial.println("WiFi terputus! Security dinonaktifkan.");
mpuEnabled = false;
digitalWrite(relayMPUPin, HIGH); // Matikan relay MPU
relayActive = false;
// Coba hubungkan kembali
WiFi.begin(WIFI_SSID, WIFI_PASSWORD);
}
}
// Update lastActive dengan unix timestamp
if (millis() - lastHeartbeatTime >= heartbeatInterval) {
lastHeartbeatTime = millis();
unsigned long epochTime = time(nullptr);
if (Firebase.setInt(firebaseData, "/device/lastActive", epochTime)) {
Serial.println("Heartbeat sent: " + String(epochTime));
// Tambahkan update status Device online saat heartbeat berhasil
Firebase.setString(firebaseData, "/logs/systemESP", "Device online");
Serial.println("Device status updated: Online");
// Cek apakah terhubung ke WiFi
if (WiFi.status() == WL_CONNECTED) {
unsigned long epochTime = time(nullptr);
if (Firebase.setInt(firebaseData, "/device/lastActive", epochTime)) {
Serial.println("Heartbeat sent: " + String(epochTime));
// Tambahkan update status Device online saat heartbeat berhasil
Firebase.setString(firebaseData, "/logs/systemESP", "Device online");
Serial.println("Device status updated: Online");
} else {
Serial.println("Failed to send heartbeat");
// Jika gagal mengirim heartbeat, set status offline
Firebase.setString(firebaseData, "/logs/systemESP", "Device offline");
// Matikan security karena Firebase tidak dapat diakses
mpuEnabled = false;
digitalWrite(relayMPUPin, HIGH);
relayActive = false;
Serial.println("Firebase tidak dapat diakses! Security dinonaktifkan.");
}
} else {
Serial.println("Failed to send heartbeat");
// Jika gagal mengirim heartbeat, set status offline
Firebase.setString(firebaseData, "/logs/systemESP", "Device offline");
Serial.println("WiFi terputus! Tidak dapat mengirim heartbeat.");
// Pastikan security mati ketika WiFi terputus
mpuEnabled = false;
digitalWrite(relayMPUPin, HIGH);
relayActive = false;
}
}
// Cek jika tidak ada perubahan dalam 70 detik
if (millis() - lastHeartbeatTime > 70000) {
Firebase.setString(firebaseData, "/logs/systemESP", "Device offline");
// WiFi atau Firebase bermasalah, set security off
mpuEnabled = false;
digitalWrite(relayMPUPin, HIGH);
relayActive = false;
if (WiFi.status() == WL_CONNECTED) {
Firebase.setString(firebaseData, "/logs/systemESP", "Device offline");
}
}
// Tambahkan pengecekan status restart di awal loop
if (Firebase.getString(firebaseData, "/control/restartESP")) {
if (WiFi.status() == WL_CONNECTED && Firebase.getString(firebaseData, "/control/restartESP")) {
String restartStatus = firebaseData.stringData();
if (restartStatus == "true") {
Serial.println("Perintah restart diterima dari Firebase");
@ -143,8 +185,10 @@ void loop() {
// Cek auto-restart berdasarkan interval
if (millis() > restartInterval) {
Serial.println("Auto-restart setelah " + String(restartInterval/3600000) + " jam");
Firebase.setString(firebaseData, "/logs/systemESP", "Device auto-restarting...");
delay(1000);
if (WiFi.status() == WL_CONNECTED) {
Firebase.setString(firebaseData, "/logs/systemESP", "Device auto-restarting...");
delay(1000);
}
ESP.restart();
}
@ -153,26 +197,42 @@ void loop() {
lastI2CCheckTime = millis();
if (!mpu.testConnection()) {
Serial.println("MPU6050 tidak merespons! Reset I2C...");
Firebase.setString(firebaseData, "/logs/mpu/status", "error");
if (WiFi.status() == WL_CONNECTED) {
Firebase.setString(firebaseData, "/logs/mpu/status", "error");
}
Wire.begin(D2, D1);
mpu.initialize();
if (mpu.testConnection()) {
Firebase.setString(firebaseData, "/logs/mpu/status", "connected");
if (WiFi.status() == WL_CONNECTED) {
Firebase.setString(firebaseData, "/logs/mpu/status", "connected");
}
}
}
}
// **Baca status MPU dari Firebase**
if (Firebase.getString(firebaseData, "/security/status")) {
String mpuStatus = firebaseData.stringData();
Serial.print("Status MPU dari Firebase: ");
Serial.println(mpuStatus);
mpuEnabled = (mpuStatus == "on");
if (WiFi.status() == WL_CONNECTED) {
if (Firebase.getString(firebaseData, "/security/status")) {
String mpuStatus = firebaseData.stringData();
Serial.print("Status MPU dari Firebase: ");
Serial.println(mpuStatus);
mpuEnabled = (mpuStatus == "on");
} else {
Serial.println("Gagal membaca data dari Firebase!");
if (WiFi.status() == WL_CONNECTED) {
Firebase.setString(firebaseData, "/logs/error", firebaseData.errorReason());
}
// Matikan security karena Firebase tidak dapat diakses
mpuEnabled = false;
digitalWrite(relayMPUPin, HIGH);
relayActive = false;
Serial.println("Firebase tidak dapat diakses! Security dinonaktifkan.");
}
} else {
Serial.println("Gagal membaca data dari Firebase!");
Firebase.setString(firebaseData, "/logs/error", firebaseData.errorReason());
// Pastikan security mati ketika WiFi terputus
mpuEnabled = false;
}
// **Proses MPU6050**
@ -194,7 +254,9 @@ void loop() {
digitalWrite(relayMPUPin, LOW);
relayActive = true;
relayStartTime = millis();
Firebase.setString(firebaseData, "/security/motion", "detected");
if (WiFi.status() == WL_CONNECTED) {
Firebase.setString(firebaseData, "/security/motion", "detected");
}
}
if (relayActive && millis() - relayStartTime >= 8000) {
@ -202,13 +264,17 @@ void loop() {
digitalWrite(relayMPUPin, HIGH);
relayActive = false;
calibrateSensor();
Firebase.setString(firebaseData, "/security/motion", "clear");
if (WiFi.status() == WL_CONNECTED) {
Firebase.setString(firebaseData, "/security/motion", "clear");
}
}
}
} else {
digitalWrite(relayMPUPin, HIGH); // Pastikan relay OFF saat disabled
relayActive = false;
Firebase.setString(firebaseData, "/security/motion", "disabled");
if (WiFi.status() == WL_CONNECTED) {
Firebase.setString(firebaseData, "/security/motion", "disabled");
}
}
// **Baca suhu dan kelembaban dari DHT11**
@ -217,9 +283,16 @@ void loop() {
if (isnan(suhu) || isnan(humidity)) {
Serial.println("Gagal membaca data dari DHT11!");
Firebase.setString(firebaseData, "/logs/dht/status", "error");
if (WiFi.status() == WL_CONNECTED) {
Firebase.setString(firebaseData, "/logs/dht/status", "error");
}
} else {
Firebase.setString(firebaseData, "/logs/dht/status", "connected");
if (WiFi.status() == WL_CONNECTED) {
Firebase.setString(firebaseData, "/logs/dht/status", "connected");
Firebase.setFloat(firebaseData, "/dht11/temperature", suhu);
Firebase.setFloat(firebaseData, "/dht11/humidity", humidity);
}
Serial.print("Suhu: ");
Serial.print(suhu);
@ -227,19 +300,20 @@ void loop() {
Serial.print("Kelembaban: ");
Serial.print(humidity);
Serial.println(" %");
Firebase.setFloat(firebaseData, "/dht11/temperature", suhu);
Firebase.setFloat(firebaseData, "/dht11/humidity", humidity);
}
// **Kendalikan relay kipas**
if (suhu > 40) {
Serial.println("Suhu tinggi! Kipas ON.");
digitalWrite(relayKipasPin, LOW);
Firebase.setString(firebaseData, "/security/fan", "ON");
if (WiFi.status() == WL_CONNECTED) {
Firebase.setString(firebaseData, "/security/fan", "ON");
}
} else {
digitalWrite(relayKipasPin, HIGH);
Firebase.setString(firebaseData, "/security/fan", "OFF");
if (WiFi.status() == WL_CONNECTED) {
Firebase.setString(firebaseData, "/security/fan", "OFF");
}
}
yield();

View File

@ -3,7 +3,7 @@
#include <SPI.h>
#include <MFRC522.h>
#include <Servo.h>
#include <time.h>
#include <EEPROM.h>
// WiFi Credentials
#define WIFI_SSID "smartcab"
@ -26,87 +26,182 @@ MFRC522 mfrc522(SS_PIN, RST_PIN);
Servo myServo;
#define SERVO_PIN D2 // Pin servo
// **ID kartu yang diizinkan**
String kartuTerdaftar[] = {"53ed8434", "3b4f7d9e", "5b85e19d", "cb31e19f", "5b58179f", "1b22e9f"};
bool servoTerbuka = false; // Status awal servo (tertutup)
// EEPROM Configuration
#define EEPROM_SIZE 512
#define MAX_CARDS 20
#define CARD_SIZE 8 // 8 bytes per card (4 byte UID in hex = 8 chars)
#define EEPROM_SIGNATURE 0xAA // Signature byte to check if EEPROM is initialized
// Tambahkan variabel global untuk last active
// Status variables
bool servoTerbuka = false; // Status awal servo (tertutup)
unsigned long previousMillis = 0;
const long interval = 60000; // interval 1 menit dalam milliseconds
// Tambahkan di bagian global variables
unsigned long lastHeartbeatTime = 0;
const long heartbeatInterval = 60000; // 1 menit dalam milliseconds
unsigned long lastCommandCheck = 0;
const long commandCheckInterval = 1000; // 1 detik interval untuk cek command
unsigned long bootTime = 0; // Waktu saat device booting
unsigned long lastWiFiCheck = 0; // Waktu terakhir cek WiFi
const long wifiCheckInterval = 5000; // Interval cek koneksi WiFi (5 detik)
// Flag untuk status koneksi
bool isWiFiConnected = false;
bool isFirebaseConnected = false;
// Flag untuk indikasi ada command baru dari Firebase
bool pendingCommand = false;
String currentCommand = "";
String currentCardId = "";
void setup() {
Serial.begin(115200);
Serial.println("\n=== Smart Cabinet System Starting ===");
// Koneksi ke WiFi
// Catat waktu boot untuk referensi waktu lokal
bootTime = millis();
// Initialize EEPROM
EEPROM.begin(EEPROM_SIZE);
initializeEEPROM();
// Mulai koneksi WiFi (non-blocking)
WiFi.begin(WIFI_SSID, WIFI_PASSWORD);
Serial.print("Menghubungkan ke WiFi");
while (WiFi.status() != WL_CONNECTED) {
Serial.print(".");
delay(500);
}
Serial.println("\nWiFi Terhubung!");
// Konfigurasi Firebase
config.host = FIREBASE_HOST;
config.signer.tokens.legacy_token = FIREBASE_AUTH;
Firebase.begin(&config, &auth);
Firebase.reconnectWiFi(true);
// Konfigurasi NTP dan tunggu sinkronisasi
configTime(7 * 3600, 0, "pool.ntp.org"); // GMT+7
Serial.println("Menunggu sinkronisasi waktu");
while (time(nullptr) < 1000000000) {
Serial.print(".");
delay(100);
}
Serial.println("\nWaktu tersinkronisasi!");
// Setelah waktu tersinkronisasi, baru kirim timestamp pertama
unsigned long epochTime = time(nullptr);
if (Firebase.setInt(firebaseData, "/device/lastActiveWemos", epochTime)) {
Serial.println("Initial timestamp sent: " + String(epochTime));
} else {
Serial.println("Failed to send initial timestamp");
}
Serial.println("Memulai koneksi WiFi di background...");
// Inisialisasi RFID
SPI.begin();
mfrc522.PCD_Init();
// Cek koneksi RFID dengan cara yang lebih aman
// Cek koneksi RFID
byte v = mfrc522.PCD_ReadRegister(mfrc522.VersionReg);
if (v == 0x00 || v == 0xFF) {
Firebase.setString(firebaseData, "/logs/RFID/status", "Disconnected");
Serial.println("RFID Connection Failed!");
} else {
Firebase.setString(firebaseData, "/logs/RFID/status", "Connected");
Serial.println("RFID Connected!");
}
// Inisialisasi Servo
myServo.attach(SERVO_PIN, 500, 2500);
if (myServo.attached()) {
Firebase.setString(firebaseData, "/logs/servo/status", "Connected");
Serial.println("Servo Connected!");
} else {
Firebase.setString(firebaseData, "/logs/servo/status", "Disconnected");
Serial.println("Servo Connection Failed!");
}
myServo.write(0);
// Update path untuk restart control
Firebase.setBool(firebaseData, "/control/restartWemos", false);
// Update status device saat startup
Firebase.setString(firebaseData, "/logs/systemWemos", "Device Online");
Serial.println("Setup selesai. Sistem berjalan...");
}
void loop() {
// Update path untuk pengecekan restart
// Cek dan update status koneksi WiFi
if (millis() - lastWiFiCheck >= wifiCheckInterval) {
lastWiFiCheck = millis();
checkWiFiConnection();
}
// Cek apakah kartu RFID terdeteksi - selalu berjalan terlepas dari WiFi
checkRFID();
// Fungsi-fungsi yang membutuhkan koneksi WiFi
if (isWiFiConnected) {
// Cek perintah restart
checkRestartCommand();
// Cek perintah dari Firebase dengan interval untuk menghindari blocking
if (millis() - lastCommandCheck >= commandCheckInterval) {
lastCommandCheck = millis();
checkFirebaseCommands();
}
// Update status perangkat secara berkala
updateDeviceStatus();
// Heartbeat ke Firebase
sendHeartbeat();
}
delay(100); // Kurangi delay untuk respon lebih cepat
}
// Fungsi untuk mengecek dan memperbarui status koneksi WiFi
void checkWiFiConnection() {
if (WiFi.status() == WL_CONNECTED) {
if (!isWiFiConnected) {
isWiFiConnected = true;
Serial.println("WiFi terhubung!");
Serial.print("IP Address: ");
Serial.println(WiFi.localIP());
// Setup Firebase saat WiFi baru terhubung
setupFirebase();
}
} else {
if (isWiFiConnected) {
isWiFiConnected = false;
isFirebaseConnected = false;
Serial.println("WiFi terputus!");
}
}
}
// Setup Firebase setelah WiFi terhubung
void setupFirebase() {
config.host = FIREBASE_HOST;
config.signer.tokens.legacy_token = FIREBASE_AUTH;
Firebase.begin(&config, &auth);
Firebase.reconnectWiFi(true);
// Cek koneksi Firebase
if (Firebase.ready()) {
isFirebaseConnected = true;
Serial.println("Firebase terhubung!");
// Kirim timestamp lokal ke Firebase
unsigned long localTime = millis();
if (Firebase.setInt(firebaseData, "/device/lastActiveWemos", localTime)) {
Serial.println("Initial timestamp sent: " + String(localTime));
} else {
Serial.println("Failed to send initial timestamp");
}
// Update status device saat terhubung
Firebase.setString(firebaseData, "/logs/systemWemos", "Device Online");
// Setup struktur data kartu di Firebase
Firebase.setString(firebaseData, "/registered_cards/card", "");
Firebase.setString(firebaseData, "/registered_cards/delete", "");
Firebase.setString(firebaseData, "/registered_cards/status", "System connected");
// Update status peripheral
updatePeripheralStatus();
// Upload daftar kartu ke Firebase
uploadCardListToFirebase();
} else {
Serial.println("Gagal terhubung ke Firebase!");
}
}
// Fungsi untuk update status peripheral
void updatePeripheralStatus() {
// Cek status RFID
byte v = mfrc522.PCD_ReadRegister(mfrc522.VersionReg);
if (v == 0x00 || v == 0xFF) {
Firebase.setString(firebaseData, "/logs/RFID/status", "Disconnected");
} else {
Firebase.setString(firebaseData, "/logs/RFID/status", "Connected");
}
// Cek status Servo
if (myServo.attached()) {
Firebase.setString(firebaseData, "/logs/servo/status", "Connected");
} else {
Firebase.setString(firebaseData, "/logs/servo/status", "Disconnected");
}
}
// Fungsi untuk cek perintah restart
void checkRestartCommand() {
if (Firebase.getBool(firebaseData, "/control/restartWemos")) {
if (firebaseData.boolData() == true) {
// Update status sebelum restart
@ -116,8 +211,78 @@ void loop() {
ESP.restart();
}
}
}
// Cek status RFID secara periodik dengan cara yang lebih aman
// Fungsi untuk cek RFID
void checkRFID() {
// Cek apakah kartu RFID terdeteksi
if (mfrc522.PICC_IsNewCardPresent() && mfrc522.PICC_ReadCardSerial()) {
Serial.println("Kartu Terdeteksi!");
// Membaca UID kartu
String rfidUID = "";
for (byte i = 0; i < mfrc522.uid.size; i++) {
rfidUID += String(mfrc522.uid.uidByte[i], HEX);
}
Serial.print("UID: ");
Serial.println(rfidUID);
// Cek apakah kartu terdaftar dalam EEPROM
bool isCardRegistered = isCardInEEPROM(rfidUID);
if (isCardRegistered) {
Serial.println("Kartu Terdaftar!");
if (!servoTerbuka) {
Serial.println("Membuka kunci...");
myServo.write(180);
servoTerbuka = true;
// Update Firebase hanya jika terhubung
if (isWiFiConnected && isFirebaseConnected) {
Firebase.setString(firebaseData, "/smartcab/servo_status", "Terbuka");
}
} else {
Serial.println("Mengunci kunci...");
myServo.write(0);
servoTerbuka = false;
// Update Firebase hanya jika terhubung
if (isWiFiConnected && isFirebaseConnected) {
Firebase.setString(firebaseData, "/smartcab/servo_status", "Terkunci");
}
}
// Kirim status ke Firebase jika terhubung
if (isWiFiConnected && isFirebaseConnected) {
Firebase.setString(firebaseData, "/smartcab/last_access", "Terdaftar");
Firebase.setString(firebaseData, "/smartcab/status_device", rfidUID);
}
}
else {
Serial.println("Kartu Tidak Terdaftar! Mengunci servo...");
// Paksa servo terkunci jika kartu tidak dikenal
myServo.write(0);
servoTerbuka = false;
// Update Firebase hanya jika terhubung
if (isWiFiConnected && isFirebaseConnected) {
Firebase.setString(firebaseData, "/smartcab/servo_status", "Terkunci");
Firebase.setString(firebaseData, "/smartcab/last_access", "Tidak Terdaftar");
Firebase.setString(firebaseData, "/smartcab/status_device", rfidUID);
}
}
mfrc522.PICC_HaltA(); // Hentikan komunikasi RFID
mfrc522.PCD_StopCrypto1();
}
}
// Update status perangkat
void updateDeviceStatus() {
// Cek status RFID secara periodik
byte v = mfrc522.PCD_ReadRegister(mfrc522.VersionReg);
if (v == 0x00 || v == 0xFF) {
if (Firebase.getString(firebaseData, "/logs/RFID/status") &&
@ -138,13 +303,16 @@ void loop() {
Firebase.setString(firebaseData, "/logs/servo/status", "Disconnected");
Serial.println("Servo Disconnected!");
}
}
// Update lastActive dengan unix timestamp
// Kirim heartbeat ke Firebase
void sendHeartbeat() {
// Update lastActive dengan timestamp lokal
if (millis() - lastHeartbeatTime >= heartbeatInterval) {
lastHeartbeatTime = millis();
unsigned long epochTime = time(nullptr);
if (Firebase.setInt(firebaseData, "/device/lastActiveWemos", epochTime)) {
Serial.println("Heartbeat sent: " + String(epochTime));
unsigned long localTime = millis();
if (Firebase.setInt(firebaseData, "/device/lastActiveWemos", localTime)) {
Serial.println("Heartbeat sent: " + String(localTime));
// Tambahkan update status Device Online saat heartbeat berhasil
Firebase.setString(firebaseData, "/logs/systemWemos", "Device Online");
Serial.println("Device status updated: Online");
@ -159,62 +327,218 @@ void loop() {
if (millis() - lastHeartbeatTime > 70000) {
Firebase.setString(firebaseData, "/logs/systemWemos", "Device Offline");
}
}
// Cek apakah kartu RFID terdeteksi
if (mfrc522.PICC_IsNewCardPresent() && mfrc522.PICC_ReadCardSerial()) {
Serial.println("Kartu Terdeteksi!");
// Membaca UID kartu
String rfidUID = "";
for (byte i = 0; i < mfrc522.uid.size; i++) {
rfidUID += String(mfrc522.uid.uidByte[i], HEX);
// Fungsi untuk inisialisasi EEPROM jika belum diinisialisasi
void initializeEEPROM() {
// Cek signature byte
if (EEPROM.read(0) != EEPROM_SIGNATURE) {
Serial.println("Initializing EEPROM for first use...");
// Set signature
EEPROM.write(0, EEPROM_SIGNATURE);
// Clear card slots
for (int i = 1; i < EEPROM_SIZE; i++) {
EEPROM.write(i, 0xFF);
}
EEPROM.commit();
Serial.println("EEPROM initialized");
}
}
Serial.print("UID: ");
Serial.println(rfidUID);
// Fungsi untuk cek apakah kartu terdaftar di EEPROM
bool isCardInEEPROM(String cardId) {
int startAddr = 1; // Skip signature byte
for (int i = 0; i < MAX_CARDS; i++) {
int addr = startAddr + (i * CARD_SIZE);
if (EEPROM.read(addr) != 0xFF) { // Jika slot tidak kosong
char storedCard[CARD_SIZE+1];
for (int j = 0; j < CARD_SIZE; j++) {
storedCard[j] = char(EEPROM.read(addr + j));
}
storedCard[CARD_SIZE] = '\0';
if (cardId == String(storedCard)) {
return true;
}
}
}
return false;
}
// **Cek apakah kartu terdaftar**
bool isCardRegistered = false;
for (int i = 0; i < sizeof(kartuTerdaftar)/sizeof(kartuTerdaftar[0]); i++) {
if (rfidUID == kartuTerdaftar[i]) {
isCardRegistered = true;
// Fungsi untuk menambahkan kartu ke EEPROM
bool addCardToEEPROM(String cardId) {
if (cardId.length() != CARD_SIZE) {
Serial.println("Card ID harus " + String(CARD_SIZE) + " karakter!");
return false;
}
// Cek apakah kartu sudah terdaftar
if (isCardInEEPROM(cardId)) {
Serial.println("Kartu sudah terdaftar!");
return false;
}
int startAddr = 1; // Skip signature byte
bool cardAdded = false;
// Cari slot kosong
for (int i = 0; i < MAX_CARDS; i++) {
int addr = startAddr + (i * CARD_SIZE);
if (EEPROM.read(addr) == 0xFF) { // Slot kosong
// Simpan card ID
for (int j = 0; j < CARD_SIZE; j++) {
EEPROM.write(addr + j, cardId[j]);
}
EEPROM.commit();
cardAdded = true;
Serial.println("Kartu berhasil ditambahkan!");
break;
}
}
if (!cardAdded) {
Serial.println("EEPROM penuh! Hapus beberapa kartu terlebih dahulu.");
return false;
}
return true;
}
// Fungsi untuk menghapus kartu dari EEPROM
bool removeCardFromEEPROM(String cardId) {
int startAddr = 1; // Skip signature byte
bool cardRemoved = false;
for (int i = 0; i < MAX_CARDS; i++) {
int addr = startAddr + (i * CARD_SIZE);
if (EEPROM.read(addr) != 0xFF) { // Jika slot tidak kosong
char storedCard[CARD_SIZE+1];
for (int j = 0; j < CARD_SIZE; j++) {
storedCard[j] = char(EEPROM.read(addr + j));
}
storedCard[CARD_SIZE] = '\0';
if (cardId == String(storedCard)) {
// Hapus card dengan mengisi 0xFF
for (int j = 0; j < CARD_SIZE; j++) {
EEPROM.write(addr + j, 0xFF);
}
EEPROM.commit();
cardRemoved = true;
Serial.println("Kartu berhasil dihapus!");
break;
}
}
if (isCardRegistered) {
Serial.println("Kartu Terdaftar!");
if (!servoTerbuka) {
Serial.println("Membuka kunci...");
myServo.write(180);
servoTerbuka = true;
Firebase.setString(firebaseData, "/smartcab/servo_status", "Terbuka");
} else {
Serial.println("Mengunci kunci...");
myServo.write(0);
servoTerbuka = false;
Firebase.setString(firebaseData, "/smartcab/servo_status", "Terkunci");
}
// Kirim status ke Firebase
Firebase.setString(firebaseData, "/smartcab/last_access", "Terdaftar");
Firebase.setString(firebaseData, "/smartcab/status_device", rfidUID);
}
else {
Serial.println("Kartu Tidak Terdaftar! Mengunci servo...");
// Paksa servo terkunci jika kartu tidak dikenal
myServo.write(0);
servoTerbuka = false;
Firebase.setString(firebaseData, "/smartcab/servo_status", "Terkunci");
Firebase.setString(firebaseData, "/smartcab/last_access", "Tidak Terdaftar");
Firebase.setString(firebaseData, "/smartcab/status_device", rfidUID);
}
mfrc522.PICC_HaltA(); // Hentikan komunikasi RFID
mfrc522.PCD_StopCrypto1();
}
delay(500);
if (!cardRemoved) {
Serial.println("Kartu tidak ditemukan!");
return false;
}
return true;
}
// Fungsi untuk menghapus semua kartu dari EEPROM
void clearAllCards() {
int startAddr = 1; // Skip signature byte
for (int i = 0; i < MAX_CARDS * CARD_SIZE; i++) {
EEPROM.write(startAddr + i, 0xFF);
}
EEPROM.commit();
Serial.println("Semua kartu berhasil dihapus!");
}
// Fungsi untuk mendapatkan jumlah kartu yang tersimpan di EEPROM
int getCardCount() {
int startAddr = 1; // Skip signature byte
int count = 0;
for (int i = 0; i < MAX_CARDS; i++) {
int addr = startAddr + (i * CARD_SIZE);
if (EEPROM.read(addr) != 0xFF) { // Jika slot tidak kosong
count++;
}
}
return count;
}
// Fungsi untuk upload daftar kartu ke Firebase
void uploadCardListToFirebase() {
int startAddr = 1; // Skip signature byte
int count = 0;
// Clear existing list
Firebase.deleteNode(firebaseData, "/registered_cards/list");
// Add cards to list
for (int i = 0; i < MAX_CARDS; i++) {
int addr = startAddr + (i * CARD_SIZE);
if (EEPROM.read(addr) != 0xFF) { // Jika slot tidak kosong
char storedCard[CARD_SIZE+1];
for (int j = 0; j < CARD_SIZE; j++) {
storedCard[j] = char(EEPROM.read(addr + j));
}
storedCard[CARD_SIZE] = '\0';
String cardKey = "card" + String(count + 1);
Firebase.setString(firebaseData, "/registered_cards/list/" + cardKey, String(storedCard));
count++;
}
}
// Update total count
Firebase.setInt(firebaseData, "/registered_cards/total", count);
Serial.println("Daftar kartu berhasil diupload ke Firebase!");
}
// Fungsi untuk memeriksa perintah dari Firebase
void checkFirebaseCommands() {
// Check for new card to add
if (Firebase.getString(firebaseData, "/registered_cards/card")) {
String cardId = firebaseData.stringData();
if (cardId != "") {
Serial.println("Perintah baru: tambah kartu " + cardId);
if (addCardToEEPROM(cardId)) {
Firebase.setString(firebaseData, "/registered_cards/status", "Kartu berhasil ditambahkan");
uploadCardListToFirebase();
} else {
Firebase.setString(firebaseData, "/registered_cards/status", "Gagal menambahkan kartu");
}
// Reset command
Firebase.setString(firebaseData, "/registered_cards/card", "");
}
}
// Check for card to delete
if (Firebase.getString(firebaseData, "/registered_cards/delete")) {
String cardId = firebaseData.stringData();
if (cardId != "") {
Serial.println("Perintah baru: hapus kartu " + cardId);
if (cardId == "all") {
clearAllCards();
Firebase.setString(firebaseData, "/registered_cards/status", "Semua kartu berhasil dihapus");
uploadCardListToFirebase();
} else {
if (removeCardFromEEPROM(cardId)) {
Firebase.setString(firebaseData, "/registered_cards/status", "Kartu berhasil dihapus");
uploadCardListToFirebase();
} else {
Firebase.setString(firebaseData, "/registered_cards/status", "Kartu tidak ditemukan");
}
}
// Reset command
Firebase.setString(firebaseData, "/registered_cards/delete", "");
}
}
}

View File

@ -12,7 +12,8 @@ class Kernel extends ConsoleKernel
*/
protected function schedule(Schedule $schedule): void
{
$schedule->command('firebase:fetch')->everyMinute();
$schedule->command('firebase:fetch')
->everySecond();
}
/**
* Register the commands for the application.

View File

@ -0,0 +1,70 @@
<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
use Kreait\Firebase\Contract\Database;
class CardController extends Controller
{
protected $database;
public function __construct(Database $database)
{
$this->database = $database;
}
public function index()
{
$cards = $this->database->getReference('registered_cards/list')->getValue();
$total = $this->database->getReference('registered_cards/total')->getValue();
$lastCardId = $this->database->getReference('smartcab/status_device')->getValue();
$lastAccess = $this->database->getReference('smartcab/last_access')->getValue();
return view('view_card', compact('cards', 'total', 'lastCardId', 'lastAccess'));
}
public function addCard(Request $request)
{
$request->validate([
'card_id' => 'required|string|min:7|max:8',
]);
$cardId = $request->input('card_id');
$this->database->getReference('registered_cards/card')->set($cardId);
// Wait for 1.5 seconds before resetting
usleep(1500000);
// Reset the card value to empty string after sending command
$this->database->getReference('registered_cards/card')->set('');
return redirect()->route('view.cards')->with('success', 'Card added successfully!');
}
public function deleteCard($cardId)
{
$this->database->getReference('registered_cards/delete')->set($cardId);
// Wait for 1.5 seconds before resetting
usleep(1500000);
// Reset the delete value to empty string after sending command
$this->database->getReference('registered_cards/delete')->set('');
return redirect()->route('view.cards')->with('success', 'Card deletion requested');
}
public function deleteAllCards()
{
$this->database->getReference('registered_cards/delete')->set('all');
// Wait for 1.5 seconds before resetting
usleep(1500000);
// Reset the delete value to empty string after sending command
$this->database->getReference('registered_cards/delete')->set('');
return redirect()->route('view.cards')->with('success', 'All cards deletion requested');
}
}

View File

@ -53,7 +53,6 @@
</style>
</head>
<body class="bg-gray-50">
<div class="container mx-auto px-4 py-8">
<div class="flex justify-between items-center mb-6">
<h2 class="text-2xl font-bold text-gray-800">Laporan Keamanan dan Monitoring</h2>
@ -214,37 +213,7 @@
</div>
</div>
<!-- Tambahkan ini di dalam form filter, setelah Time Range Picker -->
<div class="md:col-span-2">
<label for="categories" class="block mb-2 text-sm font-medium text-gray-700">Filter Kategori</label>
<select id="categories" data-te-select-init multiple
class="bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5">
<optgroup label="Keamanan">
<option value="motion">Motion</option>
<option value="status">Status Keamanan</option>
<option value="fan">Fan</option>
</optgroup>
<optgroup label="Perangkat">
<option value="servo-status">Servo Status</option>
<option value="last-access">Last Access</option>
<option value="device">Device Status</option>
</optgroup>
<optgroup label="Kontrol">
<option value="restart-esp">Restart ESP</option>
<option value="restart-wemos">Restart Wemos</option>
</optgroup>
<optgroup label="Sensor">
<option value="rfid">RFID</option>
<option value="dht">DHT</option>
<option value="mpu">MPU</option>
</optgroup>
<optgroup label="Log">
<option value="servo-log">Servo Log</option>
<option value="system-esp">System ESP</option>
<option value="system-wemos">System Wemos</option>
</optgroup>
</select>
</div>
<!-- Remove the categories filter section -->
<div class="md:col-span-2 flex justify-end space-x-2">
<button type="button" id="applyFilter" class="text-white bg-blue-700 hover:bg-blue-800 focus:ring-4 focus:ring-blue-300 font-medium rounded-lg text-sm px-5 py-2.5">

View File

@ -0,0 +1,463 @@
<html lang="id">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>RFID Card Management</title>
<script src="https://cdn.tailwindcss.com"></script>
<link href="https://cdnjs.cloudflare.com/ajax/libs/flowbite/2.2.0/flowbite.min.css" rel="stylesheet" />
<style>
.highlight {
animation: highlight-row 2s ease-in-out;
}
@keyframes highlight-row {
0% { background-color: #fff; }
50% { background-color: #d1ecf1; }
100% { background-color: #fff; }
}
</style>
</head>
<body class="bg-gray-50">
<div class="container mx-auto px-4 py-8">
<div class="flex justify-between items-center mb-6">
<h2 class="text-2xl font-bold text-gray-800">RFID Card Management</h2>
<a href="{{route('welcome')}}" class="inline-flex items-center px-4 py-2 text-sm font-medium text-white bg-blue-700 rounded-lg hover:bg-blue-800 focus:ring-4 focus:outline-none focus:ring-blue-300">
<svg class="w-4 h-4 mr-2" fill="none" stroke="currentColor" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M10 19l-7-7m0 0l7-7m-7 7h18"></path>
</svg>
Back to Dashboard
</a>
</div>
@if (session('success'))
<div class="p-4 mb-4 text-sm text-green-800 rounded-lg bg-green-50" role="alert">
{{ session('success') }}
</div>
@endif
<!-- Last Tapped Card Panel -->
<div class="p-4 bg-white border border-gray-200 rounded-lg shadow-sm mb-6">
<div class="mb-4 border-b border-gray-200 pb-3">
<h5 class="text-lg font-medium text-gray-900 flex items-center">
<svg class="w-6 h-6 mr-2 text-blue-600" fill="none" stroke="currentColor" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M3 10h18M7 15h1m4 0h1m-7 4h12a3 3 0 003-3V8a3 3 0 00-3-3H6a3 3 0 00-3 3v8a3 3 0 003 3z"></path>
</svg>
Last Tapped Card
</h5>
</div>
<div class="grid grid-cols-1 md:grid-cols-2 gap-4">
<div class="p-3 bg-gray-50 rounded-lg">
<div class="flex justify-between items-center">
<span class="text-sm font-medium text-gray-700">Card ID:</span>
<span id="last-card-id" class="font-semibold text-blue-600">{{ $lastCardId ?? 'N/A' }}</span>
</div>
</div>
<div class="p-3 bg-gray-50 rounded-lg">
<div class="flex justify-between items-center">
<span class="text-sm font-medium text-gray-700">Status:</span>
<span id="last-access" class="font-semibold {{ $lastAccess == 'Terdaftar' ? 'text-green-600' : 'text-red-600' }}">{{ $lastAccess ?? 'N/A' }}</span>
</div>
</div>
</div>
</div>
<div class="grid grid-cols-1 md:grid-cols-2 gap-6 mb-6">
<!-- Combined Add New Card and Registered Cards Panel -->
<div class="p-4 bg-white border border-gray-200 rounded-lg shadow-sm">
<div class="mb-4 border-b border-gray-200 pb-3">
<h5 class="text-lg font-medium text-gray-900 flex items-center">
<svg class="w-6 h-6 mr-2 text-blue-600" fill="none" stroke="currentColor" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M3 10h18M7 15h1m4 0h1m-7 4h12a3 3 0 003-3V8a3 3 0 00-3-3H6a3 3 0 00-3 3v8a3 3 0 003 3z"></path>
</svg>
Card Management
</h5>
</div>
<!-- Add New Card Form -->
<div class="mb-6">
<button type="button" id="startRegistration"
class="text-white bg-green-600 hover:bg-green-700 focus:ring-4 focus:outline-none focus:ring-green-300 font-medium rounded-lg text-sm px-4 py-2.5 inline-flex items-center whitespace-nowrap">
<svg class="w-4 h-4 mr-1" fill="none" stroke="currentColor" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 6v6m0 0v6m0-6h6m-6 0H6"></path>
</svg>
Add Card
</button>
</div>
<!-- Registered Cards Table -->
<div class="mt-6">
<h6 class="text-md font-medium text-gray-800 mb-3 flex items-center">
<svg class="w-5 h-5 mr-2 text-indigo-600" fill="none" stroke="currentColor" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2"></path>
</svg>
Registered Cards
</h6>
<div class="relative overflow-x-auto shadow-md sm:rounded-lg">
<table class="w-full text-sm text-left text-gray-500">
<thead class="text-xs text-gray-700 uppercase bg-gray-50">
<tr>
<th scope="col" class="px-6 py-3">No.</th>
<th scope="col" class="px-6 py-3">Card ID</th>
<th scope="col" class="px-6 py-3">Action</th>
</tr>
</thead>
<tbody>
@if(!empty($cards))
@foreach($cards as $key => $cardId)
<tr class="bg-white border-b hover:bg-gray-50 {{ $cardId == $lastCardId ? 'highlight' : '' }}">
<td class="px-6 py-4">{{ $loop->iteration }}</td>
<td class="px-6 py-4">{{ $cardId }}</td>
<td class="px-6 py-4">
<a href="{{ route('cards.delete', $cardId) }}"
onclick="return confirm('Are you sure you want to delete this card?')"
class="text-white bg-red-700 hover:bg-red-800 focus:ring-4 focus:outline-none focus:ring-red-300 font-medium rounded-lg text-xs px-3 py-1.5 inline-flex items-center">
<svg class="w-4 h-4 mr-1" fill="none" stroke="currentColor" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19 7l-.867 12.142A2 2 0 0116.138 21H7.862a2 2 0 01-1.995-1.858L5 7m5 4v6m4-6v6m1-10V4a1 1 0 00-1-1h-4a1 1 0 00-1 1v3M4 7h16"></path>
</svg>
Delete
</a>
</td>
</tr>
@endforeach
@else
<tr class="bg-white border-b">
<td colspan="3" class="px-6 py-4 text-center text-gray-500">No cards registered</td>
</tr>
@endif
</tbody>
</table>
</div>
</div>
</div>
<!-- System Status Panel -->
<div class="p-4 bg-white border border-gray-200 rounded-lg shadow-sm">
<div class="mb-4 border-b border-gray-200 pb-3">
<h5 class="text-lg font-medium text-gray-900 flex items-center">
<svg class="w-6 h-6 mr-2 text-purple-600" fill="none" stroke="currentColor" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9.75 17L9 20l-1 1h8l-1-1-.75-3M3 13h18M5 17h14a2 2 0 002-2V5a2 2 0 00-2-2H5a2 2 0 00-2 2v10a2 2 0 002 2z"></path>
</svg>
System Status
</h5>
</div>
<div class="space-y-3">
<div class="flex justify-between items-center p-3 bg-gray-50 rounded-lg">
<span class="text-sm font-medium text-gray-700 flex items-center">
<svg class="w-5 h-5 mr-2 text-blue-600" fill="none" stroke="currentColor" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M17 20h5v-2a3 3 0 00-5.356-1.857M17 20H7m10 0v-2c0-.656-.126-1.283-.356-1.857M7 20H2v-2a3 3 0 015.356-1.857M7 20v-2c0-.656.126-1.283.356-1.857m0 0a5.002 5.002 0 019.288 0M15 7a3 3 0 11-6 0 3 3 0 016 0zm6 3a2 2 0 11-4 0 2 2 0 014 0zM7 10a2 2 0 11-4 0 2 2 0 014 0z"></path>
</svg>
Total Cards:
</span>
<span class="font-semibold">{{ $total ?? 0 }}</span>
</div>
<div class="flex justify-between items-center p-3 bg-gray-50 rounded-lg">
<span class="text-sm font-medium text-gray-700 flex items-center">
<svg class="w-5 h-5 mr-2 text-green-600" fill="none" stroke="currentColor" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 12l2 2 4-4m6 2a9 9 0 11-18 0 9 9 0 0118 0z"></path>
</svg>
System Status:
</span>
<span id="system-status" class="font-semibold text-gray-800">Loading...</span>
</div>
<div class="flex justify-between items-center p-3 bg-gray-50 rounded-lg">
<span class="text-sm font-medium text-gray-700 flex items-center">
<svg class="w-5 h-5 mr-2 text-yellow-600" fill="none" stroke="currentColor" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M15 7a2 2 0 012 2m4 0a6 6 0 01-7.743 5.743L11 17H9v2H7v2H4a1 1 0 01-1-1v-2.586a1 1 0 01.293-.707l5.964-5.964A6 6 0 1121 9z"></path>
</svg>
RFID Status:
</span>
<span id="rfid-status" class="font-semibold text-gray-800">Loading...</span>
</div>
<div class="flex justify-between items-center p-3 bg-gray-50 rounded-lg">
<span class="text-sm font-medium text-gray-700 flex items-center">
<svg class="w-5 h-5 mr-2 text-red-600" fill="none" stroke="currentColor" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M10.325 4.317c.426-1.756 2.924-1.756 3.35 0a1.724 1.724 0 002.573 1.066c1.543-.94 3.31.826 2.37 2.37a1.724 1.724 0 001.065 2.572c1.756.426 1.756 2.924 0 3.35a1.724 1.724 0 00-1.066 2.573c.94 1.543-.826 3.31-2.37 2.37a1.724 1.724 0 00-2.572 1.065c-.426 1.756-2.924 1.756-3.35 0a1.724 1.724 0 00-2.573-1.066c-1.543.94-3.31-.826-2.37-2.37a1.724 1.724 0 00-1.065-2.572c-1.756-.426-1.756-2.924 0-3.35a1.724 1.724 0 001.066-2.573c-.94-1.543.826-3.31 2.37-2.37.996.608 2.296.07 2.572-1.065z"></path>
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M15 12a3 3 0 11-6 0 3 3 0 016 0z"></path>
</svg>
Servo Status:
</span>
<span id="servo-status" class="font-semibold text-gray-800">Loading...</span>
</div>
<div class="mt-5">
<button onclick="if(confirm('Are you sure you want to delete ALL cards?')) window.location.href='{{ route('cards.delete.all') }}'"
class="text-white bg-red-700 hover:bg-red-800 focus:ring-4 focus:outline-none focus:ring-red-300 font-medium rounded-lg text-sm px-5 py-2.5 inline-flex items-center">
<svg class="w-4 h-4 mr-2" fill="none" stroke="currentColor" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19 7l-.867 12.142A2 2 0 0116.138 21H7.862a2 2 0 01-1.995-1.858L5 7m5 4v6m4-6v6m1-10V4a1 1 0 00-1-1h-4a1 1 0 00-1 1v3M4 7h16"></path>
</svg>
Delete All Cards
</button>
</div>
</div>
</div>
</div>
</div>
<script src="https://www.gstatic.com/firebasejs/8.10.1/firebase-app.js"></script>
<script src="https://www.gstatic.com/firebasejs/8.10.1/firebase-database.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/flowbite/2.2.0/flowbite.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/sweetalert2@11"></script>
<script>
// Firebase configuration - make sure to use your project's actual config
const firebaseConfig = {
apiKey: "AIzaSyAw9f5XkTkMg-gPE8tnLYBhTibnFyLCHG0",
authDomain: "smartcab-8bb42.firebaseapp.com",
databaseURL: "https://smartcab-8bb42-default-rtdb.firebaseio.com",
projectId: "smartcab-8bb42",
storageBucket: "smartcab-8bb42.appspot.com",
messagingSenderId: "724750966822",
appId: "1:724750966822:web:6d8af1a18e8d8b5cbc0279"
};
// Initialize Firebase
firebase.initializeApp(firebaseConfig);
const database = firebase.database();
document.addEventListener('DOMContentLoaded', function() {
// Listen for real-time updates to device status
database.ref('logs').on('value', (snapshot) => {
const data = snapshot.val();
if (data) {
// System status update
if (data.systemWemos) {
const systemStatus = document.getElementById('system-status');
systemStatus.textContent = data.systemWemos;
if (data.systemWemos === 'Device Online') {
systemStatus.classList.add('text-green-600');
systemStatus.classList.remove('text-red-600', 'text-gray-800');
} else {
systemStatus.classList.add('text-red-600');
systemStatus.classList.remove('text-green-600', 'text-gray-800');
}
}
// RFID status update
if (data.RFID && data.RFID.status) {
const rfidStatus = document.getElementById('rfid-status');
rfidStatus.textContent = data.RFID.status;
if (data.RFID.status === 'Connected') {
rfidStatus.classList.add('text-green-600');
rfidStatus.classList.remove('text-red-600', 'text-gray-800');
} else {
rfidStatus.classList.add('text-red-600');
rfidStatus.classList.remove('text-green-600', 'text-gray-800');
}
}
// Servo status update
if (data.servo && data.servo.status) {
const servoStatus = document.getElementById('servo-status');
servoStatus.textContent = data.servo.status;
if (data.servo.status === 'Connected') {
servoStatus.classList.add('text-green-600');
servoStatus.classList.remove('text-blue-600', 'text-gray-800');
} else {
servoStatus.classList.add('text-red-600');
servoStatus.classList.remove('text-green-600', 'text-gray-800');
}
}
}
});
// Listen for real-time updates to card data
database.ref('smartcab').on('value', (snapshot) => {
const data = snapshot.val();
if (data) {
// Update last tapped card
if (data.status_device) {
const lastCardId = document.getElementById('last-card-id');
lastCardId.textContent = data.status_device;
// Highlight the corresponding row
highlightCardRow(data.status_device);
}
// Update access status
if (data.last_access) {
const lastAccess = document.getElementById('last-access');
lastAccess.textContent = data.last_access;
if (data.last_access === 'Terdaftar') {
lastAccess.classList.add('text-green-600');
lastAccess.classList.remove('text-red-600');
} else {
lastAccess.classList.add('text-red-600');
lastAccess.classList.remove('text-green-600');
}
}
}
});
// Listen for real-time updates to registered cards count
database.ref('registered_cards/total').on('value', (snapshot) => {
const totalCards = snapshot.val();
const totalElement = document.querySelector('.flex.justify-between.items-center.p-3.bg-gray-50.rounded-lg:first-child .font-semibold');
if (totalElement) {
totalElement.textContent = totalCards || 0;
}
});
// Listen for changes to the card list
database.ref('registered_cards/list').on('value', (snapshot) => {
const cards = snapshot.val();
// This would require refreshing the table, but complex to do without a page reload
// Instead, show a notification that new data is available
if (cards && Object.keys(cards).length > 0) {
const tableBody = document.querySelector('tbody');
const currentCardCount = tableBody.querySelectorAll('tr:not(.empty-row)').length;
if (Object.keys(cards).length !== currentCardCount) {
showUpdateNotification();
}
}
});
function showUpdateNotification() {
// Create notification if it doesn't exist
if (!document.getElementById('update-notification')) {
const notification = document.createElement('div');
notification.id = 'update-notification';
notification.className = 'fixed bottom-4 right-4 bg-blue-600 text-white px-4 py-2 rounded-lg shadow-lg flex items-center space-x-2';
notification.innerHTML = `
<svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 4v5h.582m15.356 2A8.001 8.001 0 004.582 9m0 0H9m11 11v-5h-.581m0 0a8.003 8.003 0 01-15.357-2m15.357 2H15"></path>
</svg>
<span>Card list updated!</span>
<button class="ml-2 text-white hover:text-gray-200" onclick="location.reload()">Refresh</button>
`;
document.body.appendChild(notification);
// Auto-remove after 10 seconds
setTimeout(() => {
notification.remove();
}, 10000);
}
}
// Function to highlight the card row if found in the table
function highlightCardRow(cardId) {
const rows = document.querySelectorAll('tbody tr');
rows.forEach(row => {
// Remove any existing highlights
row.classList.remove('highlight');
const cardCell = row.querySelector('td:nth-child(2)');
if (cardCell && cardCell.textContent.trim() === cardId) {
row.classList.add('highlight');
}
});
}
const startRegistrationBtn = document.getElementById('startRegistration');
let lastScannedCard = null;
let registrationInProgress = false;
// Fungsi untuk menampilkan popup "Tempelkan Kartu"
function showScanPrompt() {
Swal.fire({
title: 'Tempelkan Kartu',
html: '<div class="text-center">' +
'<div class="mb-4">' +
'<svg class="mx-auto animate-pulse w-16 h-16 text-blue-600" fill="none" stroke="currentColor" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">' +
'<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M3 10h18M7 15h1m4 0h1m-7 4h12a3 3 0 003-3V8a3 3 0 00-3-3H6a3 3 0 00-3 3v8a3 3 0 003 3z"></path>' +
'</svg>' +
'</div>' +
'<p class="text-gray-500">Silakan tempelkan kartu RFID pada reader</p>' +
'</div>',
showConfirmButton: false,
allowOutsideClick: false,
allowEscapeKey: false,
showCancelButton: true,
cancelButtonText: 'Batal',
cancelButtonColor: '#d33',
});
}
// Fungsi untuk konfirmasi pendaftaran kartu
function confirmCardRegistration(cardId) {
Swal.fire({
title: 'Konfirmasi Pendaftaran',
html: `<div class="text-center">` +
`<p class="mb-2">ID Kartu terdeteksi:</p>` +
`<p class="text-lg font-semibold text-blue-600">${cardId}</p>` +
`<p class="mt-4">Apakah Anda ingin mendaftarkan kartu ini?</p>` +
`</div>`,
icon: 'question',
showCancelButton: true,
confirmButtonColor: '#3085d6',
cancelButtonColor: '#d33',
confirmButtonText: 'Ya, Daftarkan',
cancelButtonText: 'Batal'
}).then((result) => {
if (result.isConfirmed) {
registerCard(cardId);
}
});
}
// Fungsi untuk mendaftarkan kartu
async function registerCard(cardId) {
try {
const response = await fetch('{{ route('cards.add') }}', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-CSRF-TOKEN': '{{ csrf_token() }}'
},
body: JSON.stringify({ card_id: cardId })
});
const data = await response.json();
if (response.ok) {
Swal.fire({
title: 'Berhasil!',
text: 'Kartu berhasil didaftarkan',
icon: 'success',
confirmButtonColor: '#3085d6'
}).then(() => {
// Reload halaman untuk memperbarui daftar kartu
window.location.reload();
});
} else {
throw new Error(data.message || 'Terjadi kesalahan saat mendaftarkan kartu');
}
} catch (error) {
Swal.fire({
title: 'Error!',
text: error.message,
icon: 'error',
confirmButtonColor: '#3085d6'
});
}
}
// Listener untuk tombol Add Card
startRegistrationBtn.addEventListener('click', function() {
showScanPrompt();
registrationInProgress = true;
});
// Listener untuk data Firebase
database.ref('smartcab').on('value', (snapshot) => {
const data = snapshot.val();
if (data && data.status_device && registrationInProgress) {
const cardId = data.status_device;
// Hanya tampilkan konfirmasi jika kartu berbeda dari sebelumnya
if (cardId !== lastScannedCard) {
lastScannedCard = cardId;
Swal.close(); // Tutup popup "Tempelkan Kartu"
confirmCardRegistration(cardId);
registrationInProgress = false;
}
}
});
});
</script>
</body>
</html>

View File

@ -29,15 +29,19 @@
{{-- <a href="{{route('ai.chat')}}" class="text-gray-500 hover:text-gray-700 dark:text-gray-400 dark:hover:text-white">
AI
</a> --}}
<!-- Notification Button Mobile -->
<button
@click="isNotificationsPanelOpen = true"
class="p-2 text-gray-500 hover:text-gray-700 dark:text-gray-400 dark:hover:text-white"
>
<!-- History Button Mobile (Changed from Notification) -->
<a href="{{route('reports')}}" class="p-2 text-gray-500 hover:text-gray-700 dark:text-gray-400 dark:hover:text-white">
<svg class="w-6 h-6" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M15 17h5l-1.405-1.405A2.032 2.032 0 0118 14.158V11a6.002 6.002 0 00-4-5.659V5a2 2 0 10-4 0v.341C7.67 6.165 6 8.388 6 11v3.159c0 .538-.214 1.055-.595 1.436L4 17h5m6 0v1a3 3 0 11-6 0v-1m6 0H9"></path>
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 8v4l3 3m6-3a9 9 0 11-18 0 9 9 0 0118 0z"></path>
</svg>
</button>
</a>
<a href="{{ route('view.cards') }}" class="p-2 text-gray-500 hover:text-gray-700 dark:text-gray-400 dark:hover:text-white">
<svg class="w-6 h-6" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
d="M4 6h16M4 10h16M4 14h6m2 0h6m-16 4h16a2 2 0 002-2V8a2 2 0 00-2-2H4a2 2 0 00-2 2v8a2 2 0 002 2z" />
</svg>
</a>
<!-- Mobile Menu Button -->
<button id="mobileMenuBtn" class="text-gray-500 hover:text-gray-700 dark:text-gray-400 dark:hover:text-white">
@ -55,15 +59,18 @@ class="p-2 text-gray-500 hover:text-gray-700 dark:text-gray-400 dark:hover:text-
</a> --}}
<!-- Notification Button -->
<button
@click="isNotificationsPanelOpen = true"
class="p-2 text-gray-500 hover:text-gray-700 dark:text-gray-400 dark:hover:text-white"
>
<!-- History Button (Changed from Notification) -->
<a href="{{route('reports')}}" class="p-2 text-gray-500 hover:text-gray-700 dark:text-gray-400 dark:hover:text-white">
<svg class="w-6 h-6" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M15 17h5l-1.405-1.405A2.032 2.032 0 0118 14.158V11a6.002 6.002 0 00-4-5.659V5a2 2 0 10-4 0v.341C7.67 6.165 6 8.388 6 11v3.159c0 .538-.214 1.055-.595 1.436L4 17h5m6 0v1a3 3 0 11-6 0v-1m6 0H9"></path>
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 8v4l3 3m6-3a9 9 0 11-18 0 9 9 0 0118 0z"></path>
</svg>
</button>
</a>
<a href="{{ route('view.cards') }}" class="p-2 text-gray-500 hover:text-gray-700 dark:text-gray-400 dark:hover:text-white">
<svg class="w-6 h-6" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
d="M4 6h16M4 10h16M4 14h6m2 0h6m-16 4h16a2 2 0 002-2V8a2 2 0 00-2-2H4a2 2 0 00-2 2v8a2 2 0 002 2z" />
</svg>
</a>
<!-- Dark Mode Toggle -->
<button id="darkModeToggle" class="p-2 text-gray-500 dark:text-gray-400">
@ -1340,22 +1347,23 @@ function forceRefreshDeviceStatus() {
// Fungsi untuk memeriksa status online/offline perangkat
function checkDeviceStatus() {
const currentTime = Math.floor(Date.now() / 1000); // Konversi ke detik
console.log('Checking device status based on data changes...');
// Tambahkan pengecekan lebih agresif untuk ESP
database.ref('device/lastActive').once('value', (snapshot) => {
const lastActiveESP = snapshot.val();
if (lastActiveESP) {
const timeDiff = currentTime - lastActiveESP;
// Ambil nilai data sebelumnya untuk ESP jika tersedia
const previousDataESP = localStorage.getItem('previousESPData');
const currentDataESP = JSON.stringify(lastActiveESP);
console.log('ESP8266 last active: ' + lastActiveESP);
console.log('Current time: ' + currentTime);
console.log('Time difference: ' + timeDiff + ' seconds');
console.log('ESP8266 previous data: ' + previousDataESP);
console.log('ESP8266 current data: ' + currentDataESP);
// Jika perbedaan waktu lebih dari 65 detik, set status offline
if (timeDiff > 60) {
console.log('ESP8266 terdeteksi offline - mengirim status ke Firebase...');
// Jika data tidak berubah sejak pengecekan terakhir, anggap offline
if (previousDataESP && previousDataESP === currentDataESP) {
console.log('ESP8266 terdeteksi offline - tidak ada perubahan data');
database.ref('logs/systemESP').set('Device offline')
.then(() => {
@ -1364,6 +1372,13 @@ function checkDeviceStatus() {
.catch(error => {
console.error('Error updating ESP status:', error);
});
} else {
// Data berubah, perangkat dianggap online
console.log('ESP8266 terdeteksi online - data berubah');
database.ref('logs/systemESP').set('Device online');
// Simpan data saat ini untuk pengecekan berikutnya
localStorage.setItem('previousESPData', currentDataESP);
}
} else {
// Jika lastActive tidak ada, set status offline
@ -1377,15 +1392,16 @@ function checkDeviceStatus() {
const lastActiveWemos = snapshot.val();
if (lastActiveWemos) {
const timeDiff = currentTime - lastActiveWemos;
// Ambil nilai data sebelumnya untuk Wemos jika tersedia
const previousData = localStorage.getItem('previousWemosData');
const currentData = JSON.stringify(lastActiveWemos);
console.log('Wemos D1 Mini last active: ' + lastActiveWemos);
console.log('Current time: ' + currentTime);
console.log('Time difference: ' + timeDiff + ' seconds');
console.log('Wemos D1 Mini previous data: ' + previousData);
console.log('Wemos D1 Mini current data: ' + currentData);
// Jika perbedaan waktu lebih dari 65 detik, set status offline
if (timeDiff > 60) {
console.log('Wemos D1 Mini terdeteksi offline - mengirim status ke Firebase...');
// Jika data tidak berubah sejak pengecekan terakhir, anggap offline
if (previousData && previousData === currentData) {
console.log('Wemos D1 Mini terdeteksi offline - tidak ada perubahan data');
database.ref('logs/systemWemos').set('Device Offline')
.then(() => {
@ -1394,6 +1410,13 @@ function checkDeviceStatus() {
.catch(error => {
console.error('Error updating Wemos status:', error);
});
} else {
// Data berubah, perangkat dianggap online
console.log('Wemos D1 Mini terdeteksi online - data berubah');
database.ref('logs/systemWemos').set('Device Online');
// Simpan data saat ini untuk pengecekan berikutnya
localStorage.setItem('previousWemosData', currentData);
}
} else {
// Jika lastActiveWemos tidak ada, set status offline

View File

@ -8,6 +8,7 @@
use App\Http\Controllers\ProfileController;
use App\Http\Controllers\ReportController;
use App\Http\Controllers\ReportExportController;
use App\Http\Controllers\CardController;
Route::get('/login', [FirebaseAuthController::class, 'showLogin'])
->name('login')
@ -38,4 +39,29 @@
Route::get('/profile', [ProfileController::class, 'index'])->name('profile');
Route::post('/profile/update', [ProfileController::class, 'update'])->name('profile.update');
Route::get('/reports', [ReportController::class, 'index'])->name('reports');
Route::get('/export-reports', [ExportReportController::class, 'exportPdf']);
Route::get('/export-reports', [ExportReportController::class, 'exportPdf']);
// RFID Card Management Routesview.cards
Route::get('/cards', [CardController::class, 'index'])->name('view.cards');
Route::post('/cards/add', [CardController::class, 'addCard'])->name('cards.add');
Route::get('/cards/delete/{cardId}', [CardController::class, 'deleteCard'])->name('cards.delete');
Route::get('/cards/delete-all', [CardController::class, 'deleteAllCards'])->name('cards.delete.all');
// Optional API route for status updates
Route::get('/api/device-status', function() {
$firebase = app('firebase.database');
return response()->json([
'system' => $firebase->getReference('logs/systemWemos')->getValue(),
'rfid' => $firebase->getReference('logs/RFID/status')->getValue(),
'servo' => $firebase->getReference('logs/servo/status')->getValue(),
]);
});
// API route for card-status
Route::get('/api/card-status', function() {
$firebase = app('firebase.database');
return response()->json([
'cardId' => $firebase->getReference('smartcab/status_device')->getValue(),
'access' => $firebase->getReference('smartcab/last_access')->getValue(),
]);
});

File diff suppressed because it is too large Load Diff