ME31 PT100 温度到 InfluxDB
概览
本示例展示了如何从 ME31-XDXX0400 模块通过 Modbus RTU 或 Modbus TCP 读取 PT100 温度值,并将其写入 InfluxDB v3。它展示了 CycBox 的以下功能:
- 双模式 Modbus 通信 —— 使用单个标志即可在 RTU (串口) 和 TCP (以太网) 之间切换
- 编解码器解析的寄存器值 —— 使用内置的 Modbus 编解码器自动解析寄存器响应
- InfluxDB v3 的实时和批量写入 —— 用于仪表板的异步单次写入以及定期的批量刷新
- 有符号整数处理 —— 正确解释来自无符号 16 位寄存器的负温
这是工业监控中的常见模式,即需要将来自 Modbus 传感器的温度数据存储在时间序列数据库中,以便进行趋势分析和警报。
场景
一个 ME31-XDXX0400 PT100 温度采集模块通过 RS485 串口或以太网连接。该模块最多支持 4 通道的 PT100 温度传感器。要求如下:
- 每 5 秒通过 Modbus RTU (串口) 或 Modbus TCP (以太网) 轮询 CH1 温度
- 解析有符号 16 位整数值并转换为摄氏度 (除以 10)
- 检测传感器未连接状态 (数值 = -999)
- 异步将每个读数写入 InfluxDB v3,用于实时仪表板
- 每 30 秒批量刷新一次累积的读数,以实现高效存储
- 关闭时,同步刷新任何剩余数据
ME31 温度寄存器映射
| 寄存器地址 | 功能码 | 数据类型 | 描述 |
|---|---|---|---|
| 0x0190 (400) | 0x04 | int16 (有符号) | CH1 温度整数值 (0.1°C 分辨率) |
| 0x0191 (401) | 0x04 | int16 (有符号) | CH2 温度整数值 |
| 0x0192 (402) | 0x04 | int16 (有符号) | CH3 温度整数值 |
| 0x0193 (403) | 0x04 | int16 (有符号) | CH4 温度整数值 |
数值解释:
- 除以 10 得到 °C (例如,
0x012C= 300 = 30.0°C) - 负值使用补码 (例如,
0xFFCE= -50 = -5.0°C) 0xFC19(-999) 表示传感器未连接
配置
CycBox 配置了两个连接:
- 连接 0: 带 Modbus RTU 编解码器的串口,用于通过 RS485 轮询
- 连接 1: 带 Modbus TCP 编解码器的 TCP 客户端,用于通过以太网轮询
{
"version": "1.0.0",
"name": "ME31 PT100 温度到 InfluxDB",
"description": "通过 Modbus RTU 或 TCP 从 ME31 模块读取 PT100 温度,并发布到 InfluxDB v3",
"configs": [
{
"app": {
"app_transport": "serial",
"app_codec": "modbus_rtu_codec",
"app_transformer": "disable",
"app_encoding": "UTF-8"
},
"serial": {
"serial_port": "/dev/ttyUSB0",
"serial_baud_rate": 9600,
"serial_data_bits": 8,
"serial_parity": "none",
"serial_stop_bits": "1",
"serial_flow_control": "none"
},
"modbus_rtu_codec": {
"with_receive_timeout": 20
}
},
{
"app": {
"app_transport": "tcp_client",
"app_codec": "modbus_tcp_codec",
"app_transformer": "disable",
"app_encoding": "UTF-8"
},
"tcp_client": {
"tcp_client_host": "192.168.7.7",
"tcp_client_port": 502,
"tcp_client_timeout": 5000,
"tcp_client_keepalive": true,
"tcp_client_nodelay": true
},
"modbus_tcp_codec": {
"unit_id": 2
}
}
]
}
连接详情
| 参数 | 连接 0 (RTU) | 连接 1 (TCP) |
|---|---|---|
| 传输协议 | 串口 (RS485) | TCP 客户端 (以太网) |
| 编解码器 | modbus_rtu_codec | modbus_tcp_codec |
| 端口 / 主机 | /dev/ttyUSB0 | 192.168.7.7:502 |
| 波特率 / 超时 | 9600 | 5000ms |
| 从站 / 单元 ID | 在脚本中设置 (2) | 2 |
Lua 脚本逻辑
该脚本使用环境变量进行 InfluxDB 配置,并使用 USE_RTU 标志在 RTU 和 TCP 模式之间切换。
环境变量
| 变量 | 默认值 | 描述 |
|---|---|---|
INFLUXDB_URL | http://localhost:8181 | InfluxDB v3 API 端点 |
INFLUXDB_TOKEN | (空) | 身份验证令牌 |
INFLUXDB_DB | temperature | 目标数据库名称 |
脚本拆解
-- InfluxDB v3 配置
local INFLUXDB_URL = get_env("INFLUXDB_URL") or "http://localhost:8181"
local INFLUXDB_TOKEN = get_env("INFLUXDB_TOKEN") or ""
local INFLUXDB_DB = get_env("INFLUXDB_DB") or "temperature"
-- ME31 Modbus 设置
local SLAVE_ADDRESS = 2 -- Modbus 从站地址
local TEMP_REG_START = 400 -- 0x0190: CH1 温度整数寄存器
local USE_RTU = true -- true: RTU (连接 0), false: TCP (连接 1)
运作原理
1. 定期轮询 (on_timer)
function on_timer(elapsed_ms)
read_counter = read_counter + 100
if read_counter >= READ_INTERVAL then
read_counter = 0
if USE_RTU then
modbus_rtu_read_input_registers(SLAVE_ADDRESS, TEMP_REG_START, 1, 0, 0)
else
modbus_tcp_read_input_registers(TEMP_REG_START, 1, 0, 1)
end
end
end
on_timer()每 100ms 调用一次- 每 5 秒发送一次 Modbus 读取请求
- RTU 模式使用
modbus_rtu_read_input_registers()(连接 0),TCP 模式使用modbus_tcp_read_input_registers()(连接 1)
2. 响应解析 (on_receive)
function on_receive()
local register_num = 30001 + TEMP_REG_START
local raw_value
if USE_RTU then
raw_value = message:get_value(
string.format("modbus_rtu_%d:input_%d", SLAVE_ADDRESS, register_num))
else
raw_value = message:get_value(
string.format("modbus_tcp_%d:input_%d", SLAVE_ADDRESS, register_num))
end
if raw_value == nil then return false end
-- 将无符号 16 位转换为有符号
if raw_value >= 0x8000 then
raw_value = raw_value - 0x10000
end
-- -999 表示传感器未连接
if raw_value == -999 then
log("warn", "CH1 传感器未连接 (数值 = -999)")
return false
end
local temp = raw_value / 10.0
end
- 使用带有适当数值 ID 的
message:get_value()读取编解码器解析后的寄存器值 - Modbus 编解码器遵循
modbus_rtu_{slave}:input_{30001 + register_address}模式分配 ID - 将无符号 16 位转换为有符号,以正确处理负温
- 过滤掉传感器未连接的哨兵值 (-999)
- 除以 10 转换为摄氏度
3. InfluxDB 写入 —— 实时与批量
-- 为 UI 显示添加图表数值
message:add_float_value("CH1_Temp", temp)
-- 用于实时仪表板的异步单次写入
influxdb_write_v3_async(INFLUXDB_URL, INFLUXDB_TOKEN, INFLUXDB_DB,
string.format("temperature,sensor=me31,channel=ch1 value=%.1f", temp),
"auto", true, true)
-- 批量刷新的缓冲区
pending_lines[#pending_lines + 1] =
string.format("temperature,sensor=me31,channel=ch1 value=%.1f", temp)
- 每个读数都通过
influxdb_write_v3_async()立即写入,以实现实时可见性 - 读数也会缓存在
pending_lines中,用于定期批量写入 - 批处理在
on_timer()中每 30 秒通过influxdb_batch_write_v3_async()刷新一次
4. 优雅停机 (on_stop)
function on_stop()
if #pending_lines == 0 then return end
local ok, result = pcall(influxdb_batch_write_v3,
INFLUXDB_URL, INFLUXDB_TOKEN, INFLUXDB_DB,
pending_lines, "auto", true, false)
pending_lines = {}
end
- 停机时,任何剩余的缓冲数据都会使用
influxdb_batch_write_v3()同步刷新 (阻塞式) - 使用
pcall优雅地处理错误而不导致崩溃
Modbus 编解码器数值 ID
Modbus 编解码器会自动解析寄存器值并分配 ID:
RTU 编解码器模式: modbus_rtu_{slave_addr}:input_{30001 + register_address}
TCP 编解码器模式: modbus_tcp_{slave_addr}:input_{30001 + register_address}
对于本示例(从站地址为 2,寄存器 400):
| 模式 | 数值 ID | 描述 |
|---|---|---|
| RTU | modbus_rtu_2:input_30401 | CH1 温度 (int16) |
| TCP | modbus_tcp_2:input_30401 | CH1 温度 (int16) |
InfluxDB 行协议 (Line Protocol) 输出
每个数据点都以 InfluxDB 行协议格式写入:
temperature,sensor=me31,channel=ch1 value=25.3
| 组件 | 值 | 描述 |
|---|---|---|
| 测量 (Measurement) | temperature | 测量名称 |
| 标签 (Tag): sensor | me31 | 设备标识符 |
| 标签 (Tag): channel | ch1 | 传感器通道 |
| 字段 (Field): value | 温度 (°C) (1位小数) | 实际温度值 |
查看 GitHub 上的完整示例脚本。