WLED stairway_wipe_basic 用户插件:用 U0/U1 接口驱动楼梯灯带 Wipe 效果
【免费下载链接】WLEDControl WS2812B and many more types of digital RGB LEDs with an ESP32 over WiFi!项目地址: https://gitcode.com/GitHub_Trending/wl/WLED
本文基于 WLED 仓库中 usermods/stairway_wipe_basic/readme.md 的原始说明,结合 stairway_wipe_basic.cpp 源码逐段解析这个"楼梯照明" v2 用户插件:如何用 HTTP API 的U0/U1参数触发灯带逐段点亮(Wipe)、保持常亮或定时熄灭,以及如何通过STAIRCASE_WIPE_OFF编译选项切换"反向 Wipe 熄灭"与"渐隐熄灭"两种关闭行为。读完后你将掌握该插件的完整行为状态机、编译启用方式(build flag 与 PlatformIOcustom_usermods两种路径)以及将其对接运动传感器等输入设备的改造思路。
功能定位:楼梯灯带照明
该插件出自 usermods/stairway_wipe_basic 目录,是一个基础的 v2 用户插件(Usermod),用于在楼梯侧面或台阶上安装灯带,实现"人走上楼时灯光逐级点亮"的效果。原始 readme 的完整功能描述如下:
- 当
userVar0变量被设置后,LED 会以Wipe(逐格扫过)效果依次点亮; - 通过把
userVar0设为1或2分别控制两个方向(对应 HTTP API 命令U0=1与U0=2); - Wipe 完成后,灯要么以Solid(常亮)效果无限保持,要么在
userVar1秒后熄灭; - 如果
userVar0再次被更新(例如第二个传感器被触发),灯光会缓慢淡出直至熄灭;readme 还指出,这一行为可扩展为反向顺序的 Wipe 熄灭效果(即STAIRCASE_WIPE_OFF选项,见下文); - 该版本是"基础版":通过 HTTP API 的
U0、U1调用和/或宏(macro)实现,readme 明确提示"应该很容易改造该代码以对接运动传感器或其他输入设备"。
从源码结构看,插件的类注释也说明了其用法前提:把 usermod 拷贝到工程目录后注册(当前仓库已改为REGISTER_USERMOD宏自动注册,见后文)。
触发接口:userVar0 / userVar1 与 HTTP API
readme 给出的核心交互方式是两个用户变量,其来源是 WLED 为 usermod 预留的全局变量。在 wled00/wled.h 中可以看到它们的声明:
WLED_GLOBAL uint16_t userVar0 _INIT(0), userVar1 _INIT(0); //available for use in usermod两个变量均为uint16_t(取值范围 0~65535),初始值为 0。WLED 的 v1 usermod 模板 wled00/usermod.cpp 中的注释也明确了对应关系:"Use userVar0 and userVar1 (API calls &U0=, &U1=, uint16_t)"。
各参数的语义与取值
| 参数 | HTTP API | JSON 状态键 | 语义 |
|---|---|---|---|
userVar0 | &U0= | user0 | 触发方向与使能:1= 楼梯与控制器同侧检测到动作;2= 对侧检测到动作;0= 无动作 |
userVar1 | &U1= | user1 | 常亮保持时长(秒)。为 0 时灯将保持常亮,直到另一个 PIR 触发或外部命令 |
HTTP 请求参数的解析路径在 wled00/set.cpp 中,形如userVar0 = getNumVal(req, pos),即 URL 中的U0=查询参数直接写入该全局变量。插件自身则在readFromJsonState中同步 JSON 状态:
void readFromJsonState(JsonObject& root) { userVar0 = root["user0"] | userVar0; //if "user0" key exists in JSON, update, else keep old value }这意味着除了 HTTP GET 参数,任何向/json发送{"state": {"user0": 1}}的客户端(包括 MQTT 桥接、宏)都能触发同样的逻辑。|运算符的语义是"键存在则更新,否则保留旧值",保证非本插件的状态更新不会误清触发值。
readme 强调这是"basic 版本":真实部署中典型做法是楼梯上下各装一个 PIR 运动传感器,传感器触发时向 WLED 发U0=1/U0=2(可由 HTTP 宏、MQTT 或第三方脚本完成),人离开后灯按U1设定的秒数自动熄灭。
行为状态机:源码级逐段解析
插件的核心是一个 5 态状态机(wipeState:0 空闲、1 点亮 Wipe 中、2 常亮保持、3 准备关闭、4 反向 Wipe 熄灭中),全部逻辑位于 stairway_wipe_basic.cpp 的loop()中。
触发与方向判断(L34–L39)
if (userVar0 > 0) { if ((previousUserVar0 == 1 && userVar0 == 2) || (previousUserVar0 == 2 && userVar0 == 1)) wipeState = 3; //turn off if other PIR triggered previousUserVar0 = userVar0; if (wipeState == 0) { startWipe(); wipeState = 1; }要点:
- 只有当方向发生翻转(1→2 或 2→1)时才直接进入关闭流程(
wipeState = 3)。这正是 readme 所说"userVar0 is updated (e.g. by triggering a second sensor) the light will fade slowly until it's off" 的实现; - 首次触发(
wipeState == 0)调用startWipe()进入点亮流程。
startWipe():切换到 Wipe 效果(L93–L107)
void startWipe() { bri = briLast; //turn on jsonTransitionOnce = true; strip.setTransition(0); //no transition effectCurrent = FX_MODE_COLOR_WIPE; strip.resetTimebase(); //make sure wipe starts from beginning //set wipe direction Segment& seg = strip.getSegment(0); bool doReverse = (userVar0 == 2); seg.setOption(1, doReverse); colorUpdated(CALL_MODE_NOTIFICATION); }实现上有几个值得注意的细节:
- 亮度恢复:
bri = briLast把亮度恢复为上次非零亮度,避免用户之前手动调暗后触发失效; - 禁用过渡:
strip.setTransition(0)配合jsonTransitionOnce,让 Wipe 起始帧不受全局渐变(fade)干扰;strip.resetTimebase()确保扫光从灯带起点开始,而不是从中途开始; - 方向控制:把 WLED 内建的
FX_MODE_COLOR_WIPE(彩色 Wipe 效果)作为载体,并通过**段 0 的选项 1(reverse 标志)**决定扫描方向——userVar0 == 2时反向,对应 readme 中"Both directions are supported by setting userVar0 to 1 and 2"; colorUpdated(CALL_MODE_NOTIFICATION)通知核心刷新灯效,CALL_MODE_NOTIFICATION表示这是插件发起的变更而非网络请求。
点亮时长与完成判定(L42–L49)
} else if (wipeState == 1) { //wiping uint32_t cycleTime = 360 + (255 - effectSpeed)*75; //this is how long one wipe takes if (millis() + strip.timebase > (cycleTime - 25)) { //wipe complete effectCurrent = FX_MODE_STATIC; timeStaticStart = millis(); colorUpdated(CALL_MODE_NOTIFICATION); wipeState = 2; } }Wipe 的总时长由效果速度effectSpeed(0~255)决定:
cycleTime = 360 + (255 - effectSpeed) × 75 (毫秒)| effectSpeed | Wipe 一次时长 |
|---|---|
| 0(最慢) | 360 + 255×75 = 19485 ms(约 19.5 秒) |
| 255(最快) | 360 ms |
也就是说,用户可以直接用 WLED 常规的状态设置(speed参数)来控制楼梯灯光"扫完"的速度。完成判定预留了 25 ms 余量(源码注释:"minus 25 ms to make sure we switch in time"),保证在 Wipe 动画结束前就切到常亮;完成后效果切换为FX_MODE_STATIC(readme 所称的 Solid effect)。
常亮保持与定时熄灭(L50–L54)
} else if (wipeState == 2) { //static if (userVar1 > 0) //if U1 is not set, the light will stay on until second PIR or external command is triggered { if (millis() - timeStaticStart > userVar1*1000) wipeState = 3; } }与 readme 描述一致:userVar1 > 0时,灯在常亮userVar1秒后进入关闭流程;userVar1 == 0则无限保持,直到方向翻转或外部再次置位。
关闭流程:反向 Wipe 与渐隐两种模式(L55–L78)
} else if (wipeState == 3) { //switch to wipe off #ifdef STAIRCASE_WIPE_OFF effectCurrent = FX_MODE_COLOR_WIPE; strip.timebase = 360 + (255 - effectSpeed)*75 - millis(); //make sure wipe starts fully lit colorUpdated(CALL_MODE_NOTIFICATION); wipeState = 4; #else turnOff(); #endif } else { //wiping off if (millis() + strip.timebase > (725 + (255 - effectSpeed)*150)) turnOff(); //wipe complete }这是 readme 中-D STAIRCASE_WIPE_OFF配置项("Have the LEDs wipe off instead of fading out")的完整实现:
- 定义该宏时:进入
wipeState = 4,复用FX_MODE_COLOR_WIPE做一次反向扫光熄灭,并把strip.timebase人为回拨,使扫光看起来从"全亮"状态开始(即从尾端向头端逐格熄灭)。该反向 Wipe 的时长公式为725 + (255 - effectSpeed) × 150ms,比点亮方向慢一倍系数; - 未定义该宏时(默认):直接调用
turnOff(),走渐隐路径:
void turnOff() { jsonTransitionOnce = true; #ifdef STAIRCASE_WIPE_OFF strip.setTransition(0); //turn off immediately after wipe completed #else strip.setTransition(4000); //fade out slowly #endif bri = 0; stateUpdated(CALL_MODE_NOTIFICATION); wipeState = 0; userVar0 = 0; previousUserVar0 = 0; }默认模式下熄灭走4000 ms 的渐变过渡(readme 所称 "fade slowly until it's off")。两种模式下turnOff()都会把bri清零、复位状态机并清空userVar0,为下一次触发做好准备。
另外,loop()的else分支(userVar0 == 0)负责在触发值被外部清零时复位状态机,并在曾亮灯的情况下执行同样的关闭逻辑——即外部系统直接发U0=0也能关灯。
编译启用:build flag 与当前构建系统
readme 的 Install 章节原文是:
Add the buildflag
-D USERMOD_STAIRCASE_WIPEto your environment to activate it.
并给出唯一的配置项:
-D STAIRCASE_WIPE_OFF—— Have the LEDs wipe off instead of fading out
在源码中可以看到,STAIRCASE_WIPE_OFF的定义被有意移出了 .cpp 文件:
//moved to buildflag //comment this out if you want the turn off effect to be just fading out instead of reverse wipe //#define STAIRCASE_WIPE_OFF即默认渐隐;如需反向 Wipe 熄灭,在编译环境中添加-D STAIRCASE_WIPE_OFF。
当前仓库的实际启用方式
需要注意适用前提:当前源码文件中并没有USERMOD_STAIRCASE_WIPE宏守卫,说明 readme 中的 buildflag 写法对应的是早期"usermods 按头文件手工包含"的旧机制。当前仓库采用的是 PlatformIO +custom_usermods的模块化构建:
- platformio.ini 中提供了全局
custom_usermods选项(如common段的custom_usermods =),各环境可用custom_usermods = stairway_wipe_basic声明要编入的 usermod; - 构建脚本 pio-scripts/load_usermods.py 在配置阶段解析该选项,把每个 usermod 目录解析为
symlink://本地库并追加到lib_deps,*通配符则展开为 usermods 目录下所有含library.json的插件(例如ESP32_USERMODS环境使用custom_usermods = *); - 插件 library.json 必须满足构建脚本的硬性检查:
{ "name": "stairway_wipe_basic", "build": { "libArchive": false } }"libArchive": false是强制项——load_usermods.py 会检查每个 usermod 的库档案设置,缺失即报错退出("libArchive=false is missing on usermod(s) ... modules will not compile in correctly")。这是因为 v2 usermod 依赖链接器段(REGISTER_USERMOD宏生成的动态数组)注册,必须以目标文件而非静态库的形式参与最终链接。
注册机制
插件末尾两行完成自我注册:
static StairwayWipeUsermod stairway_wipe_basic; REGISTER_USERMOD(stairway_wipe_basic);REGISTER_USERMOD是 wled00/dynarray.h 提供的宏,把实例指针放入链接器排序的dtors段数组;运行时由 wled00/um_manager.cpp 中的UsermodManager统一驱动:setup()在开机时遍历调用、loop()在主循环中逐个调用每个 usermod 的loop()——楼梯插件全部逻辑就运行在这条每周期回调里。此外addToJsonState/readFromJsonState也由管理器在 JSON 状态收发时统一分发(um_manager.cpp),因此user0/user1才能进入/json与/json/state状态机。插件 ID 定义在 wled00/const.h:USERMOD_ID_STAIRWAY_WIPE = 44,会在/json/info的um数组中出现,便于前端确认插件已编入固件。
典型部署与传感器对接
结合 readme 与源码,一个完整的楼梯照明部署链路是:
- 灯带沿楼梯安装,WLED 控制灯带(建议使用第 0 段承载 Wipe,方向由
U0决定); - 楼梯上下各一个 PIR 传感器,通过任意方式(HTTP 宏、MQTT、ESP-NOW 或脚本)在检测到动作时发送
U0=1/U0=2; U1设置离人后保持常亮的秒数(例如U1=15表示 15 秒);- 人在对面 PIR 下出现(方向翻转)时,灯立即进入渐隐(或定义了
STAIRCASE_WIPE_OFF时进入反向 Wipe)熄灭流程; - 用
speed参数调节 Wipe 快慢:effectSpeed越高扫得越快(360 ms ~ 约 19.5 s)。
readme 的最后一段提示了扩展方向:"It should be easy to adapt this code to interface with motion sensors or other input devices." 由于触发完全依赖全局的userVar0/userVar1,扩展时只需在setup()中初始化 GPIO/传感器、在loop()中把传感器读数写入这两个变量,状态机部分可以原样复用;v2 用户插件基类还提供了handleButton、onStateChange等钩子(见 wled00/um_manager.cpp 的分发逻辑),可作为进一步集成的入口。
小结
stairway_wipe_basic 是 WLED 用户插件体系中一个麻雀虽小五脏俱全的范例:它仅靠两个 usermod 全局变量(U0/U1)和 WLED 内建的 Color Wipe / Static 效果,就实现了一个带方向判断、定时熄灭和双模式关闭行为的楼梯照明控制器。阅读 stairway_wipe_basic.cpp 约 130 行代码,可以理解 WLED v2 usermod 的标准生命周期(setup/loop/JSON 状态读写)、REGISTER_USERMOD自动注册、custom_usermods构建集成,以及插件与核心状态机(亮度、过渡、效果切换)协作的典型手法。
【免费下载链接】WLEDControl WS2812B and many more types of digital RGB LEDs with an ESP32 over WiFi!项目地址: https://gitcode.com/GitHub_Trending/wl/WLED
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考