A small, friendly desktop tool for talking to Modbus devices over RTU (serial) and TCP.
Built with Python, tkinter (standard library), and pymodbus.
Manual workbench
- Modbus TCP and RTU connections
- Read coils, discrete inputs, holding registers, and input registers
- Write single or multiple coils
- Write single or multiple registers
- Polling for read functions
- Response summary, value table, and raw response log
Device templates & workflow (new in 1.1)
- Import your register documentation once from CSV or Excel (
.xlsx) with column mapping - Data-type decoding — int/uint 16/32/64, float32/float64, string, bool — with word/byte order and scale + offset to engineering units
- Enums (
0:Off;1:Run;2:Fault) and bit-fields (40010.3) - Tag watch list: poll many tags efficiently (contiguous block reads) with change highlighting and tag search
- Double-click to write a tag (honours access, applies inverse scaling, read-modify-write for bits)
- Connection profiles and serial COM-port auto-detect
- Export the values table and activity log to CSV
- Portable project files — connection + embedded template snapshot + watch list in one shareable file
Grab the latest standalone Windows executable — no Python install required — from the
Releases page
(download ModbusApp.exe). Or build it yourself / run from source below.
- Python 3.11+ with tkinter (bundled with the standard python.org installer; on Linux install
python3-tk) - Windows (the build script targets Windows; the app itself runs anywhere Python + tkinter does)
python -m venv .venv
.venv\Scripts\Activate.ps1
python -m pip install -r requirements.txt
python main.pyOn macOS / Linux, activate the venv with source .venv/bin/activate instead.
.\build.ps1Output:
dist\ModbusApp.exe— default one-file builddist\ModbusApp\ModbusApp.exe— if you run.\build.ps1 -OneDir
Branding icons (assets\creative_factory.ico / .png) are committed to the repo and bundled into the
executable during the build. To re-render them (dev-only, requires PySide6), run
python tools\generate_brand_assets.py.
- For RTU, pick the
COMport from the dropdown (Refresh re-scans) and set the serial parameters for your device. - For TCP, the default Modbus port is
502. - Multiple write values are comma-separated.
- Register values accept decimal or
0x-prefixed hex. - Save your connection as a profile; the last-used profile loads automatically on start.
Open the Template Watch tab to work with a device's register map instead of raw addresses.
- Import Sheet… reads your existing vendor CSV/Excel. Columns are auto-mapped and the addressing
convention (
zero_based,one_based, or Modicon40001) is auto-detected — adjust either if needed; a preview shows what parsed and flags any bad rows. - The table fills in name, area + Modbus function code (FC), address, type, and unit. Turn on Edit table to correct any cell (tag / area / address / type / unit), then Save Template.
- Check the tags you care about, then Read Once or Poll — values are decoded, scaled, and shown with units; changed cells are highlighted. Right-click a row to Read now or Write….
- Writable tags (per the
accesscolumn) write directly — double-click a row. The dialog pre-fills the documented Default and rejects values outside the Range. - Save/Load Template stores the map as JSON (canonical) or CSV.
- File → Save Project bundles the connection, the template snapshot, and your watch list into
one portable
*.cfmproj.jsonyou can reopen or share.
A ready-made example device template lives in docs/samples/ —
load acme_vfd.json to see decoded, scaled, writable tags immediately.
A saved template CSV carries # key: value header lines followed by these columns:
tag_name,register_type,address,bit,data_type,word_order,length,scale,offset,unit,access,enum,description,default,range
register_type:holding|input|coil|discreteaddress: raw 0-based offset (conventions are resolved at import time)data_type:uint16int16uint32int32uint64int64float32float64stringboolword_order:ABCD(big-endian)DCBACDABBADCenum:0:Off;1:Run;2:Fault;bit: 0–15 for a bit-field
A worked example lives in docs/samples/.
main.py # entry point
src/modbus_app/
app.py # application bootstrap
service.py # Modbus client / request handling + block reads
worker.py # background polling thread
models.py # data models (settings, templates, readings)
decode.py # register <-> value codec (types, word order, scaling)
planner.py # block-read planner (grouping, gap bridging)
templates.py # template JSON/CSV storage + validation
importer.py # vendor CSV/Excel import + column mapping
projects.py # portable project (all-in-one) files
profiles.py # saved connection profiles
ports.py # serial COM-port discovery
csvexport.py # values / log CSV export
paths.py # per-user app-data locations
branding.py # ttk styling, colors, icon/logo loading
ui/main_window.py # the tkinter UI (Manual + Template tabs)
ui/template_panel.py # template watch list
ui/import_dialog.py # column-mapping dialog
ui/write_dialog.py # per-tag write dialog
ui/widgets.py # small shared widgets (scrollable panel)
tests/ # pytest suite (decoder, planner, templates, …)
tools/generate_brand_assets.py # (dev-only) re-renders the .ico/.png; requires PySide6
build.ps1 # PyInstaller build script (Windows)
Contributions are welcome! See CONTRIBUTING.md to get started.
Released under the MIT License.
The GUI is built with tkinter, part of the Python standard library (PSF license), so there are no additional GUI runtime dependencies or third-party GUI license obligations for bundled builds.