Creative Factory Modbus Workbench
A small, friendly desktop tool for talking to Modbus devices over RTU (serial) and TCP.
Built with Python, tkinter (standard library), and pymodbus.

Features
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
Download
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.
Requirements
- 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)
Run from source
python -m venv .venv
.venv\Scripts\Activate.ps1
python -m pip install -r requirements.txt
python main.py
On macOS / Linux, activate the venv with source .venv/bin/activate instead.
Build a Windows executable
.\build.ps1
Output:
dist\ModbusApp.exe — default one-file build
dist\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.
Usage notes
- For RTU, pick the
COM port 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.
Device templates
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 Modicon 40001) 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
access column) 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.json you 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 | discrete
address: raw 0-based offset (conventions are resolved at import time)
data_type: uint16 int16 uint32 int32 uint64 int64 float32 float64 string bool
word_order: ABCD (big-endian) DCBA CDAB BADC
enum: 0:Off;1:Run;2:Fault; bit: 0–15 for a bit-field
A worked example lives in docs/samples/.
Project layout
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)
Contributing
Contributions are welcome! See CONTRIBUTING.md to get started.
License
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.
评论