☰
ESP32与RC522入门指南:从零搭建RFID刷卡门禁系统
2026/10/8 23:24:13 网站建设 项目流程

1. 为什么零基础首选RFID-RC522组合:一套不到十块钱的“刷卡”启蒙课

我最早接触ESP32的时候,周围十个人里有八个都在做LED灯和温湿度传感器。不是说那些不好,而是玩到后面你会发现,按钮、屏幕、灯带这类外设调通之后,总觉得缺了点“交互感”。直到有一天朋友扔给我一块RC522模块,说让我做个刷卡开门的小东西,我才意识到:让ESP32“认识”一张卡片,这件事带来的满足感,是点灯完全给不了的。

RFID(Radio Frequency Identification,无线射频识别)这个技术名字听起来高深,放在生活里其实到处都是。小区单元门的门禁卡、公司的考勤卡、校园卡、图书馆的自助借还书机,全是它的应用场景。坐地铁刷的交通卡用ISO/IEC 14443协议,RC522模块支持的就是这个协议的衍生兼容型号,这意味着你手里那堆M1卡、S50卡,都可以拿来跟ESP32玩。

这套组合对零基础友好的核心原因有三个:

  1. 模块便宜且皮实:RC522成品模块大量产,市面上到处能买到,坏了不心疼,随便折腾。
  2. 库支持极其成熟:MFRC522(读卡芯片)是恩智浦的老牌芯片,Arduino生态里有个传播量极大的MFRC522库,示例代码开箱即用,基本不需要自己啃寄存器手册。
  3. 反馈效果好:刷卡、亮灯、蜂鸣器响、串口打印卡号,这一套交互流程做完,你会有“我真的做了一个门禁系统”的真实感,这种正反馈对零基础学习者来说非常宝贵。

这篇文章适合完全没碰过ESP32的纯新手,也适合看完各种教程但始终卡在“读不到卡”的倒霉蛋。我会把从硬件选型、环境配置、接线、代码到故障排查的完整闭环讲清楚,所有内容基于我实际跑过几十遍的Arduino开发路线,每个坑都有具体的解决办法。

2. 硬件清单与选型避坑:不只是买一块板子那么简单

2.1 主控板选择:ESP32开发板怎么挑?

ESP32的型号非常杂:经典款ESP32、ESP32-S3、ESP32-C3、ESP32-C6。对于做RFID刷卡这种基础项目,经典款ESP32(芯片型号ESP32或ESP32-WROOM-32)就足够用了,理由是它的资料最多、例程最全,遇到问题你能搜到的踩坑记录也最丰富。

市面上常见的板子有NodeMCU-32S、ESP32 DevKitC、ESP32-S3 Super Mini等,它们引脚布局略有不同,但核心芯片一致。我这里以经典ESP32 DevKitC和NodeMCU-32S为例,它们的引脚定义基本兼容。

几个选板注意点:

  • 尽量选择有板载USB转串口芯片的版本(常见的是CP2102或CH340)。系统装不上驱动的,先用鲁大师或设备管理器看看USB设备里有没有出现带感叹号的未知设备,那多半是驱动问题。CH340需要手动装驱动,CP2102一般Windows10以上能自动识别。
  • 买的时候看清引脚是否引出排针。有的板子是半孔邮票封装,没有排针,零基础焊接容易翻车。
  • 别在淘宝上买太便宜的无牌ESP32模块,有些劣质模块用的Flash芯片是杂牌,烧录到一半会随机失败,排查起来非常折磨人。

2.2 RFID读卡模块:RC522的封装差异与细节

RC522模块的供应商很多,硬件上一般有铝丝邦定的小板子、邮票孔半圆焊盘、以及带排针的成品模块这三种。零基础建议直接买带排针的成品模块,引脚标识一般长这样:SDA、SCK、MOSI、MISO、IRQ、GND、RST、VCC。

排针版本的好处是可以直接插杜邦线,不用焊接,而且板上已经帮你布好了天线匹配电路。你唯一要留意的是模块背面那个小电阻R,部分模块通过它做天线电流调节,一般情况下不需要动它。

另一个容易忽略的是卡的种类。RC522支持ISO/IEC 14443A协议的M1类卡(S50、S70),如果家里只有门禁用的ID卡(EM4100等低频卡),RC522是读不到的。低频125kHz的卡需要配RDM6300模块,这里不展开,但如果你刷卡一直没反应,先查一查卡是不是14443A协议。

2.3 基础材料清单

