ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

ESP32 RFID刷卡项目实战:MFRC522与SPI通信从入门到联网考勤

ESP32 RFID刷卡项目实战:MFRC522与SPI通信从入门到联网考勤 1. 从零上手为什么选择ESP32搭配RFID做刷卡项目很多人第一次接触物联网开发都是从一块ESP32开发板开始的。这块芯片便宜、资料多、自带WiFi和蓝牙几乎能满足入门阶段的所有想象。但光会点灯、读温湿度总觉得少了点“实战感”。这时候RFID无线射频识别技术就是一个非常好的进阶方向——它能让你的开发板真正拥有“刷卡”能力做出门禁、考勤、消费机这类看得见摸得着的项目。RFID这个词听起来有点专业其实拆开看很简单Radio Frequency Identification无线射频识别。它的核心原理是读卡器发射特定频率的电磁波卡片内部有一个线圈和芯片靠电磁感应取电然后把存储的卡号信息通过电磁波回传。整个过程不需要接触也不需要电池卡片可以做得像一张薄薄的塑料片。我们常见的门禁卡、公交卡、食堂饭卡绝大多数都是这个原理。那为什么偏偏选ESP32来做这件事原因有几个。第一ESP32的GPIO资源丰富SPI接口稳定驱动MFRC522这类读卡模块非常顺手。第二它自带网络能力读到卡号之后可以直接上传到服务器或者推送到手机做成真正的联网考勤系统。第三MicroPython和Arduino两种开发方式都有成熟的RFID库零基础也能快速跑通。第四成本极低一块ESP32开发板加一个MFRC522模块总共不到三十块钱试错成本几乎为零。这个项目适合谁如果你刚学完ESP32的点灯和串口打印想找一个有实际用途的练手项目那这个方向非常合适。如果你已经会Arduino想转到MicroPython或者ESP-IDF也可以拿这个项目做过渡。甚至你只是想给家里的抽屉做个电子锁或者给孩子做一个刷卡玩具这套方案都能直接落地。我自己的第一块MFRC522模块是好几年前买的当时照着网上的教程接线结果死活读不出卡。后来才发现是SPI引脚接错了白白折腾了一个晚上。所以这篇文章我会把接线、配置、代码、排查这几个环节都讲透让你少走弯路。2. 核心硬件与协议拆解MFRC522和SPI到底怎么配合2.1 MFRC522模块的关键参数与选型要点MFRC522是NXP推出的一款高度集成的非接触式读写芯片工作频率13.56MHz支持ISO 14443A协议。市面上常见的“RFID-RC522”模块就是基于这颗芯片做的板载了天线线圈和匹配电路通过排针引出SPI接口。它的供电范围是2.5V到3.3V这一点非常重要——ESP32的IO也是3.3V电平所以可以直接连接不需要电平转换。如果你用的是5V的Arduino Uno那就需要加电平转换或者分压否则长期工作可能损坏模块。模块上有一排8个引脚分别是SDA片选、SCK时钟、MOSI主出从入、MISO主入从出、IRQ中断、GND、RST复位、3.3V。注意这里的SDA并不是I2C的数据线而是SPI的片选信号CSNXP的命名习惯叫NSS或者SDA容易让人误解。IRQ引脚在轮询模式下可以不接但如果你想用外部中断的方式读卡就需要把它接到ESP32的某个GPIO上。选型的时候要注意市面上有些模块焊接质量参差不齐天线线圈的匹配电容虚焊会导致读卡距离很短正常应该能读到3到5厘米。如果你买到的模块只能贴着卡才能读大概率是天线部分有问题可以直接退换。另外模块的工作电流在26mA左右峰值可能到100mA所以不要用ESP32的3.3V引脚直接供电最好从开发板的3.3V稳压输出取电或者单独供3.3V。2.2 SPI通信协议的核心机制SPI全称Serial Peripheral Interface是一种高速全双工同步串行总线。它需要四根线SCK、MOSI、MISO、CS。主设备产生时钟数据在时钟的边沿上被采样。SPI有四种模式由CPOL时钟极性和CPHA时钟相位组合决定。MFRC522支持模式0CPOL0CPHA0和模式3CPOL1CPHA1实际使用中模式0最常用。SPI的片选机制值得单独说一下。每个从设备都有一根独立的CS线主设备在通信前把对应的CS拉低通信结束后拉高。如果总线上有多个从设备比如同时接了RFID模块和SD卡模块就必须用不同的GPIO分别控制片选否则数据会冲突。这就是所谓的“硬件片选”。有些场景下GPIO不够用也可以用软件片选但时序不好控制容易出问题不建议新手尝试。MFRC522的SPI通信速率最高支持10Mbps但实际用ESP32驱动的时候建议先降到1MHz左右调试等跑通之后再往上提。因为杜邦线太长、接触不良、电源纹波大都可能导致高速通信失败。我实测下来用20厘米的杜邦线4MHz可以稳定工作再高就容易出现读卡失败。2.3 ESP32的SPI资源分配ESP32有两个硬件SPI控制器分别是HSPI和VSPI。VSPI的默认引脚是GPIO23MOSI、GPIO19MISO、GPIO18SCK、GPIO5CS。HSPI的默认引脚是GPIO13、GPIO12、GPIO14、GPIO15。在Arduino环境下通常直接用VSPI的默认引脚接线最省事。在MicroPython里可以自由指定引脚灵活性更高。这里有一个坑要注意ESP32的GPIO6到GPIO11是连接内部Flash的绝对不能占用。GPIO34到GPIO39是只输入引脚不能作为输出使用所以CS、RST这些输出信号不能接在这些引脚上。另外GPIO0、GPIO2、GPIO12、GPIO15在启动时有特殊功能如果外接电路拉低了这些引脚可能导致ESP32无法正常启动。我在实际项目中就遇到过因为RST接在GPIO15上导致开发板反复重启的情况后来换到GPIO22就正常了。3. 实操环境搭建从接线到第一个读卡程序3.1 硬件接线与供电检查先把手头的材料列一下ESP32开发板一块任意型号推荐ESP32 DevKit V1或者ESP32-S3 Super Mini、MFRC522模块一个、杜邦线若干、面包板一块、 Mifare Classic 1K卡片一张买模块的时候一般会附赠一张白卡和一个钥匙扣。接线方案如下表所示以VSPI默认引脚为例MFRC522引脚ESP32引脚说明SDA/CSGPIO5片选信号SCKGPIO18SPI时钟MOSIGPIO23主出从入MISOGPIO19主入从出IRQ不接轮询模式忽略GNDGND共地RSTGPIO22复位信号3.3V3.3V供电接完线之后先不要急着写代码用万用表量一下模块的3.3V和GND之间有没有短路。我见过有人把电源接反模块瞬间发烫冒烟。确认无误后给ESP32上电观察模块上的LED有没有微弱亮光正常的话应该能看到天线附近有轻微的闪烁。注意MFRC522模块的3.3V引脚绝对不能接5V否则芯片会烧毁。ESP32的3.3V输出电流有限如果同时接了屏幕、传感器等外设建议单独给RFID模块供电。3.2 Arduino IDE环境配置与库安装如果你习惯用Arduino IDE需要先安装ESP32的开发板支持。打开文件-首选项在附加开发板管理器网址里填入ESP32的板管理地址然后在开发板管理器里搜索esp32并安装。这个过程在国内网络环境下可能比较慢可以找离线的安装包或者用PlatformIO它的库管理更省心。安装完开发板支持后还需要装MFRC522库。在库管理器里搜索“MFRC522”选择Miguel Balboa维护的那个版本安装即可。这个库封装了SPI通信的底层细节对外提供了PCD_Init()、PICC_IsNewCardPresent()、PICC_ReadCardSerial()这些函数用起来很直观。下面是一个最简读卡程序我加了详细注释#include SPI.h #include MFRC522.h #define SS_PIN 5 #define RST_PIN 22 MFRC522 rfid(SS_PIN, RST_PIN); void setup() { Serial.begin(115200); SPI.begin(18, 19, 23, 5); // SCK, MISO, MOSI, SS rfid.PCD_Init(); Serial.println(RFID reader ready); } void loop() { // 检测是否有新卡片进入感应区 if (!rfid.PICC_IsNewCardPresent()) { return; } // 读取卡片序列号 if (!rfid.PICC_ReadCardSerial()) { return; } Serial.print(UID: ); for (byte i 0; i rfid.uid.size; i) { Serial.print(rfid.uid.uidByte[i] 0x10 ? 0 : ); Serial.print(rfid.uid.uidByte[i], HEX); } Serial.println(); // 停止与卡片的通信避免重复读取 rfid.PICC_HaltA(); }烧录进去之后打开串口监视器波特率设为115200。把卡片靠近模块天线应该能看到UID打印出来。如果没反应先检查接线再检查串口是否选对了端口。3.3 MicroPython方案与固件烧录如果你更喜欢MicroPython流程也不复杂。首先需要给ESP32烧录MicroPython固件。去MicroPython官网下载对应ESP32型号的.bin文件然后用esptool工具烧录。命令大概是这样的esptool.py --chip esp32 --port /dev/ttyUSB0 erase_flash esptool.py --chip esp32 --port /dev/ttyUSB0 --baud 460800 write_flash -z 0x1000 esp32-20230426-v1.20.0.bin烧录完成后用Thonny或者mpremote连接开发板把MFRC522的MicroPython驱动文件上传上去。驱动文件可以从GitHub上找搜索“micropython-mfrc522”就能找到。上传之后写一个简单的测试脚本from machine import Pin, SPI from mfrc522 import MFRC522 import time spi SPI(2, baudrate1000000, polarity0, phase0, sckPin(18), mosiPin(23), misoPin(19)) cs Pin(5, Pin.OUT) rst Pin(22, Pin.OUT) reader MFRC522(spi, cs, rst) while True: stat, tag_type reader.request(reader.REQIDL) if stat reader.OK: stat, uid reader.anticoll() if stat reader.OK: print(UID:, [hex(i) for i in uid]) time.sleep(0.2)MicroPython的好处是交互性强可以在REPL里直接调试改一行跑一行。缺点是SPI的底层控制不如Arduino灵活遇到时序问题排查起来稍微麻烦一点。4. 功能进阶从读卡号到完整刷卡系统4.1 卡号解析与白名单匹配读到UID只是第一步真正要做门禁或者考勤还需要把卡号和权限对应起来。Mifare Classic 1K卡片的UID通常是4字节或者7字节4字节的UID理论上有约42亿种组合实际使用中碰撞概率很低。我们可以把授权的卡号存在一个数组里每次读到卡就遍历比对。byte authorizedUIDs[][4] { {0xA1, 0xB2, 0xC3, 0xD4}, {0x11, 0x22, 0x33, 0x44} }; bool isAuthorized(byte *uid, byte size) { for (int i 0; i sizeof(authorizedUIDs) / 4; i) { if (memcmp(uid, authorizedUIDs[i], 4) 0) { return true; } } return false; }这种硬编码的方式适合卡片数量少、不常变动的场景。如果卡片多或者需要远程管理就应该把白名单存到ESP32的NVS或者SPIFFS文件系统里甚至上传到服务器做校验。4.2 外部中断读卡与低功耗优化轮询方式虽然简单但CPU一直在跑循环功耗下不来。如果你的项目是电池供电比如无线门锁就需要用中断方式。MFRC522的IRQ引脚在有卡片进入时会输出低电平把它接到ESP32的某个支持中断的GPIO上配置下降沿触发就能让ESP32在待机时进入light sleep有卡才唤醒。#define IRQ_PIN 27 void IRAM_ATTR cardDetected() { // 中断服务函数尽量简短 cardPresent true; } void setup() { pinMode(IRQ_PIN, INPUT_PULLUP); attachInterrupt(digitalPinToInterrupt(IRQ_PIN), cardDetected, FALLING); }这里有个细节中断服务函数里不能做SPI通信因为SPI传输耗时较长会阻塞其他中断。正确的做法是在中断里置一个标志位然后在主循环里检测标志位再读卡。另外ESP32的light sleep唤醒后SPI外设需要重新初始化这一点在写低功耗代码时要注意。4.3 联网上传与考勤记录ESP32的WiFi能力是这个项目最大的加分项。读到卡号之后可以通过HTTP POST把数据发到服务器或者用MQTT推送到物联网平台。下面是一个简单的HTTP上传示例#include WiFi.h #include HTTPClient.h void uploadCard(String uid) { HTTPClient http; http.begin(http://your-server.com/api/attendance); http.addHeader(Content-Type, application/json); String payload {\uid\:\ uid \,\time\: String(millis()) }; int code http.POST(payload); Serial.printf(Response: %d\n, code); http.end(); }实际部署的时候建议加上断网重传机制。把读到的卡号先存到本地队列里网络恢复后再批量上传避免数据丢失。另外时间戳最好用NTP同步不要用millis()否则设备重启后时间就乱了。5. 常见问题排查与避坑经验5.1 读卡失败问题速查表现象可能原因排查方法完全无反应供电不足或接线错误量3.3V电压检查SPI四线串口无输出波特率不对或端口选错确认115200换USB线偶尔读到卡天线接触不良或距离太远换模块卡片贴近天线读到的UID乱码SPI速率过高或线太长降到1MHz缩短杜邦线开发板反复重启RST或CS接在特殊引脚换到GPIO22、GPIO5只能读一次未调用PICC_HaltA每次读完停止通信5.2 我踩过的几个坑第一个坑是电源。我一开始用ESP32的3.3V给模块供电同时接着一个OLED屏幕结果读卡距离只有不到1厘米。后来单独给RFID模块供3.3V距离立刻恢复到4厘米。所以外设多的时候供电一定要分开考虑。第二个坑是SPI引脚冲突。有一次我把MISO接在了GPIO12上结果ESP32启动就失败。查资料才知道GPIO12是MTDI启动时如果被拉高会影响Flash电压选择。后来换成GPIO19就正常了。所以接线前一定要查一下引脚的特殊功能。第三个坑是卡片类型。MFRC522只支持13.56MHz的ISO 14443A卡像125kHz的EM4100卡是读不了的。我一开始拿了一张旧门禁卡怎么都读不出来后来换Mifare Classic 1K才成功。买卡的时候一定要看清频率和协议。第四个坑是MicroPython的SPI极性。默认的SPI(2)参数和MFRC522的要求可能不一致需要显式设置polarity0、phase0。我一开始没设读出来的数据全是0xFF调了半天才发现是模式不对。5.3 提升稳定性的几个实用技巧杜邦线尽量短最好控制在10厘米以内。如果必须长距离连接考虑用屏蔽线或者把模块直接插在开发板上。电源引脚旁边并一个100uF的电解电容和一个0.1uF的陶瓷电容能有效滤除纹波。读卡天线附近不要放金属物体金属会吸收电磁波导致读卡距离骤降。如果要做成成品模块和天线最好固定在非金属外壳内。另外MFRC522的RST引脚在有些模块上默认接了上拉电阻如果你不接RST模块也能工作但复位不可控。建议还是接上初始化的时候手动复位一次能提高稳定性。6. 项目扩展方向与个人体会这套刷卡系统跑通之后可以扩展的方向很多。比如加一个继电器模块做成真正的门禁控制加一个蜂鸣器和LED刷卡时给出声光反馈加一个0.96寸OLED显示卡号和授权状态把数据上传到云端做成Web端的考勤报表。如果对边缘AI感兴趣还可以把读到的卡号和本地的人脸识别结果做双重验证提升安全性。我个人在实际操作中的体会是RFID项目最难的不是写代码而是硬件调试。SPI通信对时序和电源非常敏感同样的代码换一套线材可能就跑不起来。所以遇到问题不要怀疑代码先查硬件。另外MFRC522这个模块虽然便宜但不同批次的品质差异很大建议一次买两三个备用方便交叉验证。最后分享一个小技巧如果你手头没有Mifare卡可以用带NFC功能的手机模拟一张卡来测试。安卓手机装一个NFC工具应用把手机靠近模块也能读到UID。这样在没有实体卡的时候也能快速验证硬件是否正常。
返回列表