Skip to main content

PT100 Temperature Monitoring with Modbus and InfluxDB v3

This recipe demonstrates how to monitor high-precision PT100 sensors using the ME31-XDXX0400 4-channel acquisition module. The pipeline supports both Modbus RTU and Modbus TCP transports, implements Exponential Moving Average (EMA) filtering for noise reduction, and exports telemetry to InfluxDB v3.

PT100 Temperature Monitoring

What this recipe does

  • Dual-Transport Support: Provides a toggle-switch configuration for RS485 (Modbus RTU) or Ethernet (Modbus TCP) communication.
  • 1Hz Polling: Requests integer temperature data from Channel 1 every 1000ms.
  • EMA Filtering: Applies an Exponential Moving Average (ALPHA = 0.2) to smooth out fluctuations in the PT100 readings.
  • InfluxDB v3 Integration: Batch-exports filtered temperature data every 10 seconds using the InfluxDB Line Protocol.
  • Startup Inventory: Automatically queries the device firmware version upon script initialization.
  • Fault Detection: Logs a warning if the sensor reports a disconnected state (-99.9 °C).

End-to-end data flow

Device and wire protocol

The ME31-XDXX0400 is a 4-channel PT100 acquisition module that functions as a Modbus Slave (RTU) or Modbus Server (TCP).

  • Physical Layer: RS485 (9600 8N1) and RJ45 Ethernet (10/100M).
  • Scaling: Temperature values are stored as 16-bit signed integers where Physical Value = Register * 0.1.
  • Error Handling: A specific value 0xFC19 (-999) indicates a disconnected sensor.

The table below maps the primary Modbus registers used in this recipe for temperature and system identification.

AddressRegister NameAccessScalingUnitDescription
0x0190CH1_TEMP_INTInput (04)0.1°CChannel 1 Integer Temperature
0x07DCFW_VERSIONHolding (03)1-Firmware Version Number
0x2328CH1_OFFSETHolding (03)0.1°CCalibration Offset for Channel 1

CycBox configuration

The engine is configured with two concurrent connections. The Lua script selects the active path based on a variable flag.

PT100 Temperature Monitoring

Active Connections Index

connection_idTransportCodecTarget/Port
0Serial PortModbus RTU/dev/ttyUSB0 (9600 8N1)
1TCP ClientModbus TCP192.168.7.7:502

Configuration JSON

[
{
"app": {
"app_transport": "serial_port_transport",
"app_codec": "modbus_rtu_codec",
"app_transformer": "disable_transformer",
"app_encoding": "UTF-8"
},
"modbus_rtu_codec": {
"with_receive_timeout": 20
},
"serial_port_transport": {
"serial_port_transport_data_bits": 8,
"serial_port_transport_port": "/dev/ttyUSB0",
"serial_port_transport_parity": "none",
"serial_port_transport_baud_rate": 9600,
"serial_port_transport_stop_bits": "1",
"serial_port_transport_flow_control": "none"
}
},
{
"app": {
"app_transport": "tcp_client_transport",
"app_codec": "modbus_tcp_codec",
"app_transformer": "disable_transformer",
"app_encoding": "UTF-8"
},
"tcp_client_transport": {
"tcp_client_transport_timeout": 5000,
"tcp_client_transport_keepalive": true,
"tcp_client_transport_nodelay": true,
"tcp_client_transport_port": 502,
"tcp_client_transport_host": "192.168.7.7"
},
"modbus_tcp_codec": {
"unit_id": 2
}
}
]

Lua pipeline walkthrough

The following script manages the polling lifecycle and data processing. It uses the on_timer callback to maintain a steady sampling rate regardless of downstream latency.

-- ME31-XDXX0400 PT100 Temperature Monitor
-- Configured for Modbus RTU (serial) and Modbus TCP. A flag toggles between the two.
-- Polls CH1 temperature every 1 second, applies an exponential moving average (EMA) filter,
-- and saves the filtered data to InfluxDB every 10 seconds. Device info is read once at startup.
--
-- Device: ME31-XDXX0400 4-Channel PT100 Module
-- Protocol: Modbus RTU (Baud: 9600 8N1) or Modbus TCP
-- Modbus Address: Slave 1 (RTU), Unit 2 (TCP, per config)
--
-- Register Map:
-- 0x0190 CH1_TEMP_INT Input Reg Temperature integer (*0.1 °C). 0xFC19 (-999) = Disconnected
-- 0x07DC FW_VERSION Holding Firmware Version

local CONNECTION_MODE = "RTU" -- Change to "TCP" to use Modbus TCP
local CONN_ID = (CONNECTION_MODE == "TCP") and 1 or 0
local SLAVE_ADDR = 1
local TCP_UNIT_ID = 2

local POLL_MS = 1000
local INFLUX_INTERVAL_MS = 10000
local ALPHA = 0.2 -- Smoothing factor for EMA filter

local last_poll_ms = 0
local last_influx_ms = 0
local filtered_temp = nil

local INFLUX_URL = get_env("INFLUX_URL") or "http://localhost:8181"
local INFLUX_TOKEN = get_env("INFLUX_TOKEN") or "<REDACTED>"
local INFLUX_DB = "cycbox"

function on_start()
log("info", "Starting ME31-XDXX0400 monitoring. Mode: " .. CONNECTION_MODE)