物料建议型号/规格数量备注
主控板ESP32 DevKitC / NodeMCU-32S1也可以换ESP32-S3,接线逻辑一致
RFID读卡模块MFRC522(RC522)1买成品模块,带排针
IC卡M1白卡/M1钥匙扣卡1-3注意协议是14443A
杜邦线公对母、母对母均可若干长度尽量短,干扰小
面包板标准830孔1可选,走线方便
蜂鸣器3.3V有源蜂鸣器1可选,刷卡提示
LED红色/绿色各一各1可选,刷卡反馈

全套下来成本大约几十块钱,而且大部分是长期可复用的基础物料。如果你已经有了ESP32和面包板,那你要额外掏的钱基本就只是一个RC522模块和几张白卡,不到十几块。

3. 开发环境从零到通:Arduino IDE与PlatformIO一次讲清

3.1 Arduino IDE安装与ESP32离线包配置

RFID刷卡项目本身不需要复杂的工程结构,用Arduino IDE就够了。但很多人在第一步就被卡住:Arduino IDE里搜不到ESP32开发板,原因是ESP32不在默认的开发板管理器列表里。

你需要在“文件→首选项→附加开发板管理器网址”中添加如下地址:

https://espressif.github.io/arduino-esp32/package_esp32_index.json

添加之后,到“工具→开发板→开发板管理器”,搜索“esp32”,找到“esp32 by Espressif Systems”,点安装。

这里必须提醒:国内网络直接下载ESP32离线包经常失败,下载到一半报错是常态。我遇到过无数次“开发板管理器下载失败”“网络错误”,其中很多是你已经有加速工具也不好解决的(某些下载源在国内响应很慢)。如果反复失败,建议用以下替代方案:

  1. 用浏览器直接打开上面的JSON地址,找到下载列表里对应版本的esp32-2.0.x.zip文件链接,用浏览器或下载工具下载。
  2. 下载完成后,把它手动放到Arduino IDE的tools目录下(Windows一般在文档/Arduino外的一个独立目录,具体看你安装方式),再重启IDE。
  3. 网上存在整合好的“Arduino IDE ESP32离线包”,下载解压后覆盖到对应目录即可。这个办法省时省力,但要注意包版本和IDE版本匹配,否则工具链编译会报错。

如果你用的是PlatformIO,实际上对下载速度的容忍度更低。PlatformIO基于Python包管理器,ESP32平台文件也需要联网拉取,国内经常卡在platformio/espressif32安装阶段。解决思路类似:借助离线包手动安装,或者先安装一次完整的PlatformIO Core再处理平台文件。

3.2 安装MFRC522库

库的安装非常简单:在Arduino IDE的“库管理器”里搜索“MFRC522”,第一个就是MFRC522 by GithubCommunity(过去叫Miguel Balboa)。点安装,默认装最新版即可。

如果你用的示例代码是老博客里那种#include <MFRC522.h>写法,新旧库兼容性大体没问题。装完之后打开“文件→示例→MFRC522→ReadAndPrint”,后面我们基于这个例子展开。

3.3 开发板选择与烧录设置

拿到ESP32板子,在“工具→开发板→开发板管理器”中选择:

  • 经典ESP32:选择“ESP32 Dev Module”
  • NodeMCU-32S:同样选“ESP32 Dev Module”,因为它的引脚兼容经典DevKit
  • ESP32-S3 Super Mini:选“ESP32S3 Dev Module”,注意Flash大小选8MB(如果是8MB版本),USB CDC选启用

烧录之前有三件事必须检查:

  1. 端口选择:在“工具→端口”里选中你的板子对应的COM口。如果看不到端口,先检查USB线是不是只有充电功能的数据线,很多“不给反馈”的问题其实是数据线不合格。
  2. 开发板参数:默认我一般不改,除非你明确知道板载Flash是8MB还是16MB。选错之后编译烧录能通过,但有可能烧完无法启动。
  3. 按住BOOT键:某些ESP32板子在串口烧录时必须按住BOOT键插USB或者烧录时按住不放。新版ESP32的USB CDC可以用原生USB口,但如果你的板子是老式UART转接方案,按BOOT是常规操作。具体型号已刷固件的板子否则自动烧录,遇到“同步失败”或“等待下载”时,先试BOOT键。

烧录成功后,串口监视器右下角波特率我习惯设置为115200(有的板子默认9600,打印乱码就先查波特率)。

4. 接线实操:SPI四线说透了,你就不会再虚焊

