💡 划重点

  • 硬件核心:ESP32 + PN532 NFC模块 + OLED同屏显示
  • 联网上报:刷卡后自动通过飞书 Webhook 写入签到记录
  • 成本控制:总物料费控制在 80 元以内
  • 场景适配:专为自习馆、共享书吧、小型合作社设计,即刷即走
  • 开发难度:基于 Arduino 框架,提供可编译骨架代码

需求洞察

目标用户:自习馆管理员、合作社运营者、共享空间主理人。这类用户通常没有专业的考勤系统,仍在使用纸质签到或微信群接龙,效率低且容易遗漏。

真实痛点

  1. 管理散乱:多人共用空间,无法快速确认谁在馆、停留时长;
  2. 账务对不上:月卡、次卡用户混用,人工记录易出错;
  3. 信息滞后:管理员不在现场时,无法实时获知到馆情况;
  4. 成本敏感:动辄上千的门禁系统不现实,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_PN532Adafruit_SSD1306WiFiHTTPClient 库。


#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 与用户姓名绑定,并增加实时时钟模块以显示精确时间。

调试步骤

  1. 供电检查:上电后 OLED 应亮起并显示“NFC Check-in”,若黑屏检查 I2C 地址及接线;
  2. PN532 自检:查看串口输出,若打印“PN532 not found”,检查 I2C 连接或更换杜邦线;
  3. 读卡测试:用手机 NFC 工具或空白卡靠近天线,OLED 应显示卡号,同时 LED 长亮;
  4. 联网验证:确认飞书群收到机器人消息,若无,检查 Webhook 地址是否正确、WiFi 是否连接;
  5. 按键测试:按下按键后屏幕切换为 Admin Mode,松开恢复主界面;
  6. 异常处理:连续刷卡无效时,在串口监视器观察 PN532 返回的错误码,常见为射频干扰,调整天线位置即可。

设计图

NFC 打卡自习馆签到结构flow
1
感知输入
2
控制处理
3
执行反馈
4
数据上报