跳到主要内容

人体感应灯

重要提示:关于开发板的兼容性

本教程的核心逻辑适用于所有 ESP32 开发板,但所有操作步骤均以 微雪 ESP32-S3-Zero 迷你开发板 为例进行讲解。如果您使用其他型号的开发板,请根据实际情况修改相应设置。

项目介绍

本项目通过 PIR(被动红外)传感器检测人体存在,自动控制 WS2812 LED 灯带的开关,实现智能感应照明功能。当传感器检测到人体活动时,灯带自动点亮;当持续一段时间未检测到活动后,灯带自动熄灭。

硬件连接

需要使用的器件有:

  • PIR 运动传感器 * 1
  • WS2812 灯条 * 1
  • 面包板 * 1
  • 导线
  • ESP32 开发板

按照下面接线图连接电路:

ESP32-S3-Zero 引脚图

ESP32-S3-Zero-Pinout

接线图
硬件连接注意事项
  • 电源连接:PIR 传感器和 WS2812 灯带都需要连接到 5V 电源。
  • 跳线帽设置:PIR 模块的跳线帽需要设置为 H 模式(高电平触发模式)。
  • 调试建议:为方便初次调试,建议先将 PIR 模块的检测距离调节到最低,避免误触发,待功能验证正常后再根据实际需求调整。

代码实现

import time
import machine
import neopixel

# --- 配置 ---
PIR_PIN = 7
LED_PIN = 8
NUM_LEDS = 8
TIMEOUT = 5000 # 也就是人离开后,灯还亮多久(毫秒)
COLOR = (128, 0, 128)

# --- 初始化 ---
pir = machine.Pin(PIR_PIN, machine.Pin.IN, machine.Pin.PULL_DOWN)
np = neopixel.NeoPixel(machine.Pin(LED_PIN), NUM_LEDS)

def switch_light(on):
color = COLOR if on else (0, 0, 0)
np.fill(color)
np.write()

# --- 主程序 ---
try:
print("系统启动。.. (按 Ctrl+C 停止)")
is_on = False
last_motion_time = time.ticks_ms()

# 刚开始先强制关灯,防止状态不同步
switch_light(False)

while True:
current_time = time.ticks_ms()

# --- 核心逻辑修正 ---

# 1. 只要传感器是高电平 (有人),就一直刷新时间戳
if pir.value() == 1:
last_motion_time = current_time
# 如果灯没亮,赶紧点亮
if not is_on:
print("检测到有人 -> 开灯")
switch_light(True)
is_on = True

# 2. 如果传感器是低电平 (无人),才开始计算超时
else:
# 只有灯亮着的时候才需要判断是否超时
if is_on:
# 计算:现在时间 - 最后一次有人时间
duration = time.ticks_diff(current_time, last_motion_time)

if duration > TIMEOUT:
print("超时无人 -> 关灯")
switch_light(False)
is_on = False
# 这里加个小延时,防止关灯电压波动导致的瞬间误触
time.sleep_ms(1000)

# 循环检查频率,100ms 足够了,反应很快且不费电
time.sleep_ms(100)

except KeyboardInterrupt:
print("\n 用户手动停止")

finally:
# 无论发生什么,确保最后灯是灭的
print("清理资源,关闭灯带。..")
switch_light(False)

代码解释

  • 导入库machine 库用于控制硬件(GPIO),neopixel 库用于控制 WS2812 LED 灯带,time 库用于实现延时和时间戳管理。

  • 常量定义:程序开头定义了 PIR 传感器和 LED 灯带的 GPIO 引脚号、LED 数量、超时时长和灯光颜色。将这些参数集中管理,便于后续根据实际需求快速调整,而无需深入修改逻辑代码。

    # --- 配置 ---
    PIR_PIN = 7
    LED_PIN = 8
    NUM_LEDS = 8
    TIMEOUT = 5000 # 也就是人离开后,灯还亮多久(毫秒)
    COLOR = (128, 0, 128)
  • GPIO 初始化:使用 machine.Pin 类创建 PIR 传感器对象,配置为输入模式并启用下拉电阻;使用 neopixel.NeoPixel 类创建灯带对象,控制 WS2812 LED。

    # --- 初始化 ---
    pir = machine.Pin(PIR_PIN, machine.Pin.IN, machine.Pin.PULL_DOWN)
    np = neopixel.NeoPixel(machine.Pin(LED_PIN), NUM_LEDS)

    PIR 传感器(被动红外传感器)通过检测人体红外辐射来判断是否有人移动。配置为输入模式并启用下拉电阻,确保在无信号时引脚读数为低电平。

  • 辅助函数switch_light(on) 函数封装了灯带的开关控制。

    def switch_light(on):
    color = COLOR if on else (0, 0, 0)
    np.fill(color)
    np.write()
    • 参数 onTrue 时,所有 LED 设置为 COLOR;为 False 时,设置为 (0, 0, 0) 即关闭。
    • np.fill(color) 将所有 LED 设置为指定颜色。
    • np.write() 将颜色数据写入灯带,使其显示。
  • 主循环逻辑:程序在一个无限循环 while True 中持续监测 PIR 传感器并控制灯光。

    • 初始化状态:程序启动时强制关灯(switch_light(False)),确保状态同步。

    • 核心逻辑

      1. 检测到有人(PIR 高电平)

        • 立即刷新时间戳 last_motion_time = current_time
        • 如果灯未亮,则点亮灯并设置 is_on = True
      2. 未检测到人(PIR 低电平)

        • 仅在灯已亮的情况下才计算超时时长。
        • 使用 time.ticks_diff() 计算自最后一次检测到人以来的时长。
        • 如果超过 TIMEOUT 设定的时长,则关灯并设置 is_on = False
        • 关灯后延时 1 秒,防止关灯时的电压波动导致误触发。
    • 循环检查频率:每 100ms 检查一次,既保证快速响应,又避免过度占用 CPU。

  • 异常处理:使用 try...except...finally 结构增强程序稳定性。

    • except KeyboardInterrupt:捕获用户中断(Ctrl+C),允许程序优雅退出。
    • finally:无论程序如何结束,都强制调用 switch_light(False) 关闭灯带,防止硬件处于不确定状态。

参考链接