4.1 RC522与ESP32的标准SPI接线

RC522模块内部芯片支持SPI和I2C两种接口,绝大多数模块默认工作在SPI模式,由模块上的三个选择位确定。成品模块一般不让你改,所以我们直接用SPI。

ESP32的Arduino内核提供两个SPI通道(VSPI和HSPI),默认的SPI对象对应VSPI,引脚如下:

ESP32引脚(DevKit/NodeMCU-32S)功能接RC522
5SCK(时钟)SCK
23MOSI(主出从入)MOSI
19MISO(主入从出)MISO
18SS(片选,CSN/SDA)SDA
21RST(复位)RST
3.3VVCCVCC
GNDGNDGND
不接IRQIRQ(暂时悬空)

这是最常用的默认配置,MFRC522库的示例代码ReadAndPrint也恰好用这一组引脚,你把ESP32引脚写进去后就能直接跑。

注意:示例代码里用的是SS_PIN = 5还是SS_PIN = 10之类取决于你抄的教程版本。如果某篇博客用10,那个是针对NodeMCU(ESP8266)的软串口映射,不能直接套到ESP32上。一定按你板子的真实SPI引脚来。

4.2 供电逻辑:5V还是3.3V,搞错了会烧模块

RC522芯片工作电压是3.3V,但很多RC522模块板上带了电平转换电路,因此有些卖家会告诉你“可以直接接5V”。这个说法有隐患:

  • 如果你的RC522模块是带三极管电平转换的大板子,接5V是可以工作的,但长期运行模块发热明显。
  • 如果模块是纯3.3V小模块(板子上没有明显电平转换IC),你接5V大概率当场冒烟或者永久损伤。

我的建议统一接3.3V。ESP32的GPIO也是3.3V逻辑,这样最安全,不需要考虑电平匹配。事实上MFRC522的SPI接口过压能力较低,5V直接灌入会有风险。

还要注意:RC522的天线驱动电流较大,接在同一面包板电源轨上时,如果电源走线质量差,可能出现读卡距离缩水的问题。ESP32的USB供电一般能提供500mA,带RC522和蜂鸣器绰绰有余,但面包板电源轨的劣质接插件会导致接触电阻升高,换一根质量好的杜邦线就能解决。

4.3 蜂鸣器和LED怎么接

刷卡反馈是个很重要的体验细节。我通常给蜂鸣器接IO25、LED接IO26,中间连一个220欧姆电阻到GND,蜂鸣器同样串一个限流电阻(有源蜂鸣器3.3V驱动时最好串100欧姆左右)。

这样代码里控制这两个引脚高低电平,就能实现“刷到卡→滴滴+绿灯闪”的效果。要是你的蜂鸣器是无源蜂鸣器,那需要PWM特定频率才能响,新手建议直接买有源蜂鸣器,随便给高电平就发声,省心。

5. 代码走读:从初始化到读卡号的完整链路

5.1 完整示例代码(可直接烧录)

下面这段代码是我在官方示例基础上精简并加了中文注释的版本,直接覆盖MFRC522的读取流程。需要先确保你已经装好了MFRC522库。

#include <SPI.h> #include <MFRC522.h> // 引脚定义(ESP32 DevKit/NodeMCU-32S) #define RST_PIN 21 #define SS_PIN 18 MFRC522 mfrc522(SS_PIN, RST_PIN); // 创建读卡器对象 // 刷卡成功后的提示灯和蜂鸣器 #define BUZZER_PIN 25 #define LED_PIN 26 void setup() { Serial.begin(115200); while (!Serial); // 等待串口就绪 SPI.begin(); // 初始化SPI总线 mfrc522.PCD_Init(); // 初始化RC522芯片 delay(10); mfrc522.PCD_DumpVersionToSerial(); // 打印固件版本,正常能看到 0x92(2.0)等 pinMode(BUZZER_PIN, OUTPUT); pinMode(LED_PIN, OUTPUT); Serial.println("把卡片放到读卡器上方..."); } void loop() { // 每次循环先检查有没有新卡进入感应区 if (!mfrc522.PICC_IsNewCardPresent()) { delay(50); return; } // 选中这张卡,得到它的UID(唯一标识) if (!mfrc522.PICC_ReadCardSerial()) { return; } // 打印卡号 Serial.print("检测到卡片,UID: "); for (byte i = 0; i < mfrc522.uid.size; i++) { Serial.print(mfrc522.uid.uidByte[i] < 0x10 ? "0" : ""); Serial.print(mfrc522.uid.uidByte[i], HEX); if (i != mfrc522.uid.size - 1) { Serial.print(" "); } } Serial.println(); // 提示音和亮灯 digitalWrite(LED_PIN, HIGH); digitalWrite(BUZZER_PIN, HIGH); delay(200); digitalWrite(LED_PIN, LOW); digitalWrite(BUZZER_PIN, LOW); // 停止与当前卡的会话,等待下一张 mfrc522.PICC_HaltA(); delay(1000); }

