人体存在传感器建议采用边沿触发还是电平触发?

19 2025-07-17 10:31

对于人体存在传感器(如热释电红外、毫米波雷达、FP1等),建议优先采用“电平触发”,而不是边沿触发,理由如下:

为什么选电平触发?

表格
 
复制
特性 人体存在传感器 推荐理由
信号持续时间 有人时持续输出高电平,无人后延时恢复低电平 电平触发能直接反映“是否有人”这一状态,而不是“是否刚进入”
 
抗抖动需求低 传感器输出已滤波,信号稳定 不需要像按键那样防抖动,边沿触发反而可能漏报或误报
 
逻辑简单 高电平=有人,低电平=无人 电平触发可直接用于控制灯、空调、报警器等持续动作
 
中断响应 电平触发可配合中断唤醒MCU 适合低功耗设备,如电池供电的人体感应灯
 

⚠️ 边沿触发的局限性

  • 只能捕捉“进入瞬间”,无法判断“是否还在”。
  • 如果人静止不动,传感器可能保持高电平但无新边沿,导致系统误判为无人。
  • 若用于触发报警或计数,边沿触发可能导致重复触发或遗漏

典型应用场景对比

表格
 
复制
场景 推荐触发方式 原因
人来灯亮,人走灯灭 电平触发 需要持续状态判断
有人进门就拍照一次 边沿触发(上升沿) 只关心“进入”这一瞬间
检测入侵并持续报警 电平触发 有人就报警,直到离开

结论

人体存在传感器建议采用“电平触发”,因为它直接对应“是否有人”的持续状态,逻辑清晰、响应稳定,适合绝大多数智能照明、安防、节能场景。
边沿触发仅适用于只需要“首次进入”信号的特殊场合,如单次计数或触发拍照。

 

 

 

采用中断还是轮询?

对于人体存在传感器,**“中断”还是“轮询”**的核心取舍在于:功耗、实时性、代码复杂度

优先推荐:中断(电平触发)

表格
 
复制
维度 评价
功耗 MCU 可进入睡眠,仅在电平变化时被唤醒,适合电池供电
实时性 人来/人走瞬间即可响应,延迟 < 1 ms
代码简洁 主循环无需反复 digitalRead(),逻辑解耦
典型写法 attachInterrupt(digitalPinToInterrupt(PIN), isr, CHANGE);

⚠️ 轮询的适用场景

表格
 
复制
场景 说明
非低功耗产品 如常供电的插座、网关,功耗不敏感,可 10~50 ms 轮询一次
多传感器并发 若需同时轮询多个传感器,用 millis() 定时轮询更直观
Arduino UNO/Nano 等中断引脚有限 若传感器接在非中断脚,只能轮询

低功耗设计示例(中断)

cpp
 
复制
volatile bool humanPresent = false;

void setup() {
  pinMode(PIR_PIN, INPUT);
  attachInterrupt(digitalPinToInterrupt(PIR_PIN), pirISR, CHANGE);
  sleepNow();  // 进入睡眠,等待中断唤醒
}

void pirISR() {
  humanPresent = digitalRead(PIR_PIN);  // 高=有人,低=无人
}

void loop() {
  if (humanPresent) {
    turnOnLight();
  } else {
    turnOffLight();
    sleepNow();  // 再次睡眠
  }
}
 

一句话结论

  • 电池供电 / 对实时性有要求 → 中断(电平触发 CHANGE)
  • 市电供电 / 中断引脚已用完 → 轮询(50 ms 周期即可)

 

 

 

在上面代码基础上增加省电模式

 

下面在“即时亮 / 灭 WS2811”的代码基础上,加入 ESP8266 Deep-sleep 省电逻辑
  • 有人 → 立即唤醒,点亮灯带
  • 无人 1 秒后 → MCU 进入 Deep-sleep(约 20 µA)
  • 传感器 OUT → RST(D0) 用于唤醒
  • 灯带、传感器 共 5 V 供电(Deep-sleep 时灯带掉电,功耗仅传感器待机)

1️⃣ 硬件改动

| 传感器 OUT | → | NodeMCU RST(=D0 / GPIO16) |
| 传感器 VCC | → | VIN(5 V) |
| 传感器 GND | → | GND |
| WS2811 DIN | → | D2(GPIO4) |
| WS2811 5 V | → | VIN |
| WS2811 GND | → | GND |
RST 脚必须 直接连传感器 OUT;高电平唤醒后,RST 被拉高→重新启动 → 重新执行 setup()

