59 rf433-433MHz遥控接收
作者:黄何 | 最后修改:2026-10-27
一、概述
rf433 是 LuatOS 的 433MHz 遥控接收核心库,用一路普通 GPIO 接 433 接收模块的 DAT 脚,在 C 层用 GPIO 双边沿中断采集脉宽并解码 EV1527(1527 类脉宽编码),Lua 层只负责拿句柄和读结果。
和常见的“中断里回调 Lua”不同,本库的边界很清晰:
- 中断里只做采集、解码和投递通知,不执行 Lua、不动态分配内存,也不创建轮询定时器或专用接收任务;
- Lua 回调只收到一个接收器句柄,不直接带键码,应用要在回调里循环调用 rf433.read(handle) 把 FIFO 读空;
- FIFO 从空变为非空时才通知一次,所以必须读到返回 nil 为止,否则下一批数据不会再次通知。
使用前必须理解的两条解码规则:
- sync_level 只决定同步码的电平,数据部分永远按物理高电平解码:短高电平是 1、长高电平是 0,和 sync_level 选 LOW 还是 HIGH 无关;
- 完整帧要通过重复确认才输出:同一码值需要在 repeat_window_us(默认 500ms)内再收到一次才上报,用于滤掉误码;按住不放时,后续的同码帧会继续输出。所以遥控器只按一下、且没有重复发码的,可能收不到结果。
资源上限是固定的:最多同时开启 rf433.MAX_RECEIVERS(4)个接收器,每个接收器独占一个 GPIO,各自有一份解码状态和容量为 rf433.FIFO_CAPACITY(16)条的结果 FIFO,FIFO 满时丢弃新到的结果。
二、核心示例
1、核心示例是指:使用本库文件提供的核心 API,开发的基础业务逻辑的演示代码;
2、核心示例的作用是:帮助开发者快速理解如何使用本库,所以核心示例的逻辑都比较简单;
3、更加完整和详细的 demo,请参考 LuatOS 仓库 中对应产品目录下的 demo。
-- GPIO 编号和同步电平按实际接收模块调整
-- 接收模块一般有 VCC/GND/DAT 三根线,DAT 接普通 GPIO,本库开启期间独占该 GPIO
local handle, err = rf433.open({pin = 26, sync_level = rf433.LOW}, function(h)
-- 回调里只拿到句柄,必须循环读到 nil,否则下一批数据不会再次通知
while true do
local result = rf433.read(h)
if not result then break end
-- code 是 24 位原始码,地址码和按键码没有拆开,需要按遥控器实际编码自行解析
log.info("rf433", string.format("0x%06X", result.code), result.bits, result.protocol)
end
end)
if not handle then
log.error("rf433", "开启失败", err)
end
-- 不需要时关闭,关闭后句柄立即失效
-- rf433.close(handle)
三、常量详解
核心库常量,顾名思义是由合宙 LuatOS 内核固件中定义的、不可重新赋值或修改的固定值,在脚本代码中不需要声明,可直接调用;
每个常量对应的常量取值仅做日志打印时查询使用,不要将这个常量取值用做具体的业务逻辑判断,因为LuatOS内核固件可能会变更每个常量对应的常量取值;
如果用做具体的业务逻辑判断,一旦常量取值发生改变,业务逻辑就会出错;
rf433.LOW
常量含义:同步电平为低电平,即同步码是一段低电平,适合绝大多数 433 接收模块(DAT 空闲为高、收到载波拉低)的接法;
数据类型:number;
示例代码:rf433.open({pin = 26, sync_level = rf433.LOW}, cb);
rf433.HIGH
常量含义:同步电平为高电平,接收模块输出极性相反时使用;
数据类型:number;
示例代码:rf433.open({pin = 26, sync_level = rf433.HIGH}, cb);
rf433.EV1527
常量含义:协议编号,表示当前解码器是 EV1527(1527 类脉宽编码),read 返回的 protocol 字段就是这个值;
数据类型:number;
示例代码:if result.protocol == rf433.EV1527 then ... end;
rf433.FIFO_CAPACITY
常量含义:单个接收器的结果 FIFO 容量,固定 16 条,满了以后新到的结果会被丢弃;
数据类型:number;
示例代码:log.info("rf433", rf433.FIFO_CAPACITY);
rf433.MAX_RECEIVERS
常量含义:同时可以开启的接收器个数,固定 4 个,超过以后 rf433.open 只返回 nil;
数据类型:number;
示例代码:log.info("rf433", rf433.MAX_RECEIVERS);
四、函数详解
rf433.open(config, callback)
功能
开启一个 433MHz 接收器,占用一个 GPIO 做双边沿采集和解码,并注册数据可读回调。
参数
config
参数含义:接收配置表,pin 必填,其余字段可选,表中出现未知字段会直接抛 Lua 错误;
数据类型:table;
取值范围:见下方配置字段说明;
是否必选:必须传入此参数;
注意事项:所有时间参数单位都是微秒;sync_min_us 必须大于 pulse_max_us,否则同步码识别不出来;
参数示例:{pin = 26, sync_level = rf433.LOW};
配置字段(除 pin 外都可省略,省略时取默认值):
| 字段 | 默认值 | 含义 |
|---|---|---|
| pin | 无(必填) | 接收模块 DAT 所接的普通 GPIO 编号,开启期间由本库独占,不是句柄 |
| sync_level | rf433.LOW | 同步电平,只影响同步码识别;数据始终按物理高电平解码 |
| sync_min_us | 6000 | 同步最短时长,必须大于 pulse_max_us |
| sync_max_us | 18000 | 同步最长时长,上下界都包含在有效范围内 |
| pulse_min_us | 200 | 数据脉宽下限,短于它的脉冲视为干扰 |
| pulse_max_us | 1600 | 数据脉宽上限 |
| pulse_mid_us | 600 | 长短脉宽分界,高电平短于该值是 1,否则是 0 |
| glitch_us | 150 | 毛刺阈值,反向脉冲严格短于该值才合并,填 0 表示关闭毛刺合并 |
| repeat_window_us | 500000 | 重复确认窗口,同码帧在这个时间内再来一次才输出 |
callback
参数含义:数据可读回调;
数据类型:function;
取值范围:形参只有一个,即接收器句柄,不直接传键码;
是否必选:必须传入此参数;
注意事项:回调在 Lua 虚拟机线程中执行,里面要循环调用 rf433.read 直到返回 nil,不要调用 sys.wait 之类会阻塞的函数;回调里可以关闭或重新开启接收器;
参数示例:function(handle) ... end;
返回值
local handle, err = rf433.open(config, callback)
handle
含义说明:接收器句柄,成功返回正整数,失败返回 nil;
数据类型:number;
取值范围:正整数,注意句柄不是 GPIO 编号;
注意事项:关闭后原句柄失效,重新开启会得到新句柄,队列里旧句柄的通知会被丢弃;
返回示例:1;
err
含义说明:错误码,只在 C 层接收服务开启失败时返回;
数据类型:number;
取值范围:Lua 接收器槽位已满时只返回 nil,没有这个错误码;
注意事项:常见失败原因是 GPIO 已被占用、平台没有适配(返回 ENOTSUP);
返回示例:暂无;
示例
local handle, err = rf433.open({pin = 26, glitch_us = 0}, function(h)
while true do
local r = rf433.read(h)
if not r then break end
log.info("rf433", string.format("0x%06X", r.code))
end
end)
log.info("rf433", "handle", handle, err)
rf433.read(handle)
功能
从结果 FIFO 中取出一条已通过重复确认的完整帧,不等待新数据。
参数
handle
参数含义:rf433.open 返回的接收器句柄;
数据类型:number;
取值范围:正整数,必须是有效句柄;
是否必选:必须传入此参数;
注意事项:传入非正整数会直接抛 Lua 错误;句柄已关闭时返回 nil,不报错;
参数示例:handle;
返回值
local result = rf433.read(handle)
result
含义说明:接收结果,FIFO 为空、句柄已关闭或读取失败时返回 nil;
数据类型:table;
取值范围:4 个字段,见下表;
注意事项:收到回调通知后要一直读到 nil,避免 FIFO 积压;
返回示例:{code = 0x123456, timestamp_us = 1234567, bits = 24, protocol = 1};
结果字段:
| 字段 | 含义 |
|---|---|
| code | 24 位原始码,按接收顺序高位在前,没有拆分地址码和按键码 |
| timestamp_us | 确认帧结束同步边沿在中断里的采样时刻,单位微秒;32 位计数会回绕,比较时间间隔要用无符号减法;32 位 Lua 用浮点数表示,精度由虚拟机决定 |
| bits | 有效位数,当前固定 24 |
| protocol | 协议编号,当前固定为 rf433.EV1527 |
示例
while true do
local result = rf433.read(handle)
if not result then break end
log.info("rf433", string.format("0x%06X", result.code), result.timestamp_us)
end
rf433.close(handle)
功能
关闭一个接收器,先关 GPIO 接收,再释放 Lua 回调引用。
参数
handle
参数含义:rf433.open 返回的接收器句柄;
数据类型:number;
取值范围:正整数,必须是有效句柄;
是否必选:必须传入此参数;
注意事项:传入非正整数会直接抛 Lua 错误;
参数示例:handle;
返回值
local ok, err = rf433.close(handle)
ok
含义说明:关闭结果,成功返回 true,句柄已关闭或关闭失败返回 false;
数据类型:boolean;
取值范围:true 或 false;
注意事项:关闭后原句柄立即失效;
返回示例:true;
err
含义说明:错误码,只在仍有 Lua 回调记录、但 C 接收器已经关闭的情况下返回;
数据类型:number;
取值范围:其他失败分支只返回 false,没有这个错误码;
注意事项:暂无;
返回示例:暂无;
示例
local ok = rf433.close(handle)
log.info("rf433", "关闭结果", ok)
五、产品支持说明
rf433 由固件编译开关 LUAT_USE_RF433 控制,库里第一个版本是 2026-10-12 提交的,属于比较新的核心库,使用前先确认固件是否带这个库:
if rf433 then
log.info("rf433", "当前固件支持 rf433 核心库")
else
log.info("rf433", "当前固件不支持 rf433 核心库")
end
各型号固件是否支持,以对应固件版本页的核心库支持列表为准:
Air780EX2/Air700ECP/Air780EPM/Air780EGP固件支持列表
Air700ECH/Air780EHM/EHV/EGH/EGG/EHU/EHN固件支持列表
硬件要求
- 需要一路普通 GPIO 接 433 接收模块的 DAT 脚,开启期间由本库独占,不能同时做别的用途;
- 目前 EC7xx 只支持普通 GPIO,不支持把特殊功能脚(如带模拟功能或复用受限的脚)当接收脚;
- 模块的供电和天线按模块规格给,DAT 空闲电平要配合 sync_level 设置,接反了会一直解不出同步码。
使用限制
- 解码器目前只支持 EV1527(1527 类脉宽编码),输出固定 24 位,不做地址码/按键码拆分;
- 一次只能开 4 个接收器,每个接收器 FIFO 16 条,满了丢新数据;
- 没有超时轮询:待确认的最后一帧要等下一次真实边沿提交,遥控器停止发射时最后一帧可能不输出,不能靠等待超时强制输出;
- 尚未适配的平台调用 rf433.open 会返回 ENOTSUP,不会假装开启成功。