人体存在传感器建议采用边沿触发还是电平触发?
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 |
| 传感器 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️⃣ 运行流程
-
上电/唤醒 →
setup()
立即运行 -
若传感器为高:亮灯 → 轮询等待人离开 → 熄灭 → 睡觉
-
若传感器为低:直接熄灭 → 睡觉
-
Deep-sleep 期间仅传感器耗电(μA 级),灯带全灭
4️⃣ 注意事项
-
RST 引脚只能 高电平唤醒,传感器输出极性务必为高电平有效。
-
需要 每 1 s 唤醒一次检查,若想更长周期,把
SLEEP_MS
改大即可。 -
若传感器 自带延时(如 LD2410 可设 15 s 输出保持),把
SLEEP_MS
设成 ≥ 传感器延时 即可避免频繁唤醒。
为什么代码全写在setup里而不是loop里?
把主逻辑全部放进
setup()
而几乎不用 loop()
,是 ESP8266 Deep-sleep 模式下的标准写法:-
Deep-sleep 的本质
调用ESP.deepSleep()
后,整个芯片(除 RTC 外)断电;
定时到期 或 RST 引脚电平变化 → 芯片复位 → 重新从setup()
开始执行。
因此loop()
永远不会被走到。 -
“每醒来一次就是一次新的程序”
把需要做的事(检测传感器、点亮/熄灭灯带)全部在setup()
里完成,然后立即再次进入 Deep-sleep —— 这就相当于 事件驱动的单次任务,省掉了loop()
持续运行的功耗。 -
对比常规永驻内存方式
• 普通模式:loop()
持续轮询,MCU 一直跑,功耗 mA 级。
• Deep-sleep:setup()
只做一次任务 → 睡觉 → 醒来再跑一次,平均功耗降到 μA 级。 -
代码结构最小化
在 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_PIN
,digitalRead()
会始终读到 低电平,导致程序逻辑错误。 -
正确做法是:
– 传感器 OUT → NodeMCU RST(D0) 负责“把芯片从 Deep-sleep 里拉醒”;
– 传感器 OUT → 另外再飞一根线到 D1(GPIO 5) 供程序digitalRead()
判断“当前有没有人”。 -
如果你的传感器只有一根 OUT 线,就把 OUT 同时接到 RST 和 D1(两根线并在一起),或者干脆用 GPIO 16(D0) 做传感器输入,但这样就不能再用 RST 唤醒了(GPIO16 不能唤醒)。
因此最保险的接法仍是 OUT→RST + OUT→D1。
全部评论