💡 划重点
- 硬件核心:ESP32 + PN532 NFC模块 + OLED同屏显示
- 联网上报:刷卡后自动通过飞书 Webhook 写入签到记录
- 成本控制:总物料费控制在 80 元以内
- 场景适配:专为自习馆、共享书吧、小型合作社设计,即刷即走
- 开发难度:基于 Arduino 框架,提供可编译骨架代码
需求洞察
目标用户:自习馆管理员、合作社运营者、共享空间主理人。这类用户通常没有专业的考勤系统,仍在使用纸质签到或微信群接龙,效率低且容易遗漏。
真实痛点:
- 管理散乱:多人共用空间,无法快速确认谁在馆、停留时长;
- 账务对不上:月卡、次卡用户混用,人工记录易出错;
- 信息滞后:管理员不在现场时,无法实时获知到馆情况;
- 成本敏感:动辄上千的门禁系统不现实,80 元以内的方案才是刚需。
为什么值得做:这是一个“轻量化数字化签到”的典型 IoT 场景。通过一张不到 5 元的 NFC 标签或卡片,即可完成身份识别,再结合飞书机器人自动通知,让管理员即使在家也能掌握自习馆动态。整套方案技术门槛低,复制性强,非常适合学生科创、小微创业以及社区共享空间改造。
方案设计
整个系统遵循“感知—控制—执行—联网”四层架构。
感知层:采用 PN532 NFC 模块,支持 ISO14443A/B、FeliCa 等协议,可读取 M1 卡、NFC 手环、手机模拟卡片的 UID 或指定数据块。用户只需将卡片靠近感应区,即可触发签到。
控制层:ESP32 作为主控,负责轮询 PN532 数据、解析卡号、驱动 OLED 显示签到结果,并通过板载 WiFi 连接互联网。ESP32 双核性能足以同时处理读卡与网络请求。
执行层:0.96 寸 OLED(128×64)用于显示“请刷卡”、“签到成功”、“无效卡”等提示,以及当前时间或剩余座位数。一颗高亮 LED 通过不同闪烁频率给出视觉反馈:识卡成功长亮 1 秒,失败快闪 3 次。
日志/联网层:成功签到后,ESP32 通过 HTTP POST 向飞书自定义机器人 Webhook 发送 JSON 消息,消息内容包含卡号、签到时间、设备编号等。飞书群内即时弹出通知,方便管理员追踪和统计。可实现无人值守的打卡记录云端化。
物料清单
| 物料 | 数量 | 用途 | 单价(元) | 小计(元) |
|---|---|---|---|---|
| ESP32 开发板 | 1 | 主控+WiFi | 18 | 18 |
| PN532 NFC 模块 | 1 | 读卡识别 | 22 | 22 |
| 0.96 寸 OLED(SSD1306) | 1 | 信息显示 | 12 | 12 |
| 5mm LED + 220Ω 电阻 | 1 | 刷卡指示 | 1 | 1 |
| 轻触按键 | 1 | 管理员模式/复位 | 0.5 | 0.5 |
| NFC 空白卡片或标签 | 5 | 用户身份凭证 | 1.5 | 7.5 |
| 面包板 + 杜邦线 | 1套 | 原型搭建 | 8 | 8 |
| Micro USB 供电线 | 1 | 供电 | 3 | 3 |
| 合计 | 72 |
合计 72 元,预留 8 元缓冲,完全控制在 80 元以内。
接线与结构
遵循 I2C 总线复用的原则,PN532 与 OLED 共用 SDA、SCL。
| 外设 | 引脚 | ESP32 GPIO | 备注 |
|---|---|---|---|
| PN532 | SDA | GPIO 21 | I2C 数据 |
| PN532 | SCL | GPIO 22 | I2C 时钟 |
| OLED | SDA | GPIO 21 | 与 PN532 共享 |
| OLED | SCL | GPIO 22 | 与 PN532 共享 |
| LED | 正极(串电阻) | GPIO 2 | 状态指示 |
| 按键 | 一端 | GPIO 15 | 内部上拉,按下为低 |
| 按键 | 另一端 | GND |
说明:PN532 和 OLED 均支持 I2C 地址选择,PN532 默认地址 0x24,OLED 默认 0x3C,不冲突,因此可以安全挂载在同一条 I2C 总线上,节省 GPIO 资源。LED 使用 GPIO 2,因其带有板载 LED 复用,方便初期调试。
结构上,所有模块安装在一个小型亚克力外壳或 3D 打印底座内,读卡区域朝上,OLED 朝向用户,旁边布置状态灯和按键。供电由手机充电器通过 Micro USB 提供。
核心代码
以下为 Arduino 骨架代码,使用 PlatformIO 或 Arduino IDE 均可编译。需安装 Adafruit_PN532、Adafruit_SSD1306、WiFi、HTTPClient 库。
#include <WiFi.h>
#include <HTTPClient.h>
#include <Wire.h>
#include <Adafruit_PN532.h>
#include <Adafruit_SSD1306.h>
// WiFi 与飞书 Webhook
const char* ssid = "your_ssid";
const char* password = "your_password";
const char* webhook_url = "https://open.feishu.cn/open-apis/bot/v2/hook/xxxx";
// I2C 引脚
#define SDA_PIN 21
#define SCL_PIN 22
// OLED
#define SCREEN_WIDTH 128
#define SCREEN_HEIGHT 64
Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire;, -1);
// PN532
// LED 与按键
#define LED_PIN 2
#define BUTTON_PIN 15
void setup() {
Serial.begin(115200);
pinMode(LED_PIN, OUTPUT);
Wire.begin(SDA_PIN, SCL_PIN);
display.clearDisplay();
display.setTextSize(1);
display.setCursor(0,0);
display.display();
nfc.begin();
uint32_t version = nfc.getFirmwareVersion();
if (!version) {
display.display();
while(1);
}
nfc.SAMConfig();
WiFi.begin(ssid, password);
while (WiFi.status() != WL_CONNECTED) {
delay(500);
}
display.println("WiFi OK");
display.display();
}
void loop() {
uint8_t uid[7] = {0};
uint8_t uidLength;
display.clearDisplay();
display.setCursor(0,0);
display.display();
if (nfc.readPassiveTargetID(PN532_MIFARE_ISO14443A, uid, &uidLength;, 200)) {
// 拼接卡号字符串
String cardID = "";
for (uint8_t i = 0; i < uidLength; i++) {
if (uid[i] < 0x10) cardID += "0";
cardID += String(uid[i], HEX);
}
cardID.toUpperCase();
display.clearDisplay();
display.setCursor(0,0);
display.println("Signed in:");
display.println(cardID);
display.display();
// LED 长亮 1 秒
digitalWrite(LED_PIN, HIGH);
delay(1000);
digitalWrite(LED_PIN, LOW);
// 飞书推送
if (WiFi.status() == WL_CONNECTED) {
HTTPClient http;
http.begin(webhook_url);
http.addHeader("Content-Type", "application/json");
String payload = "{\"msg_type\":\"text\",\"content\":{\"text\":\"签到通知:卡号 " + cardID + " 于 " + String(millis()/1000) + "s 进入自习馆\"}}";
http.POST(payload);
http.end();
}
delay(2000); // 防重复刷卡
}
// 按键按下可显示管理员菜单(示例仅重置显示)
if (digitalRead(BUTTON_PIN) == LOW) {
display.clearDisplay();
display.setCursor(0,0);
display.println("Admin Mode");
display.display();
delay(1000);
}
}
说明:代码每次循环等待刷卡,读取成功后通过飞书 Webhook 发送文本消息,卡号以十六进制字符串显示。实际部署时可将 UID 与用户姓名绑定,并增加实时时钟模块以显示精确时间。
调试步骤
- 供电检查:上电后 OLED 应亮起并显示“NFC Check-in”,若黑屏检查 I2C 地址及接线;
- PN532 自检:查看串口输出,若打印“PN532 not found”,检查 I2C 连接或更换杜邦线;
- 读卡测试:用手机 NFC 工具或空白卡靠近天线,OLED 应显示卡号,同时 LED 长亮;
- 联网验证:确认飞书群收到机器人消息,若无,检查 Webhook 地址是否正确、WiFi 是否连接;
- 按键测试:按下按键后屏幕切换为 Admin Mode,松开恢复主界面;
- 异常处理:连续刷卡无效时,在串口监视器观察 PN532 返回的错误码,常见为射频干扰,调整天线位置即可。