传感器与可信数据 / 传感器实验发布 内容更新

门磁开合记录器:给 ESP32 做一个可靠的状态输入

固定两线PID375门磁,用稳定窗口分开初始状态、当前开合与事件次数;完整串口记录器覆盖30周期、保持和信号断开方法,明确二线输入的诊断边界。

读完能做什么
固定两线PID375门磁,用稳定窗口分开初始状态、当前开合与事件次数;完整串口记录器覆盖30周期、保持和信号断开方法,明确二线输入的诊断边界。
开始之前
已做按钮消抖,能按GPIO丝印接线、识别电源/共地和LED极性。
所需器材
官方DevKitC-1 v1.1 N8R8、桥USB数据线、Adafruit PID375常开干接点门磁,2021-06-02后面包板线端款、可选WP7113ID红LED、330Ω±5%0.25W、短线、通断档万用表;可选预先断电安装的信号支路开关
版本组合
开发板:官方DevKitC-1 v1.1 / WROOM-1-N8R8;桥UART0/115200,CDC关闭软件:Arduino CLI1.5.1 / esp32:esp32 core3.3.12;完整8MB/QIO/OPI FQBN示例依赖:Arduino内置API;无第三方库;完整sketch.yaml与MIT
适用范围

只适用所列S3板、器件和锁定软件。真实编译与同源主机逻辑已执行;没有传感器、接线、物理动作或长期实验结果。

这篇验证到了哪一步

资料核对已通过
docs/esp32-evidence/sensors-root.md;固定官方/厂家来源与输入SHA验证日期:
构建 / 配置已通过
docs/esp32-evidence/sensors-root-builds.json;实际编译Exit0对象:完整S3 N8R8 FQBN版本:CLI1.5.1 / core3.3.12验证日期:
仿真已通过
docs/esp32-evidence/sensors-root-host.json;同源纯逻辑与合成输入检查,非GPIO/物理模拟验证日期:
本站实板待验证
没有本套板/器件,未接线、上传、观察电压/磁体/土壤或长期运行

构建通过只说明指定环境中的代码或配置通过检查;实物功能、功耗和稳定性以对应的实板记录为准。

01 / 传感器实验

门现在开着,与已经开过几次,是两个变量

门磁触点开不代表刚刚发生一次开门。若板上电时门本已开,应该先建立初始状态,再开始记录后续变化;否则每次重启都会伪造一个事件。本例用独立state/age/opens/closes日志,把INITIAL与OPENED/CLOSED分开。

这里只做桌面低压回路实验,固定PID375和短线,不将二线接点称作安防设备。本站没有实际磁体/板,30周期、开机状态和线缆注入均给出完整待执行方法;已完成的软件证据不能代替这些动作。

参考:[1] Adafruit PID375常开、磁体距离参考、2021线端修订[2] core3.3.12 GPIO:INPUT_PULLUP与digitalRead

02 / 传感器实验

确认这是无VCC的常开干接点

PID375一半是干簧开关,另一半是磁体。厂家描述磁体靠近时闭合;13mm描述与15mm技术栏只是厂家参考,实际安装方向、间隙和周边材料必须用通断档与本机读数确认,不能承诺这个距离内一定可靠。2021后线端易接面包板,未向其他有源模块外推。

断电把任意一线接GPIO5/J1-5,另一线GND,内部INPUT_PULLUP。触点开时HIGH、闭时LOW。可选GPIO4经330Ω到WP7113ID阳极、阴极GND,已接受CLOSED时亮。厂家触点额定200VDC不代表GPIO能接高压;全篇仅3.3V域,不给门磁另接5V/VCC。

连接解释
门磁一线→GPIO5INPUT_PULLUP;按GPIO丝印
门磁另一线→GND两线无极性,共地
GPIO4→330Ω→LED阳极,阴极GND可选闭合指示;GPIO38 RGB不用

参考:[1] Adafruit PID375常开、磁体距离参考、2021线端修订[2] core3.3.12 GPIO:INPUT_PULLUP与digitalRead[3] 固定官方S3板引脚:GPIO4/5和桥接口[4] Kingbright WP7113ID极性与电气条件

03 / 传感器实验

稳定30ms后先INITIAL,计数仍是零

首次采样仅建立候选值,UNKNOWN持续到它连续稳定至少30ms。若中途变化就重置稳定起点;第一次接受后输出INITIAL,无论开或闭都不增加opens/closes。之后只有接受到与当前状态不同的稳定值,才产生OPENED或CLOSED并增加对应一项。

