💡 划重点

  • 用 ESP32 做主控,PN532 读 NFC 卡或手机模拟卡,OLED 显示签到结果,按键用于手动补签,LED 和蜂鸣器做反馈,联网后通过飞书 webhook 实时上报。整机成本控制在 80 元内,适合自习馆、合作社、共享办公区做轻量签到。

需求洞察

目标用户是自习馆经营者、合作社管理员,以及每天到店自习的学生和自由职业者。真实痛点是:纸质签到易代签、人工登记打断学习氛围、会员到店数据难统计、续费提醒缺少依据。NFC 打卡不用掏手机扫码,卡片或手机碰一下即可,安静、快速、可追溯。对经营者来说,飞书表格或群机器人能实时收到签到记录,后续可做时长统计和会员运营。这个项目硬件便宜、部署简单,值得作为自习馆数字化第一步。

方案设计

感知层用 PN532 读取 NFC 卡 UID,支持 Mifare 卡和手机模拟卡;按键用于忘带卡时手动补签。控制层由 ESP32 完成 UID 去重、60 秒防重复、白名单判断和联网调度。执行层用 OLED 显示“签到成功/请刷卡”,LED 闪亮、蜂鸣器短响提示。日志与联网层通过 WiFi 将 UID、时间、设备编号发送到飞书 webhook,同时串口输出调试日志。后续可加 SD 卡或云端数据库,实现入馆/离馆双记录。

物料清单

物料 数量 用途 估算单价 合计
ESP32 开发板 1 主控与联网 18 元 18 元
PN532 NFC 模块 1 读卡 28 元 28 元
0.96 寸 OLED 1 显示状态 9 元 9 元
轻触按键 1 手动补签 0.5 元 0.5 元
LED 与 220Ω 电阻 1 套 成功提示 0.5 元 0.5 元
有源蜂鸣器 1 声音反馈 1.5 元 1.5 元
杜邦线、洞洞板 1 套 接线与固定 6 元 6 元
USB 数据线 1 供电与烧录 5 元 5 元
NFC 白卡 2 会员卡 2 元 4 元
合计 72.5 元

接线与结构

PN532 的 SDA 接 GPIO21,SCL 接 GPIO22;OLED 同 I2C 总线,地址 0x3C,PN532 地址 0x24,不冲突。LED 接 GPIO2,串 220Ω 电阻。按键一端接 GPIO4,另一端接 GND,使用内部上拉。蜂鸣器接 GPIO5。微调说明:增加 PN532 复位脚到 GPIO32,上电更稳定;IRQ 不接,用轮询读取。PN532 必须拨到 I2C 模式,供电 3.3V,逻辑电平不要接 5V。外壳可用纸盒或亚克力,PN532 线圈朝外,OLED 开窗,按键和 LED 露出。

核心代码


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

#define SDA_PIN 21
#define SCL_PIN 22
#define LED_PIN 2
#define KEY_PIN 4
#define BUZZER_PIN 5
#define PN532_RST 32

Adafruit_SSD1306 oled(128, 64, &Wire;, -1);

const char* ssid = "自习馆WiFi";
const char* pass = "密码";
const char* webhook = "飞书机器人地址";

String lastUid = "";
unsigned long lastTime = 0;

void show(String a, String b) {
  oled.clearDisplay();
  oled.setTextSize(1);
  oled.setCursor(0, 0);
  oled.println(a);
  oled.setCursor(0, 24);
  oled.println(b);
  oled.display();
}

void sendFeishu(String uid) {
  if (WiFi.status() != WL_CONNECTED) return;
  HTTPClient http;
  http.begin(webhook);
  http.addHeader("Content-Type", "application/json");
  String body = "{\"msg_type\":\"text\",\"content\":{\"text\":\"NFC签到 UID:" + uid + "\"}}";
  http.POST(body);
  http.end();
}

void setup() {
  pinMode(LED_PIN, OUTPUT);
  pinMode(BUZZER_PIN, OUTPUT);
  Wire.begin(SDA_PIN, SCL_PIN);
  show("NFC签到", "启动中");
  nfc.begin();
  nfc.SAMConfig();
  WiFi.begin(ssid, pass);
  while (WiFi.status() != WL_CONNECTED) delay(500);
  show("NFC签到", "请刷卡");
}

void loop() {
  uint8_t uid[7];
  uint8_t len;
  if (nfc.readPassiveTargetID(PN532_MIFARE_ISO14443A, uid, &len;, 100)) {
    String nowUid = "";
    for (uint8_t i = 0; i < len; i++) {
      if (uid[i] < 16) nowUid += "0";
      nowUid += String(uid[i], HEX);
    }
    if (nowUid != lastUid || millis() - lastTime > 60000) {
      lastUid = nowUid;
      lastTime = millis();
      digitalWrite(LED_PIN, HIGH);
      tone(BUZZER_PIN, 1800, 120);
      show("签到成功", nowUid);
      sendFeishu(nowUid);
      delay(800);
      digitalWrite(LED_PIN, LOW);
      show("NFC签到", "请刷卡");
    }
  }
  if (digitalRead(KEY_PIN) == LOW) {
    show("手动补签", lastUid);
    sendFeishu(lastUid);
    delay(500);
  }
}

调试步骤

先不接 PN532,只烧录 I2C 扫描程序,确认 OLED 地址 0x3C 和 PN532 地址 0x24。再检查 PN532 拨码是否为 I2C,供电是否 3.3V,复位脚是否接 GPIO32。第三步串口查看刷卡 UID,若读不到卡,靠近线圈并检查天线是否被金属遮挡。第四步测试 OLED 显示、LED 和蜂鸣器。第五步配置 WiFi 和飞书 webhook,先发测试消息,确认机器人关键词或签名设置正确。最后联调:同一张卡 60 秒内只上报一次,按键可补签,断电重启后自动重连。

设计图

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