Skip to main content

modpoll2mqtt — Modbus to MQTT Gateway

PyPI Release License checks tests Downloads

Documentation: yoch.github.io/modpoll2mqtt · PyPI: pypi.org/project/modpoll2mqtt

modpoll2mqtt is a command-line Modbus-to-MQTT gateway. It polls Modbus devices (RTU, TCP, UDP) using CSV configuration files, publishes values to an MQTT broker, and accepts write commands by CSV reference name.

Install the PyPI package as modpoll2mqtt; the executable command remains modpoll.

Fork of modpoll, using the latest pymodbus release, with semantic MQTT writes by CSV reference name and numerous bugfixes and improvements.

Features

  • Modbus RTU, TCP, and UDP (CSV configuration files)
  • Local display of polled data (debug mode)
  • MQTT publishing of references (configurable topics)
  • MQTT writes by CSV reference name (modpoll/<device>/set)
  • Atomic JSON export of all configured devices
  • Bit-level access on registers and coils

Installation

Python 3.10+ required.

pip install modpoll2mqtt

Optional serial support (pyserial):

pip install 'modpoll2mqtt[serial]'

Upgrade:

pip install -U modpoll2mqtt

On Windows, pipx is recommended:

pipx install modpoll2mqtt

Quickstart

Single poll of a Modbus TCP device (replace the IP address with yours):

modpoll --once \
  --tcp 192.168.1.10 \
  --config examples/modsim.csv

Publish to an MQTT broker:

modpoll \
  --tcp 192.168.1.10 \
  --mqtt-host broker.emqx.io \
  --config examples/modsim.csv

Data is published to modpoll/<device_name>/data by default.

Add --mqtt-retain so the broker keeps the last data and diagnostics message per topic for new subscribers. The status topic modpoll/status is always retained. If a device goes offline, retained data values may still appear live until the next successful publish.

Add --mqtt-on-change to publish data only when values change instead of on every poll cycle. The first successful poll is always published; MQTT reconnects, publish errors and device recoveries republish the full payload. Pair it with --mqtt-qos 1 (and preferably --mqtt-retain) so changed values are not silently missed.

MQTT write by reference

Once connected to the broker, modpoll subscribes to modpoll/+/set. Publish to modpoll/<device>/set:

{
  "holding_reg01": 100,
  "coil01-08": [true, false, false, false, false, false, false, false]
}

Unknown reference names in legacy payloads are skipped with a warning. Known writes are validated together before bus I/O; results are returned on the request topic followed by /response. Responses are never retained. Never retain a command.

bool8 / bool16 references require a JSON array of 8 or 16 booleans at write time. Single-coil bool references accept a scalar boolean.

Example: an int16 reference with scale 0.1 accepts 21.5 as input; the raw register value 215 is written.

Only coil and holding_register references can be written. input_register and discrete_input are read-only at the Modbus protocol level even when marked rw in the CSV.

Migration from modpoll 1.6.x: the low-level format (object_type, address, value) is no longer supported.

Migration from modpoll2mqtt 2.0.x: the {"ref": "...", "value": ...} object format was removed in 2.1.0; use a reference map ({"ref_name": value}) instead.

Configuration pitfalls

  • On coil or discrete_input pollers, use bool with the absolute Modbus coil address for a single boolean.
  • bool8 / bool16: legacy grouped reads (group index, not a direct coil address).
  • On holding_register or input_register, use bool with address:bit (0–15), e.g. 40019:15,bool.
  • <endian> must be BE_BE, LE_BE, LE_LE, or BE_LE only.
  • --autoremove disables a poller after 3 consecutive attempted failures; add --poller-recover-after 60 to probe it again.
  • Addresses are passed literally as zero-based protocol addresses. A vendor label such as 40001 is not automatically converted to 0.
  • All files are validated before starting; run modpoll --check-config --config file.csv to check without a Modbus transport.

Reliable commands and operation

Use --mqtt-namespace site/gateway1 to isolate the default data, command, diagnostic and retained presence topics for each gateway. Configure MQTT ACLs and use --mqtt-use-tls (TLS 1.2 minimum) with --mqtt-pass-file /run/secrets/mqtt_password. --read-only blocks all writes. --mqtt-insecure explicitly disables certificate verification and should only be used for controlled diagnostics.

The versioned command topic is modpoll/<device>/commands (customize with --mqtt-command-topic-pattern). For example:

{"version": 1, "request_id": "control-001", "operation": "set", "values": {"holding_reg01": 100}}

Optional expires_at is a UTC Unix timestamp. Versioned GET uses "operation": "get", "references": ["holding_reg01"]. The response includes the request ID and a result for each reference: confirmed, rejected, failed, not_attempted, or unknown when a write acknowledgement was lost. Repeating an identical request returns a cached response while its ID remains in the bounded process-local cache. This protection does not survive process restarts. An ID reused with different content is rejected. See the operational and migration guide for limits and failure semantics.

--once polls once, flushes MQTT publications, and exits with 0 on success, 1 on runtime/poll/publication failure, or 2 for invalid configuration/options. It does not execute commands. Invalid configurations previously skipped, numeric wraparound, fractional unscaled integer writes, and oversized UTF-8 strings now fail explicitly. See CHANGELOG.md for the complete migration list.

Examples

# Modbus TCP
modpoll --tcp 192.168.1.10 --config examples/modsim.csv

# Modbus serial
modpoll --serial /dev/ttyUSB0 --serial-baud 9600 --config contrib/eniwise/scpms6.csv

# MQTT + JSON export
modpoll --tcp 192.168.1.10 --mqtt-host localhost --export data.json --config examples/modsim.csv

# Multiple config files
modpoll --tcp 192.168.1.10 --config examples/modsim.csv examples/modsim2.csv

See examples/ and contrib/ for more device configurations.

Credits

This project builds on:

License

MIT — see LICENSE.

Metadata

Release files for modpoll2mqtt 2.4.1

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for modpoll2mqtt 2.4.1
File Size Uploaded
modpoll2mqtt-2.4.1.tar.gz 40.7 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for modpoll2mqtt 2.4.1
File Interpreter ABI Platform
modpoll2mqtt-2.4.1-py3-none-any.whl Python 3 none any Details

Total release size: 85.9 kB

Release files / modpoll2mqtt-2.4.1.tar.gz

Download URL modpoll2mqtt-2.4.1.tar.gz
Size 40.7 kB
Tags Source
SHA-256 checksum
How to use checksums
b0864cd7fa67ac7ab604e79e845d5e519bded97e32a85f8e158c30fc2330f86c
BLAKE2b-256 checksum
How to use checksums
310887cb85c646677aa7cb7ded26b9ac79e2cfcde8f5ff09c085a4b8de93a0a3
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 19, 2026.

Transparency log

Release files / modpoll2mqtt-2.4.1-py3-none-any.whl

Download URL modpoll2mqtt-2.4.1-py3-none-any.whl
Size 45.2 kB
Tags Python 3
SHA-256 checksum
How to use checksums
127586238f76a2f7dbd05eb9033e1ca2b28e0898a349d8fb64643e62a35a713b
BLAKE2b-256 checksum
How to use checksums
d3f802961740d381532b12c970372a5da770d9480c386a4bd588bad3280e472d
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 19, 2026.

Transparency log

Release history Release notifications | RSS feed

This release

2.4.1 This release

2 release files

2.4.0

2 release files

2.3.1

2 release files

2.3.0

2 release files

2.2.1

2 release files

2.2.0

2 release files

2.1.2

2 release files

2.1.1

2 release files

2.1.0

2 release files

2.0.0

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page