每秒状态行给出电气状态和age_ms,年龄从最近一次被接受状态开始,并非门运动的精确时刻。delay(1)是让出执行机会,采样/串口不保证硬实时,极短变化可能漏采;uint32差跨一次millis回绕有效,长时间计数溢出也会回绕。RAM计数RESET/断电归零,不冒充持久历史。

参考:[2] core3.3.12 GPIO:INPUT_PULLUP与digitalRead[5] core3.3.12 millis实现与uint32返回

04 / 传感器实验

完整记录器:模型与入口各有一个文件

把DoorContact.h和同名.ino保存在下载包同一工程目录,保留sketch.yaml的完整板选项。没有第三方库。桥UART0/115200、CDC关闭,不等待串口连接才开始采样;即使不看日志,模型也会更新。

开机LED先LOW,首次确认闭合后才亮,避免UNKNOWN时给出已闭合的错觉。灯的锁存值只反映程序已接受状态,既非光学结果也不证明磁体位置;真正记录仍须查state与两项计数。

代码 / 配置
// ---- DoorContact.h ----
// SPDX-License-Identifier: MIT
#pragma once
#include <stdint.h>
class DoorContact {
 public:
  enum Event { NONE, INITIAL, OPENED, CLOSED };
  Event update(bool electricalOpen, uint32_t now) {
    if (!started_) { started_=true; candidate_=electricalOpen; changed_=now; }
    if (electricalOpen!=candidate_) { candidate_=electricalOpen; changed_=now; }
    if (uint32_t(now-changed_)<30) return NONE;
    if (!known_) { known_=true; open_=candidate_; since_=now; return INITIAL; }
    if (candidate_==open_) return NONE;
    open_=candidate_; since_=now;
    if(open_) { ++opens_; return OPENED; }
    ++closes_; return CLOSED;
  }
  bool known() const { return known_; }
  bool open() const { return open_; }
  uint32_t opens() const { return opens_; }
  uint32_t closes() const { return closes_; }
  uint32_t age(uint32_t now) const { return known_?uint32_t(now-since_):0; }
 private:
  bool started_=false,known_=false,candidate_=true,open_=true;
  uint32_t changed_=0,since_=0,opens_=0,closes_=0;
};

// ---- esp32-reed-switch-door-state.ino ----
// SPDX-License-Identifier: MIT
// S05: PID375 two-wire dry contact; GPIO5 pull-up, other wire GND.
#include <Arduino.h>
#include "DoorContact.h"
constexpr uint8_t CONTACT_PIN=5, LED_PIN=4;
DoorContact contact;
void setup() {
  pinMode(CONTACT_PIN,INPUT_PULLUP);
  pinMode(LED_PIN,OUTPUT); digitalWrite(LED_PIN,LOW);
  Serial.begin(115200);
  Serial.println("S05 core=3.3.12 PID375 GPIO5; HIGH means electrical open OR broken wire, not a security diagnosis");
}
void loop() {
  static uint32_t reportedAt=0;
  const uint32_t now=millis();
  const auto event=contact.update(digitalRead(CONTACT_PIN)==HIGH,now);
  if(event!=DoorContact::NONE) {
    digitalWrite(LED_PIN,contact.open()?LOW:HIGH);
    Serial.printf("event=%s electrical=%s opens=%lu closes=%lu at=%lu\n",
      event==DoorContact::INITIAL?"INITIAL":event==DoorContact::OPENED?"OPENED":"CLOSED",
      contact.open()?"OPEN":"CLOSED",(unsigned long)contact.opens(),
      (unsigned long)contact.closes(),(unsigned long)now);
  }
  if(uint32_t(now-reportedAt)>=1000) {
    reportedAt=now;
    Serial.printf("state=%s age_ms=%lu opens=%lu closes=%lu; counters reset on reboot\n",
      !contact.known()?"UNKNOWN":contact.open()?"CONTACT_OPEN":"CONTACT_CLOSED",
      (unsigned long)contact.age(now),(unsigned long)contact.opens(),(unsigned long)contact.closes());
  }
  delay(1);
}

参考:[2] core3.3.12 GPIO:INPUT_PULLUP与digitalRead[3] 固定官方S3板引脚:GPIO4/5和桥接口[5] core3.3.12 millis实现与uint32返回

05 / 传感器实验

两次开机检查与30个完整周期