5.2 代码里的关键原理:这部分看懂了,以后玩任何读卡器都不慌

这一段值得多说几句,因为MFRC522库把底层寄存器操作封装得很好,很多人用起来反而不知道为什么“卡号从这里来”。掌握它,你以后换STM32、树莓派或者其他读卡模块时思路是通的。

第一步是SPI.begin()。RC522通过SPI协议和主控通信,SPI有四个引脚:SCK是时钟线,主机和从机都靠这个时钟同步数据;MOSI是主机到从机的数据线;MISO是从机到主机的数据线;SS是从机片选信号,拉低代表主机选中了这一个从机设备。ESP32就是SPI主机,RC522是从机,数据按字节来回交换。

第二步是PCD_Init()。PCD是“Proximity Coupling Device”的缩写,也就是读卡器端。它做的事情包括:复位RC522芯片、设置发送/接收缓冲区、设置天线增益、激活天线。校准天线是一个细节,读卡距离不够时,很多时候就是初始化后天线驱动电流偏低,可以通过MFRC522的PCD_SetAntennaGain()函数调整寄存器值来加大天线功率。

第三步是PICC_IsNewCardPresent()和PICC_ReadCardSerial()。PICC(Proximity Integrated Circuit Card)指的是卡片端。卡片靠近天线时,天线的电磁场会为卡片内的芯片供能,卡芯片通电后开始响应读卡器的请求。IsNewCardPresent本质上是读RC522的寄存器,看有没有检测到新的卡应答信号。紧接着ReadCardSerial会让读卡器发送防碰撞指令,如果感应区内有多张卡,它会先根据卡号逐位仲裁,最终选中一张,把它的UID取回来。

UID就是卡的全局唯一ID,出厂时已经固化在卡内。M1卡的UID一般4字节(也有7字节的),我们打印的就是这个值。UID对门禁来说就是“身份”,你后续要做考勤、权限管理,都是基于这个UID做比对。

5.3 串口监视器看不到调试信息的自检流程

烧录完打开串口监视器,如果大片空白或者乱码,优先顺序排查:

  1. 波特率是否设置了115200?Arduino IDE右下角修改波特率。
  2. 板子复位后串口监视器会不会打印Boot信息?如果连Boot信息都没有,说明串口硬件连接或驱动有问题。
  3. 如果能看到“把卡片放到读卡器上方...”但没有固件版本号,大概率是SPI接线错误。重查SDA/SCK/MOSI/MISO四条线是否接对,特别是SDA别接到RC522模块的电源引脚上去。
  4. 如果固件版本打印出来了,但放卡没反应:检查卡片协议是不是14443A,以及卡片是否接触到天线感应范围(贴近模块线圈上方,不要隔着金属覆盖物)。

6. 不响应的真正原因:仔细排查比换模块重要得多

6.1 “模块识别不了卡”的常见故障树

作为一个踩坑无数的人,我可以负责任地说:RC522读不到卡,绝大多数时候模块是好的,问题出在以下几类,按概率从高到低排列:

故障现象最可能原因解决办法
初始化打印版本号失败SPI接线错位、SDA/SCK接反用万用表蜂鸣档测连通性,重插杜邦线
版本号正常但读不到卡卡片不是14443A协议(比如是ID低频卡)换M1卡测试
版本号正常且能读卡,但刚刷完就没反应卡停留在感应区时间过长,卡片进入休眠代码加PICC_HaltA后再放卡,或手动拿开再放
能读卡但卡号随机跳变供电不稳、天线接触不良检查电源轨和杜邦线,缩短导线长度
能读卡但偶尔死机供电瞬时电流不足改用独立稳压模块供电,别只用USB

我这里特别强调一种很隐蔽的情况:卡片放在金属面上,或者手掌完全盖住天线。RC522靠电磁感应给卡片供电,金属导体在感应区内会涡流损耗信号能量,导致卡片“无法被唤醒”。如果你把模块贴在铁制桌面板上测试,读不到卡太正常了。把模块悬空或者垫一个绝缘泡沫再测试。

