Basic Usage
modpoll2mqtt v2.2.1 - Modbus to MQTT gateway
usage: modpoll [-h] [-v] -f CONFIG [CONFIG ...] [--csv-delimiter {comma,tab}]
[--no-output] [-r RATE] [-1] [--interval INTERVAL] [--tcp TCP]
[--tcp-port TCP_PORT] [--udp UDP] [--udp-port UDP_PORT]
[--serial SERIAL] [--serial-baud SERIAL_BAUD]
[--serial-parity {none,odd,even}] [--timeout TIMEOUT]
[--modbus-backoff-base MODBUS_BACKOFF_BASE]
[--modbus-backoff-max MODBUS_BACKOFF_MAX]
[--modbus-max-connection-age MODBUS_MAX_CONNECTION_AGE]
[-o EXPORT] [--mqtt-version {3.1.1,5.0}]
[--mqtt-host MQTT_HOST] [--mqtt-port MQTT_PORT]
[--mqtt-clientid MQTT_CLIENTID]
[--mqtt-publish-topic-pattern MQTT_PUBLISH_TOPIC_PATTERN]
[--mqtt-subscribe-topic-pattern MQTT_SUBSCRIBE_TOPIC_PATTERN]
[--mqtt-get-topic-pattern MQTT_GET_TOPIC_PATTERN]
[--mqtt-diagnostics-topic-pattern MQTT_DIAGNOSTICS_TOPIC_PATTERN]
[--mqtt-qos {0,1,2}] [--mqtt-rx-queue-size N]
[--mqtt-user MQTT_USER] [--mqtt-pass MQTT_PASS]
[--mqtt-use-tls] [--mqtt-insecure]
[--mqtt-cacerts MQTT_CACERTS]
[--mqtt-tls-version {tlsv1.2,tlsv1.1,tlsv1}] [--mqtt-single]
[--mqtt-keys {name-with-unit,name-only}] [--mqtt-retain]
[--diagnostics-rate DIAGNOSTICS_RATE] [--autoremove]
[--loglevel {DEBUG,INFO,WARNING,ERROR,CRITICAL}] [--timestamp]
[--delay DELAY] [--framer {default,ascii,rtu,socket}]
Named Arguments
- -v, --version
show program’s version number and exit
- -f, --config
A local path or URL of Modbus configuration file. Required!
- --csv-delimiter
Possible choices: comma, tab
Column delimiter code for Modbus config files (comma, tab). Defaults to comma
Default:
'comma'- --no-output
Do not print poll results to stdout (useful under systemd)
Default:
False- -r, --rate
The sampling rate (s) to poll modbus device, Defaults to 10.0
Default:
10.0- -1, --once
Only run polling at one time
Default:
False- --interval
Delay in seconds between pollers and between references in one MQTT set message. Defaults automatically by transport: 0.0 for TCP/UDP, or the RTU frame gap derived from –serial-baud with a 0.005s floor for serial/RTU
- --tcp
Act as a Modbus TCP master, connecting to host TCP
- --tcp-port
Port for MODBUS TCP. Defaults to 502
Default:
502- --udp
Act as a Modbus UDP master, connecting to host UDP
- --udp-port
Port for MODBUS UDP. Defaults to 502
Default:
502- --serial, --rtu
pyserial URL (or port name) for serial transport (alias: –rtu)
- --serial-baud, --rtu-baud
Baud rate for serial port. Defaults to 9600
Default:
9600- --serial-parity, --rtu-parity
Possible choices: none, odd, even
Parity for serial port. Defaults to none
Default:
'none'- --timeout
Response time-out seconds for MODBUS devices, Defaults to 3.0
Default:
3.0- --modbus-backoff-base
Initial Modbus reconnect backoff in seconds. Defaults to 1.0
Default:
1.0- --modbus-backoff-max
Maximum Modbus reconnect backoff in seconds. Defaults to 60.0
Default:
60.0- --modbus-max-connection-age
Maximum age in seconds for a persistent Modbus connection before recycling. Disabled by default
- -o, --export
The file name to export references/registers
- --mqtt-version
Possible choices: 3.1.1, 5.0
MQTT version. Defaults to MQTT v3.1.1
Default:
'3.1.1'- --mqtt-host
MQTT server address. Skip MQTT setup if not specified
- --mqtt-port
1883 for non-TLS or 8883 for TLS, Defaults to 1883
Default:
1883- --mqtt-clientid
MQTT client name, If qos > 0, set unique name for multiple clients
- --mqtt-publish-topic-pattern
Topic pattern for MQTT publish. Use {{device_name}} as placeholder for the device names in Modbus config. Defaults to “modpoll/{{device_name}}/data”
Default:
'modpoll/{{device_name}}/data'- --mqtt-subscribe-topic-pattern
Topic pattern for MQTT write commands. Use + as placeholder for device name. Defaults to “modpoll/+/set”
Default:
'modpoll/+/set'- --mqtt-get-topic-pattern
Topic pattern for MQTT on-demand read commands. Use + as placeholder for device name. Defaults to “modpoll/+/get”
Default:
'modpoll/+/get'- --mqtt-diagnostics-topic-pattern
Topic pattern for MQTT diagnostics. Use {{device_name}} as placeholder for the device names in Modbus config. Defaults to modpoll/{{device_name}}/diagnostics
Default:
'modpoll/{{device_name}}/diagnostics'- --mqtt-qos
Possible choices: 0, 1, 2
MQTT QoS value. Defaults to 0
Default:
0- --mqtt-rx-queue-size
Max MQTT subscribe messages buffered between polls (default: 1000)
Default:
1000- --mqtt-user
Username for authentication (optional)
- --mqtt-pass
Password for authentication (optional)
- --mqtt-use-tls
Use TLS
Default:
False- --mqtt-insecure
Use TLS without providing certificates
Default:
False- --mqtt-cacerts
Path to ca keychain
- --mqtt-tls-version
Possible choices: tlsv1.2, tlsv1.1, tlsv1
TLS protocol version, can be one of tlsv1.2 tlsv1.1 or tlsv1
Default:
'tlsv1.2'- --mqtt-single
Publish each value in a single topic. If not specified, groups all values in one topic.
Default:
False- --mqtt-keys
Possible choices: name-with-unit, name-only
MQTT JSON payload key format. “name-with-unit” appends the unit suffix when configured (default). “name-only” uses reference names only.
Default:
'name-with-unit'- --mqtt-retain
Set the MQTT retain flag on published data messages.
Default:
False- --diagnostics-rate
Time in seconds as publishing period for each device diagnostics
Default:
0- --autoremove
Automatically remove poller if modbus communication has failed 3 times.
Default:
False- --loglevel
Possible choices: DEBUG, INFO, WARNING, ERROR, CRITICAL
Set log level, Defaults to INFO
Default:
'INFO'- --timestamp
Add timestamp to the result
Default:
False- --delay
Time to delay sending first request in seconds after connecting. Default to 0
Default:
0- --framer
Possible choices: default, ascii, rtu, socket
The type of framer for Modbus messages. Serial supports ascii/rtu; TCP/UDP use socket.
Default:
'default'
The config option is required.
Commandline Usage
Connect to Modbus TCP device
modpoll --tcp 192.168.1.10 --config examples/modsim.csv
Connect to Modbus serial device
modpoll --serial /dev/ttyUSB0 --serial-baud 9600 --config contrib/eniwise/scpms6.csv
Connect to Modbus TCP device and publish data to remote MQTT broker
modpoll --tcp 192.168.1.10 --config examples/modsim.csv --mqtt-host broker.emqx.io
Connect to Modbus TCP device and export data to local csv file
modpoll --tcp 192.168.1.10 --config examples/modsim.csv --export data.csv
Connect to Modbus UDP device
modpoll --udp 192.168.1.10 --config examples/modsim.csv
Configuration sources
--config accepts one or more local file paths or HTTP(S) URLs. Multiple files are loaded into separate logical configs that share the same Modbus connection.
If columns are not split correctly (for example tab-separated files), use --csv-delimiter tab (default: comma).
Export
--export writes polled reference values to a JSON file keyed by device name, then by reference name. Use --timestamp to add a timestamp field to each device’s export object and to grouped MQTT publish payloads.
modpoll --tcp 192.168.1.10 --config examples/modsim.csv --export data.json --timestamp
Operational flags
--no-outputsuppresses poll result tables on stdout (replaces the former--daemon/-dflag; does not fork).--delaywaits N seconds after connecting before the first Modbus poll.--intervalwaits between pollers and between successive references in a single MQTT write command. If omitted, the default is transport-aware:0.0seconds for TCP/UDP; for serial/RTU it is derived from--serial-baudusing the Modbus RTU 3.5-character silent interval with a practical0.005s floor. Set it explicitly for slow devices that need extra settling time.
Default serial/RTU examples:
|
Auto |
|---|---|
|
|
|
|
|
|
|
|
|
|
Configuration File
The configuration file (–config) is a CSV file that defines the devices, pollers, and references to be read.
Coil and discrete input references
On coil or discrete_input pollers:
5,boolreads a single coil/discrete input at Modbus address 5 and publishes one boolean value.0,bool8/0,bool16read a legacy bit group (8 or 16 booleans). Withpoll,coil,0,16, group address1returns Modbus coil addresses 8–15 (often labeled coils 9–16 in vendor tables). If the poll ends before the full group is read, missing bits are padded withfalse.address:bitsyntax is not supported on coil/discrete_input pollers.
Register bit references
For register references (i.e., Holding or Input registers) with a dtype of bool, you can specify a single bit to be extracted from the 16-bit register. This is done by appending :bit to the address, where bit is an integer from 0 to 15.
40110: Reads the entire 16-bit register at address 40110.40110:15: Reads the 16-bit register at address 40110, extracts bit 15, and returns a boolean value.
The bit is extracted from the final 16-bit value after byte/word swapping based on the poller’s endianness configuration.
Framers and transports
Serial (–serial, alias –rtu) supports framers rtu and ascii (e.g., –serial … –framer ascii). Binary framer was removed in pymodbus 3.9+. If –framer default is used, pymodbus defaults to RTU framer.
TCP/UDP (–tcp/–udp) use the socket framer; other framers are rejected. If –framer default is used, pymodbus defaults to socket framer.
Persistent Modbus connection
modpoll keeps the Modbus client open between poll cycles and MQTT commands. The connection is closed on process shutdown and after transport failures, then retried with a non-blocking exponential backoff. This avoids repeated connect/close overhead while preventing a dead bus or half-open socket from blocking the main loop for a long backoff sleep.
Operational notes:
--timeoutstill bounds individual Modbus operations at the pymodbus client level.--intervaldefaults to0.0on TCP/UDP so persistent connections are not hidden behind an artificial 0.5 s poller delay. Serial/RTU derives its default from--serial-baudusing the Modbus RTU 3.5-character silent interval, with a0.005s floor for practical scheduling. The same delay is used between pollers in a poll cycle and between references in one MQTTsetmessage.--modbus-backoff-baseand--modbus-backoff-maxcontrol reconnect pacing after failures.--modbus-max-connection-agecan recycle a long-lived connection periodically; it is disabled by default.On serial/RTU transports, the port remains reserved while
modpollruns. Stopmodpollbefore debugging the same port with another tool.Transport exceptions raised during polling, MQTT get, or MQTT set close the Modbus connection and enter backoff immediately. Modbus exception responses returned by a device are counted as operation failures without forcing a reconnect.
MQTT retain
By default, published data and diagnostics messages are not retained by the broker. Use --mqtt-retain to set the MQTT retain flag on data and diagnostics publishes (publish_data and --diagnostics-rate topics). The status topic modpoll/status is always retained independently of --mqtt-retain.
This is useful when subscribers (dashboards, automations) connect after modpoll has already started: they receive the last known values immediately instead of waiting for the next poll cycle.
modpoll --tcp 192.168.1.10 --mqtt-host localhost --mqtt-retain --config examples/modsim.csv
Caveats:
If a Modbus device becomes unreachable,
modpollstops publishing for that device but the broker may still serve the last retained message, which can look like a live value.Retain is not a last-will/offline signal for data or diagnostics; use
modpoll/statusfor process presence (see below).
MQTT status
When --mqtt-host is set, modpoll publishes process presence on modpoll/status:
{"online": true}
On a clean shutdown, online is set to false before disconnecting. On an unexpected disconnect (crash, network loss), the broker publishes a last-will message with {"online": false}. Status messages are always retained.
MQTT diagnostics
When --diagnostics-rate is greater than zero, modpoll periodically publishes diagnostics.
Per device (--mqtt-diagnostics-topic-pattern, default modpoll/{device}/diagnostics):
{
"poll_count": 42,
"error_count": 3,
"last_poll_success": true,
"get_count": 5,
"get_errors": 2,
"get_success": 3,
"get_unknown_refs": 1,
"get_read_errors": 4,
"set_count": 8,
"set_errors": 1,
"set_success": 7,
"set_unknown_refs": 2,
"config_source": "/path/to/config.csv"
}
Process-wide (modpoll/diagnostics):
{
"mqtt_connected": true,
"modbus_ok": true,
"devices_failing": 1,
"last_cycle_s": 10.1,
"modbus_connection_state": "READY",
"modbus_connected": true,
"modbus_connected_since": 1760000000.0,
"modbus_last_success_at": 1760000010.0,
"modbus_last_failure_at": null,
"modbus_last_error": null,
"modbus_consecutive_failures": 0,
"modbus_backoff_until": null,
"modbus_connect_count": 1,
"modbus_reconnect_count": 0,
"modbus_transaction_failure_count": 0
}
Diagnostics retain follows --mqtt-retain (same as data topics). Diagnostics report operational health; they are not a substitute for modpoll/status presence. last_cycle_s is 0 until the first poll cycle completes. modbus_ok reports whether the Modbus transport was available for all scheduled poll handlers in the latest cycle; per-device logical failures are reflected separately by devices_failing and device diagnostics. When Modbus is unavailable, modbus_connection_state, modbus_last_error and modbus_backoff_until show whether the process is waiting before the next reconnect attempt.
MQTT payload keys
By default, grouped MQTT publish payloads use reference names as JSON keys, appending \|unit when a unit is configured in the CSV (e.g. "temp\|°C"). Use --mqtt-keys name-only to publish keys without the unit suffix:
modpoll --tcp 192.168.1.10 --mqtt-host localhost --mqtt-keys name-only --config examples/modsim.csv
MQTT single publish
By default, all references for a device are published in one JSON object on the data topic. With --mqtt-single, each reference is published on its own topic under the publish pattern, e.g. modpoll/{device}/data/{ref_name}. List values (bool8 / bool16) are split into indexed sub-topics (.../{ref_name}/0, .../1, …).
Publish behavior
Data is not published for a device unless its latest poll cycle succeeded (
last_poll_successin diagnostics).References with no value (
null) are omitted from grouped MQTT payloads.Non-finite floats (
NaN,Inf) are omitted from MQTT publish and export.
MQTT write commands
Subscribe pattern (default): modpoll/+/set. Publish to modpoll/{device}/set with a JSON object mapping reference names to values:
{
"PID_V3V_EC_Consigne_reprise": 21.5,
"BP_MA_CTA": true
}
The device is taken from the MQTT topic, not from the JSON payload.
Reference names in the payload must match the CSV configuration; unknown keys are skipped with a warning.
Values use the same decoded engineering units as MQTT publish (scale and dtype from the CSV are handled by modpoll).
Only references marked
rworwin the CSV can be written.Only
coilandholding_registerpollers are writable;discrete_inputandinput_registerare read-only at the Modbus protocol level even when markedrw.boolon coils or registers (includingaddress:bit) expects a scalar boolean.bool8/bool16expect a JSON array of 8 or 16 booleans.stringNNNreferences expect a string value.Multiple references can be written in a single message.
Unknown reference keys are skipped with a warning;
set_unknown_refs,set_errors, andset_successin device diagnostics track write attempts (see MQTT diagnostics).
Duplicate reference names on the same device are rejected when loading the config file.
MQTT on-demand read (get)
Subscribe pattern (default): modpoll/+/get. Publish to modpoll/{device}/get with a JSON object whose keys are reference names (values are ignored, use null):
{
"temp": null,
"pressure": null
}
On success, the response is published to
modpoll/{device}/get/responsewith a JSON object of reference names to decoded values (same units as periodic MQTT publish). References that could not be read are omitted from the payload.An empty payload
{}on the request is ignored (warning logged, no response, no diagnostics). An empty response{}means the request was processed but no value could be read.On partial failure,
get_errorsis incremented once per request (andget_successwhen there were no errors).get_unknown_refsandget_read_errorscount individual skipped or failed references.Multiple references in one request are read independently: known refs are returned even if others fail.
Reads use targeted Modbus requests (minimal register/coil count per reference), not a full poller block.
Uses the shared persistent Modbus connection; if the connection is in backoff, the request fails quickly and returns an empty response.
Breaking change (2.1.0+): the ref/value object format is no longer supported; use a reference map instead.
Breaking change (2.0.0+): the previous low-level format (object_type, address, value) is no longer supported.