Lua 脚本#
Lua 脚本模块为 CycBox 提供了强大的沙箱化脚本环境,用于自定义消息处理。它能够实时转换、解析和分析接收到的消息,无需编译插件。
概述#
Lua 脚本系统拦截处理管道中的消息,允许您:
- 解析自定义协议 - 解码专有或未公开的数据格式
- 转换消息数据 - 修改载荷、提取值或添加元数据
- 定时发送消息 - 以精确的时间控制发送命令
- 提取遥测数据 - 添加可用于图表的值以实现实时可视化
- 记录诊断信息 - 在开发过程中输出调试信息
架构#
Lua 脚本在 CycBox 消息处理管道中执行:
传输层 → 编解码器 → 格式化器 → 面板 → Lua 脚本 → 用户界面
↓
on_message()on_message() 钩子函数接收每条传入消息作为全局 message 对象,可以直接读取和修改。脚本运行具有可配置的超时时间(默认:
1000ms)以防止阻塞。
快速入门#
基本脚本结构#
每个 Lua 脚本必须实现 on_message() 函数:
-- 为每条接收到的消息调用
-- 通过全局 'message' 对象访问消息字段进行读取/修改
-- 必须返回 true(如果修改了消息)或 false(未修改)
function on_message()
-- 您的处理逻辑
return false -- 如果修改了消息则返回 true
end重要: 返回值控制是否将修改后的消息复制回去:
true- 应用消息修改(载荷、值等)false- 消息未更改地通过(性能优化)
消息 API#
全局 message 对象提供以下方法:
载荷访问#
-- 读取/写入原始载荷(字节)
local payload = message:get_payload()
message:set_payload(bytes)
-- 读取/写入帧数据(包括协议开销)
local frame = message:get_frame()
message:set_frame(bytes)值提取(用于图表)#
读取管道早期阶段解码的现有值:
-- 如果存在则按 id 返回值,否则返回 nil
-- 支持的类型: boolean, int8-64, uint8-64, float32/64, string
local temperature = message:get_value("temperature")写入用于仪表板面板可视化的新值:
-- 添加整数值
message:add_int_value(id, value, [group])
-- 添加浮点数值
message:add_float_value(id, value, [group])
-- 添加字符串值(用于标签/状态)
message:add_string_value(id, value, [group])
-- 添加布尔值
message:add_bool_value(id, value, [group])参数:
id(字符串) - 值标识符,显示为图表标题value- 数值或字符串数据group(可选字符串) - 多系列图表的组名(具有相同 id 但不同 group 的值显示在同一图表上)
元数据#
-- 获取消息时间戳(单位为微秒)
local timestamp = message:get_timestamp()工具函数#
日志记录#
log(level, message)级别: "info", "warn", "error"
示例:
log("info", "Processing message with " .. #payload .. " bytes")
log("warn", "Unexpected payload length")延迟发送消息#
success = send_after(payload, delay_ms)定时发送消息,在指定延迟后执行,可多次调用。
参数:
payload(字符串) - 要发送的消息数据delay_ms(整数) - 延迟时间(毫秒)
返回: 成功返回 true,失败返回 false
示例:
-- 500ms 后发送命令
local ok = send_after("AT+INFO\r\n", 500)
if not ok then
log("error", "Failed to schedule message")
end二进制读取辅助函数#
CycBox 提供了从字节串读取二进制数据的辅助函数。所有偏移量都是从 1 开始索引(Lua 约定)。
8 位整数#
value = read_u8(bytes, offset) -- 无符号 (0-255)
value = read_i8(bytes, offset) -- 有符号 (-128 到 127)16 位整数#
value = read_u16_be(bytes, offset) -- 无符号大端序
value = read_u16_le(bytes, offset) -- 无符号小端序
value = read_i16_be(bytes, offset) -- 有符号大端序
value = read_i16_le(bytes, offset) -- 有符号小端序32 位整数#
value = read_u32_be(bytes, offset) -- 无符号大端序
value = read_u32_le(bytes, offset) -- 无符号小端序
value = read_i32_be(bytes, offset) -- 有符号大端序
value = read_i32_le(bytes, offset) -- 有符号小端序浮点数#
value = read_float_be(bytes, offset) -- 32 位大端序
value = read_float_le(bytes, offset) -- 32 位小端序
value = read_double_be(bytes, offset) -- 64 位大端序
value = read_double_le(bytes, offset) -- 64 位小端序错误处理: 如果偏移量超出范围,函数会抛出运行时错误。
示例: PMS9103M 空气质量传感器#
PMS9103M 是一种颗粒物传感器,以自定义二进制协议输出数据:
协议规范#
| 字段 | 偏移 | 类型 | 描述 |
|---|---|---|---|
| 前缀 | 0-1 | 字节 | 固定头部 0x42 0x4D (“BM”) |
| 长度 | 2-3 | U16 BE | 载荷长度(始终为 28 = 26 字节数据 + 2 字节校验和) |
| PM1.0 CF=1 | 4-5 | U16 BE | PM1.0 浓度(μg/m³, CF=1) |
| PM2.5 CF=1 | 6-7 | U16 BE | PM2.5 浓度(μg/m³, CF=1) |
| PM10 CF=1 | 8-9 | U16 BE | PM10 浓度(μg/m³, CF=1) |
| PM1.0 ATM | 10-11 | U16 BE | PM1.0 浓度(μg/m³, 大气环境) |
| PM2.5 ATM | 12-13 | U16 BE | PM2.5 浓度(μg/m³, 大气环境) |
| PM10 ATM | 14-15 | U16 BE | PM10 浓度(μg/m³, 大气环境) |
| 颗粒 >0.3μm | 16-17 | U16 BE | 每 0.1L 空气中的计数 |
| 颗粒 >0.5μm | 18-19 | U16 BE | 每 0.1L 空气中的计数 |
| 颗粒 >1.0μm | 20-21 | U16 BE | 每 0.1L 空气中的计数 |
| 颗粒 >2.5μm | 22-23 | U16 BE | 每 0.1L 空气中的计数 |
| 颗粒 >5.0μm | 24-25 | U16 BE | 每 0.1L 空气中的计数 |
| 颗粒 >10μm | 26-27 | U16 BE | 每 0.1L 空气中的计数 |
| 保留 | 28-29 | U16 BE | 版本/错误码 |
| 校验和 | 30-31 | U16 BE | Sum16 校验和 |
完整实现#
-- PMS9103M 空气质量传感器解析器
-- 解析颗粒物浓度和颗粒计数
function on_message()
local payload = message:get_payload()
-- 验证载荷长度(去除前缀/长度/校验和后为 26 字节)
if #payload ~= 26 then
log("warn", "Invalid PMS9103M payload length: " .. #payload .. " (expected 26)")
return false
end
-- 解析 PM 浓度(CF=1, 标准颗粒) 单位: μg/m³
local pm1_0_cf1 = read_u16_be(payload, 1) -- 偏移 1 (完整帧中的字节 4-5)
local pm2_5_cf1 = read_u16_be(payload, 3) -- 偏移 3 (字节 6-7)
local pm10_cf1 = read_u16_be(payload, 5) -- 偏移 5 (字节 8-9)
-- 解析 PM 浓度(大气环境) 单位: μg/m³
local pm1_0_atm = read_u16_be(payload, 7) -- 偏移 7 (字节 10-11)
local pm2_5_atm = read_u16_be(payload, 9) -- 偏移 9 (字节 12-13)
local pm10_atm = read_u16_be(payload, 11) -- 偏移 11 (字节 14-15)
-- 解析颗粒计数(每 0.1L 空气中的颗粒数)
local particles_0_3um = read_u16_be(payload, 13) -- 偏移 13 (字节 16-17)
local particles_0_5um = read_u16_be(payload, 15) -- 偏移 15 (字节 18-19)
local particles_1_0um = read_u16_be(payload, 17) -- 偏移 17 (字节 20-21)
local particles_2_5um = read_u16_be(payload, 19) -- 偏移 19 (字节 22-23)
local particles_5_0um = read_u16_be(payload, 21) -- 偏移 21 (字节 24-25)
local particles_10um = read_u16_be(payload, 23) -- 偏移 23 (字节 26-27)
-- 将 PM 浓度添加到图表(CF=1 vs 大气环境对比)
-- 具有相同 id 但不同 group 的值显示在同一图表上
message:add_int_value("PM1.0 (μg/m³)", pm1_0_cf1, "CF=1")
message:add_int_value("PM1.0 (μg/m³)", pm1_0_atm, "ATM")
message:add_int_value("PM2.5 (μg/m³)", pm2_5_cf1, "CF=1")
message:add_int_value("PM2.5 (μg/m³)", pm2_5_atm, "ATM")
message:add_int_value("PM10 (μg/m³)", pm10_cf1, "CF=1")
message:add_int_value("PM10 (μg/m³)", pm10_atm, "ATM")
-- 将颗粒计数添加到图表(所有在同一组中进行对比)
message:add_int_value("颗粒 >0.3μm", particles_0_3um, "颗粒计数 (每 0.1L)")
message:add_int_value("颗粒 >0.5μm", particles_0_5um, "颗粒计数 (每 0.1L)")
message:add_int_value("颗粒 >1.0μm", particles_1_0um, "颗粒计数 (每 0.1L)")
message:add_int_value("颗粒 >2.5μm", particles_2_5um, "颗粒计数 (每 0.1L)")
message:add_int_value("颗粒 >5.0μm", particles_5_0um, "颗粒计数 (每 0.1L)")
message:add_int_value("颗粒 >10μm", particles_10um, "颗粒计数 (每 0.1L)")
-- 记录解析值用于调试
log("info", string.format("PM2.5: CF=%d ATM=%d μg/m³", pm2_5_cf1, pm2_5_atm))
-- 返回 true 因为我们向消息添加了值
return true
end预期输出#
连接到 PMS9103M 传感器后,仪表板面板将显示:
-
三个 PM 浓度图表(PM1.0、PM2.5、PM10):
- 每个图表有两条系列线: “CF=1”(蓝色)和 “ATM”(红色)
- Y 轴: 浓度单位 μg/m³
- 实时折线图,包含历史数据
-
颗粒计数图表:
- 六条系列线显示不同粒径阈值的颗粒计数
- Y 轴: 每 0.1L 空气中的颗粒数
- 用于分析空气质量分布