6.2 从寄存器层面排查:PCD_DumpVersionToSerial打印0x00的含义

当你看到版本号输出0x00而不是0x92时,很多人直接怀疑模块坏了。但实际我见过太多案例,问题在:

  • 模块的RST引脚没接对。RC522的复位脚不是摆设,初始化时MCU会控制这个引脚做一次硬件复位。RST悬空或者接错到GND,芯片会一直处于复位状态。
  • MOSI和MISO焊点短路,或者杜邦线内部断裂。
  • 模块的SDA引脚名对应的是SPI的CS(片选)信号,很多人以为是I2C的SDA数据线。接线时如果按字面理解接到21或其他I2C引脚上,SPI自然通不了。

你可以直接把RST_PIN从21改成其他IO口(如16),再用软件复位试试。MFRC522库支持硬件复位和软件复位两种方式,如果硬件RST那条线有干扰,换引脚往往立竿见影。

6.3 给新手的排障实践:断电重插、单独测试、替换法

完整排障顺序,我个人是按“看-摸-换”三步走的:

  1. 看:串口监视器是否打印初始化信息、固件版本。如果没有任何输出,回到第5.3节的串口自检。
  2. 摸:上电后手指轻轻触碰RC522天线线圈,看读卡器是否有响应(某些模块靠近金属会有天线场变化)。如果模块微微发热,基本可以确认供电和芯片工作正常。
  3. 换:换一张卡、换一根杜邦线、换一个USB口。替换法是最无脑但最有效的排查方式,尤其适合零基础——一次只换一个变量,才能定位真凶。

7. 让刷卡事件“主动”起来:外部中断实战

7.1 轮询方式的局限

上面示例代码是轮询模式,loop循环里不停地问“有没有新卡?”。对RFID读卡这种操作,轮询完全够用。但如果你未来想把刷卡做成一个真正的事件触发系统——比如刷卡后执行很长的任务流程,轮询就会暴露问题:长时间执行其他任务时,卡片感应到了但代码根本来不及读取,卡已经离开了。

这也是关联热搜词里“esp32外部中断实战”很重要的原因。ESP32支持GPIO外部中断,你可以在IRQ引脚上做一个“卡片靠近”的通知机制。

RC522模块的IRQ引脚在感应到卡片时会拉低(低电平有效)。我们只需要把这个引脚接在ESP32的一个支持中断的GPIO上,在代码里注册一个下降沿中断服务函数,设置一个标志位,主循环检测到标志位后再去读卡,效率会高很多,逻辑也更优雅。

7.2 ESP32中断代码改造示例

#include <SPI.h> #include <MFRC522.h> #define RST_PIN 21 #define SS_PIN 18 #define IRQ_PIN 22 // RC522的IRQ脚接ESP32的GPIO22 MFRC522 mfrc522(SS_PIN, RST_PIN); volatile bool cardDetected = false; void IRAM_ATTR onCardInterrupt() { cardDetected = true; } void setup() { Serial.begin(115200); SPI.begin(); mfrc522.PCD_Init(); // RC522 IRQ配置:启用“新卡到达”中断 pinMode(IRQ_PIN, INPUT_PULLUP); attachInterrupt(digitalPinToInterrupt(IRQ_PIN), onCardInterrupt, FALLING); Serial.println("中断模式启动"); } void loop() { // 中断来了,再执行读卡流程 if (cardDetected) { cardDetected = false; if (mfrc522.PICC_IsNewCardPresent() && mfrc522.PICC_ReadCardSerial()) { for (byte i = 0; i < mfrc522.uid.size; i++) { Serial.print(mfrc522.uid.uidByte[i], HEX); } Serial.println(); mfrc522.PICC_HaltA(); } } }

这里需要提醒:中断服务函数里不能做耗时操作,比如打印、读卡、驱动蜂鸣器。ESP32的中断服务函数被限制在IRAM中运行,代码量要尽量少,我们只置一个标志位,实际工作交给loop完成。另外,RC522模块的IRQ在复位后默认是“天线驱动开启”状态,需要确保初始化时芯片的IRQ输出模式已正确配置,否则可能一直触发中断。MFRC522库本身没有直接开放这一层配置,如果你发现IRQ乱跳,可以用mfrc522.PCD_WriteRegister手动设置寄存器,但零基础阶段建议先用轮询模式跑通项目,再做中断优化。

7.3 中断方案的适用场景

