This shows you the differences between two versions of the page.
|
iothings:proiecte:2025sric:rfidscanner [2025/04/10 08:39] robert_ionut.alexa |
iothings:proiecte:2025sric:rfidscanner [2025/05/29 10:07] (current) flaviu.ghena |
||
|---|---|---|---|
| Line 1: | Line 1: | ||
| ====== ESP32 RFID scanner ====== | ====== ESP32 RFID scanner ====== | ||
| + | Author: [[mailto:ghena.flaviu@gmail.com|Ghena Flaviu]] \\ | ||
| + | Master: SRIC | ||
| - | Author: Flaviu Ghena | + | ===== Project Description ===== |
| + | |||
| + | This project implements a secure RFID-based access control system using an ESP32-WROOM-32 microcontroller and an RC522 RFID reader. The system grants or denies access based on scanned RFID cards, with visual feedback provided by green (access granted) and red (access denied) LEDs. | ||
| + | |||
| + | ===== Concept ===== | ||
| + | |||
| + | This project implements an IoT-based RFID access control system using an ESP32 microcontroller. It scans RFID cards, validates them against a predefined list, and provides visual feedback via LEDs. The system logs all access attempts to Firebase in real-time and sends Telegram notifications for unauthorized access. A web interface allows remote monitoring and manual control. | ||
| + | |||
| + | Key features include: | ||
| + | Real-time access logging to Firebase Realtime Database [1] | ||
| + | Remote monitoring and control through a web interface | ||
| + | Instant Telegram notifications for unauthorized access attempts [2] | ||
| + | Manual access control via web interface buttons | ||
| + | Time-synchronized logging with NTP server | ||
| + | |||
| + | The system validates scanned cards against a predefined list of authorized cards, providing immediate LED feedback and updating the cloud database. Unauthorized access attempts trigger Telegram alerts to the system administrator. | ||
| + | |||
| + | ===== Hardware Description ===== | ||
| + | |||
| + | {{:iothings:proiecte:2025sric:flaviu4.jpg?600|}} | ||
| + | |||
| + | **Parts List:** | ||
| + | * ESP32-WROOM-32: Main microcontroller for processing and connectivity. | ||
| + | * RFID-RC522 Module: Reads RFID card UIDs. | ||
| + | * Green LED: Indicates authorized access. | ||
| + | * Red LED: Indicates denied access. | ||
| + | * Resistors (2x): Current-limiting resistors for LEDs | ||
| + | * Breadboard/Jumper Wires: For prototyping connections. | ||
| + | * Power Supply: 3.3V for ESP32 and RC522. | ||
| + | |||
| + | **ESP32-WROOM-32:** \\ | ||
| + | - Connects to WiFi for cloud communication.\\ | ||
| + | - Interfaces with the RC522 via SPI (GPIO 5, 18, 19, 23).\\ | ||
| + | - Controls LEDs via GPIO 25 (green) and 26 (red).\\ | ||
| + | |||
| + | **RFID-RC522:** \\ | ||
| + | - Communicates with ESP32 via SPI pins.\\ | ||
| + | - Powered by 3.3V \\ | ||
| + | - Antenna gain boosted for better sensitivity.\\ | ||
| + | |||
| + | **LEDs:** \\ | ||
| + | - Green LED: Activated for valid cards or manual "grant" commands.\\ | ||
| + | - Red LED: Activated for invalid cards or manual "deny" commands.\\ | ||
| + | |||
| + | {{:iothings:proiecte:2025sric:flaviu3.png?600|}} | ||
| + | |||
| + | ===== Functionality Breakdown ===== | ||
| + | |||
| + | **Key Components** | ||
| + | |||
| + | **RFID Handling:** | ||
| + | - The MFRC522 library interfaces with the RFID reader\\ | ||
| + | - Cards are identified by their UID and converted to decimal format\\ | ||
| + | - Reader automatically resets after periods of inactivity\\ | ||
| + | |||
| + | **Access Control Logic:** | ||
| + | - Compares scanned card IDs against authorized list\\ | ||
| + | - Provides immediate visual feedback via LEDs\\ | ||
| + | - Green LED for authorized access, red for denied\\ | ||
| + | |||
| + | **Firebase Integration:** | ||
| + | - Stores all access events with timestamps\\ | ||
| + | - Allows remote control via the ledControl path\\ | ||
| + | - Uses anonymous authentication for security\\ | ||
| + | |||
| + | **Realtime Database rules** | ||
| + | <code> | ||
| + | { | ||
| + | "rules": { | ||
| + | "rfidSystem": { | ||
| + | "control": { | ||
| + | ".read": true, | ||
| + | ".write": true | ||
| + | }, | ||
| + | "logs": { | ||
| + | ".read": true, | ||
| + | ".write": true | ||
| + | }, | ||
| + | "ledControl": { | ||
| + | ".read": true, | ||
| + | ".write": true | ||
| + | } | ||
| + | } | ||
| + | } | ||
| + | } | ||
| + | |||
| + | </code> | ||
| + | |||
| + | {{:iothings:proiecte:2025sric:flaviu6.png?300|}} | ||
| + | |||
| + | **Telegram Notifications:** | ||
| + | - Sends instant alerts for all access attempts\\ | ||
| + | - Differentiates between authorized and unauthorized access\\ | ||
| + | - Includes system startup notification\\ | ||
| + | |||
| + | {{:iothings:proiecte:2025sric:flaviu2.jpeg?250|}} | ||
| + | |||
| + | |||
| + | **Web Interface:** | ||
| + | - Provides real-time access logs display\\ | ||
| + | - Allows manual access control via buttons\\ | ||
| + | - Shows system connection status\\ | ||
| + | - The system combines local hardware control with cloud connectivity for a comprehensive access control solution with remote monitoring capabilities.\\ | ||
| + | |||
| + | **Error Handling:** | ||
| + | - Auto-resets RFID reader if inactive.\\ | ||
| + | - Reconnects to WiFi/Firebase if disconnected.\\ | ||
| + | |||
| + | ^ RC522 Pin ^ ESP32 Pin ^ | ||
| + | | SDA | GPIO 5 | | ||
| + | | SCK | GPIO 18 | | ||
| + | | MOSI | GPIO 23 | | ||
| + | | MISO | GPIO 19 | | ||
| + | | IRQ | Not connected | | ||
| + | | GND | GND | | ||
| + | | RST | GPIO 22 | | ||
| + | | 3.3V | 3.3V | | ||
| + | |||
| + | ^ LED Pin ^ ESP32 Pin ^ | ||
| + | | GREEN LED PIN | GPIO 25 | | ||
| + | | RED LED PIN | GPIO 26 | | ||
| + | |||
| + | {{:iothings:proiecte:2025sric:flaviu1.png?600|}} | ||
| + | |||
| + | |||
| + | |||
| + | |||
| + | ===== Code Breakdown ===== | ||
| + | |||
| + | <code> | ||
| + | #include <SPI.h> | ||
| + | #include <MFRC522.h> | ||
| + | #include <WiFi.h> | ||
| + | #include <WiFiClientSecure.h> | ||
| + | #include <HTTPClient.h> | ||
| + | #include <Firebase_ESP_Client.h> | ||
| + | #include "addons/TokenHelper.h" | ||
| + | #include "addons/RTDBHelper.h" | ||
| + | #include <time.h> | ||
| + | |||
| + | // Configuration Section | ||
| + | #define WIFI_SSID "DIGI-x39P" | ||
| + | #define WIFI_PASSWORD "xxxx" | ||
| + | #define API_KEY "xxxx" | ||
| + | #define DATABASE_URL "https://iotproiectflaviu-default-rtdb.europe-west1.firebasedatabase.app" | ||
| + | |||
| + | // Telegram Bot Settings | ||
| + | #define BOT_TOKEN "7635387808:AAGwtP_Y6fJ0LEhLbCuXkeLQb5a-0iDDQgs" | ||
| + | #define CHAT_ID "2124464812" | ||
| + | const char* TELEGRAM_HOST = "api.telegram.org"; | ||
| + | |||
| + | // Hardware Configuration | ||
| + | #define RST_PIN 22 | ||
| + | #define SS_PIN 5 | ||
| + | #define GREEN_LED_PIN 25 | ||
| + | #define RED_LED_PIN 26 | ||
| + | #define READ_TIMEOUT 300 | ||
| + | #define LED_FEEDBACK_MS 1000 | ||
| + | |||
| + | // System Objects | ||
| + | MFRC522 mfrc522(SS_PIN, RST_PIN); | ||
| + | FirebaseData fbdo; | ||
| + | FirebaseData ledControlStream; | ||
| + | FirebaseAuth auth; | ||
| + | FirebaseConfig config; | ||
| + | |||
| + | // Database Paths | ||
| + | String databasePath = "rfidSystem"; | ||
| + | String logsPath = "/logs"; | ||
| + | String ledControlPath = "/ledControl"; | ||
| + | |||
| + | // Authorized Cards List | ||
| + | const unsigned long validCards[] = {2318660736, 240163822}; | ||
| + | const int numValidCards = sizeof(validCards)/sizeof(validCards[0]); | ||
| + | |||
| + | // NTP Configuration | ||
| + | const char* ntpServer = "pool.ntp.org"; | ||
| + | const long gmtOffset_sec = 0; | ||
| + | const int daylightOffset_sec = 0; | ||
| + | |||
| + | // Function to send Telegram alerts | ||
| + | void sendTelegramAlert(unsigned long cardID, bool isAuthorized) { | ||
| + | WiFiClientSecure client; | ||
| + | client.setInsecure(); // Bypass SSL verification | ||
| + | |||
| + | HTTPClient https; | ||
| + | String url = "https://" + String(TELEGRAM_HOST) + "/bot" + String(BOT_TOKEN) + "/sendMessage"; | ||
| + | |||
| + | // Customize message based on access status | ||
| + | String message = isAuthorized ? | ||
| + | "✅ Authorized access: Card " + String(cardID) : | ||
| + | "🚨 Unauthorized access attempt: Card " + String(cardID); | ||
| + | |||
| + | String payload = "chat_id=" + String(CHAT_ID) + "&text=" + message; | ||
| + | |||
| + | if (https.begin(client, url)) { | ||
| + | https.addHeader("Content-Type", "application/x-www-form-urlencoded"); | ||
| + | int httpCode = https.POST(payload); | ||
| + | if (httpCode == HTTP_CODE_OK) { | ||
| + | Serial.println("Telegram alert sent"); | ||
| + | } else { | ||
| + | Serial.printf("Telegram error: %d\n", httpCode); | ||
| + | } | ||
| + | https.end(); | ||
| + | } else { | ||
| + | Serial.println("Failed to connect to Telegram"); | ||
| + | } | ||
| + | } | ||
| + | |||
| + | // Initialize WiFi connection | ||
| + | void connectToWiFi() { | ||
| + | Serial.print("Connecting to WiFi"); | ||
| + | WiFi.begin(WIFI_SSID, WIFI_PASSWORD); | ||
| + | |||
| + | while (WiFi.status() != WL_CONNECTED) { | ||
| + | Serial.print('.'); | ||
| + | // Blink both LEDs while connecting | ||
| + | digitalWrite(GREEN_LED_PIN, !digitalRead(GREEN_LED_PIN)); | ||
| + | digitalWrite(RED_LED_PIN, !digitalRead(RED_LED_PIN)); | ||
| + | delay(300); | ||
| + | } | ||
| + | |||
| + | Serial.println("\nConnected with IP: " + WiFi.localIP().toString()); | ||
| + | // Quick green LED confirmation | ||
| + | digitalWrite(GREEN_LED_PIN, HIGH); | ||
| + | delay(500); | ||
| + | digitalWrite(GREEN_LED_PIN, LOW); | ||
| + | digitalWrite(RED_LED_PIN, LOW); | ||
| + | } | ||
| + | |||
| + | // Initialize time synchronization | ||
| + | void initTime() { | ||
| + | configTime(gmtOffset_sec, daylightOffset_sec, ntpServer); | ||
| + | Serial.println("Waiting for NTP time sync..."); | ||
| + | |||
| + | time_t now; | ||
| + | while ((now = time(nullptr)) < 8 * 3600 * 2) { | ||
| + | Serial.print("."); | ||
| + | delay(500); | ||
| + | } | ||
| + | |||
| + | Serial.println("\nCurrent time: " + String(ctime(&now))); | ||
| + | } | ||
| + | |||
| + | // Initialize Firebase connection | ||
| + | void initFirebase() { | ||
| + | config.api_key = API_KEY; | ||
| + | config.database_url = DATABASE_URL; | ||
| + | config.token_status_callback = tokenStatusCallback; | ||
| + | |||
| + | Firebase.reconnectWiFi(true); | ||
| + | fbdo.setResponseSize(4096); | ||
| + | |||
| + | // Anonymous authentication | ||
| + | if (Firebase.signUp(&config, &auth, "", "")) { | ||
| + | Serial.println("Anonymous signup successful"); | ||
| + | } else { | ||
| + | Serial.println("Signup error: " + fbdo.errorReason()); | ||
| + | } | ||
| + | |||
| + | Firebase.begin(&config, &auth); | ||
| + | |||
| + | // Wait for Firebase connection | ||
| + | unsigned long startTime = millis(); | ||
| + | while (!Firebase.ready() && millis() - startTime < 15000) { | ||
| + | Serial.print("."); | ||
| + | delay(300); | ||
| + | } | ||
| + | |||
| + | if (Firebase.ready()) { | ||
| + | Serial.println("Firebase connected!"); | ||
| + | // Start listening for LED control commands | ||
| + | if (!Firebase.RTDB.beginStream(&ledControlStream, (databasePath + ledControlPath).c_str())) { | ||
| + | Serial.println("Stream begin error: " + ledControlStream.errorReason()); | ||
| + | } | ||
| + | } else { | ||
| + | Serial.println("Failed to connect to Firebase"); | ||
| + | // Blink red LED indefinitely on failure | ||
| + | while(1) { | ||
| + | digitalWrite(RED_LED_PIN, HIGH); | ||
| + | delay(500); | ||
| + | digitalWrite(RED_LED_PIN, LOW); | ||
| + | delay(500); | ||
| + | } | ||
| + | } | ||
| + | } | ||
| + | |||
| + | // Handle LED control commands from Firebase | ||
| + | void handleLEDControl() { | ||
| + | if (Firebase.RTDB.readStream(&ledControlStream)) { | ||
| + | if (ledControlStream.streamAvailable()) { | ||
| + | FirebaseJsonData jsonData; | ||
| + | FirebaseJson *json = ledControlStream.jsonObjectPtr(); | ||
| + | |||
| + | if (json->get(jsonData, "command")) { | ||
| + | String command = jsonData.stringValue; | ||
| + | Serial.println("Received LED command: " + command); | ||
| + | |||
| + | if (command == "grant") { | ||
| + | digitalWrite(GREEN_LED_PIN, HIGH); | ||
| + | digitalWrite(RED_LED_PIN, LOW); | ||
| + | delay(LED_FEEDBACK_MS); | ||
| + | digitalWrite(GREEN_LED_PIN, LOW); | ||
| + | sendToFirebase(0, true, true); // Manual grant | ||
| + | sendTelegramAlert(0, true); // Notify manual grant | ||
| + | } else if (command == "deny") { | ||
| + | digitalWrite(RED_LED_PIN, HIGH); | ||
| + | digitalWrite(GREEN_LED_PIN, LOW); | ||
| + | delay(LED_FEEDBACK_MS); | ||
| + | digitalWrite(RED_LED_PIN, LOW); | ||
| + | sendToFirebase(0, false, true); // Manual deny | ||
| + | sendTelegramAlert(0, false); // Notify manual deny | ||
| + | } | ||
| + | } | ||
| + | } | ||
| + | } | ||
| + | } | ||
| + | |||
| + | // Send access event to Firebase | ||
| + | void sendToFirebase(unsigned long cardID, bool accessGranted, bool isManual) { | ||
| + | if (Firebase.ready()) { | ||
| + | time_t now; | ||
| + | time(&now); | ||
| + | |||
| + | String path = databasePath + logsPath + "/" + String(now); | ||
| + | FirebaseJson json; | ||
| + | |||
| + | // Prepare JSON data | ||
| + | json.set("card_id", String(cardID)); | ||
| + | json.set("timestamp", now); | ||
| + | json.set("access_granted", accessGranted); | ||
| + | json.set("is_manual", isManual); | ||
| + | |||
| + | if (Firebase.RTDB.setJSON(&fbdo, path.c_str(), &json)) { | ||
| + | Serial.println("Data sent to Firebase"); | ||
| + | } else { | ||
| + | Serial.println("Firebase error: " + fbdo.errorReason()); | ||
| + | } | ||
| + | } | ||
| + | } | ||
| + | |||
| + | // Check if card is authorized | ||
| + | bool isCardValid(unsigned long cardID) { | ||
| + | for (int i = 0; i < numValidCards; i++) { | ||
| + | if (cardID == validCards[i]) return true; | ||
| + | } | ||
| + | return false; | ||
| + | } | ||
| + | |||
| + | // Reset RFID reader | ||
| + | void resetReader() { | ||
| + | mfrc522.PCD_Reset(); | ||
| + | mfrc522.PCD_Init(); | ||
| + | mfrc522.PCD_SetAntennaGain(mfrc522.RxGain_48dB); // Increase sensitivity | ||
| + | Serial.println("Reader was reset"); | ||
| + | } | ||
| + | |||
| + | // Setup function - runs once at startup | ||
| + | void setup() { | ||
| + | Serial.begin(115200); | ||
| + | |||
| + | // Initialize hardware | ||
| + | pinMode(GREEN_LED_PIN, OUTPUT); | ||
| + | pinMode(RED_LED_PIN, OUTPUT); | ||
| + | digitalWrite(GREEN_LED_PIN, LOW); | ||
| + | digitalWrite(RED_LED_PIN, LOW); | ||
| + | |||
| + | SPI.begin(); | ||
| + | resetReader(); // Initialize RFID reader | ||
| + | |||
| + | // Connect to services | ||
| + | connectToWiFi(); | ||
| + | initTime(); | ||
| + | initFirebase(); | ||
| + | |||
| + | // Send startup notification | ||
| + | sendTelegramAlert(0, true); // System startup notification | ||
| + | Serial.println("System ready"); | ||
| + | } | ||
| + | |||
| + | // Main program loop | ||
| + | void loop() { | ||
| + | static bool readerEnabled = true; | ||
| + | static unsigned long lastReadTime = 0; | ||
| + | |||
| + | // Handle incoming Firebase commands | ||
| + | handleLEDControl(); | ||
| + | |||
| + | // Reset reader if inactive for too long | ||
| + | if (millis() - lastReadTime > 3000 && readerEnabled) { | ||
| + | readerEnabled = false; | ||
| + | resetReader(); | ||
| + | lastReadTime = millis(); | ||
| + | readerEnabled = true; | ||
| + | } | ||
| + | |||
| + | // Skip if not time to read yet or reader disabled | ||
| + | if (millis() - lastReadTime < READ_TIMEOUT || !readerEnabled) return; | ||
| + | |||
| + | // Check for new card | ||
| + | if (!mfrc522.PICC_IsNewCardPresent()) return; | ||
| + | if (!mfrc522.PICC_ReadCardSerial()) { | ||
| + | Serial.println("Failed to read card"); | ||
| + | return; | ||
| + | } | ||
| + | |||
| + | lastReadTime = millis(); | ||
| + | readerEnabled = false; | ||
| + | |||
| + | // Read card ID | ||
| + | unsigned long cardID = 0; | ||
| + | Serial.print("Card UID:"); | ||
| + | |||
| + | for (byte i = 0; i < mfrc522.uid.size; i++) { | ||
| + | cardID = (cardID << 8) | mfrc522.uid.uidByte[i]; | ||
| + | Serial.print(mfrc522.uid.uidByte[i] < 0x10 ? " 0" : " "); | ||
| + | Serial.print(mfrc522.uid.uidByte[i], HEX); | ||
| + | } | ||
| + | |||
| + | Serial.print(" | Decimal: "); | ||
| + | Serial.println(cardID); | ||
| + | |||
| + | // Check access and provide feedback | ||
| + | bool accessGranted = isCardValid(cardID); | ||
| + | digitalWrite(GREEN_LED_PIN, accessGranted ? HIGH : LOW); | ||
| + | digitalWrite(RED_LED_PIN, accessGranted ? LOW : HIGH); | ||
| + | |||
| + | // Log event and send notifications | ||
| + | sendToFirebase(cardID, accessGranted, false); | ||
| + | sendTelegramAlert(cardID, accessGranted); | ||
| + | |||
| + | // LED feedback duration | ||
| + | delay(LED_FEEDBACK_MS); | ||
| + | digitalWrite(GREEN_LED_PIN, LOW); | ||
| + | digitalWrite(RED_LED_PIN, LOW); | ||
| + | |||
| + | // Clean up RFID communication | ||
| + | mfrc522.PICC_HaltA(); | ||
| + | mfrc522.PCD_StopCrypto1(); | ||
| + | readerEnabled = true; | ||
| + | } | ||
| + | </code> | ||
| + | |||
| + | |||
| + | ===== Webpage ===== | ||
| + | |||
| + | {{:iothings:proiecte:2025sric:flaviu5.png?600|}} | ||
| + | |||
| + | <code> | ||
| + | <!DOCTYPE html> | ||
| + | <html> | ||
| + | <head> | ||
| + | <title>RFID Access Control</title> | ||
| + | <meta name="viewport" content="width=device-width, initial-scale=1"> | ||
| + | <script src="https://www.gstatic.com/firebasejs/8.10.0/firebase-app.js"></script> | ||
| + | <script src="https://www.gstatic.com/firebasejs/8.10.0/firebase-auth.js"></script> | ||
| + | <script src="https://www.gstatic.com/firebasejs/8.10.0/firebase-database.js"></script> | ||
| + | <style> | ||
| + | body { font-family: Arial, sans-serif; max-width: 800px; margin: 0 auto; padding: 20px; } | ||
| + | .card { background: #f9f9f9; border-radius: 8px; padding: 20px; margin-bottom: 20px; box-shadow: 0 2px 4px rgba(0,0,0,0.1); } | ||
| + | button { padding: 10px 15px; margin: 5px; border: none; border-radius: 4px; cursor: pointer; } | ||
| + | .grant-btn { background-color: #4CAF50; color: white; } | ||
| + | .deny-btn { background-color: #f44336; color: white; } | ||
| + | table { width: 100%; border-collapse: collapse; margin-top: 20px; } | ||
| + | th, td { padding: 12px; text-align: left; border-bottom: 1px solid #ddd; } | ||
| + | .access-granted { color: #4CAF50; } | ||
| + | .access-denied { color: #f44336; } | ||
| + | .manual-access { font-style: italic; } | ||
| + | </style> | ||
| + | </head> | ||
| + | <body> | ||
| + | <div class="card"> | ||
| + | <h1>RFID Access Control System</h1> | ||
| + | <div id="authStatus"></div> | ||
| + | <div> | ||
| + | <h2>Manual Control</h2> | ||
| + | <button id="grantBtn" class="grant-btn">Grant Access</button> | ||
| + | <button id="denyBtn" class="deny-btn">Deny Access</button> | ||
| + | </div> | ||
| + | </div> | ||
| + | |||
| + | <div class="card"> | ||
| + | <h2>Access Logs</h2> | ||
| + | <div id="connectionStatus"></div> | ||
| + | <table> | ||
| + | <thead> | ||
| + | <tr> | ||
| + | <th>Timestamp</th> | ||
| + | <th>Card ID</th> | ||
| + | <th>Access</th> | ||
| + | </tr> | ||
| + | </thead> | ||
| + | <tbody id="logsBody"></tbody> | ||
| + | </table> | ||
| + | </div> | ||
| + | |||
| + | <script> | ||
| + | const firebaseConfig = { | ||
| + | apiKey: "AIzaSyAK0KP-a7qqbuoqp-qyPt1e-8xuktIrHVo", | ||
| + | authDomain: "iotproiectflaviu.firebaseapp.com", | ||
| + | databaseURL: "https://iotproiectflaviu-default-rtdb.europe-west1.firebasedatabase.app" | ||
| + | }; | ||
| + | |||
| + | firebase.initializeApp(firebaseConfig); | ||
| + | const database = firebase.database(); | ||
| + | const ledControlRef = database.ref('rfidSystem/ledControl'); | ||
| + | const logsRef = database.ref('rfidSystem/logs'); | ||
| + | |||
| + | // Auth state | ||
| + | firebase.auth().signInAnonymously() | ||
| + | .then(() => { | ||
| + | document.getElementById('authStatus').textContent = "Authenticated"; | ||
| + | }) | ||
| + | .catch(error => { | ||
| + | document.getElementById('authStatus').innerHTML = | ||
| + | `<span style="color:red">Auth error: ${error.message}</span>`; | ||
| + | }); | ||
| + | |||
| + | // Connection state | ||
| + | database.ref('.info/connected').on('value', (snapshot) => { | ||
| + | const isConnected = snapshot.val(); | ||
| + | document.getElementById('connectionStatus').innerHTML = isConnected ? | ||
| + | '<span style="color:green">Connected to Firebase</span>' : | ||
| + | '<span style="color:red">Disconnected</span>'; | ||
| + | }); | ||
| + | |||
| + | // Button handlers | ||
| + | document.getElementById('grantBtn').addEventListener('click', () => { | ||
| + | ledControlRef.set({ command: "grant", timestamp: Date.now() }) | ||
| + | .then(() => console.log("Grant command sent")) | ||
| + | .catch(error => console.error("Error:", error)); | ||
| + | }); | ||
| + | |||
| + | document.getElementById('denyBtn').addEventListener('click', () => { | ||
| + | ledControlRef.set({ command: "deny", timestamp: Date.now() }) | ||
| + | .then(() => console.log("Deny command sent")) | ||
| + | .catch(error => console.error("Error:", error)); | ||
| + | }); | ||
| + | |||
| + | // Logs display | ||
| + | logsRef.limitToLast(20).on('value', (snapshot) => { | ||
| + | const logsBody = document.getElementById('logsBody'); | ||
| + | logsBody.innerHTML = ''; | ||
| + | |||
| + | if (!snapshot.exists()) { | ||
| + | logsBody.innerHTML = '<tr><td colspan="3">No logs found</td></tr>'; | ||
| + | return; | ||
| + | } | ||
| + | |||
| + | const logs = []; | ||
| + | snapshot.forEach(child => { | ||
| + | logs.push({ | ||
| + | key: child.key, | ||
| + | ...child.val() | ||
| + | }); | ||
| + | }); | ||
| + | |||
| + | logs.sort((a, b) => b.timestamp - a.timestamp); | ||
| + | |||
| + | logs.forEach(log => { | ||
| + | const row = document.createElement('tr'); | ||
| + | if (log.is_manual) row.classList.add('manual-access'); | ||
| + | |||
| + | const timeCell = document.createElement('td'); | ||
| + | timeCell.textContent = new Date(log.timestamp * 1000).toLocaleString(); | ||
| + | row.appendChild(timeCell); | ||
| + | |||
| + | const idCell = document.createElement('td'); | ||
| + | idCell.textContent = log.card_id === "0" ? "MANUAL" : log.card_id; | ||
| + | row.appendChild(idCell); | ||
| + | |||
| + | const accessCell = document.createElement('td'); | ||
| + | accessCell.textContent = log.access_granted ? 'Granted' : 'Denied'; | ||
| + | accessCell.className = log.access_granted ? 'access-granted' : 'access-denied'; | ||
| + | row.appendChild(accessCell); | ||
| + | |||
| + | logsBody.appendChild(row); | ||
| + | }); | ||
| + | }); | ||
| + | </script> | ||
| + | </body> | ||
| + | </html> | ||
| + | </code> | ||
| + | |||
| + | ===== Libraries ===== | ||
| + | |||
| + | * SPI.h: Enables SPI communication for the RC522. | ||
| + | * MFRC522.h: Interfaces with the RFID reader. | ||
| + | * WiFi.h: Manages WiFi connectivity. | ||
| + | * Firebase_ESP_Client.h: Handles Firebase Realtime Database operations. | ||
| + | * WiFiClientSecure.h/HTTPClient.h: Sends Telegram alerts via HTTPS. | ||
| + | * time.h: Syncs time via NTP for accurate timestamps. | ||
| + | |||
| + | |||
| + | ===== Conclusions ===== | ||
| + | This project demonstrates a scalable IoT access control system combining hardware (ESP32, RFID, LEDs) with cloud services (Firebase, Telegram). Key achievements: | ||
| + | - Security: Real-time validation and alerts for unauthorized access. | ||
| + | - Remote Monitoring: Web interface and Telegram notifications. | ||
| + | - Reliability: Auto-reset functions and error handling. | ||
| + | - Future enhancements could include adding biometric verification or integrating with physical locks. | ||
| + | |||
| + | ===== Demo ===== | ||
| + | |||
| + | <html><iframe width="456" height="811" src="https://www.youtube.com/embed/BdYKw6g49gY" title="RFID22" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" allowfullscreen></iframe></html> | ||
| + | |||
| + | <html><iframe width="456" height="811" src="https://www.youtube.com/embed/UwR_TnAOcD8" title="RFID11" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" allowfullscreen></iframe></html> | ||
| + | |||
| + | ===== References ===== | ||
| + | [1] https://randomnerdtutorials.com/control-esp-gpios-firebase-web-app/ \\ | ||
| + | [2] https://randomnerdtutorials.com/esp32-door-status-telegram/ | ||