2️⃣ 代码(Deep-sleep 版)

cpp
 
复制
#include <Adafruit_NeoPixel.h>

#define SENSOR_PIN   5      // D1,仅用于第一次上电检测
#define LED_PIN      4      // D2
#define NUM_LEDS     16
#define SLEEP_MS     1000UL // 无人 1 s 后睡觉

Adafruit_NeoPixel strip(NUM_LEDS, LED_PIN, NEO_GRB + NEO_KHZ800);

void setup() {
  // 上电即检查传感器
  pinMode(SENSOR_PIN, INPUT);
  bool humanNow = digitalRead(SENSOR_PIN) == HIGH;

  strip.begin();
  strip.show();

  if (humanNow) {
    // 有人:全亮中等白
    for (uint8_t i = 0; i < NUM_LEDS; i++)
      strip.setPixelColor(i, strip.Color(128, 128, 128));
    strip.show();

    // 轮询 50 ms,直到人离开
    while (digitalRead(SENSOR_PIN) == HIGH) {
      delay(50);
    }
  }

  // 无人:准备 Deep-sleep
  strip.clear();
  strip.show();
  ESP.deepSleep(SLEEP_MS * 1000);   // 单位微秒
}

void loop() {
  // Deep-sleep 唤醒后重新执行 setup(),loop() 永远不会到
}
 

3️⃣ 运行流程

  1. 上电/唤醒setup() 立即运行
  2. 若传感器为高:亮灯 → 轮询等待人离开 → 熄灭 → 睡觉
  3. 若传感器为低:直接熄灭 → 睡觉
  4. Deep-sleep 期间仅传感器耗电(μA 级),灯带全灭

4️⃣ 注意事项

  • RST 引脚只能 高电平唤醒,传感器输出极性务必为高电平有效。
  • 需要 每 1 s 唤醒一次检查,若想更长周期,把 SLEEP_MS 改大即可。
  • 若传感器 自带延时(如 LD2410 可设 15 s 输出保持),把 SLEEP_MS 设成 ≥ 传感器延时 即可避免频繁唤醒。
 
为什么代码全写在setup里而不是loop里?
 
把主逻辑全部放进 setup() 而几乎不用 loop(),是 ESP8266 Deep-sleep 模式下的标准写法
  1. Deep-sleep 的本质
    调用 ESP.deepSleep() 后,整个芯片(除 RTC 外)断电;
    定时到期 或 RST 引脚电平变化 → 芯片复位 → 重新从 setup() 开始执行
    因此 loop() 永远不会被走到。
  2. “每醒来一次就是一次新的程序”
    把需要做的事(检测传感器、点亮/熄灭灯带)全部在 setup() 里完成,然后立即再次进入 Deep-sleep —— 这就相当于 事件驱动的单次任务,省掉了 loop() 持续运行的功耗。
  3. 对比常规永驻内存方式
    • 普通模式:loop() 持续轮询,MCU 一直跑,功耗 mA 级。
    • Deep-sleep:setup() 只做一次任务 → 睡觉 → 醒来再跑一次,平均功耗降到 μA 级
  4. 代码结构最小化
    在 Deep-sleep 场景里,setup() 就是“主函数”;
    loop() 反而成了永远到不了的“死代码”,干脆留空。
因此,把逻辑全部放在 setup() 不是偷懒,而是 Deep-sleep 省电模型的正确姿势
 
 

上面程序#define SENSOR_PIN   5 是不是应该改成NodeMCU RST(=D0 / GPIO16)

不是的。
SENSOR_PIN 仍然应该接 D1(GPIO 5),而不是 RST(GPIO 16 / D0)
原因:
  • ESP8266 的 RST 引脚只能作为“唤醒源”不能作为普通 GPIO 去 digitalRead()
    如果把它定义成 SENSOR_PINdigitalRead() 会始终读到 低电平,导致程序逻辑错误。
  • 正确做法是:
    传感器 OUT → NodeMCU RST(D0)  负责“把芯片从 Deep-sleep 里拉醒”;
    传感器 OUT → 另外再飞一根线到 D1(GPIO 5)  供程序 digitalRead() 判断“当前有没有人”。
  • 如果你的传感器只有一根 OUT 线,就把 OUT 同时接到 RST 和 D1(两根线并在一起),或者干脆用 GPIO 16(D0) 做传感器输入,但这样就不能再用 RST 唤醒了(GPIO16 不能唤醒)。
    因此最保险的接法仍是 OUT→RST + OUT→D1

 

全部评论

·