NFC 打卡自习馆签到

💡 划重点

  • 用 PN532 读卡,ESP32 联网,OLED 显示状态,按键用于手动确认或切换模式,LED 做打卡成功/失败提示。
  • 目标成本 80 元内,适合自习馆、合作社、小型共享空间。
  • 打卡数据经 Wi‑Fi 发送到飞书 webhook,管理员在飞书群实时看到签到记录。
  • 核心链路:刷卡 → 本地校验/显示 → 联网上报 → 飞书通知。

需求洞察

自习馆和合作社的日常运营里,签到往往靠纸质登记、微信群接龙或前台人工确认。用户到了座位,先找笔、翻本子,或者掏手机在群里发“已到”,操作繁琐;管理员事后还得把散落的信息整理成表格,费时且容易漏记。对于按小时计费或需要统计自习时长的场景,签到不准会直接影响结算和座位调度。

目标用户是两类人:一是自习馆会员和合作社成员,他们希望“刷一下就走”,不想被签到流程打断学习状态;二是空间管理员,他们需要低成本、可追溯、能自动汇总的签到数据,而不是每天手工抄写。真实痛点是:签到动作要快、身份要准、记录要自动同步、设备要便宜且不依赖专人维护。

为什么值得做?ESP32 加 PN532 的方案成熟、开源资料多,80 元内能做出稳定原型。NFC 卡可以贴在会员卡、手机壳或钥匙扣上,用户无需打开 App。飞书 webhook 让数据直接进入群机器人,管理员不用登录后台。对小型自习馆来说,这套设备可以放在门口或每张桌子旁,即装即用,后期还能扩展占座检测、灯光联动和时长统计。

方案设计

感知层由 PN532 负责。它通过 I2C 与 ESP32 通信,读取 Mifare Classic 或 NTAG 卡片的 UID。OLED 同挂在 I2C 总线上,显示“请刷卡”“打卡成功”“卡未注册”等状态。按键用于两种场景:一是管理员短按进入注册模式,把新卡 UID 写入白名单;二是用户刷卡异常时,长按触发重新上报。

控制层是 ESP32。它维护一个本地卡号白名单,可以用数组或 Preferences 存储。读到 UID 后,先比对白名单,再决定是否上报。控制层还负责去重,比如同一张卡 30 秒内只记录一次,避免反复刷。

执行层包括 OLED、LED 和蜂鸣器(可选)。OLED 显示卡号后四位和打卡结果,LED 用绿色常亮 1 秒表示成功,红色快闪表示失败。如果加蜂鸣器,成功时短鸣一声,失败时长鸣一声,适合嘈杂环境。

日志/联网层由 ESP32 的 Wi‑Fi 完成。打卡成功后,把卡号、时间戳、设备编号组装成 JSON,POST 到飞书 webhook。飞书群机器人会以卡片消息形式展示“张三 已签到 09:32”。如果网络失败,ESP32 先把记录写入 SPIFFS 或 Preferences,恢复后再补传。设备编号用于多台设备区分,比如“前台机”“A 区机”。

物料清单

物料 数量 用途 估算单价
ESP32 开发板 1 主控,Wi‑Fi 联网 22 元
PN532 NFC 模块 1 读卡 28 元
0.96 寸 OLED 1 显示状态 9 元
轻触按键 1 注册/重传 0.5 元
LED 与电阻 2 状态指示 0.5 元
杜邦线/面包板 1 套 接线 5 元
外壳/卡贴 若干 固定与会员卡 8 元
合计 73 元

接线与结构

PN532 使用 I2C 模式:SDA 接 GPIO21,SCL 接 GPIO22,VCC 接 3.3V,GND 共地。OLED 也接同一组 SDA/SCL,地址不冲突:PN532 通常为 0x24,OLED 为 0x3C。LED 正极串 220Ω 电阻接 GPIO2,负极接地;按键一端接 GPIO4,另一端接地,使用内部上拉。若 PN532 的 I2C 地址被改成 0x3C,需要改回 0x24,否则与 OLED 冲突。结构上建议把 PN532 线圈朝外,OLED 朝上,按键隐藏在外壳侧面,避免用户误触。

