这应该不算教程,只是分享一些思路
前言
之前用 ESP32-S3-DEVKIT 做过中二的 Aime 读卡器,固件是用下面这个项目改的
端午闲来无事,觉得之前做的读卡器体积太大了,决定重新做一版,顺便试试固件自己写
固件已开源
硬件
- Elechouse PN532
NFC 收发模块,用来和卡片交互
- ESP32
主控,这次选择了 ESP32C3 Supermini
体积足够小,半孔工艺可以当贴片处理,以及GPIO刚刚好可以用完
- SSD1306
OLED 显示屏,用来显示读卡器模式以及读卡状态
- 若干 WS2812B + 若干开关
开关用来切换模式,WS2812B 装饰
- 接线
0
, 8
, 20
, 21
这四个引脚不要接
每个模块按他们的协议接线,PN532 用SPI,SSD1306 用I²C
WS2812B 由于用了级联,所以只需要接第一个LED的DIN
开关接那些下拉不会导致主控出问题的引脚 (例如 1
, 2
)
- 原理图

PCB 新版全部用了贴片,之前做的的全是插件,PCB 背面不太美观还割手
软件
具体实现可以查看 src/sega.cpp,这里只大致讲讲处理的逻辑
用 PlatformIO
+ arduino
framework
由于 ESP32C3 Supermini 没有串口转USB以及自动下载电路
所以需要手动进入下载模式,用USB CDC上传固件 (实测只有第一次上传需要手动进入下载模式,之后就不用了)
串口交互逻辑来自 segatools 模拟层
串口波特率需要根据 segatools.ini -> keychip -> dipsw3
动态设置
如果是 CVT -> 38400
,否则 115200
请求帧
游戏默认通过 COM4 与读卡器通信,每次需要让读卡器做些什么时就会发送一个请求帧 (req_frame)
样本:E0 05 00 02 30 00 37
0xE0
-> 同步字节0x05
-> 帧长度 (包含自身)0x00
-> 地址0x02
-> 序号,代表第 n+1 个请求帧0x30
-> CMD0x00
-> Payload 长度 (不包含自身)....
-> 如果 Payload 长度大于零,这里就会出现与Payload 长度
相同数量的字节 (Payload Body)0x37
-> 校验值,除其自身与同步字节外其他字节的和 (0x05
+0x02
+0x30
=0x37
)
0xD0
为转义字节,需要转义,流程为:忽略 0xD0
,读取下一个字节并 +1
例:D0 DF
,转义后的字节为:0xDF
+ 1 = 0xE0
如果转义后的字节为 0xE0
,那么它不应再被视为同步字节
如果直接用 Windows API
的话,我们可以读到一个完整的帧 (IRQ_MJ_WRITE
)
但是 Arduino 不行,所以我们只能按照上述含义用 Serial.read()
一个个读
处理 CMD
CMD 字节就是这个指令的类型,映射表可以查看 src/sega.h
...?
即代表不确定
0x30
-> 返回固件版本0x32
-> 返回硬件版本0x40
-> 开启射频天线…?0x41
-> 关闭射频天线…?0x42
-> 开始轮询卡片,Mifare 返回 UID 长度与 UID,Felica 返回 IDm, PMm 与各自的长度
此处注意,因为主线程是同步的,所以单次轮询读取的超时时间应该控制在 500ms 以内以防阻塞
0x43
-> 根据接收帧的 Payload 选择对应 UID 的 Mifare 卡片0x44
-> 未知0x50
-> Payload 为与 Aime 卡交互所需的 Key,储存0x51
-> 用上述 Aime Key 认证0x52
-> 读取 Mifare Block (一定发生在 Key 验证后)0x54
-> Payload 为与 Bana 卡交互所需的 Key,储存0x55
-> 用上述 Bana Key 认证0x60
-> 进入升级模式…?0x61
-> 发送数据…?0x62
-> 重置设备…?0x71
-> 用 Payload 中提供的命令请求 Felica 卡片,并返回卡片处理的结果0x81
-> 用 Payload 提供的颜色 (三个字节,对应 RGB) 设置 LED 灯0xF5
-> 重置 LED 灯0xF0
-> 返回 LED 灯版本
回复帧
处理完请求帧后,我们自然也需要构建回复帧 (res_frame)
这里的样本也对应上述请求帧的样本
样本:E0 07 00 02 30 00 01 94 ce
结构基本与请求帧保持一致,只是:
CMD
后新增一个字节,对应响应状态:
0x00
-> 成功
0x01
-> 失败 (游戏默认会重试,可以善用这个机制)
其他字节意义不明
- 请求帧中的
Payload 长度
,Payload Body
在此处变为返回的 Payload:
上文中提到的 “返回xxx” 就是指填充此处数据,在样本中对应0x01
,0x94
然后将构建好的回复依次写回串口
后记
WS2812B 在级联灯比较少的情况下用 3.3v
供电也能跑起来,还挺稳定的,就是不知道寿命了
看着一条条请求帧被正确回复真是有很大的成就感