Arduino 门磁开合计数器:消抖与 EEPROM 断电记忆

2026-10-11 / 0 评论 / 0 阅读

Arduino 门磁开合计数器:消抖与 EEPROM 断电记忆 教程示意图

适用场景

这个项目适合统计柜门、工具箱盖或低速机械挡板的开启次数,用于学习数字输入、边沿检测、软件消抖和非易失存储。成品在磁铁离开干簧开关时把次数加一,板载 LED 显示当前门处于打开状态,串口输出累计数;按下独立按钮可清零。它只是教学计数器,不是防盗报警器,也不能替代经过认证的门禁、消防或安全联锁设备。

项目以 Arduino UNO R3 为例,只使用 USB 提供的安全低压直流。干簧开关和按钮都是无源触点,不驱动电机、锁具或继电器。换线前必须拔掉 USB;若使用其他开发板,应先查工作电压、引脚编号和 EEPROM 实现方式,不能假定所有兼容板都与 UNO 相同。

原理

干簧开关靠近磁铁时闭合,离开时断开。将输入配置为内部上拉后,触点断开读到高电平,闭合接地读到低电平,因此门从关闭变为打开对应低到高的边沿。机械触点在切换瞬间可能快速抖动,程序若直接累计,会把一次开门误算成多次。Bounce2 库通过要求输入稳定一段时间再确认变化来过滤抖动。

UNO R3 的 ATmega328P 带有 1 KB EEPROM,断电后仍能保留内容,但单个位置的写入次数有限。Arduino 的 EEPROM 指南说明,EEPROM.update() 只在字节发生变化时写入;EEPROM.put() 写结构体时也利用更新逻辑。本文还把写入推迟到最后一次变化五秒后,避免一次抖动或连续操作产生密集写入。代价是突然断电时,最近五秒内尚未落盘的计数可能丢失,这个取舍需要提前告诉使用者。

准备工作

准备 Arduino UNO R3、一只常开干簧开关、一块小磁铁、一只瞬时按钮和若干导线。使用板载 LED,不外接灯,因此不需要额外 LED 限流电阻。干簧开关一端接 D2、另一端接 GND;复位按钮一端接 D3、另一端接 GND。内部上拉会让断开状态保持确定电平。

断电完成接线并检查 D2、D3 没有误接 5V。在 Arduino IDE 中打开库管理器(Library Manager),搜索并安装成熟开源库 Bounce2。随后在开发板(Board)中选择真实板型,在端口(Port)中选择开发板实际串口。不要仅凭端口序号猜测,可在拔插 USB 前后比较列表。

分步骤操作

  1. 先让磁铁靠近干簧开关,用万用表通断档确认触点闭合;移开磁铁后应断开。通断档测量必须在 Arduino 断电时进行。调整磁铁位置,使门完全关闭时可靠闭合、刚打开时可靠断开,避免把磁铁放在临界距离。

  2. 新建草图并输入以下程序:

#include <EEPROM.h>
#include <Bounce2.h>

constexpr byte DOOR_PIN = 2;
constexpr byte RESET_PIN = 3;
constexpr uint32_t MAGIC = 0x4D474331;
constexpr unsigned long SAVE_DELAY_MS = 5000UL;

struct StoredRecord {
  uint32_t magic;
  uint32_t openings;
};

Bounce doorInput;
Bounce resetInput;
StoredRecord record;
bool dirty = false;
unsigned long lastChangeMs = 0;

void scheduleSave() {
  dirty = true;
  lastChangeMs = millis();
}

void setup() {
  pinMode(LED_BUILTIN, OUTPUT);

  doorInput.attach(DOOR_PIN, INPUT_PULLUP);
  doorInput.interval(20);
  resetInput.attach(RESET_PIN, INPUT_PULLUP);
  resetInput.interval(30);

  Serial.begin(115200);
  EEPROM.get(0, record);

  if (record.magic != MAGIC) {
    record.magic = MAGIC;
    record.openings = 0;
    EEPROM.put(0, record);
  }

  Serial.print("累计开启次数:");
  Serial.println(record.openings);
}