外部中断适合:

  • 复用MCU做多任务,不想让loop循环频繁轮询。
  • 刷卡事件需要以“低延迟”响应,比如门禁锁控制系统。
  • 想学ESP32中断编程,为更复杂项目打基础。

轮询适合:

  • 新手第一次跑通RFID流程。
  • 一个项目里只有刷卡这一件事,没必要引入中断复杂度。

说实话,我自己做的门禁原型用的就是中断方案,因为同时还要驱动OLED显示、继电器开锁、做网络事件上报。但如果你还在入门阶段,先把轮询代码跑通,再升级到中断,你会对两者的差异有非常直观的感受。

8. 把“刷卡”变成“门禁”:数据校验与多卡管理

8.1 把卡号变成白名单

单纯打印UID只是第一步,真正让ESP32“拥有刷卡能力”的是对卡号的判断。比如你已经读出了卡号A1 B2 C3 D4,你希望在只有特定卡被刷时开锁。

代码追加一个简单的校验:

byte authorizedUID[4] = {0xA1, 0xB2, 0xC3, 0xD4}; bool isAuthorized(MFRC522::Uid uid) { if (uid.size != 4) return false; for (byte i = 0; i < 4; i++) { if (uid.uidByte[i] != authorizedUID[i]) return false; } return true; }

然后在读取到卡号后判断:

