add new fitur register cards and change configuration
This commit is contained in:
parent
837112d41e
commit
8e48853055
|
@ -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!");
|
||||||
|
}
|
|
@ -36,6 +36,10 @@ const unsigned long restartInterval = 6 * 60 * 60 * 1000; // Restart otomatis 6
|
||||||
unsigned long lastHeartbeatTime = 0;
|
unsigned long lastHeartbeatTime = 0;
|
||||||
const unsigned long heartbeatInterval = 60000; // 60 detik = 1 menit
|
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() {
|
void setup() {
|
||||||
Serial.begin(115200);
|
Serial.begin(115200);
|
||||||
Wire.begin(D2, D1); // SDA = D2, SCL = D1
|
Wire.begin(D2, D1); // SDA = D2, SCL = D1
|
||||||
|
@ -105,29 +109,67 @@ void setup() {
|
||||||
}
|
}
|
||||||
|
|
||||||
void loop() {
|
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
|
// Update lastActive dengan unix timestamp
|
||||||
if (millis() - lastHeartbeatTime >= heartbeatInterval) {
|
if (millis() - lastHeartbeatTime >= heartbeatInterval) {
|
||||||
lastHeartbeatTime = millis();
|
lastHeartbeatTime = millis();
|
||||||
unsigned long epochTime = time(nullptr);
|
|
||||||
if (Firebase.setInt(firebaseData, "/device/lastActive", epochTime)) {
|
// Cek apakah terhubung ke WiFi
|
||||||
Serial.println("Heartbeat sent: " + String(epochTime));
|
if (WiFi.status() == WL_CONNECTED) {
|
||||||
// Tambahkan update status Device online saat heartbeat berhasil
|
unsigned long epochTime = time(nullptr);
|
||||||
Firebase.setString(firebaseData, "/logs/systemESP", "Device online");
|
if (Firebase.setInt(firebaseData, "/device/lastActive", epochTime)) {
|
||||||
Serial.println("Device status updated: Online");
|
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 {
|
} else {
|
||||||
Serial.println("Failed to send heartbeat");
|
Serial.println("WiFi terputus! Tidak dapat mengirim heartbeat.");
|
||||||
// Jika gagal mengirim heartbeat, set status offline
|
// Pastikan security mati ketika WiFi terputus
|
||||||
Firebase.setString(firebaseData, "/logs/systemESP", "Device offline");
|
mpuEnabled = false;
|
||||||
|
digitalWrite(relayMPUPin, HIGH);
|
||||||
|
relayActive = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Cek jika tidak ada perubahan dalam 70 detik
|
// Cek jika tidak ada perubahan dalam 70 detik
|
||||||
if (millis() - lastHeartbeatTime > 70000) {
|
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
|
// 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();
|
String restartStatus = firebaseData.stringData();
|
||||||
if (restartStatus == "true") {
|
if (restartStatus == "true") {
|
||||||
Serial.println("Perintah restart diterima dari Firebase");
|
Serial.println("Perintah restart diterima dari Firebase");
|
||||||
|
@ -143,8 +185,10 @@ void loop() {
|
||||||
// Cek auto-restart berdasarkan interval
|
// Cek auto-restart berdasarkan interval
|
||||||
if (millis() > restartInterval) {
|
if (millis() > restartInterval) {
|
||||||
Serial.println("Auto-restart setelah " + String(restartInterval/3600000) + " jam");
|
Serial.println("Auto-restart setelah " + String(restartInterval/3600000) + " jam");
|
||||||
Firebase.setString(firebaseData, "/logs/systemESP", "Device auto-restarting...");
|
if (WiFi.status() == WL_CONNECTED) {
|
||||||
delay(1000);
|
Firebase.setString(firebaseData, "/logs/systemESP", "Device auto-restarting...");
|
||||||
|
delay(1000);
|
||||||
|
}
|
||||||
ESP.restart();
|
ESP.restart();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -153,26 +197,42 @@ void loop() {
|
||||||
lastI2CCheckTime = millis();
|
lastI2CCheckTime = millis();
|
||||||
if (!mpu.testConnection()) {
|
if (!mpu.testConnection()) {
|
||||||
Serial.println("MPU6050 tidak merespons! Reset I2C...");
|
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);
|
Wire.begin(D2, D1);
|
||||||
mpu.initialize();
|
mpu.initialize();
|
||||||
|
|
||||||
if (mpu.testConnection()) {
|
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**
|
// **Baca status MPU dari Firebase**
|
||||||
if (Firebase.getString(firebaseData, "/security/status")) {
|
if (WiFi.status() == WL_CONNECTED) {
|
||||||
String mpuStatus = firebaseData.stringData();
|
if (Firebase.getString(firebaseData, "/security/status")) {
|
||||||
Serial.print("Status MPU dari Firebase: ");
|
String mpuStatus = firebaseData.stringData();
|
||||||
Serial.println(mpuStatus);
|
Serial.print("Status MPU dari Firebase: ");
|
||||||
mpuEnabled = (mpuStatus == "on");
|
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 {
|
} else {
|
||||||
Serial.println("Gagal membaca data dari Firebase!");
|
// Pastikan security mati ketika WiFi terputus
|
||||||
Firebase.setString(firebaseData, "/logs/error", firebaseData.errorReason());
|
mpuEnabled = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// **Proses MPU6050**
|
// **Proses MPU6050**
|
||||||
|
@ -194,7 +254,9 @@ void loop() {
|
||||||
digitalWrite(relayMPUPin, LOW);
|
digitalWrite(relayMPUPin, LOW);
|
||||||
relayActive = true;
|
relayActive = true;
|
||||||
relayStartTime = millis();
|
relayStartTime = millis();
|
||||||
Firebase.setString(firebaseData, "/security/motion", "detected");
|
if (WiFi.status() == WL_CONNECTED) {
|
||||||
|
Firebase.setString(firebaseData, "/security/motion", "detected");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (relayActive && millis() - relayStartTime >= 8000) {
|
if (relayActive && millis() - relayStartTime >= 8000) {
|
||||||
|
@ -202,13 +264,17 @@ void loop() {
|
||||||
digitalWrite(relayMPUPin, HIGH);
|
digitalWrite(relayMPUPin, HIGH);
|
||||||
relayActive = false;
|
relayActive = false;
|
||||||
calibrateSensor();
|
calibrateSensor();
|
||||||
Firebase.setString(firebaseData, "/security/motion", "clear");
|
if (WiFi.status() == WL_CONNECTED) {
|
||||||
|
Firebase.setString(firebaseData, "/security/motion", "clear");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
digitalWrite(relayMPUPin, HIGH); // Pastikan relay OFF saat disabled
|
digitalWrite(relayMPUPin, HIGH); // Pastikan relay OFF saat disabled
|
||||||
relayActive = false;
|
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**
|
// **Baca suhu dan kelembaban dari DHT11**
|
||||||
|
@ -217,9 +283,16 @@ void loop() {
|
||||||
|
|
||||||
if (isnan(suhu) || isnan(humidity)) {
|
if (isnan(suhu) || isnan(humidity)) {
|
||||||
Serial.println("Gagal membaca data dari DHT11!");
|
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 {
|
} 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: ");
|
||||||
Serial.print(suhu);
|
Serial.print(suhu);
|
||||||
|
@ -227,19 +300,20 @@ void loop() {
|
||||||
Serial.print("Kelembaban: ");
|
Serial.print("Kelembaban: ");
|
||||||
Serial.print(humidity);
|
Serial.print(humidity);
|
||||||
Serial.println(" %");
|
Serial.println(" %");
|
||||||
|
|
||||||
Firebase.setFloat(firebaseData, "/dht11/temperature", suhu);
|
|
||||||
Firebase.setFloat(firebaseData, "/dht11/humidity", humidity);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// **Kendalikan relay kipas**
|
// **Kendalikan relay kipas**
|
||||||
if (suhu > 40) {
|
if (suhu > 40) {
|
||||||
Serial.println("Suhu tinggi! Kipas ON.");
|
Serial.println("Suhu tinggi! Kipas ON.");
|
||||||
digitalWrite(relayKipasPin, LOW);
|
digitalWrite(relayKipasPin, LOW);
|
||||||
Firebase.setString(firebaseData, "/security/fan", "ON");
|
if (WiFi.status() == WL_CONNECTED) {
|
||||||
|
Firebase.setString(firebaseData, "/security/fan", "ON");
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
digitalWrite(relayKipasPin, HIGH);
|
digitalWrite(relayKipasPin, HIGH);
|
||||||
Firebase.setString(firebaseData, "/security/fan", "OFF");
|
if (WiFi.status() == WL_CONNECTED) {
|
||||||
|
Firebase.setString(firebaseData, "/security/fan", "OFF");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
yield();
|
yield();
|
||||||
|
|
|
@ -3,7 +3,7 @@
|
||||||
#include <SPI.h>
|
#include <SPI.h>
|
||||||
#include <MFRC522.h>
|
#include <MFRC522.h>
|
||||||
#include <Servo.h>
|
#include <Servo.h>
|
||||||
#include <time.h>
|
#include <EEPROM.h>
|
||||||
|
|
||||||
// WiFi Credentials
|
// WiFi Credentials
|
||||||
#define WIFI_SSID "smartcab"
|
#define WIFI_SSID "smartcab"
|
||||||
|
@ -26,87 +26,182 @@ MFRC522 mfrc522(SS_PIN, RST_PIN);
|
||||||
Servo myServo;
|
Servo myServo;
|
||||||
#define SERVO_PIN D2 // Pin servo
|
#define SERVO_PIN D2 // Pin servo
|
||||||
|
|
||||||
// **ID kartu yang diizinkan**
|
// EEPROM Configuration
|
||||||
String kartuTerdaftar[] = {"53ed8434", "3b4f7d9e", "5b85e19d", "cb31e19f", "5b58179f", "1b22e9f"};
|
#define EEPROM_SIZE 512
|
||||||
bool servoTerbuka = false; // Status awal servo (tertutup)
|
#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;
|
unsigned long previousMillis = 0;
|
||||||
const long interval = 60000; // interval 1 menit dalam milliseconds
|
const long interval = 60000; // interval 1 menit dalam milliseconds
|
||||||
|
|
||||||
// Tambahkan di bagian global variables
|
|
||||||
unsigned long lastHeartbeatTime = 0;
|
unsigned long lastHeartbeatTime = 0;
|
||||||
const long heartbeatInterval = 60000; // 1 menit dalam milliseconds
|
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() {
|
void setup() {
|
||||||
Serial.begin(115200);
|
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);
|
WiFi.begin(WIFI_SSID, WIFI_PASSWORD);
|
||||||
Serial.print("Menghubungkan ke WiFi");
|
Serial.println("Memulai koneksi WiFi di background...");
|
||||||
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");
|
|
||||||
}
|
|
||||||
|
|
||||||
// Inisialisasi RFID
|
// Inisialisasi RFID
|
||||||
SPI.begin();
|
SPI.begin();
|
||||||
mfrc522.PCD_Init();
|
mfrc522.PCD_Init();
|
||||||
|
|
||||||
// Cek koneksi RFID dengan cara yang lebih aman
|
// Cek koneksi RFID
|
||||||
byte v = mfrc522.PCD_ReadRegister(mfrc522.VersionReg);
|
byte v = mfrc522.PCD_ReadRegister(mfrc522.VersionReg);
|
||||||
if (v == 0x00 || v == 0xFF) {
|
if (v == 0x00 || v == 0xFF) {
|
||||||
Firebase.setString(firebaseData, "/logs/RFID/status", "Disconnected");
|
|
||||||
Serial.println("RFID Connection Failed!");
|
Serial.println("RFID Connection Failed!");
|
||||||
} else {
|
} else {
|
||||||
Firebase.setString(firebaseData, "/logs/RFID/status", "Connected");
|
|
||||||
Serial.println("RFID Connected!");
|
Serial.println("RFID Connected!");
|
||||||
}
|
}
|
||||||
|
|
||||||
// Inisialisasi Servo
|
// Inisialisasi Servo
|
||||||
myServo.attach(SERVO_PIN, 500, 2500);
|
myServo.attach(SERVO_PIN, 500, 2500);
|
||||||
if (myServo.attached()) {
|
if (myServo.attached()) {
|
||||||
Firebase.setString(firebaseData, "/logs/servo/status", "Connected");
|
|
||||||
Serial.println("Servo Connected!");
|
Serial.println("Servo Connected!");
|
||||||
} else {
|
} else {
|
||||||
Firebase.setString(firebaseData, "/logs/servo/status", "Disconnected");
|
|
||||||
Serial.println("Servo Connection Failed!");
|
Serial.println("Servo Connection Failed!");
|
||||||
}
|
}
|
||||||
myServo.write(0);
|
myServo.write(0);
|
||||||
|
|
||||||
// Update path untuk restart control
|
|
||||||
Firebase.setBool(firebaseData, "/control/restartWemos", false);
|
|
||||||
|
|
||||||
// Update status device saat startup
|
Serial.println("Setup selesai. Sistem berjalan...");
|
||||||
Firebase.setString(firebaseData, "/logs/systemWemos", "Device Online");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void loop() {
|
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 (Firebase.getBool(firebaseData, "/control/restartWemos")) {
|
||||||
if (firebaseData.boolData() == true) {
|
if (firebaseData.boolData() == true) {
|
||||||
// Update status sebelum restart
|
// Update status sebelum restart
|
||||||
|
@ -116,8 +211,78 @@ void loop() {
|
||||||
ESP.restart();
|
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);
|
byte v = mfrc522.PCD_ReadRegister(mfrc522.VersionReg);
|
||||||
if (v == 0x00 || v == 0xFF) {
|
if (v == 0x00 || v == 0xFF) {
|
||||||
if (Firebase.getString(firebaseData, "/logs/RFID/status") &&
|
if (Firebase.getString(firebaseData, "/logs/RFID/status") &&
|
||||||
|
@ -138,13 +303,16 @@ void loop() {
|
||||||
Firebase.setString(firebaseData, "/logs/servo/status", "Disconnected");
|
Firebase.setString(firebaseData, "/logs/servo/status", "Disconnected");
|
||||||
Serial.println("Servo 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) {
|
if (millis() - lastHeartbeatTime >= heartbeatInterval) {
|
||||||
lastHeartbeatTime = millis();
|
lastHeartbeatTime = millis();
|
||||||
unsigned long epochTime = time(nullptr);
|
unsigned long localTime = millis();
|
||||||
if (Firebase.setInt(firebaseData, "/device/lastActiveWemos", epochTime)) {
|
if (Firebase.setInt(firebaseData, "/device/lastActiveWemos", localTime)) {
|
||||||
Serial.println("Heartbeat sent: " + String(epochTime));
|
Serial.println("Heartbeat sent: " + String(localTime));
|
||||||
// Tambahkan update status Device Online saat heartbeat berhasil
|
// Tambahkan update status Device Online saat heartbeat berhasil
|
||||||
Firebase.setString(firebaseData, "/logs/systemWemos", "Device Online");
|
Firebase.setString(firebaseData, "/logs/systemWemos", "Device Online");
|
||||||
Serial.println("Device status updated: Online");
|
Serial.println("Device status updated: Online");
|
||||||
|
@ -159,62 +327,218 @@ void loop() {
|
||||||
if (millis() - lastHeartbeatTime > 70000) {
|
if (millis() - lastHeartbeatTime > 70000) {
|
||||||
Firebase.setString(firebaseData, "/logs/systemWemos", "Device Offline");
|
Firebase.setString(firebaseData, "/logs/systemWemos", "Device Offline");
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Cek apakah kartu RFID terdeteksi
|
// Fungsi untuk inisialisasi EEPROM jika belum diinisialisasi
|
||||||
if (mfrc522.PICC_IsNewCardPresent() && mfrc522.PICC_ReadCardSerial()) {
|
void initializeEEPROM() {
|
||||||
Serial.println("Kartu Terdeteksi!");
|
// Cek signature byte
|
||||||
|
if (EEPROM.read(0) != EEPROM_SIGNATURE) {
|
||||||
// Membaca UID kartu
|
Serial.println("Initializing EEPROM for first use...");
|
||||||
String rfidUID = "";
|
// Set signature
|
||||||
for (byte i = 0; i < mfrc522.uid.size; i++) {
|
EEPROM.write(0, EEPROM_SIGNATURE);
|
||||||
rfidUID += String(mfrc522.uid.uidByte[i], HEX);
|
// Clear card slots
|
||||||
|
for (int i = 1; i < EEPROM_SIZE; i++) {
|
||||||
|
EEPROM.write(i, 0xFF);
|
||||||
}
|
}
|
||||||
|
EEPROM.commit();
|
||||||
|
Serial.println("EEPROM initialized");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Serial.print("UID: ");
|
// Fungsi untuk cek apakah kartu terdaftar di EEPROM
|
||||||
Serial.println(rfidUID);
|
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**
|
// Fungsi untuk menambahkan kartu ke EEPROM
|
||||||
bool isCardRegistered = false;
|
bool addCardToEEPROM(String cardId) {
|
||||||
for (int i = 0; i < sizeof(kartuTerdaftar)/sizeof(kartuTerdaftar[0]); i++) {
|
if (cardId.length() != CARD_SIZE) {
|
||||||
if (rfidUID == kartuTerdaftar[i]) {
|
Serial.println("Card ID harus " + String(CARD_SIZE) + " karakter!");
|
||||||
isCardRegistered = true;
|
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;
|
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", "");
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -12,7 +12,8 @@ class Kernel extends ConsoleKernel
|
||||||
*/
|
*/
|
||||||
protected function schedule(Schedule $schedule): void
|
protected function schedule(Schedule $schedule): void
|
||||||
{
|
{
|
||||||
$schedule->command('firebase:fetch')->everyMinute();
|
$schedule->command('firebase:fetch')
|
||||||
|
->everySecond();
|
||||||
}
|
}
|
||||||
/**
|
/**
|
||||||
* Register the commands for the application.
|
* Register the commands for the application.
|
||||||
|
|
|
@ -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');
|
||||||
|
}
|
||||||
|
}
|
|
@ -53,7 +53,6 @@
|
||||||
</style>
|
</style>
|
||||||
</head>
|
</head>
|
||||||
<body class="bg-gray-50">
|
<body class="bg-gray-50">
|
||||||
|
|
||||||
<div class="container mx-auto px-4 py-8">
|
<div class="container mx-auto px-4 py-8">
|
||||||
<div class="flex justify-between items-center mb-6">
|
<div class="flex justify-between items-center mb-6">
|
||||||
<h2 class="text-2xl font-bold text-gray-800">Laporan Keamanan dan Monitoring</h2>
|
<h2 class="text-2xl font-bold text-gray-800">Laporan Keamanan dan Monitoring</h2>
|
||||||
|
@ -214,37 +213,7 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Tambahkan ini di dalam form filter, setelah Time Range Picker -->
|
<!-- Remove the categories filter section -->
|
||||||
<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>
|
|
||||||
|
|
||||||
<div class="md:col-span-2 flex justify-end space-x-2">
|
<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">
|
<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">
|
||||||
|
|
|
@ -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>
|
|
@ -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">
|
{{-- <a href="{{route('ai.chat')}}" class="text-gray-500 hover:text-gray-700 dark:text-gray-400 dark:hover:text-white">
|
||||||
AI
|
AI
|
||||||
</a> --}}
|
</a> --}}
|
||||||
<!-- Notification Button Mobile -->
|
<!-- History Button Mobile (Changed from Notification) -->
|
||||||
<button
|
<a href="{{route('reports')}}" class="p-2 text-gray-500 hover:text-gray-700 dark:text-gray-400 dark:hover:text-white">
|
||||||
@click="isNotificationsPanelOpen = true"
|
|
||||||
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">
|
<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>
|
</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 -->
|
<!-- Mobile Menu Button -->
|
||||||
<button id="mobileMenuBtn" class="text-gray-500 hover:text-gray-700 dark:text-gray-400 dark:hover:text-white">
|
<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> --}}
|
</a> --}}
|
||||||
|
|
||||||
|
|
||||||
<!-- Notification Button -->
|
<!-- History Button (Changed from Notification) -->
|
||||||
<button
|
<a href="{{route('reports')}}" class="p-2 text-gray-500 hover:text-gray-700 dark:text-gray-400 dark:hover:text-white">
|
||||||
@click="isNotificationsPanelOpen = true"
|
|
||||||
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">
|
<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>
|
</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 -->
|
<!-- Dark Mode Toggle -->
|
||||||
<button id="darkModeToggle" class="p-2 text-gray-500 dark:text-gray-400">
|
<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
|
// Fungsi untuk memeriksa status online/offline perangkat
|
||||||
function checkDeviceStatus() {
|
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
|
// Tambahkan pengecekan lebih agresif untuk ESP
|
||||||
database.ref('device/lastActive').once('value', (snapshot) => {
|
database.ref('device/lastActive').once('value', (snapshot) => {
|
||||||
const lastActiveESP = snapshot.val();
|
const lastActiveESP = snapshot.val();
|
||||||
|
|
||||||
if (lastActiveESP) {
|
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('ESP8266 previous data: ' + previousDataESP);
|
||||||
console.log('Current time: ' + currentTime);
|
console.log('ESP8266 current data: ' + currentDataESP);
|
||||||
console.log('Time difference: ' + timeDiff + ' seconds');
|
|
||||||
|
|
||||||
// Jika perbedaan waktu lebih dari 65 detik, set status offline
|
// Jika data tidak berubah sejak pengecekan terakhir, anggap offline
|
||||||
if (timeDiff > 60) {
|
if (previousDataESP && previousDataESP === currentDataESP) {
|
||||||
console.log('ESP8266 terdeteksi offline - mengirim status ke Firebase...');
|
console.log('ESP8266 terdeteksi offline - tidak ada perubahan data');
|
||||||
|
|
||||||
database.ref('logs/systemESP').set('Device offline')
|
database.ref('logs/systemESP').set('Device offline')
|
||||||
.then(() => {
|
.then(() => {
|
||||||
|
@ -1364,6 +1372,13 @@ function checkDeviceStatus() {
|
||||||
.catch(error => {
|
.catch(error => {
|
||||||
console.error('Error updating ESP status:', 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 {
|
} else {
|
||||||
// Jika lastActive tidak ada, set status offline
|
// Jika lastActive tidak ada, set status offline
|
||||||
|
@ -1377,15 +1392,16 @@ function checkDeviceStatus() {
|
||||||
const lastActiveWemos = snapshot.val();
|
const lastActiveWemos = snapshot.val();
|
||||||
|
|
||||||
if (lastActiveWemos) {
|
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('Wemos D1 Mini previous data: ' + previousData);
|
||||||
console.log('Current time: ' + currentTime);
|
console.log('Wemos D1 Mini current data: ' + currentData);
|
||||||
console.log('Time difference: ' + timeDiff + ' seconds');
|
|
||||||
|
|
||||||
// Jika perbedaan waktu lebih dari 65 detik, set status offline
|
// Jika data tidak berubah sejak pengecekan terakhir, anggap offline
|
||||||
if (timeDiff > 60) {
|
if (previousData && previousData === currentData) {
|
||||||
console.log('Wemos D1 Mini terdeteksi offline - mengirim status ke Firebase...');
|
console.log('Wemos D1 Mini terdeteksi offline - tidak ada perubahan data');
|
||||||
|
|
||||||
database.ref('logs/systemWemos').set('Device Offline')
|
database.ref('logs/systemWemos').set('Device Offline')
|
||||||
.then(() => {
|
.then(() => {
|
||||||
|
@ -1394,6 +1410,13 @@ function checkDeviceStatus() {
|
||||||
.catch(error => {
|
.catch(error => {
|
||||||
console.error('Error updating Wemos status:', 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 {
|
} else {
|
||||||
// Jika lastActiveWemos tidak ada, set status offline
|
// Jika lastActiveWemos tidak ada, set status offline
|
||||||
|
|
|
@ -8,6 +8,7 @@
|
||||||
use App\Http\Controllers\ProfileController;
|
use App\Http\Controllers\ProfileController;
|
||||||
use App\Http\Controllers\ReportController;
|
use App\Http\Controllers\ReportController;
|
||||||
use App\Http\Controllers\ReportExportController;
|
use App\Http\Controllers\ReportExportController;
|
||||||
|
use App\Http\Controllers\CardController;
|
||||||
|
|
||||||
Route::get('/login', [FirebaseAuthController::class, 'showLogin'])
|
Route::get('/login', [FirebaseAuthController::class, 'showLogin'])
|
||||||
->name('login')
|
->name('login')
|
||||||
|
@ -38,4 +39,29 @@
|
||||||
Route::get('/profile', [ProfileController::class, 'index'])->name('profile');
|
Route::get('/profile', [ProfileController::class, 'index'])->name('profile');
|
||||||
Route::post('/profile/update', [ProfileController::class, 'update'])->name('profile.update');
|
Route::post('/profile/update', [ProfileController::class, 'update'])->name('profile.update');
|
||||||
Route::get('/reports', [ReportController::class, 'index'])->name('reports');
|
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(),
|
||||||
|
]);
|
||||||
|
});
|
26496
storage/app/reports.json
26496
storage/app/reports.json
File diff suppressed because it is too large
Load Diff
Loading…
Reference in New Issue