跳到主要内容

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 温度传感器。要求如下:

  1. 每 5 秒通过 Modbus RTU (串口) 或 Modbus TCP (以太网) 轮询 CH1 温度
  2. 解析有符号 16 位整数值并转换为摄氏度 (除以 10)
  3. 检测传感器未连接状态 (数值 = -999)
  4. 异步将每个读数写入 InfluxDB v3,用于实时仪表板
  5. 每 30 秒批量刷新一次累积的读数,以实现高效存储
  6. 关闭时,同步刷新任何剩余数据

ME31 温度寄存器映射

寄存器地址功能码数据类型描述
0x0190 (400)0x04int16 (有符号)CH1 温度整数值 (0.1°C 分辨率)
0x0191 (401)0x04int16 (有符号)CH2 温度整数值
0x0192 (402)0x04int16 (有符号)CH3 温度整数值
0x0193 (403)0x04int16 (有符号)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_codecmodbus_tcp_codec
端口 / 主机/dev/ttyUSB0192.168.7.7:502
波特率 / 超时96005000ms
从站 / 单元 ID在脚本中设置 (2)2

Lua 脚本逻辑

该脚本使用环境变量进行 InfluxDB 配置,并使用 USE_RTU 标志在 RTU 和 TCP 模式之间切换。

环境变量

变量默认值描述
INFLUXDB_URLhttp://localhost:8181InfluxDB v3 API 端点
INFLUXDB_TOKEN(空)身份验证令牌
INFLUXDB_DBtemperature目标数据库名称

脚本拆解

-- 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描述
RTUmodbus_rtu_2:input_30401CH1 温度 (int16)
TCPmodbus_tcp_2:input_30401CH1 温度 (int16)

InfluxDB 行协议 (Line Protocol) 输出

每个数据点都以 InfluxDB 行协议格式写入:

temperature,sensor=me31,channel=ch1 value=25.3
组件描述
测量 (Measurement)temperature测量名称
标签 (Tag): sensorme31设备标识符
标签 (Tag): channelch1传感器通道
字段 (Field): value温度 (°C) (1位小数)实际温度值

查看 GitHub 上的完整示例脚本

相关文档