核心代码

以下为 Arduino 骨架,可编译上传。需要安装 Adafruit PN532、Adafruit SSD1306、ArduinoJson。


#include <Wire.h>
#include <Adafruit_PN532.h>
#include <Adafruit_SSD1306.h>
#include <WiFi.h>
#include <HTTPClient.h>
#include <ArduinoJson.h>

#define SDA 21
#define SCL 22
#define LED 2
#define KEY 4
Adafruit_PN532 nfc(-1, -1);
Adafruit_SSD1306 oled(128, 64, &Wire;, -1);

const char* ssid = "YOUR_WIFI";
const char* pass = "YOUR_PASS";
const char* webhook = "https://open.feishu.cn/open-apis/bot/v2/hook/xxx";

uint8_t whitelist[][4] = {{0x12,0x34,0x56,0x78}};
unsigned long lastRead = 0;

bool checkCard(uint8_t* uid) {
  for (auto &c; : whitelist) {
    if (memcmp(c, uid, 4) == 0) return true;
  }
  return false;
}

void sendFeishu(String uidStr) {
  if (WiFi.status() != WL_CONNECTED) return;
  HTTPClient http;
  http.begin(webhook);
  http.addHeader("Content-Type", "application/json");
  StaticJsonDocument<256> doc;
  doc["msg_type"] = "text";
  doc["content"]["text"] = "NFC签到:" + uidStr;
  String body;
  serializeJson(doc, body);
  http.POST(body);
  http.end();
}

void setup() {
  pinMode(LED, OUTPUT);
  pinMode(KEY, INPUT_PULLUP);
  Wire.begin(SDA, SCL);
  oled.clearDisplay();
  oled.setTextSize(1);
  oled.setCursor(0, 0);
  oled.println("NFC Check-in");
  oled.display();
  nfc.begin();
  nfc.SAMConfig();
  WiFi.begin(ssid, pass);
}

void loop() {
  uint8_t uid[7], uidLen;
  if (nfc.readPassiveTargetID(PN532_MIFARE_ISO14443A, uid, &uidLen;, 100)) {
    if (millis() - lastRead < 30000) return;
    lastRead = millis();
    String uidStr = "";
    for (int i = 0; i < uidLen; i++) {
      if (uid[i] < 0x10) uidStr += "0";
      uidStr += String(uid[i], HEX);
    }
    oled.clearDisplay();
    oled.setCursor(0, 0);
    if (checkCard(uid)) {
      digitalWrite(LED, HIGH);
      oled.println("OK " + uidStr);
      sendFeishu(uidStr);
      delay(1000);
      digitalWrite(LED, LOW);
    } else {
      oled.println("UNKNOWN");
      for (int i = 0; i < 3; i++) {
      }
    }
    oled.display();
    delay(1500);
  }
  if (digitalRead(KEY) == LOW) {
    delay(200);
    oled.println("REG MODE");
    oled.display();
  }
}

调试步骤

  1. 先只接 OLED,运行 I2C 扫描,确认地址 0x3C。
  2. 接入 PN532,扫描地址应为 0x24。若找不到,检查 PN532 的 DIP 开关是否在 I2C 模式。
  3. 烧录代码,串口查看 Wi‑Fi 是否连接成功。
  4. 用手机 NFC 或 Mifare 卡刷卡,观察 OLED 是否显示 UID。
  5. 把 UID 加入白名单,重新刷卡,确认 LED 亮起且飞书收到消息。
  6. 测试断网场景:拔掉路由器,刷卡后记录应暂存,恢复网络后补传。
  7. 最后固定外壳,调整 PN532 天线方向,确保刷卡距离 2 至 4 厘米。

设计图

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