2026年10月30日 星期五

ESP32 雙 MFRC522 讀卡機管理

 ESP32 雙 MFRC522 讀卡機管理 (中斷方式)





#include <SPI.h>
#include <MFRC522.h>

// 定義 Reader 1 腳位 (GPIO 13 支援內建 Pull-Up)
#define SS_1_PIN   5
#define RST_1_PIN 21
#define IRQ_1_PIN 13
const String CARD1_PREFIX = "AA:";

// 定義 Reader 2 腳位 (GPIO 14 支援內建 Pull-Up)
#define SS_2_PIN   4
#define RST_2_PIN 22
#define IRQ_2_PIN 14
const String CARD2_PREFIX = "BB:";

MFRC522 rfid1(SS_1_PIN, RST_1_PIN);
MFRC522 rfid2(SS_2_PIN, RST_2_PIN);

// 讀取卡片 UID 並串接前綴碼
void readCard(MFRC522 &mfrc522, String prefix) {
  // 檢查是否有卡片靠近
  if (!mfrc522.PICC_IsNewCardPresent() || !mfrc522.PICC_ReadCardSerial()) {
    return;
  }

  String fullCode = prefix;
  for (byte i = 0; i < mfrc522.uid.size; i++) {
    if (i > 0) fullCode += ":";
    if (mfrc522.uid.uidByte[i] < 0x10) fullCode += "0";
    fullCode += String(mfrc522.uid.uidByte[i], HEX);
  }
  fullCode.toUpperCase();

  Serial.print("感應結果 -> ");
  Serial.println(fullCode);

  mfrc522.PICC_HaltA();
  mfrc522.PCD_StopCrypto1();
  delay(1000); // 避免同一次感應重複刷卡
}

void setup() {
  Serial.begin(115200);
  SPI.begin();

  // 預設將中斷腳位設為可用的 INPUT_PULLUP(消除 GPIO 34/35 錯誤警示)
  pinMode(IRQ_1_PIN, INPUT_PULLUP);
  pinMode(IRQ_2_PIN, INPUT_PULLUP);

  // 初始化 Reader 1
  rfid1.PCD_Init();
  Serial.println("MFRC522 #1 (CARD1) 就緒");

  // 初始化 Reader 2
  rfid2.PCD_Init();
  Serial.println("MFRC522 #2 (CARD2) 就緒");
}

void loop() {
  // 快速輪詢,兼顧響應速度與 Wokwi 模擬器相容性
  readCard(rfid1, CARD1_PREFIX);
  readCard(rfid2, CARD2_PREFIX);
 
  delay(20);
}

ESP32 雙 MFRC522 讀卡機管理,主要解決了原本 Wokwi 模擬器不支援硬體中斷 (IRQ) 暫存器以及 GPIO 34/35 無法設定內建上拉電阻 (Input-Only) 的問題。

一、 核心功能摘要

  1. 雙讀卡機獨立識別:透過獨立的 CS/SDA 腳位(GPIO 5 與 GPIO 4)交替控制兩台 MFRC522,並在輸出時分別加上自訂前綴標籤 (AA: 與 BB:)。

  2. 高響應速度(輕量輪詢):主迴圈設定 20ms 的微短延遲,既不會過度佔用 CPU 資源,又能提供接近「即時感應」的刷卡體驗。

  3. GPIO 腳位相容性修正:將原本引發警告的 GPIO 34/35 改為支援 INPUT_PULLUP 的 GPIO 13 與 GPIO 14,完全消除了 ESP32 底層驅動的 gpio_pullup_en 錯誤。

  4. 防重複刷卡機制:每次成功讀卡後自動將卡片進入休眠(PICC_HaltA()),並加入 1000ms 的冷卻延遲,防止卡片放在感應區時連續重複觸發。

二、 程式碼逐段詳細說明

1. 標頭檔與腳位定義區

C++
#include <SPI.h>
#include <MFRC522.h>

// 定義 Reader 1 (CARD1) 的腳位與前綴
#define SS_1_PIN   5    // 片選腳位 (SDA)
#define RST_1_PIN 21    // 重置腳位
#define IRQ_1_PIN 13    // 中斷預留腳位 (支援內建 Pull-Up)
const String CARD1_PREFIX = "AA:";

// 定義 Reader 2 (CARD2) 的腳位與前綴
#define SS_2_PIN   4    // 片選腳位 (SDA)
#define RST_2_PIN 22    // 重置腳位
#define IRQ_2_PIN 14    // 中斷預留腳位 (支援內建 Pull-Up)
const String CARD2_PREFIX = "BB:";

