ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

ESP32与MFRC522实战:从零搭建RFID读卡系统

ESP32与MFRC522实战:从零搭建RFID读卡系统 1. 从零上手为什么选ESP32加RFID这个组合很多人第一次接触物联网硬件都是从一块ESP32开发板开始的。它便宜、资料多、自带Wi-Fi和蓝牙拿来做个小项目成就感来得特别快。但玩久了你会发现光会点灯、读温湿度总觉得少了点什么——设备只能“感知”环境却没法“识别”人。而RFID无线射频卡这个东西恰好补上了这块短板刷一下卡设备就知道“你是谁”这一下子就把项目从玩具级别拉到了实用级别。我最早做RFID项目是为了给工作室做一个简单的门禁记录。当时试过好几种方案最后发现ESP32加MFRC522模块是最省心的组合。MFRC522是一颗非常经典的13.56MHz非接触式读写芯片支持ISO 14443A协议市面上常见的M1卡、S50卡、UID卡它都能读。关键是这个模块便宜十几块钱就能买到配套的库也成熟MicroPython和Arduino两边都有现成轮子不用自己从寄存器开始啃。这篇文章面向的是完全没有RFID基础的ESP32玩家。你不需要懂射频理论也不需要会画电路板只要你会给ESP32烧录程序、能看懂简单的接线图就能跟着把整套东西跑起来。我会从硬件选型、接线、SPI通信原理一路讲到代码实现、常见故障排查最后再聊聊这个技术能延伸出哪些真正有用的项目。中间会穿插我自己踩过的坑比如读卡距离忽远忽近、SPI片选搞错导致读不到卡、断电后程序不跑等等这些都是文档里不会写、但实际做的时候一定会遇到的问题。先把结论放前面ESP32负责逻辑和联网MFRC522负责射频识别两者通过SPI总线通信。你只要把这三者的关系理顺剩下的就是调参数和写业务逻辑的事。下面我按实际操作的顺序一步步拆开讲。2. 硬件选型与接线别让一根线毁掉整个项目2.1 MFRC522模块怎么挑市面上MFRC522模块版本挺多价格从几块到几十块都有。我建议优先选带3.3V稳压和电平转换的版本因为ESP32的IO是3.3V逻辑而有些便宜模块是5V供电的直接接上去短时间可能能用时间长了容易出问题。模块上一般有8个引脚SDA片选、SCK时钟、MOSI主出从入、MISO主入从出、IRQ中断、GND、RST复位、3.3V供电。这里有个特别容易搞混的点MFRC522模块上标的SDA其实就是SPI的CS片选不是I2C那个SDA。我第一次用的时候就被这个名字坑了以为是I2C接口接了半天没反应。后来查资料才知道这个模块支持SPI、I2C、UART三种模式但默认出厂是SPI模式板子上印的SDA只是沿用了I2C的丝印习惯。所以你接线的时候把它当CS用就对了。配套的卡建议买M1卡Mifare Classic 1K这是最通用的每个扇区有独立的密钥可以做简单的权限管理。另外备几张UID卡用来做复制测试。钥匙扣卡和异形卡也可以买几个做成品的时候好看。2.2 和ESP32的接线方案ESP32的SPI引脚不是固定的大部分GPIO都可以通过矩阵映射成SPI功能但为了稳定我习惯用默认的VSPI引脚MFRC522引脚ESP32引脚说明SDA/CSGPIO5片选低电平有效SCKGPIO18时钟MOSIGPIO23主设备输出MISOGPIO19主设备输入IRQ不接用轮询方式即可GNDGND共地RSTGPIO22复位3.3V3.3V供电接线的时候有两条铁律第一供电一定是3.3V不要接5V第二GND必须共地不共地的话SPI通信会时好时坏这个坑我踩过查了一晚上才发现是地线没接牢。另外线不要太长超过20厘米就容易受干扰读卡距离会明显缩短。2.3 读卡距离的影响因素官方标称读卡距离是0到60毫米但实际用下来普通M1卡大概在2到3厘米比较稳定。影响距离的因素有几个天线线圈的大小、卡的朝向、周围有没有金属或强电磁干扰。如果你把模块贴在金属表面上读卡距离会直接掉到几毫米因为金属会吸收射频能量。解决办法是在模块和金属之间垫一层绝缘材料或者干脆换个位置。提示调试阶段先把模块悬空放置不要固定在任何金属或带铜箔的表面上等程序跑通了再考虑结构安装。3. SPI通信原理搞懂它排查问题快十倍3.1 SPI到底是怎么工作的SPI全称是Serial Peripheral Interface中文叫串行外设接口。你可以把它想象成一条主从结构的流水线ESP32是主设备MFRC522是从设备主设备负责发时钟信号从设备跟着时钟节拍收发数据。这条流水线上有四根线SCK是节拍器MOSI是主设备往从设备送数据MISO是从设备往主设备送数据CS是选中信号告诉从设备“现在轮到你了”。SPI是全双工的也就是说发送和接收可以同时进行。每来一个时钟脉冲主设备移出一位到MOSI同时从MISO移入一位。所以你在写代码的时候经常会看到“发送一个字节同时收到一个字节”这种操作这不是bug是SPI的固有特性。MFRC522的SPI模式支持最高10MHz的时钟但实际用的时候我建议先降到1MHz左右调试稳定之后再往上提。ESP32的SPI时钟分频很灵活MicroPython里可以直接设波特率Arduino里通过SPISettings配置。3.2 片选信号的硬件与软件之分片选CS是SPI里最容易出问题的地方。一个SPI总线上可以挂多个从设备靠CS来区分。CS有两种控制方式硬件片选和软件片选。硬件片选是ESP32的SPI外设自动控制的你配置好之后发送数据时硬件会自动拉低对应的CS引脚。软件片选就是你自己用GPIO控制发送前拉低发送后拉高。MFRC522模块一般用软件片选就够了因为总线上通常只有它一个设备。但这里有个细节CS拉低和拉高之间要保证足够的建立时间和保持时间。如果拉得太快从设备还没反应过来数据就丢了。我在MicroPython里遇到过这个问题后来在CS操作之间加了微秒级的延时才稳定。Arduino的库一般已经处理好了不用太操心。3.3 寄存器操作的基本逻辑MFRC522内部有一堆寄存器读写卡的过程本质上就是通过SPI读写这些寄存器。比如你要寻卡就是往FIFO寄存器写命令然后读状态寄存器看结果。MFRC522的寄存器地址是6位的加上1位读写标志和1位地址对齐凑成一个字节。以读一个寄存器为例流程是这样的先拉低CS发送地址字节最高位置1表示读然后发送一个空字节同时从MISO读回寄存器的值最后拉高CS。写寄存器则是最高位清0后面跟着要写的数据。这个时序在数据手册里有详细说明但实际写代码的时候库函数已经封装好了你只需要调用PCD_ReadRegister和PCD_WriteRegister就行。不过理解这个底层逻辑有个好处当库函数不工作的时候你可以用逻辑分析仪抓SPI波形看地址和数据对不对。我有一次读不到卡抓波形发现地址字节发错了原因是库版本不匹配换了个版本就好了。如果不懂底层这种问题根本无从下手。4. 开发环境搭建MicroPython还是Arduino4.1 两种路线的取舍ESP32玩RFID开发环境主要有两条路MicroPython和Arduino IDE。MicroPython的好处是语法简单交互式调试方便适合快速验证想法Arduino的好处是库多、性能好、社区大适合做成品。我个人的习惯是前期验证用MicroPython后期成品用Arduino。因为MicroPython的REPL太方便了接上串口就能一行行试读卡成功马上能看到UID。但MicroPython的SPI速度相对慢一些而且断电后程序不会自动运行需要额外处理。Arduino编译上传后直接跑稳定性更好。如果你是完全零基础我建议先从MicroPython入手因为门槛低不容易被编译错误劝退。等把读卡逻辑跑通了再考虑要不要转Arduino。4.2 MicroPython固件烧录MicroPython需要先给ESP32烧录固件。去官网下载对应ESP32型号的固件文件然后用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烧完之后用串口工具连上去能看到提示符就说明成功了。然后你需要把MFRC522的驱动库传到板子上。MicroPython的库一般是一个mfrc522.py文件用ampy或者Thonny直接拖进去就行。注意MicroPython固件版本和驱动库版本要匹配不然会出现ImportError或者寄存器读写异常。我建议用比较新的稳定版固件驱动库去GitHub找star多的那个。4.3 Arduino IDE配置要点Arduino这边需要在首选项里添加ESP32的开发板管理网址然后在开发板管理器里安装esp32包。这个过程在国内可能比较慢因为要下载几百兆的编译工具链。如果网络不好可以找离线包或者用PlatformIO它的依赖管理更顺畅。装好之后选对开发板型号比如ESP32 Dev Module。然后安装MFRC522库在库管理器里搜MFRC522选那个by GithubCommunity的版本。这个库封装得比较好基本功能都有。编译速度慢是Arduino IDE的老毛病尤其是第一次编译。有个技巧是把杀毒软件的实时扫描关掉或者把项目放在SSD上能快不少。另外如果你只用SPI和基本GPIO可以在菜单里把不必要的功能关掉减少编译量。5. 代码实战从读UID到写数据5.1 MicroPython读卡示例先来看MicroPython的最小可用代码。这段代码的作用是初始化SPI和MFRC522然后循环寻卡读到卡就打印UID。from machine import Pin, SPI from mfrc522 import MFRC522 import time spi SPI(1, 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.SelectTagSN() if stat reader.OK: print(Card UID:, [hex(i) for i in uid]) time.sleep(0.5)这段代码里REQIDL表示寻感应区内所有卡SelectTagSN是防冲突后选一张卡并读序列号。UID就是卡的唯一标识每张卡不一样。读出来之后你就可以拿这个UID去比对数据库做权限判断。有个细节SPI的极性和相位要设对。MFRC522要求CPOL0CPHA0也就是空闲时时钟低电平第一个边沿采样。如果设错了读出来的数据全是乱的。我在MicroPython里试过设成polarity1结果一个字节都读不对改回来立马正常。5.2 Arduino读卡与写块Arduino这边用MFRC522库读UID更简单#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(); rfid.PCD_Init(); Serial.println(Ready to read...); } 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(); }如果要写数据到卡里比如往第1扇区的第0块写一个字符串需要先验证密钥。M1卡默认密钥是FF FF FF FF FF FF。写块的代码大概是这样MFRC522::MIFARE_Key key; for (byte i 0; i 6; i) key.keyByte[i] 0xFF; byte block 4; byte data[16] Hello RFID!; if (rfid.PCD_Authenticate(MFRC522::PICC_CMD_MF_AUTH_KEY_A, block, key, (rfid.uid)) MFRC522::STATUS_OK) { rfid.MIFARE_Write(block, data, 16); }写块的时候要注意每个扇区的第3块是控制块不能随便写写错了卡就锁死了。要写数据就写第0、1、2块。另外写之前一定要验证密钥验证失败就写不进去。5.3 断电自动运行的实现MicroPython默认上电后停在REPL不会自动跑你的程序。要让程序断电后自动运行需要把代码保存为main.py放在文件系统根目录。这样上电后MicroPython会自动执行main.py。但有个问题如果main.py里有死循环REPL就进不去了调试会很不方便。我的做法是在main.py里加一个按键检测按住某个键就跳过主程序直接进REPL。这样既能自动运行又能随时调试。Arduino这边不存在这个问题上传后直接跑复位就重启。所以如果你要做成品Arduino的体验更省心。6. 常见问题排查读不到卡怎么办6.1 排查流程速查表现象可能原因排查方法完全没反应供电不对万用表量3.3V引脚电压读不到卡接线松动重新插拔杜邦线检查GND读卡距离短天线受干扰移开金属物体调整卡朝向数据乱码SPI模式错检查CPOL/CPHA设置偶尔读到时钟太快降低SPI波特率到500kHz写卡失败密钥错误确认密钥是否为默认FF程序不跑固件问题检查main.py是否存在6.2 几个我踩过的坑第一个坑是CS引脚接错。我一开始把SDA接到了GPIO21那是I2C的默认SDA结果SPI死活不通。后来换成GPIO5就好了。所以接线前一定要确认引脚功能不要看到SDA就往上接。第二个坑是电源噪声。我用USB供电的时候读卡正常换成电池供电就读不到了。后来在3.3V和GND之间加了一个100uF的电解电容问题解决。原因是电池供电的纹波比较大影响了射频电路。第三个坑是卡的类型不对。我买过一批便宜的卡读UID正常但写数据总是失败。后来查资料才知道那是只读的UID卡不能写块。所以买卡的时候要看清是M1卡还是UID卡用途不一样。第四个坑是SPI总线冲突。我在同一个SPI总线上还接了一个TFT屏幕结果两个设备互相干扰。解决办法是给每个设备独立的CS引脚并且在操作前先拉高其他设备的CS。这个在Arduino里要手动管理MicroPython里也一样。提示调试RFID的时候先用一张确认能用的卡测试。如果这张卡都读不到那肯定是硬件或代码问题不要怀疑卡。6.3 用逻辑分析仪抓SPI波形如果以上方法都不行那就上逻辑分析仪。几十块钱的USB逻辑分析仪就够用接上SCK、MOSI、MISO、CS四根线抓一段波形。正常的SPI通信应该能看到CS拉低然后SCK有8个脉冲MOSI上有地址字节MISO上有返回数据。重点看两个地方一是CS有没有在通信期间保持低电平如果中间跳高了说明片选控制有问题二是地址字节对不对比如读版本寄存器应该是0x37如果发出来不是这个值说明库或者接线有问题。抓波形这个技能一旦学会排查SPI设备的问题会快很多。7. 进阶玩法从读卡到真正有用的项目7.1 考勤记录系统最基本的应用就是考勤。ESP32读到UID后通过Wi-Fi把UID和时间戳发到服务器服务器比对数据库记录签到时间。这个方案的关键是时间同步ESP32可以用NTP协议从网络获取时间保证记录准确。如果要离线运行可以加一个SD卡模块把记录写到CSV文件里联网后再上传。SD卡也是SPI接口可以和MFRC522共用总线但要注意CS分开控制。7.2 门禁控制门禁比考勤多了一个执行机构通常是电磁锁或舵机。读到合法UID后ESP32控制继电器吸合打开电锁延时几秒后自动关闭。这里要注意继电器的续流二极管不然断电瞬间的反向电动势可能烧坏ESP32的IO口。安全方面不要只靠UID判断因为UID是可以复制的。更安全的做法是用M1卡的加密扇区把密钥分散存储或者用CPU卡做动态认证。当然对于普通场景UID加白名单已经够用了。7.3 与边缘AI结合最近有个热词叫“ESP32边缘AI”其实RFID也能和它结合。比如你可以用ESP32-S3跑一个轻量级神经网络把刷卡行为序列作为输入判断是不是异常刷卡。这个玩法比较前沿需要一定的机器学习基础但思路是通的RFID提供身份数据AI做行为分析。7.4 无线配网与远程管理ESP32的Wi-Fi能力可以让RFID设备联网。第一次使用时通过手机App给ESP32配网之后它就能自动连接。配网方案可以用SmartConfig或者蓝牙配网MicroPython和Arduino都有现成库。联网后你可以远程查看刷卡记录、下发白名单、升级固件整个系统就活了。8. 一些实操心得与建议做RFID项目这几年我最大的体会是硬件项目80%的问题出在连接和供电上代码问题反而好查。所以每次遇到读不到卡先别急着改代码拿万用表量一下电压检查一下接线往往能省下大量时间。另外不要一上来就追求远距离和高速。先把1厘米距离、1MHz时钟跑通再慢慢优化。我见过太多人一开始就想做10厘米读卡结果调了几天都没成功最后发现是天线匹配的问题。循序渐进每一步都验证才是最快的路。最后说个细节MFRC522模块用久了会发热尤其是连续读卡的时候。这是正常现象但如果烫手就不正常了可能是供电电压偏高或者短路。摸一下模块温度也是判断硬件状态的一个小技巧。这个项目后续还可以扩展很多方向比如加个OLED屏幕显示刷卡结果加个蜂鸣器做声音提示或者用ESP32的蓝牙把UID发给手机。核心的读卡逻辑不变外围想怎么加就怎么加。等你把基础跑通了这些扩展都是顺手的事。
RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进