This shows you the differences between two versions of the page.
iothings:proiecte:2025sric:rfidscanner [2025/04/10 08:37] robert_ionut.alexa created |
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 | ||
+ | |||
+ | ===== 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/ |