ESP32 RFID User Management System (修正 新增/比對 模式)
系統邏輯設計
模式切換:在 Python 後端設定一個變數來紀錄當前模式(比對或新增)。
新增模式:ESP32 讀取 UID 後發送給伺服器,伺服器將 UID 傳送到網頁表單,並啟動 5 秒倒數,若無人存檔則設為 Unknown。
比對模式:即原本的邏輯,檢查白名單並回傳信號控制繼電器。
Python 後端程式整合了 MQTT 監聽、Flask 網頁伺服器、模式切換 API 以及 Tkinter 監控視窗。
請將此程式存為 User Management.py。
程式邏輯說明:
/set_mode API:讓前端網頁的按鈕可以切換 current_mode。
/get_latest_uid API:前端網頁透過此 API 獲取剛才在 ESP32 刷下的卡號。
on_message 判斷:
Tkinter 視窗:提供管理員即時查看後端正在進行「比對」還是「新增」,並顯示刷卡結果。
ESP32 程式修正 (控制繼電器與警示)
修改 callback 函式來處理繼電器與警示聲(假設繼電器在 GPIO 4)。
#include <SPI.h>
#include <MFRC522.h>
#include <WiFi.h>
#include <PubSubClient.h>
#include <Wire.h>
#include <LiquidCrystal_I2C.h>
#define I2C_SDA 17
#define I2C_SCL 16
#define LED_PIN 2
#define RELAY_PIN 4
#define SS_PIN 5
#define RST_PIN 22
LiquidCrystal_I2C lcd(0x27, 16, 2);
MFRC522 mfrc522(SS_PIN, RST_PIN);
const char* ssid = "Wokwi-GUEST";
const char* password = "";
const char* mqtt_server = "broker.emqx.io";
const char* topic_detect = "alex9ufo/rfid/detect";
const char* topic_control = "alex9ufo/rfid/control";
WiFiClient espClient;
PubSubClient client(espClient);
void callback(char* topic, byte* payload, unsigned int length) {
String msg = "";
for (int i = 0; i < length; i++) msg += (char)payload[i];
Serial.print("收到指令: "); Serial.println(msg);
lcd.setCursor(0, 1);
if (msg == "ON") {
digitalWrite(RELAY_PIN, HIGH); // 打開電動鎖
digitalWrite(LED_PIN, HIGH);
lcd.print("Access: Granted ");
delay(3000);
digitalWrite(RELAY_PIN, LOW); // 自動關鎖
digitalWrite(LED_PIN, LOW);
} else if (msg == "OFF") {
//digitalWrite(BUZZER_PIN, HIGH); // 報警聲
lcd.print("Access: Denied ");
delay(500);
//digitalWrite(BUZZER_PIN, LOW);
} else if (msg == "MODE_ADD") {
lcd.print("Mode: Registering");
}
}
void setup() {
Serial.begin(115200);
pinMode(LED_PIN, OUTPUT);
pinMode(RELAY_PIN, OUTPUT);
//pinMode(BUZZER_PIN, OUTPUT);
Wire.begin(I2C_SDA, I2C_SCL);
lcd.init(); lcd.backlight();
WiFi.begin(ssid, password);
while (WiFi.status() != WL_CONNECTED) { delay(500); Serial.print("."); }
lcd.clear(); lcd.print("Ready to Scan");
SPI.begin();
mfrc522.PCD_Init();
client.setServer(mqtt_server, 1883);
client.setCallback(callback);
}
void reconnect() {
while (!client.connected()) {
if (client.connect("ESP32_RFID_alex9ufo")) {
client.subscribe(topic_control);
} else { delay(5000); }
}
}
void loop() {
if (!client.connected()) reconnect();
client.loop();
if (mfrc522.PICC_IsNewCardPresent() && mfrc522.PICC_ReadCardSerial()) {
String uid = "";
for (byte i = 0; i < mfrc522.uid.size; i++) {
uid += String(mfrc522.uid.uidByte[i] < 0x10 ? "0" : "");
uid += String(mfrc522.uid.uidByte[i], HEX);
}
lcd.setCursor(0, 0);
lcd.print("UID: " + uid);
Serial.println("偵測 UID: " + uid);
client.publish(topic_detect, uid.c_str());
mfrc522.PICC_HaltA();
delay(2000);
}
}
系統運作說明:
新增模式:管理員在網頁點擊「進入新增模式」,此時刷卡 UID 會自動跳轉到網頁的輸入框,網頁啟動 5 秒倒數。若管理員沒選 User1~6,時間一到網頁會自動以 Unknown 角色存檔。
比對模式:系統預設狀態。刷卡後 Python 檢查 users.txt,若 UID 存在,發送 ON 給 ESP32。
硬體執行:ESP32 收到 ON 後將 RELAY_PIN 設為高電位(打開門鎖);若收到 OFF 則觸發 BUZZER_PIN 警報。
前端網頁介面 (add-user.html)
我們在表單上方加入「模式切換按鈕」,並利用 JavaScript 實作 5 秒倒數功能。
3. 操作流程說明
進入新增模式:在網頁點擊「進入新增模式」按鈕。
刷卡感應:拿一張新卡片去感應 ESP32。
自動填入:網頁會透過每秒一次的 fetch 抓到這個 UID,並自動填入 input 欄位。
倒數與決策:
返回比對:存檔成功後,點擊「返回比對模式」,系統恢復正常的門禁開鎖功能。
這樣您的「控制面板」就真正具備了遠端操控硬體行為的能力。