📢 Announcement: Since the release of v1.3.0, our official dockerhub namespace has been changed to
topmaker, you can pull the latest images from topmaker/modpoll at dockerhub.
Learn more about modpoll usage at documentation site.
The idea for creating this tool originated from my need to efficiently check new devices during site surveys. These surveys are often time-constrained and space-limited, with on-site work adding to the pressure. In such situations, a portable Swiss Army knife toolkit becomes a valuable companion.
This tool can be easily deployed on a Raspberry Pi or similar embedded devices, polling data from a Modbus network or connected devices. Users can choose to log the data locally or publish it to an MQTT broker for further troubleshooting.
The MQTT broker can be set up either on the same Raspberry Pi or in the cloud. Once data is successfully published, users can subscribe to the relevant MQTT topics and conveniently view the data on their smartphones.
Moreover, you can also run this tool continuously on a server as a Modbus-MQTT gateway, i.e. polling from local Modbus devices and forwarding data to a centralized cloud service.
In fact, modpoll helps to bridge between the traditional field-bus world and the new IoT world.
💡 Tips: This tool is designed to be a standalone executable application, which works out-of-the-box on Linux/macOS/Windows. If you are looking for a Modbus python library, please consider the following great open-source projects, pymodbus or minimalmodbus
- Support Modbus RTU/TCP/UDP devices
- Show polling data for local debugging, like a typical modpoll tool
- Publish polling data to MQTT broker for remote debugging, especially on smart phone
- Export polling data to local storage for further investigation
- Provide docker solution for continuous data polling use case
This tool tested on Python 3.10+ (Ubuntu 22.04+ or equivalent), the package is available in the Python Package Index, users can easily install it using pip or pipx. Ubuntu 20.04 and Python 3.8 are no longer supported.
Install modpoll using the following command,
pip install modpollOptionally, pyserial library can be installed for Modbus-RTU communication,
pip install 'modpoll[serial]'Upgrade the tool via the following command,
pip install -U modpollIt is recommended to use pipx for installing modpoll on Windows, refer to here for more information about pipx.
Once pipx installed, you can run the following command in a Command Prompt terminal.
pipx install modpollUpgrade the tool via the following command,
pipx upgrade modpollUse the public Modbus TCP simulator at modsim.topmaker.net:502 for a quick test:
modpoll --once \
--tcp modsim.topmaker.net --tcp-port 502 \
--config https://raw.githubusercontent.com/top-maker/modpoll/main/examples/modsim.csvStart a local modsim server, then use the provided configuration to read its sample data:
docker run --rm -p 5020:5020 topmaker/modsim:latestIn another terminal, run:
modpoll --once \
--tcp 127.0.0.1 --tcp-port 5020 \
--config https://raw.githubusercontent.com/top-maker/modpoll/main/examples/modsim.csvYou can download the configuration and pass a local file instead of the URL.
the modsim code is also available here
- Serial transports: use
--serial PORT --framer ascii(alias:--rtustill accepted); pyserial URLs such assocket://host:portandrfc2217://host:portwork for serial-over-TCP tunnels. - Serial supports framers
asciiandrtu(pymodbus defaults to RTU if--framer default). Binary framer is no longer supported in pymodbus 3.9+.
The modsim configuration maps the simulator's four tables as follows:
device,modsim01,1,,
poll,coil,0,16,BE_BE
ref,coil01-08,0,bool8,rw
ref,coil09-16,1,bool8,rw
poll,discrete_input,10000,16,BE_BE
ref,di01-08,10000,bool8,rw
ref,di09-16,10001,bool8,rw
poll,input_register,30000,20,BE_BE
ref,input_reg01,30000,uint16,rw
ref,input_reg02,30001,uint16,rw
ref,input_reg03,30002,uint16,rw
ref,input_reg04,30003,uint16,rw
ref,input_reg05,30004,int16,rw
ref,input_reg06,30005,int16,rw
ref,input_reg07,30006,int16,rw
ref,input_reg08,30007,int16,rw
ref,input_reg09,30008,uint32,rw
ref,input_reg10,30010,uint32,rw
ref,input_reg11,30012,int32,rw
ref,input_reg12,30014,int32,rw
ref,input_reg13,30016,float32,rw
ref,input_reg14,30018,float32,rw
poll,holding_register,40000,44,BE_BE
ref,holding_reg01,40000,uint16,rw
ref,holding_reg02,40001,uint16,rw
ref,holding_reg03,40002,uint16,rw
ref,holding_reg04,40003,uint16,rw
ref,holding_reg05,40004,int16,rw
ref,holding_reg06,40005,int16,rw
ref,holding_reg07,40006,int16,rw
ref,holding_reg08,40007,int16,rw
ref,holding_reg09,40008,uint32,rw
ref,holding_reg10,40010,uint32,rw
ref,holding_reg11,40012,int32,rw
ref,holding_reg12,40014,int32,rw
ref,holding_reg13,40016,float32,rw
ref,holding_reg14,40018,float32,rw
ref,holding_reg15,40020,uint64,rw
ref,holding_reg16,40024,int64,rw
ref,holding_reg17,40028,float64,rw
ref,holding_reg18,40032,float64,rw
ref,holding_reg19,40036,string16,rwThis configuration tells modpoll to do the following for each poll,
- Read
16coils from the address starting from0and parse the response as two 8-bits boolean values; - Read
16discrete inputs from the address starting from10000and parse the response as two 8-bits boolean values; - Read
20input registers from the address starting from30000and parse data accordingly; - Read
44holding registers from the address starting from40000and parse data accordingly;
Notice the holding registers contain a 16-byte long string, which counts as 8 registers in poll size, because Modbus protocol defines a holding register as 2-byte width.
In practical, you usually need to customize a Modbus configuration file for your own device before running modpoll tool, which defines the optimal polling patterns and register mappings according to device vendor's documents.
You can also take a look at contrib folder, which collects a few types of device configuration shared by contributors.
The configuration can be either a local file or a remote public URL resource.
Refer to the documentation site for more details.
CSV validation requires a unique device name, a device ID from 1 to 254, a positive poll size within the Modbus function-code limit, and an address range that stays within 0–65535. Reference data types, read/write modes, endian values, and scales must be valid; errors include the CSV line number. A scale of 0 is valid; non-finite scales are rejected. Non-finite floating-point samples are treated as invalid and omitted from MQTT output. Device names must also be unique across all configuration files; duplicate names stop startup. Rename conflicting devices and update their MQTT topics before upgrading.
Read polls support up to 2000 bits or 125 registers. stringN reads exactly N bytes and occupies (N + 1) // 2 registers. The address:bit syntax applies to bool references on register polls.
When --autoremove is enabled, a poller is disabled after three consecutive failed polling attempts, including connection failures. A successful poll resets its failure count. Restart modpoll to retry a disabled poller; the default is to keep retrying without disabling pollers.
For coil and discrete input polls, reference addresses identify consecutive eight-bit groups beginning at the poll start, as shown in the configuration example. Values follow Modbus bit order; a final partial group contains only the requested bits. Earlier versions could reverse bits or decode out-of-order references incorrectly, so consumers relying on those results should review the change.
To run the simulator locally, start the container:
docker run --rm -p 5020:5020 topmaker/modsimIt will create a virtual Modbus TCP device running at localhost:5020, and you can open a new terminal, poll the virtual device using modpoll tool,
modpoll \
--tcp localhost \
--tcp-port 5020 \
--config https://raw.githubusercontent.com/top-maker/modpoll/main/examples/modsim.csvUse
sudobefore the docker command if you want to use the standard port502.
sudo docker run --rm -p 502:5020 topmaker/modsimIn a new terminal,
modpoll \
--tcp localhost \
--config https://raw.githubusercontent.com/top-maker/modpoll/main/examples/modsim.csv
This is a useful function of this new modpoll tool, which provides a simple way to publish collected Modbus data to MQTT broker, so users can view data from a smart phone via a MQTT client.
The following example uses a public MQTT broker broker.emqx.io for test purpose. You can also set up your own MQTT broker locally using mosquitto.
modpoll \
--tcp 127.0.0.1 --tcp-port 5020 \
--mqtt-host broker.emqx.io \
--config https://raw.githubusercontent.com/top-maker/modpoll/main/examples/modsim.csvWith successful data polling and publishing, you can subscribe the default data topic modpoll/modsim01/data on the same MQTT broker broker.emqx.io to view the collected data.
The MQTT topics can be customized by providing the following arguments,
mqtt-publish-topic-patternmqtt-subscribe-topic-patternmqtt-diagnostics-topic-pattern
See document for details.
The modpoll tool will subscribe to the topic modpoll/{{device_name}}/set by default once it successfully connected to MQTT broker, user can write device register(s) via MQTT publish,
-
To write a single holding register (address at
40001){ "object_type": "holding_register", "address": 40001, "value": 12 } -
To write multiple holding registers (address starting from
40001){ "object_type": "holding_register", "address": 40001, "value": [12, 13, 14, 15] }
A register array is written with FC16 and must contain 1–123 unsigned 16-bit values within the address range. Invalid, oversized, malformed, ambiguous, or retained write messages are ignored and logged. Brokers may clear the retained flag on live forwarding, so configure the broker not to retain commands on the set topic; QoS does not make a write exactly-once.
When a sample is invalid in a poll cycle, modpoll omits it from MQTT output and writes null for that reference in the JSON snapshot. JSON snapshots from multiple configuration files are combined and replaced atomically. When --timestamp is enabled, the timestamp is the start time of the poll cycle in Unix seconds, not the time each individual value was read.
A docker image has been provided for user to directly run the tool without local installation,
docker run --rm topmaker/modpollIt shows the version of the tool by default.
On Linux, use host networking to connect the container to a simulator on the host. Add --daemon or -d to suppress the table output.
docker run --rm --network host topmaker/modpoll \
modpoll -d \
--tcp 127.0.0.1 --tcp-port 5020 \
--config https://raw.githubusercontent.com/top-maker/modpoll/main/examples/modsim.csvFor host networking on other platforms, consult the Docker networking documentation.
If you want to load a local configure file, you need to mount a local folder onto container volume,
for example, if the child folder examples contains the config file modsim.csv, we can use it via the following command,
docker run --rm --network host -v $(pwd)/examples:/app/examples topmaker/modpoll \
modpoll -d \
--tcp 127.0.0.1 --tcp-port 5020 \
--config /app/examples/modsim.csv-
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 MQTT broker
modpoll \ --tcp 127.0.0.1 --tcp-port 5020 \ --mqtt-host broker.emqx.io \ --config examples/modsim.csv
-
Connect to Modbus TCP device and export a local JSON snapshot
modpoll \ --tcp 127.0.0.1 --tcp-port 5020 \ --export data.json \ --config examples/modsim.csv
-
Connect to Modbus TCP devices using multiple config files
modpoll \ --tcp 127.0.0.1 --tcp-port 5020 \ --config examples/modsim.csv examples/modsim2.csv
Refer to the documentation site for more details about the configuration and examples.
If you find this tool useful, please support us by starring 🌟 our repository to encourage further improvements.
The implementation of this project is heavily inspired by the following two projects:
- https://github.com/owagner/modbus2mqtt (MIT license)
- https://github.com/mbs38/spicierModbus2mqtt (MIT license)
Thanks to Max Brueggemann and Oliver Wagner for their great work.
MIT © 2021-2026, Ying Shaodong