-- Read Firmware Version at startup
if CONNECTION_MODE == "TCP" then
modbus_tcp_read_holding_registers(0x07DC, 1, 100, CONN_ID)
else
modbus_rtu_read_holding_registers(SLAVE_ADDR, 0x07DC, 1, 100, CONN_ID)
end
end

function on_timer(now_ms)
-- Poll CH1 Temperature
if now_ms - last_poll_ms >= POLL_MS then
if CONNECTION_MODE == "TCP" then
modbus_tcp_read_input_registers(0x0190, 1, 0, CONN_ID)
else
modbus_rtu_read_input_registers(SLAVE_ADDR, 0x0190, 1, 0, CONN_ID)
end
last_poll_ms = now_ms
end

-- Save to InfluxDB periodically
if now_ms - last_influx_ms >= INFLUX_INTERVAL_MS then
if filtered_temp ~= nil then
local line_data = string.format("me31_temp,channel=1 temperature=%.2f", filtered_temp)
influxdb_write_v3_async(INFLUX_URL, INFLUX_TOKEN, INFLUX_DB, line_data, "auto", true, false)
end
last_influx_ms = now_ms
end
end

function on_receive()
-- Only process messages from the active connection
if message.connection_id ~= CONN_ID then return false end
local modified = false

-- Check Firmware Version
local fw_key = (CONNECTION_MODE == "TCP") and string.format("modbus_tcp_%d:holding_07DC", TCP_UNIT_ID)
or string.format("modbus_rtu_%d:holding_07DC", SLAVE_ADDR)
local fw_ver = message:get_value(fw_key)
if fw_ver then
message:add_int_value("Firmware_Version", fw_ver)
modified = true
end

-- Check CH1 Temperature
local temp_key = (CONNECTION_MODE == "TCP") and string.format("modbus_tcp_%d:input_0190", TCP_UNIT_ID)
or string.format("modbus_rtu_%d:input_0190", SLAVE_ADDR)
local raw_temp = message:get_value(temp_key)
if raw_temp then
-- Convert uint16 to int16 (Two's complement)
if raw_temp > 32767 then
raw_temp = raw_temp - 65536
end

if raw_temp == -999 then
log("warn", "CH1 Sensor Disconnected")
else
local temp_c = raw_temp * 0.1

-- Apply EMA smoothing filter
if filtered_temp == nil then
filtered_temp = temp_c
else
filtered_temp = ALPHA * temp_c + (1 - ALPHA) * filtered_temp
end

message:add_float_value("CH1_Temperature_Filtered", filtered_temp)
message:add_float_value("CH1_Temperature_Raw", temp_c)
modified = true
end
end

return modified
end

Callback Logic

  1. on_start: Dispatches a one-time request for register 0x07DC (Firmware Version).
  2. on_timer: Orchestrates two distinct loops. The first triggers a Modbus read every 1s. The second pushes the accumulated filtered value to InfluxDB every 10s.
  3. on_receive: This is the parser. It handles two's complement conversion for negative temperatures, scales the integer by 0.1, and calculates the EMA using the formula EMA_now = (ALPHA * Value) + ((1 - ALPHA) * EMA_prior).

Downstream service contracts

InfluxDB v3

Data is written via the InfluxDB v3 write_async API using Line Protocol.

  • Measurement: me31_temp
  • Tags: channel=1
  • Fields: temperature (float)
  • Precision: Auto

Example Line Protocol Payload:

me31_temp,channel=1 temperature=24.52

Operational concerns

  • Environment Variables: The script requires INFLUX_URL and INFLUX_TOKEN to be set in the CycBox environment.
  • Filtering Response: The ALPHA value of 0.2 provides moderate smoothing. Increasing this value towards 1.0 reduces filtering (more responsive, more noise); decreasing it towards 0.0 increases filtering (smoother, slower response to changes).
  • Address Conflicts: Note that in the provided session, the Lua script was targeting Slave ID 1, while the manual input templates were configured for Slave ID 2. Ensure the SLAVE_ADDR (RTU) and TCP_UNIT_ID (TCP) variables in the script match your physical device DIP switch settings.

Frequently asked questions

Why is my reading -99.9?

The ME31-XDXX0400 returns a value of -999 (0xFC19) in the integer register when a PT100 sensor is disconnected or the wiring is faulty. Check your 2-wire or 3-wire connections at the module terminals.

How do I toggle between Modbus RTU and TCP modes?

The Lua script includes a CONNECTION_MODE flag at the top. Set this to "RTU" for serial or "TCP" for Ethernet. The script automatically adjusts the CONN_ID and register keys.

What is the scaling factor for temperature registers?

Temperature registers in this device use a 0.1x scaling factor. The script automatically handles this by multiplying the raw register value by 0.1 to get the actual degrees Celsius.

Gotchas and recommendations

  • 2-Wire PT100 Wiring: If using 2-wire sensors, you must short the CHx- and COM x terminals as per the manufacturer's reference manual, otherwise the module will report a disconnected sensor state.
  • TCP Timeout: The Modbus TCP codec is configured with a 5000ms timeout. If the network is congested, the on_receive callback may not fire consistently, which will delay the InfluxDB updates.
  • Secrets Management: Never hardcode the INFLUX_TOKEN in the script if you intend to share the configuration. Always use the get_env function to pull secrets from the engine's secure environment storage.