RFID(Radio Frequency Identification,射频识别)是一种非接触式自动识别技术。RC522 模块基于 NXP 的 MFRC522 芯片,工作在 13.56 MHz 高频频段,通过 SPI 接口与 ESP32 通信,支持读取和写入 Mifare S50(1KB)/ S70(4KB) 类型的 IC 卡。
每张 Mifare 卡都有一个全球唯一的 4 字节 UID(唯一标识符)——这就是门禁系统识别"谁在刷卡"的核心依据。RC522 的典型读写距离为 3~6 cm,非常适合门禁考勤、会员卡识别、物品追踪等场景。
为什么选 ESP32 而不是 Arduino Uno?RC522 是 3.3V 器件,Arduino Uno 的 5V 逻辑电平需要加电平转换电路才能安全连接,而 ESP32 原生 3.3V——直接杜邦线对接即可,零外围电路。
| 物料 | 型号/参数 | 数量 | 参考价格 |
|---|---|---|---|
| ESP32 开发板 | ESP32-WROOM-32(CH340 版) | 1 | ¥18~25 |
| RFID 读写模块 | RC522(MFRC522 芯片),13.56 MHz | 1 | ¥5~10 |
| IC 卡 + 钥匙扣 | Mifare Classic 1K(S50),通常随模块附赠 | 2~3 | ¥1~3/张 |
| 有源蜂鸣器 | 3.3V~5V,低电平触发 | 1 | ¥1~2 |
| LED 指示灯 | 红色 5mm + 绿色 5mm + 220Ω 电阻 ×2 | 各1 | ¥1 |
| 继电器模块 | 1 路 5V 光耦隔离,高/低电平触发可选 | 1 | ¥4~7 |
| 杜邦线 | 母对母 + 公对母,20 cm | 各12条 | ¥3/组 |
| 面包板 | 830 孔 | 1 | ¥5~8 |
| 电磁锁(可选) | 12V 推拉式电磁铁/电插锁 | 1 | ¥12~25 |
总预算:基础版(不含电磁锁)约 ¥40~60,完整门禁版约 ¥60~90。
┌──────────────────────────────────────────────────────────┐ │ ESP32 ←→ RC522 SPI 引脚连接 │ ├────────────┬─────────────────────────────────────────────┤ │ ESP32 │ RC522 模块 说明 │ ├────────────┼─────────────────────────────────────────────┤ │ 3.3V │ VCC (3.3V) 供电(切勿接5V!) │ │ GND │ GND 共地 │ │ GPIO18 │ SCK SPI 时钟线 │ │ GPIO19 │ MISO SPI 主入从出 │ │ GPIO23 │ MOSI SPI 主出从入 │ │ GPIO5 │ SDA (SS/NSS) SPI 片选(自定义引脚) │ │ GPIO22 │ RST 复位引脚(自定义引脚) │ ├────────────┴─────────────────────────────────────────────┤ │ 外设引脚连接(门禁功能) │ ├────────────┬─────────────────────────────────────────────┤ │ GPIO15 │ 红色 LED(+220Ω 限流电阻→GND) 验证失败指示 │ │ GPIO4 │ 绿色 LED(+220Ω 限流电阻→GND) 验证通过指示 │ │ GPIO2 │ 蜂鸣器 I/O 刷卡提示音 │ │ GPIO13 │ 继电器 IN 控制电磁锁/电插锁 │ ├────────────┴─────────────────────────────────────────────┤ │ 注意:RC522 的 SDA 引脚是 SPI 片选(Slave Select), │ │ 不是 I2C 的 SDA!不同卖家丝印可能标为 SS / NSS / CS。 │ │ RC522 模块工作电压 3.3V,电流约 13~26mA(待机<1mA)。 │ └──────────────────────────────────────────────────────────┘
核心逻辑:刷卡 → 读取卡片 UID → 与白名单比对 → 通过则亮绿灯 + 开锁 + 蜂鸣器响一声,拒绝则亮红灯 + 蜂鸣器响三声。
// ========== RC522 白名单门禁系统 ========== #include <SPI.h> #include <MFRC522.h> // ---------- 引脚定义 ---------- #define SS_PIN 5 // SDA / SS #define RST_PIN 22 // RST #define LED_GREEN 4 // 验证通过 #define LED_RED 15 // 验证失败 #define BUZZER 2 // 蜂鸣器 #define RELAY 13 // 继电器(控制门锁) MFRC522 mfrc522(SS_PIN, RST_PIN); // ---------- 白名单 UID(授权卡片) ---------- // 用串口监视器刷一次卡即可看到 UID,将其加入数组 byte whitelist[][4] = { {0xA3, 0x7B, 0x2C, 0x15}, // 示例:卡1(替换为你自己的UID) {0xD2, 0xE4, 0x8F, 0x0B}, // 示例:卡2 }; const int whitelistSize = sizeof(whitelist) / sizeof(whitelist[0]); // ---------- 初始化 ---------- void setup() { Serial.begin(115200); delay(500); Serial.println("\n🔐 RC522 门禁系统启动中..."); pinMode(LED_GREEN, OUTPUT); pinMode(LED_RED, OUTPUT); pinMode(BUZZER, OUTPUT); pinMode(RELAY, OUTPUT); digitalWrite(LED_GREEN, LOW); digitalWrite(LED_RED, LOW); digitalWrite(BUZZER, LOW); digitalWrite(RELAY, LOW); // 默认锁闭 SPI.begin(); // 初始化 SPI 总线 mfrc522.PCD_Init(); // 初始化 RC522 mfrc522.PCD_DumpVersionToSerial(); // 打印固件版本 Serial.println("✅ 系统就绪,请刷卡..."); Serial.printf("📋 白名单共 %d 张卡\n", whitelistSize); } void loop() { // 检测是否有新卡片 if (!mfrc522.PICC_IsNewCardPresent()) return; if (!mfrc522.PICC_ReadCardSerial()) return; // 读取 UID 并打印 Serial.print("💳 检测到卡片 UID: "); for (byte i = 0; i < mfrc522.uid.size; i++) { Serial.printf("%02X ", mfrc522.uid.uidByte[i]); } Serial.println(); // 白名单验证 bool authorized = checkWhitelist(mfrc522.uid.uidByte, mfrc522.uid.size); if (authorized) { accessGranted(); } else { accessDenied(); } // 停止与卡片的通信(释放天线,准备下一次刷卡) mfrc522.PICC_HaltA(); mfrc522.PCD_StopCrypto1(); } // ---------- 白名单查询 ---------- bool checkWhitelist(byte* uid, byte uidLen) { for (int i = 0; i < whitelistSize; i++) { bool match = true; for (byte j = 0; j < uidLen; j++) { if (whitelist[i][j] != uid[j]) { match = false; break; } } if (match) return true; } return false; } // ---------- 验证通过:开锁 ---------- void accessGranted() { Serial.println("✅ 验证通过!开门"); digitalWrite(LED_GREEN, HIGH); digitalWrite(RELAY, HIGH); // 继电器吸合,电锁通电 tone(BUZZER, 2000, 150); // 短促哔声 delay(3000); // 开门保持 3 秒 digitalWrite(RELAY, LOW); // 继电器断开,重新锁门 digitalWrite(LED_GREEN, LOW); } // ---------- 验证失败:报警 ---------- void accessDenied() { Serial.println("❌ 未授权卡片!拒绝访问"); for (int i = 0; i < 3; i++) { digitalWrite(LED_RED, HIGH); tone(BUZZER, 800, 200); delay(300); digitalWrite(LED_RED, LOW); delay(200); } }
💳 检测到卡片 UID: A3 7B 2C 15 的信息。将这四个十六进制字节替换到白名单数组中即可。HIGH 触发(RELAY=HIGH 时锁通电)。如果你的模块是低电平触发,将 RELAY 的 HIGH/LOW 对调即可。
Mifare 1K 卡有 16 个扇区(Sector 0~15),每扇区 4 个数据块(Block 0~3),每块 16 字节。扇区 0 的 Block 0 存储 UID 和厂商数据(只读),其余扇区默认需要密钥认证才能读写。
下面的代码演示如何在扇区 1 写入用户数据(比如姓名、工号、余额),认证密钥为出厂默认值 FF FF FF FF FF FF:
// ===== 向扇区 1 / Block 4 写入数据 ===== byte sector = 1; byte blockAddr = 4; // 扇区1的第一个块 = 块地址4 byte dataBlock[16] = { 'A','I','K','n','o','w','-','U', // "AIKnow-U" 's','e','r','0','0','1',0x00,0x00 }; // 默认密钥 MFRC522::MIFARE_Key key; for (byte i = 0; i < 6; i++) key.keyByte[i] = 0xFF; // 认证扇区 1 byte trailerBlock = 7; // 扇区1的最后一块(块7)是控制块 MFRC522::StatusCode status; status = mfrc522.PCD_Authenticate(MFRC522::PICC_CMD_MF_AUTH_KEY_A, trailerBlock, &key, &(mfrc522.uid)); if (status != MFRC522::STATUS_OK) { Serial.printf("❌ 认证失败: %d\n", status); return; } // 写入数据块 status = mfrc522.MIFARE_Write(blockAddr, dataBlock, 16); if (status == MFRC522::STATUS_OK) { Serial.println("✅ 数据写入成功!"); } // 读回验证 byte buffer[18]; byte size = sizeof(buffer); status = mfrc522.MIFARE_Read(blockAddr, buffer, &size); if (status == MFRC522::STATUS_OK) { Serial.print("📖 读出数据: "); for (byte i = 0; i < 16; i++) { Serial.printf("%c", buffer[i]); } Serial.println(); }
Mifare 卡有 4 字节 UID(经典 S50)和 7 字节 UID(S70 / Ultralight)两种。rc522.uid.size 字段自动提供 UID 长度,兼容代码如下:
void printUID(MFRC522 &rc522) { Serial.print("UID ("); Serial.print(rc522.uid.size); Serial.print(" bytes): "); for (byte i = 0; i < rc522.uid.size; i++) { if (rc522.uid.uidByte[i] < 0x10) Serial.print("0"); Serial.print(rc522.uid.uidByte[i], HEX); Serial.print(" "); } Serial.println(); Serial.printf("卡片类型: %s\n", rc522.PICC_GetTypeName(rc522.PICC_Type)); }
出厂默认密钥全 FF 极不安全——任何人拿到你的卡都能读写数据。修改扇区尾块(Trailer Block)即可设置自定义密钥:
// 将扇区1的密钥A改为自定义值 byte newKeyA[6] = {0x12, 0x34, 0x56, 0x78, 0x9A, 0xBC}; // 先读取扇区1的尾块(块7),保留访问位 byte trailer[18]; byte size = sizeof(trailer); mfrc522.MIFARE_Read(7, trailer, &size); // 替换密钥A(前6字节),保留访问位(6-9字节)和密钥B(10-15字节) memcpy(trailer, newKeyA, 6); // 写回尾块 mfrc522.MIFARE_Write(7, trailer, 16); Serial.println("🔑 密钥已更新!");
场景描述:快递员刷卡→系统根据快递编号匹配→自动弹开指定柜门。用户在手机上获取取件码,刷自己的 IC 卡也能打开对应柜门。适合社区或办公室的共享快递柜。
物料组合:ESP32 + RC522 ×1 + SG90 舵机 ×3(模拟三格柜门)+ OLED 显示屏 ×1
工作流程:刷卡 → 读取 UID → 查询 UID-柜号映射表 → OLED 显示"3号柜已开" → 对应舵机旋转 90° 弹开柜门 → 5 秒后自动回位。
联动已学模块:结合 5月15日的 SG90 舵机教程和 5月14日的 OLED 显示屏教程,一个完整的智能柜系统只需 60 行代码!
场景描述:大学实验室的贵重仪器(如示波器、信号发生器)需要刷卡才能通电使用。刷卡记录(谁、什么时间、用了什么设备)通过 BLE 实时同步到管理员的手机 App。
物料组合:ESP32 + RC522 + 继电器模块 + 实时时钟 DS3231 + BLE(ESP32 内置)
数据流:刷卡 → RC522 读取 UID → ESP32 记录时间戳 → 继电器通电(设备可用) → BLE 广播使用日志 → 手机 App 接收并上传后台。
联动已学模块:结合 5月11日的 BLE 配对通信教程,手机可实时收到"张三 14:32 使用示波器"的推送。
mfrc522.PCD_Init() 之前调用 SPI.begin();~/Arduino/libraries/ 中清理冲突的库文件夹后重启 Arduino IDE。