void loop() {
  doorInput.update();
  resetInput.update();

  bool doorIsOpen = doorInput.read() == HIGH;
  digitalWrite(LED_BUILTIN, doorIsOpen ? HIGH : LOW);

  if (doorInput.rose()) {
    record.openings++;
    scheduleSave();
    Serial.print("检测到开启,累计:");
    Serial.println(record.openings);
  }

  if (resetInput.fell()) {
    record.openings = 0;
    scheduleSave();
    Serial.println("计数已清零,等待保存");
  }

  if (dirty && (millis() - lastChangeMs >= SAVE_DELAY_MS)) {
    EEPROM.put(0, record);
    dirty = false;
    Serial.println("计数已写入 EEPROM");
  }
}

DOOR_PIN 和 RESET_PIN 是实际接线引脚,换板或换针脚时只修改这两个常量。SAVE_DELAY_MS 是延迟保存时间,单位为毫秒;增大它能合并更多连续变化,但突然断电时可能丢失更长时间内的新计数。MAGIC 是识别存储结构是否已经初始化的固定标记,不是密码、密钥或网络凭据。

  1. 点击验证(Verify)编译。若提示找不到 Bounce2.h,回到库管理器确认安装的是 Bounce2,而不是名称相近的其他库。编译成功后点击上传(Upload)。上传期间不要操作门磁或复位按钮,避免初次输出难以判断。

  2. 打开串口监视器(Serial Monitor),将速率设为 115200。启动时应看到当前累计次数。磁铁靠近代表门关闭,此时板载 LED 熄灭;缓慢移开磁铁代表门打开,LED 点亮且累计次数只增加一次。把磁铁保持在远处不应持续增加计数,因为程序只计算低到高的边沿。

  3. 连续开合几次后等待至少五秒,直到串口显示已经写入 EEPROM。拔掉 USB,等待片刻再重新连接;启动输出应保留上次保存的计数。最后按一下 D3 上的复位按钮,等待保存提示,再断电重启,计数应保持为零。

验证方法

完整验证至少做四组。第一组慢速开合十次,计数应增加十次。第二组让磁铁停在临界附近轻微晃动,观察一次实际开启是否只计一次;若仍重复,可在机械位置稳定的前提下把消抖时间从 20 ms 小幅增加。第三组在出现保存提示后断电重启,计数必须恢复。第四组在增加计数后五秒内立即断电,允许最后几次尚未保存,这用于确认自己理解延迟写入的边界,而不是把它误认为程序损坏。

还应检查门保持关闭时 D2 为低、打开时为高,板载 LED 与物理状态一致。若设备要长期使用,记录每天大约发生多少次开门,据此评估 EEPROM 写入频率。高频生产计数不适合每次都写同一片内部 EEPROM,应改用更合适的存储、磨损均衡或可靠的断电保存设计。

常见错误

次数不断自动增加通常是输入悬空、GND 没有连接或误用了普通 INPUT。关闭和打开状态相反,是磁铁位置、开关类型或边沿选择与假设不同;先用万用表确认,不要盲目把 rose() 改成 fell()。每次开门增加多次,多半是磁铁停在临界距离、导线过长受干扰或消抖时间太短。上传失败常见原因是板型或端口选错。断电后总是归零,要确认使用的是带 EEPROM 的目标板及对应核心库;某些板卡用闪存模拟 EEPROM,提交方式和寿命策略可能不同,必须查该板官方文档。

复位按钮一按就反复清零,通常是程序被改成按住期间持续处理,而不是使用 fell() 边沿。计数接近 uint32_t 上限才会回绕,对于普通门磁用途很遥远,但需要永久审计的数据仍应在上位机定期导出,不能只保存在单片机中。

安全与回退

接线和改线前拔掉 USB,只使用开发板允许的 3.3 V 或 5 V 低压逻辑,不把 D2、D3 接到负电压或高于板卡电源轨的信号。干簧玻璃管较脆,应使用绝缘外壳固定,不要弯折玻璃根部。磁铁应远离磁条卡、机械硬盘和不适合接近强磁场的设备。该项目不连接门锁、市电、电磁铁或其他大电流负载。

需要回退时,先在 Arduino IDE 另存当前草图,再断电拆除 D2、D3 外设,重新上传官方的最小空白示例或 Blink 示例即可恢复开发板的一般用途。上传新草图不会自动清除 EEPROM;若确需清除计数,应使用本项目的复位按钮并等待保存提示,不要反复编写整片 EEPROM。出现芯片发热、USB 反复断连、线材异味或电源保护动作时立即拔线,回到断电状态逐根核对接线。

参考资料与转载说明

本文依据上述公开资料进行中文原创整理,不是逐句翻译,也不替代厂商手册。操作前请核对自己的版本、型号和备份。

评论一下?

OωO
取消