按README完整FQBN编译,替换真实桥端口上传,115200打开monitor。先磁体靠近、RESET,应在稳定窗口后得到INITIAL/CLOSED且计数0;再磁体移远、RESET,另存INITIAL/OPEN且计数0。不能把一次启动输出当两种情况都已验证。

重新以CLOSED初始状态开始,做30个“移远→靠近”完整周期,每个位置至少保持约0.2秒;预期opens=30/closes=30。开和闭两类边沿加起来60,不把周期数与事件数混写。每个位置另保持2秒应无新增;记录日期、初态、实际计数和日志路径,非实测表保持空白。

  1. 先通断档确认磁体靠近/移远确实改变触点。
  2. 分开保存初始开、初始闭两轮RESET日志。
  3. 从初始闭做30完整周期,并检查额外保持不增。
  4. 观察LED只作辅助,核对串口事件与独立计数。
做到这里,你应该看到
INITIAL不计事件,30周期的两项计数均30;本站硬件结果仍待测。

参考:[1] Adafruit PID375常开、磁体距离参考、2021线端修订[2] core3.3.12 GPIO:INPUT_PULLUP与digitalRead[3] 固定官方S3板引脚:GPIO4/5和桥接口

06 / 传感器实验

信号断开实验:展示边界,不编造断线诊断

保持磁体靠近时断开信号回路,电平升HIGH,稳定后应只增加一次OPENED;恢复后一次CLOSED。建议先断电装低压支路开关,再上电操作,避免带电拔线误碰电源。若本来已是OPEN,断线没有新电平,不应期待额外事件。

这也说明二线限制:断线与磁体远离同样可读HIGH,短接地与磁体靠近同样LOW。当前代码无独立FAULT态,不能辨别所有导线松动、触点失效或短路。长线/安防需要另做受监测回路及电气设计;去抖只处理稳定时间,不能解决一切线缆问题。

如果卡住了
故障实验后断电恢复最小接线,保存每个条件实际日志。

参考:[1] Adafruit PID375常开、磁体距离参考、2021线端修订[2] core3.3.12 GPIO:INPUT_PULLUP与digitalRead

07 / 传感器实验

先测触点和地,再看模型

一直OPEN:先断电测门磁靠近能否连通,查GND、GPIO5丝印和面包板接点,再核磁体位置。一直CLOSED:查同一排误短接或GPIO5接地,移开磁体再读。计数过多先查安装振动、线接触与停留时间;真实反复开合不能靠加大消抖假装稳定。

上传失败回到桥USB数据线、实际端口、BOOT/RESET和固定FQBN,保留首错;看不到日志核115200及CDC配置,不先换库。恢复后先重跑初始状态,再重跑30周期,既有RAM计数不当旧历史。

参考:[1] Adafruit PID375常开、磁体距离参考、2021线端修订[2] core3.3.12 GPIO:INPUT_PULLUP与digitalRead[3] 固定官方S3板引脚:GPIO4/5和桥接口

08 / 传感器实验

同源逻辑已检查,物理可靠性仍需补记录

CLI1.5.1/core3.3.12、完整8MB/QIO/OPI S3 FQBN实际编译Exit0,原命令与源摘要在sensors-root-builds.json。主机直接用同一个DoorContact.h检查29/30ms边界、初始开/闭、反弹、保持、30周期、状态年龄与uint32回绕;没有复制一套测试模型。

主机合成电平不模拟磁场、GPIO、电气和轮询调度。本站observations.csv只含表头,实板与长期稳定性未测;不将函数正确推成“门磁永不丢事件”。下一步可继续串口分帧,把日志协议也做成有边界的输入。

代码 / 配置
clang++ -std=c++17 -Wall -Wextra -Werror tests/contact_test.cpp -o /tmp/s05-contact-test
/tmp/s05-contact-test

参考:[2] core3.3.12 GPIO:INPUT_PULLUP与digitalRead[5] core3.3.12 millis实现与uint32返回

BACK TO THE SOURCE

资料来源与版本说明

已实读固定core源码、板级引脚及具体厂家资料;物理实验与构建/合成输入检查分开记录。资料核对日期:。资料核对、程序构建与实板测试分别记录,不互相替代。

  1. Adafruit PID375常开、磁体距离参考、2021线端修订
  2. core3.3.12 GPIO:INPUT_PULLUP与digitalRead
  3. 固定官方S3板引脚:GPIO4/5和桥接口
  4. Kingbright WP7113ID极性与电气条件
  5. core3.3.12 millis实现与uint32返回