MFRC522 rfid1(SS_1_PIN, RST_1_PIN);
MFRC522 rfid2(SS_2_PIN, RST_2_PIN);
  • 說明:宣告 SPI 與 MFRC522 函式庫,並為兩台讀卡機建立獨立的實體物件(rfid1 與 rfid2)。設定 CARD1_PREFIX 為 "AA:",CARD2_PREFIX 為 "BB:"。

2. 自訂讀卡與格式化函式 readCard()

C++
void readCard(MFRC522 &mfrc522, String prefix) {
  // 1. 檢查是否有新卡片靠近,並嘗試讀取卡片 Serial 序號
  if (!mfrc522.PICC_IsNewCardPresent() || !mfrc522.PICC_ReadCardSerial()) {
    return; // 若沒有卡片或讀取失敗,直接跳出函式
  }

  // 2. 組裝 Prefix + 16 進位 UID 格式 (如 "AA:01:02:03:04")
  String fullCode = prefix;
  for (byte i = 0; i < mfrc522.uid.size; i++) {
    if (i > 0) fullCode += ":";
    if (mfrc522.uid.uidByte[i] < 0x10) fullCode += "0"; // 小於 0x10 時補上首位 0
    fullCode += String(mfrc522.uid.uidByte[i], HEX);
  }
  fullCode.toUpperCase(); // 將字母轉為大寫

  // 3. 輸出結果至 Serial Monitor
  Serial.print("感應結果 -> ");
  Serial.println(fullCode);

  // 4. 卡片休眠與防重複感應延遲
  mfrc522.PICC_HaltA();
  mfrc522.PCD_StopCrypto1();
  delay(1000); 
}
  • 說明:採用引用傳遞(MFRC522 &mfrc522),讓同一個函式可重複用於 rfid1 與 rfid2。負責將讀到的 16 進位 UID 位元組轉換為直觀的大寫字串格式。

3. 初始化設定 setup()

C++
void setup() {
  Serial.begin(115200);
  SPI.begin(); // 初始化 ESP32 的預設 SPI 匯流排 (SCK=18, MISO=19, MOSI=23)

  // 設定 IRQ 腳位模式(消除 GPIO 34/35 的系統警告)
  pinMode(IRQ_1_PIN, INPUT_PULLUP);
  pinMode(IRQ_2_PIN, INPUT_PULLUP);

  // 分別初始化兩台 MFRC522 晶片
  rfid1.PCD_Init();
  Serial.println("MFRC522 #1 (CARD1) 就緒");

  rfid2.PCD_Init();
  Serial.println("MFRC522 #2 (CARD2) 就緒");
}
  • 說明:開啟串口並初始化硬體 SPI 介面。將 GPIO 13/14 宣告為 INPUT_PULLUP,確保轉移到實體板子時能隨時啟用中斷,並分別呼叫 PCD_Init() 完成讀卡機晶片暫存器初始化。

4. 主迴圈 loop()

C++
void loop() {
  // 依序輪詢 Reader 1 與 Reader 2
  readCard(rfid1, CARD1_PREFIX);
  readCard(rfid2, CARD2_PREFIX);
  
  delay(20); // 20ms 微延遲,兼顧響應速度與降低 CPU 負載
}

  • 說明:以每秒約 50 次的頻率交替檢查兩台讀卡機。當任何一台讀卡機感應到卡片時,即時在 Serial Monitor 印出帶有前綴的卡號。


連線總覽對照表

功能腳位Reader 1 (rfid1)Reader 2 (rfid2)ESP32 GPIO電源 / 備註
SCKSCKSCKGPIO 18SPI 時脈線(共用)
MISOMISOMISOGPIO 19SPI 主入從出(共用)
MOSIMOSIMOSIGPIO 23SPI 主出從入(共用)
SDA / SSSDA—GPIO 5Reader 1 片選線
——SDAGPIO 4Reader 2 片選線
RSTRST—GPIO 21Reader 1 重置線
——RSTGPIO 22Reader 2 重置線
IRQIRQ—GPIO 13支援內建 Pull-Up 中斷
——IRQGPIO 14支援內建 Pull-Up 中斷
VCC / GND3.3V / GND3.3V / GND3V3 / GND供電 3.3V(切勿接 5V)

沒有留言:

張貼留言

MQTT + SQLite + Tkinter + Telegram 雙向門禁管理後台系統 (作業3)

MQTT + SQLite + Tkinter + Telegram 雙向門禁管理後台系統 。 它透過多執行緒(Multithreading)同時處理 MQTT 訊息接收、GUI 視窗介面更新、SQLite 資料庫讀寫,以及 Telegram Bot 的推播與指令監聽。 一、 系...