WLED JSON IR Remote:用 ir.json 把任意红外遥控器变成 WLED 控制器
【免费下载链接】WLEDControl WS2812B and many more types of digital RGB LEDs with an ESP32 over WiFi!项目地址: https://gitcode.com/GitHub_Trending/wl/WLED
WLED 的 JSON IR Remote 是一种“零编译”的红外遥控扩展方案:用户不需要修改 C 代码、不需要重新编译固件,只需上传一个名为ir.json的 JSON 配置文件,就可以把任何与 WLED 红外接收头兼容的遥控器按键映射到任意 HTTP Request API 或 JSON API 命令。读完本文,你将掌握ir.json的完整键值规范(键、cmd、rpt、label、PL/FX/FP)、三种命令形态的写法、可重复按键的底层机制,以及如何利用仓库自带的 7 个遥控器配置模板和 ir_json_maker.py 批量生成自己的配置。
设计目标:让遥控器适配 WLED,而不是让 WLED 适配遥控器
WLED 固件内置了 7 种固定遥控器解码方案(24 键、40 键、44 键、21 键、6 键、9 键及 24 键 CT 白键版),每种对应一段硬编码的 C 代码。当你手里的遥控器不属于这些型号时,传统做法是改源码重新编译——这正是 JSON IR Remote 要消除的步骤。
从源码结构看,这一机制由 wled00/ir.cpp 中的decodeIR()统一分发:
if (irEnabled == 8) { // any remote configurable with ir.json file decodeIRJson(code); stateUpdated(CALL_MODE_BUTTON_PRESET); return; }即红外遥控类型取值为8时,所有解码逻辑被旁路,改为在 Flash 文件系统的/ir.json文件中按键查表。该类型在 Web 界面上显示为 "JSON remote",见 settings_leds.htm 中的<option value=8>JSON remote</option>以及 index.js 中的8: "json-remote"映射。配置持久化时写入hw.ir.type字段(cfg.cpp 中CJSON(irEnabled, hw["ir"]["type"])),因此重启后依然生效。
三步完成配置
ir.json的完整使用流程只有三步(与官方 readme 一致):
上传配置文件:通过设备 IP 的
/edit页面,把名为ir.json的配置文件上传到主控板。文件可以选自仓库 usermods/JSON_IR_remote/ 目录下按按键数命名的现成模板,也可以自己编写(格式见下文)。设置 IR 引脚:在 设置 > LED 设置 页面,把 IR 引脚设置为红外接收头所接的 GPIO。该参数由 set.cpp 中的
IR参数处理,并经PinManager::allocatePin()校验分配:int hw_ir_pin = request->arg(F("IR")).toInt(); if (PinManager::allocatePin(hw_ir_pin,false, PinOwner::IR)) { irPin = hw_ir_pin; } else { irPin = -1; } irEnabled = request->arg(F("IT")).toInt(); initIR();若引脚分配失败,
irPin置为 -1,红外功能实际不可用。选择遥控类型:在 设置 > 同步接口 页面,将“红外遥控器”一项选为JSON Remote(内部即
IT=8)。
ir.json 文件格式详解
文件是一个 JSON 对象,每个键是十六进制编码的 IR 码(例如0xFF629D),值为该按键按下时要执行的命令描述对象。各属性说明如下:
| 属性 | 必填 | 说明 |
|---|---|---|
键(如0xFF629D) | 是 | 十六进制 IR 码。源码按"0x%lX":格式精确拼键查表,因此键必须写成 24 位(或更宽)十六进制并带0x前缀 |
cmd | 是 | 按键执行的命令,可以是 HTTP API 字符串、JSON 对象,或以!开头的 C 函数名 |
rpt | 否 | 布尔值。当命令可重复触发但不含~字符时置为true,长按时才会持续重复执行 |
label | 否 | 仅用于编辑时的人类可读标注,运行时无作用 |
PL/FX/FP | 条件 | 仅在使用!presetFallback时提供:要加载的预置、回退效果、回退调色板编号 |
desc/pos/cmnt | 否 | 仓库模板文件中的额外元数据(文件描述、按键位置、备注),运行逻辑同样忽略它们 |
官方示例(完整继承自 readme.md):
{ "0xFF629D": {"cmd": "T=2", "rpt": true, "label": "Toggle on/off"}, "0xFF9867": {"cmd": "A=~16", "label": "Inc brightness"}, "0xFF38C7": {"cmd": {"bri": 10}, "label": "Dim to 10"}, "0xFF22DD": {"cmd": "!presetFallback", "PL": 1, "FX": 16, "FP": 6, "label": "Preset 1 or fallback to Saw - Party"} }四种典型条目分别展示了:带rpt的开关命令、含~的相对增量命令、JSON 对象命令、以及 C 函数命令。
cmd 的三种命令形态
cmd属性的写法由源码 decodeIRJson() 决定,共三条执行路径:
1. HTTP Request API 命令(字符串)
"0xFF629D": {"cmd": "T=2", "rpt": true}源码会把字符串包装成win&T=2形式后交给handleSet()处理——与通过 URL 直接发?win&T=2完全等价,因此 WLED 全部 HTTP 短命令语法都能用:
T=2:开关切换;T=1开,T=0关;A=~16:亮度相对增加 16(~表示相对增量);SI=~16/SI=~-16:效果速度增减;CY=0&FX=~:清空选中分段并循环切换到下一个效果(仓库 9 键模板中“Select”键即此写法);FP=~/FP=~+:循环上一/下一个调色板。
还有一个源码层面的细节:如果当前处于“应用到所有选中间”模式且命令中没有指定分段(无SS=参数),固件会自动追加&SS=<主分段号>,让命令只作用于主分段,避免误改其他分段。
2. JSON API 对象命令
"0xFF38C7": {"cmd": {"bri": 10}}cmd为 JSON 对象时走deserializeState(jsonCmdObj, CALL_MODE_BUTTON_PRESET),即按 WLED JSON API(/state的入参格式)整体应用状态。除常规状态字段外,源码还支持两个专门特性(这是 readme 未提及、但从 decodeIRJson() 可确认的实现事实):
psave:{"cmd": {"psave": 5, "bri": 80, ...}}会把该 JSON 保存为名为IR Preset 5的预置(编号 1–250),而不是立即应用;seg:当处于“应用到所有选中间”模式且seg为数组时,固件取数组第一个分段对象、去掉其id后应用到所有选中间,实现“一个按键统一改写多个分段”。
3. 受限 C 函数命令(!前缀)
"0xFF22DD": {"cmd": "!presetFallback", "PL": 1, "FX": 16, "FP": 6}以!开头的字符串调用固件内置函数,目前仅开放三个:
| 命令 | 源码匹配前缀 | 行为 |
|---|---|---|
!incBrightness | !incBri | 亮度升到下一个档位 |
!decBrightness | !decBri | 亮度降到下一个档位 |
!presetFallback | !presetF | 加载预置PL;若该预置不存在,则回退使用效果FX与调色板FP |
注意源码是按前缀匹配(startsWith)判断的,且亮度档位并非线性步进,而是沿一组预定义的几何级数档位表移动:
const uint8_t brightnessSteps[] = { 5, 7, 9, 12, 16, 20, 26, 34, 43, 56, 72, 93, 119, 154, 198, 255 };(ir.cpp)。低亮度时步进细、高亮度时步进粗,视觉体感比固定步长更均匀。!presetFallback的PL缺省为 1、FX缺省随机、FP缺省为 0,即三个参数可以部分省略。
长按重复机制:~与rpt
红外遥控器长按某键时会持续发送同一码,但 WLED 的解码器(IRrecv/IRremote)在收到“重复码”时会给出特殊值0xFFFFFFFF。源码中的处理链条是:
decodeIRJson()执行命令前,若命令字符串含~或条目声明了rpt: true,则记录lastValidCode = code(ir.cpp);- 收到重复码时
decodeIR()进入applyRepeatActions():
static void applyRepeatActions() { if (irEnabled == 8) { decodeIRJson(lastValidCode); // 用上次有效码重放 JSON 命令 stateUpdated(CALL_MODE_BUTTON_PRESET); return; } ... }也就是说,重复码会完整重放上一次的 JSON 命令。这正是A=~16这类相对增量命令能实现“长按连续调光”的原因——每次重放都相对当前值再 +16。而绝对值命令(如{"bri": 10})重放无意义,含~时也不会被标记为可重复;对于T=2这类确实可重复但字符串里没有~的命令,必须显式写"rpt": true。
从源码看 decodeIRJson 的完整执行流
结合 decodeIRJson(),一次按键的完整链路如下:
- 取锁:
requestJSONBufferLock(JSON_LOCK_IR)获取共享 JSON 缓冲区锁,与其他 JSON 解析逻辑互斥; - 查表:拼出键
"0x%lX":后调用readObjectFromFile("/ir.json", objKey, pDoc)。若文件中找不到该码,fdo为空——此时若/ir.json文件本身都不存在,会置errorFlag = ERR_FS_IRLOAD(Web 界面同步页可看到相应提示,index.js 中对应错误文案为Missing ir.json.); - 分流执行:
cmd是!字符串 → C 函数;cmd是普通字符串 → 加win&前缀走handleSet();cmd是对象 →deserializeState()或savePreset(); - 解锁并广播:
releaseJSONBufferLock()后由外层decodeIR()调stateUpdated(CALL_MODE_BUTTON_PRESET),把本次改动当作“按钮预置”来源广播给 MQTT/UDP 等下游。
命令分发后,handleIR()以约 120ms 的轮询节奏调用irrecv->decode()取码(ir.cpp),解码期间若灯带正在刷帧且距上次检查不足 240ms 会让出本次检查,避免占用渲染时间。
仓库自带的遥控器配置模板
usermods/JSON_IR_remote/ 目录提供了 7 个按按键数命名的现成配置,覆盖常见红外遥控面板。选取按键数与你的遥控器一致的模板,再按实际标注微调即可——官方 readme 特别提示:许多不同外观的遥控器内部共用同一套编码,只是按键标注不同。
| 文件 | 说明 |
|---|---|
| 6-key_ir.json | 6 键学习遥控器,含pos(1x1–6x1)位置标注;开关映射为T=2,上下键映射为调色板循环FP=~/FP=~+ |
| 9-key_ir.json | 9 键,A/B/C 映射为预置 1/2/3,方向键映射为速度与亮度 ±16 |
| 21-key_ir.json | 21 键彩色面板 |
| 24-key_ir.json | 24 键(与固件内置 24 键遥控同码集,可用 JSON 方式重新定义其行为) |
| 32-key_ir.json | 32 键 |
| 40-key-black_ir.json / 40-key-blue_ir.json | 40 键黑/蓝两版,键位略有差异 |
| 44-key_ir.json | 44 键,含 DIY 键与色温键 |
以 9-key_ir.json 为例,完整文件仅 40 余行,展示了模板的标准结构(desc文件描述 + 每键label/cmnt/cmd):
{ "desc": "9-key", "0xFF629D": { "label": "Power", "cmd": "T=2" }, "0xFF22DD": { "label": "A", "cmnt": "Preset 1", "cmd": "PL=1" }, "0xFF30CF": { "label": "Left", "cmnt": "Speed -", "cmd": "SI=~-16" }, "0xFF18E7": { "label": "Select", "cmnt": "Cycle effects", "cmd": "CY=0&FX=~" } }如何拿到自己遥控器的 IR 码
ir.json的键来自遥控器实际发出的十六进制码,获取方式在源码里已有内置支持:handleIR()在串口开启的情况下会把每帧解码结果打印出来:
if (results.value != 0 && serialCanTX) { Serial.printf_P(PSTR("IR recv: 0x%lX\n"), (unsigned long)results.value); }因此实操流程为:先在 LED 设置里选好 IR 引脚、遥控类型任选(只要irEnabled > 0接收器就会初始化),开启 WLED 串口输出,用遥控器逐键按下,从串口日志中抄下IR recv:后面的十六进制码,即可作为ir.json的键。长按时观察到的重复值0xFFFFFFFF不需要写入配置——它由applyRepeatActions()自动处理。
用 ir_json_maker.py 批量生成配置
当遥控器按键多、标注复杂时,手写 JSON 容易出错。仓库提供了 ir_json_maker.py,配合 IR_Remote_Codes.xlsx 批量生成各按键数模板。脚本逻辑(基于 openpyxl)为:
- 每个工作表生成一个
<表名>_ir.json,desc字段取表名; - 每行按表头映射出
code(IR 码)、row/col(生成pos)、comment(生成cmnt)、rpt、cmd、颜色字段等; - 若某行未直接给
cmd但给了主/次/第三色(十六进制),脚本合成调色板命令FP=5&CL=h<主色>&C2=h<次色>&C3=h<第三色>(FP=5即自定义三色调色板),第三色缺省时由主色做 HSV 色相偏移 + 降饱和自动生成; - 键名直接命中内置 CSS 命名色表(Red、Blue、GoldenRod 等约 140 种)时,同样自动合成
FP=5&CL=...命令。
运行方式(在usermods/JSON_IR_remote/目录下):
pip install openpyxl python ir_json_maker.py脚本会依次打印Parsing worksheet <表名>,并在同目录输出各<表名>_ir.json。修改 xlsx 中任一行后重跑即可再生成,适合把模板配置纳入版本管理。
常见问题与限制
- 按键无反应:优先确认三处——
/ir.json是否已上传成功(同步接口页会报 “Missing ir.json.”)、IR 引脚是否分配成功、遥控类型是否已选 “JSON remote”。另外检查 JSON 键的写法:源码按"0x%lX":精确匹配,0xff629d这类小写或无前缀写法不会命中(源码用%lX大写格式拼键,从源码结构看应统一写成0xFF629D样式的大写十六进制)。 - 长按不连续:检查命令是否含
~,或是否为rpt: true的条目;!incBrightness等 C 函数路径天然支持重复(源码中它们会主动记录lastValidCode)。 - 命令只作用于单个分段:HTTP 命令在未指定
SS=时会被自动绑定到主分段;想批量改写所有选中间,请改用 JSON 对象命令并配合seg字段,或先调整界面顶部的“应用到所有选中间”开关状态。 - 适用前提:该机制依赖 WLED 固件的红外接收功能(
WLED_DISABLE_INFRARED未启用的构建)、IRremote 系解码库支持的协议(模板均为 NEC 系 24 位码0xFFxxxx),以及 Web 文件系统可写(LFS/FFatFS 分区)。若固件构建禁用了红外支持,irEnabled相关代码整体不参与编译,JSON Remote 也就无从谈起。
小结
JSON IR Remote 把 WLED 的红外遥控从“固件写死 7 种型号”变成了“Flash 上一份 JSON 说了算”:ir.json的每个键是一个十六进制 IR 码,cmd可以写 HTTP API、JSON API 对象或三个受限 C 函数之一,~/rpt机制让长按连续调节成立。配合 usermods/JSON_IR_remote/ 下的 7 个按键数模板、串口码捕获与 ir_json_maker.py 批量生成工具,绝大多数市售通用红外遥控器都能在不动一行 C 代码的情况下成为 WLED 的专用控制器。核心实现全部集中在 wled00/ir.cpp 的decodeIRJson()(L556-L633)与applyRepeatActions()(L635-L661),便于进一步定制。
【免费下载链接】WLEDControl WS2812B and many more types of digital RGB LEDs with an ESP32 over WiFi!项目地址: https://gitcode.com/GitHub_Trending/wl/WLED
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考