if (isAuthorized(mfrc522.uid)) { Serial.println("授权卡,开锁!"); // 控制继电器或者舵机 } else { Serial.println("未授权卡,报警?"); }

这个白名单你可以存放在数组中,也可以后续扩展到Preferences库写入NVS闪存、通过WiFi从云端拉取、或者在串口/小程序里动态添加。多卡管理本质上就是管理一个数组或者列表,比如:

byte whitelist[8][4] = { {0xA1, 0xB2, 0xC3, 0xD4}, {0x12, 0x34, 0x56, 0x78}, // 最多8张 };

零基础阶段先把数组做起来,后面再慢慢换成更复杂的存储方案。多卡管理时记得留一条“忘记自己卡号导致连锁不上”的路:给系统加一个“录入模式”,用串口发指令进入学习状态,然后刷一张新卡自动把UID加入白名单。

这套思路可以直接套用到考勤系统。热搜词里出现“C# RFID考勤系统”,说明不少人在做上位机。ESP32读卡后通过串口或WiFi把UID发到PC端的C#程序,C#负责和数据库交互、统计考勤记录。对零基础来说,先把ESP32端“读到的卡号可靠地发给电脑”这件事做扎实,后面的上位机只是流程问题。

8.2 一卡一密与安全边界

RC522能做的远不止读UID。M1卡(S50)内部有16个扇区,每个扇区有独立的KEY A、KEY B和数据块。这意味着你可以在卡内存储自定义数据,比如钱包余额、工号、权限等级。

但这里必须提一句:M1卡的安全性并没有你想的那么高。经典的M1卡加密算法已经被破解多年,卡内的密文理论上可以被克隆和篡改,这就是为什么很多新门禁系统开始转向CPU卡和手机NFC动态凭证。如果你用RC522做门禁,建议:

  • 不要只凭UID判断权限,UID可以复制到空白卡(俗称“复制卡”),物理安全级别很低。
  • 如果只是学习和技术研究,完全无所谓;如果真要部署到真实场景,要把安全风险考虑进去。

我在做考勤项目时,会把卡内写入用户ID+校验位,不直接暴露数据库主键,并且每次读取做二次校验。这套方案不够高级,但足以应对学习场景。

8.3 写卡操作:往M1卡里存数据

除了读UID,你还可以写卡。MFRC522库自带一个MFRC522_DumpInfo示例,展示如何读取扇区数据。往指定扇区写数据的关键代码大致是:

MFRC522::MIFARE_Key key; for (byte i = 0; i < 6; i++) key.keyByte[i] = 0xFF; // 默认密钥FF MFRC522::StatusCode status = mfrc522.PCD_Authenticate( MFRC522::PICC_CMD_MF_AUTH_KEY_A, trailerBlock, &key, &(mfrc522.uid) ); status = mfrc522.MIFARE_Write(blockNumber, buffer, 16);

注意写卡前必须先用密钥认证(默认出厂密钥是FFFFFFFFFFFF)。写卡之后可以用读卡器读回来校验数据是否一致。这个操作的实际意义是:你可以给每张卡打上编号、姓名编码、有效期等信息,下次刷卡时不靠UID而靠卡内数据判断身份。

对零基础来说,写卡是一个很好的进阶练手项目,能帮你理解卡的内部存储结构,但我不建议第一个项目就碰,容易因为一个字节的误差卡住一整晚。

9. 扩展玩法:从读卡到真正的IoT门禁系统

9.1 接入WiFi:让刷卡记录远程可见

ESP32自带WiFi和蓝牙,这是它比普通Arduino强太多的地方。你可以把每一张刷卡记录通过MQTT或者HTTP请求发给服务器、小程序、或者本地已经跑起来的Nodered。

我最常用的一个组合是:

  • ESP32做主控,RC522读卡。
  • 刷卡后ESP32通过WiFi向本地MQTT broker(比如Mosquitto)发送一条包含UID和时间戳的JSON消息。
  • 服务端订阅MQTT消息,存入数据库,前端展示。

ESP32发送MQTT数据极其简单,先安装PubSubClient库,然后:

#include <WiFi.h> #include <PubSubClient.h> const char* ssid = "你的WiFi"; const char* password = "密码"; const char* mqttServer = "192.168.1.100"; WiFiClient espClient; PubSubClient client(espClient); void setup() { WiFi.begin(ssid, password); while (WiFi.status() != WL_CONNECTED) { delay(500); } client.setServer(mqttServer, 1883); } void sendLog(byte* uid, byte size) { if (!client.connected()) { client.connect("ESP32RFID"); } char msg[64]; sprintf(msg, "{\"uid\":\""); for (byte i = 0; i < size; i++) { sprintf(msg + strlen(msg), "%02X", uid[i]); } strcat(msg, "\"}"); client.publish("home/rfid/access", msg); }

这一套实现之后,刷卡就不仅仅是一个单机活动,而是整个IoT系统的一部分。你可以在手机小程序上看到谁在几点刷了卡,也可以在服务端做异常时段报警。

9.2 用PlatformIO重构:工程化管理与OTA升级

如果你的项目规模变大,比如有了Web配置界面、OTA、自定义库文件,从Arduino IDE迁移到PlatformIO会很值得。PlatformIO本质上是一套基于VS Code的嵌入式开发环境,它帮你管理库依赖和编译配置。

在PlatformIO里新建ESP32项目时,platformio.ini配置核心只需要这几行:

[env:esp32dev] platform = espressif32 board = esp32dev framework = arduino lib_deps = miguelbalboa/MFRC522

然后把Arduino代码放到src/main.cpp中,编译烧录就完成了。PlatformIO的好处是每次构建不会因为“库版本被自动更新”而出现莫名其妙的问题,所有依赖都锁定在lib_deps里。另一个优点是你可以很方便地配置board_build.flash_size等参数,对不同开发板做适配。

这里又回到热搜词里的“platformio开发esp32项目,esp32加快编译速度”。PlatformIO首次编译ESP32项目要拉取工具链,非常慢。我的建议是:别开着实时杀毒软件扫项目文件夹,把项目的~/.platformio目录加入杀毒排除名单;另外编译时不开太多VS Code插件,能明显改善编译速度。

9.3 MicroPython思路:换一种语言玩RFID

如果你对C++编译不感兴趣,也可以用MicroPython。ESP32刷入MicroPython固件后,可以用Python代码直接调用MFRC522模块。网上有一个叫mfrc522-python的库,GitHub上能找到,安装方式通常是上传mfrc522.py到板子。

一个最简单的MicroPython读取版本:

from mfrc522 import MFRC522 from machine import Pin, SPI spi = SPI(2, baudrate=1000000, polarity=0, phase=0) rdr = MFRC522(spi=spi, gpioRst=21, gpioCs=18) print("Place card...") while True: (stat, tag_type) = rdr.request(rdr.REQIDL) if stat == rdr.OK: (stat, raw_uid) = rdr.anticoll() if stat == rdr.OK: uid = "{} {} {} {}".format( raw_uid[0], raw_uid[1], raw_uid[2], raw_uid[3]) print("Read UID:", uid) rdr.stop_crypto1() # 在这里做判断和处理

MicroPython的调试速度是真快,改完就能跑,尤其适合逻辑迭代。不过MicroPython生态在ESP32上的中断、底层寄存器访问、时序敏感任务上表现不如Arduino框架稳健。如果你要做高频读卡或复杂延时逻辑,还是回Arduino C++更稳。

10. 项目中后期必须面对的稳定性问题

10.1 看门狗:死机后自动重启

在真实运行场景中,ESP32偶尔会卡死。最常见的原因是WiFi连接超时等待阻塞了loop,或者电源波动导致外设无响应。解决思路是启用看门狗定时器(WDT)。

Arduino框架下有两种方式:软件看门狗esp_task_wdt_init或者简单地在loop里用millis()做超时判断。如果是WiFi卡住,更要紧的是给WiFi.begin设置超时重试逻辑,而不是依赖看门狗去重启。

// 简易WiFi连接超时处理 int wifiAttempts = 0; WiFi.begin(ssid, password); while (WiFi.status() != WL_CONNECTED && wifiAttempts < 20) { delay(500); wifiAttempts++; } if (WiFi.status() != WL_CONNECTED) { Serial.println("WiFi连接失败,稍后重试"); }

这一点非常重要,很多做门禁项目的人把设备部署后,过几天就发现设备“掉线了”,其实就是WiFi连接状态的异常导致系统假死。

10.2 数据的持久化:断电后白名单不能丢

默认情况下,白名单存在数组里,断电就清零。如果想让设备记录最新状态,有两条路线:

  1. 使用ESP32的Preferences库(对Arduino框架好用):
#include <Preferences.h> Preferences prefs; prefs.begin("rfid", false); uint8_t uid = prefs.getUChar("auth0", 0); prefs.putUChar("auth0", uid);
  1. 存到SD卡/TF卡模块。RC522项目和SD卡模块可以共存于同一个SPI总线上,但要处理好片选引脚,SD卡用CS15,RC522用CS18,互不干扰。

  2. 通过WiFi把白名单放到云端,掉电后从云端拉取。这才是真正稳定的方案。

10.3 低功耗:电池供电的门禁怎么省电

如果你的刷卡设备要装到户外或者门边,大概率不能用插电适配器。ESP32的深睡模式(Deep Sleep)方案很成熟:

  • 平时进入深度睡眠,电流可以做到几十微安。
  • 外部唤醒方式很多,比如RC522的IRQ引脚可以通过EXT1唤醒ESP32。

但要命的问题是:RC522模块本身耗电不低,模块通电时的功耗通常在几十到上百毫安,深睡电路设计不好等于没省。所以低功耗方案需要给RC522单独加MOS管控制电源,刷卡前才给模块上电,读完之后再断电,同时配合外部中断唤醒。这是另外一个深入的话题,零基础可以先了解方向,等基础扎实再搞。

11. 五六个项目之后回头看:我的几点实在经验

做RFID刷卡这个项目,我从“读个卡号都欣喜若狂”到后来做完整的门禁考勤系统,中间踩过的坑基本都写在这篇文章里了。最后随手列几条我自己最深的体会:

一是先把最简单的流程完整跑通,再谈优化。很多人一上来就想做“刷卡+OLED+WiFi+小程序”,结果每个环节都在翻车,最后根本不知道是哪个模块的问题。我的习惯是:先只读卡号,驱动亮灯;再判断白名单;再上OLED;再连WiFi。每加一块功能,都确认前面还好好的没有坏,这样排障半径始终很小。

二是RC522的读卡距离天生有限。它的天线是PCB蚀刻线圈,有效距离大约3到6厘米。如果你的项目需要远距离感应(比如停车场扣费),RC522不是合适的方案,去看125kHz或超高频UHF模块。

三是接线永远比你想象的更关键。我见过太多“模块坏了”的冤案,最终发现是杜邦线内部断了,或者是面包板同一排插孔因为老化接触不良。你要养成一个好习惯:模块不工作,第一件事是万用表测一下引脚电压和导通性,而不是立刻下单买新模块。

四是记录你的卡号。做测试时打印的UID一定要截图或者抄下来存好。因为一旦你做了白名单校验,忘记录入白名单的卡就会变成“打不开门的卡”,而你自己又不知道哪张才是对的,那画面很难看。

最后再说一个实用技巧:串口监视器里把波特率统一设置成115200后,无论是Arduino IDE还是PlatformIO的串口监视器都要保持一致,否则你看到的调试输出是乱码或者完全没有。这个细节我教过好几个人,他们都在这里白白浪费过时间。

RFID这个方向,入门门槛低,但往上走的空间其实不小——从读UID到读写扇区、从单机判断到联网考勤、从轮询到中断唤醒,每一步都能学到新的东西。你拿着这篇文章跑通第一张卡之后,后面想怎么玩,就全凭你的想象力了。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询