A cross-platform Modbus simulator — Slave and Master, in one desktop toolkit.
Built with Rust · Tauri 2 · Vue 3
English · 中文
Slave register simulation → scan-group polling → random mutation → wire-level logs
Testing a Modbus integration usually means wiring up a real PLC or borrowing a master station. This project puts both ends on your desktop:
- 🛰️ Slave & Master in one repo — simulate a field device, or drive one, with no external hardware.
- 🔌 Five transports, one core — TCP, TCP+TLS, RTU, ASCII and RTU-over-TCP, covering function codes FC01–FC06 / FC15 / FC16.
- 📈 Built-in data simulation — every register can run its own data source (Fixed / Random / Sine / Sawtooth / Triangle / Counter / CSV playback) or an independent periodic mutation schedule; 20,000+ registers with virtual scrolling.
- 🌐 Network reach — route Master TCP, TCP+TLS and RTU-over-TCP connections through a SOCKS5 proxy with optional authentication and proxy-side DNS.
- 🖥️ Native desktop app — small Rust + Tauri binaries for Windows, macOS and Linux, with in-app auto-update.
- 🌏 Bilingual UI — full English / 简体中文, switchable at runtime.
- Screenshots
- Features
- Download
- Supported Function Codes
- Transport Modes
- Build from Source
- Quick Start (Tutorial)
- Architecture
- Contributing
- Changelog
- macOS First Launch
- Anonymous Usage Analytics
- License
Slave · 20,000 registers with live random mutation
ModbusSlave runs a real Modbus TCP server on 0.0.0.0:502 with two slave devices. The register table virtual-scrolls through 20,000+ holding registers; Random Mutation changes values in place (orange flashes) and the value panel on the right decodes the selected register as Signed / Unsigned / Hex / Binary at once.
Master · New Connection dialog
One dialog covers every transport — TCP (with optional TLS), RTU serial, ASCII serial or RTU-over-TCP — with target address, port, slave ID and timeout. Network transports also accept a SOCKS5 proxy with optional username/password authentication and proxy-side DNS resolution, so the target is reachable even behind a jump host or an industrial gateway.
Master · scan groups fill the table
Add scan groups (e.g. holding registers 0–59 and coils 0–31) with a custom poll interval per group and a per-group slave ID override. The connection tree shows each group's function code and range; the data table refreshes on every poll with the values served by the Slave, decoded as Unsigned / Signed / Hex / Binary / Float32.
Master · communication log with decoded TX/RX frames
The bottom log panel records every request/response pair — direction, function code and a readable detail (R 0 x60, 60 regs) side by side, filterable by direction / FC / text and exportable to CSV.
- Multi-transport — TCP, TCP+TLS, RTU (serial), ASCII (serial), RTU-over-TCP
- Modbus TCP over TLS — TLS 1.2+ encryption, PEM and PKCS#12 certificate formats, optional mTLS (mutual authentication with client certificates)
- Multiple slave devices — create connections on any port, add multiple slave devices per connection
- Four register types — Coils (FC01), Discrete Inputs (FC02), Holding Registers (FC03), Input Registers (FC04)
- Full protocol support — Read (FC01–04), Write Single (FC05/06), Write Multiple (FC15/16), with Modbus exception codes
- Register table — address search/filter, inline value editing, Ctrl/Shift multi-select, virtual scrolling (20,000+ registers), multi-format display (Auto / U16 / I16 / Hex / Bin / Float32 with 4 byte orders)
- Default initialization — new slaves pre-fill addresses 0–20,000 across all four register types; batch-add supports up to 50,000 entries per operation
- Value panel — Signed/Unsigned/Hex/Binary (16-bit), Long/Float (32-bit), Double (64-bit), all byte orders (AB CD / CD AB / BA DC / DC BA)
- Per-point data sources — every register can run its own simulation source: Fixed, Random, Sine, Sawtooth, Triangle, Counter or CSV sequence playback; each point carries an independent update interval and wave parameters (amplitude / frequency / offset / phase / wave period), and the configuration round-trips through project save/load
- Per-point periodic mutation — right-click a register for single-point config, or multi-select rows and open the Simulation Settings drawer to apply one schedule (Flip / Increment / Decrement / Random) to every selected point at once; discrete points flip, analog points ramp between min/max bounds with a configurable step; the drawer lists all active simulations with mode, period, bounds and a live current value, each with per-row stop; the minimum period is 100 ms, all schedules run concurrently and independently on a single 100 ms backend tick, and settings persist with the project
- Communication log — real-time TX/RX logging with search, direction/function-code filtering, and CSV export
- Project files — save/load complete configurations as
.modbusprojfiles for quick scenario switching - Serial port support — auto-detect system serial ports, configurable baud rate, data bits, stop bits, parity
- Multi-transport — TCP, TCP+TLS, RTU (serial), ASCII (serial), RTU-over-TCP
- Modbus TCP over TLS — TLS 1.2+ encryption, PEM and PKCS#12 certificate formats, accept-invalid-certs mode for self-signed certificate testing
- SOCKS5 proxy — route TCP, TCP+TLS and RTU-over-TCP connections through a SOCKS5 proxy (IPv4/IPv6) with optional username/password authentication and proxy-side DNS resolution; credentials are redacted from Rust debug output and the UI explains the RFC 1929 / project-file security boundary
- Scan groups — periodic polling with custom intervals per register group, per-group slave ID override
- Device discovery — slave ID scan (1–247), register address scan, auto-add discovered devices to scan groups
- Multi-format data view — Unsigned, Signed, Hex, Binary, Float32 (AB CD / CD AB), virtual scrolling
- Write operations — write single/multiple coils and registers (FC05/06/15/16)
- Communication log — TX/RX logging with search/filter (direction, function code, text) and CSV export
- Auto-reconnect — configurable reconnection with exponential backoff (1s → 2s → 4s → … → 30s max)
- Project files — save/load connection and scan group configurations
- Connection-on-scan — auto-prompt to scan devices after a successful connection
- In-app auto-update — silently downloads and verifies signed updates in the background, then offers install now / skip this version / install on next launch (6 h check throttle, "later" snoozes 24 h)
- Unified error system — structured
ModbusErrorwith categorized error types (connection/protocol/application), serialized to JSON for frontend parsing - Shared Vue components — common composables, types and i18n shared between both Tauri apps via the
shared-frontendnpm workspace
Pre-built installers for every platform are on the Releases page.
| Platform | Installer |
|---|---|
| Windows | .msi / .exe (NSIS) |
| macOS | .dmg (Apple Silicon & Intel) |
| Linux | .AppImage / .deb / .rpm |
Both apps auto-update from GitHub Releases since v0.16.0. Since v0.17.2 updates download and verify in the background first, then offer install now / skip this version / install on next launch. macOS users need one extra step on first launch.
Note: As of v0.16, the native egui edition (assets with the
-egui-suffix) is discontinued. Please migrate to the Tauri installers listed above.
Users in mainland China may have unstable access to GitHub Releases. Recommended mirror for direct installer downloads:
Starting from v0.16.0, the in-app updater automatically falls back through multiple proxies — no manual action needed. However, the very first upgrade from a pre-0.16 build has no in-app updater at all; download and install the new version once via the mirror above, after which updates route through proxies automatically.
| Code | Function | Slave (Server) | Master (Client) |
|---|---|---|---|
| FC01 | Read Coils | Read | Read/Poll |
| FC02 | Read Discrete Inputs | Read | Read/Poll |
| FC03 | Read Holding Registers | Read | Read/Poll |
| FC04 | Read Input Registers | Read | Read/Poll |
| FC05 | Write Single Coil | Write | Write |
| FC06 | Write Single Register | Write | Write |
| FC15 | Write Multiple Coils | Write | Write |
| FC16 | Write Multiple Registers | Write | Write |
| Mode | Transport | Framing | Use Case |
|---|---|---|---|
| TCP | TCP/IP socket | MBAP header | Standard Modbus TCP |
| TCP+TLS | TLS over TCP | MBAP header | Secure Modbus TCP (TLS 1.2+) |
| RTU | Serial port | Slave ID + CRC-16 | RS-485/RS-232 devices |
| ASCII | Serial port | : + hex + LRC + CRLF |
Legacy serial devices |
| RTU-over-TCP | TCP/IP socket | Slave ID + CRC-16 | Industrial gateways |
# install frontend dependencies (npm workspaces — run once at the repo root)
npm install
# run the Slave
cd crates/modbussim-app && cargo tauri dev
# run the Master
cd crates/modbusmaster-app && cargo tauri devcd crates/modbussim-app && cargo tauri build
cd crates/modbusmaster-app && cargo tauri buildcargo test --workspaceA full round-trip in six steps — drive the simulated Slave from the Master, no hardware required. (Screenshots show the Chinese UI; flip to English any time with the 中 / EN toggle.)
Install first: grab the installer for your platform from the Releases page, or run from source (
cargo tauri dev). Open bothModbusSlaveandModbusMaster.
Open ModbusSlave and click 新建连接 (New Connection): pick TCP, a port (e.g. 502) and random initialization — the server starts with a device pre-filled across all four register types (coils, discrete inputs, holding and input registers). Turn on 随机变化 (Random Mutation) to make values move; click any row to decode it in the value panel.
Tip · default init & batch add: new slaves pre-fill addresses 0–20,000 across all four register types, and batch-add handles up to 50,000 entries per operation.
Open ModbusMaster and click 新建连接 (New Connection). The defaults already target the local Slave: address 127.0.0.1, port 502, slave ID 1. Tick 启用 TLS (Enable TLS) for a secure link. Click 创建 (Create), then 连接 (Connect).
Tip · SOCKS5 proxy: on TCP, TCP+TLS and RTU-over-TCP, tick Use SOCKS5 proxy to route the connection through a proxy with optional username/password authentication and proxy-side DNS resolution — handy when the target sits behind a jump host or industrial gateway.
Add scan groups (e.g. holding registers 0–59 and coils 0–31) and start polling. The connection tree shows each group with its function code and range; the table refreshes on every poll with the values served by the Slave.
Back on the Slave, make the registers move and watch them surface on the Master's next poll:
- Right-click → 数据源 (Data Source) on any register — pick Fixed, Random, Sine, Sawtooth, Triangle, Counter or CSV sequence playback, each with its own update interval and wave parameters.
- Right-click → 模拟设置 (Open Simulation Settings) on any row — or Ctrl/Shift multi-select several rows first — to apply one schedule (翻转 / 递增 / 递减 / 随机) to all selected points at once. The drawer also lists every active simulation with its live current value and a per-row stop; single-point tuning stays one right-click away via 周期变位 (Periodic Mutation).
- Both settings persist in
.modbusprojproject files, so a saved scenario replays identically after reload.
Use 写入 (Write) on the Master to send FC05/06/15/16 — single/multiple coils and registers. Confirm the change lands on the Slave's table (and in its communication log) before the next poll.
Expand 通信日志 (Communication Log) at the bottom: every TX/RX pair is decoded — direction, function code and a readable detail (R 0 x60, 60 regs), filterable by direction / function code / text and exportable to CSV. The master's auto-reconnect attempts are all logged.
That's the full round-trip — server, registers, polling, simulation, write-back and wire-level inspection, all on your desktop.
ModbusSim/
├── crates/
│ ├── modbussim-core/ # Core library: protocol, transports, registers, logging
│ │ └── src/
│ │ ├── slave.rs / master.rs # Slave & Master connection (TCP/RTU/ASCII/RtuOverTcp dispatch)
│ │ ├── frame.rs / pdu.rs / parse.rs # RTU/ASCII frame encode/decode, Modbus PDU parsing
│ │ ├── mbap.rs # MBAP frame encoding/decoding (for TLS mode)
│ │ ├── tls_slave.rs / tls_master.rs # TLS-enabled Modbus TCP slave server & master client
│ │ ├── ascii_slave.rs / ascii_master.rs # ASCII serial transport
│ │ ├── rtu_slave.rs / rtu_master.rs # RTU serial transport
│ │ ├── rtu_tcp_slave.rs / rtu_tcp_master.rs # RTU-over-TCP transport
│ │ ├── socks5.rs # SOCKS5 CONNECT proxying for Master network transports
│ │ ├── transport.rs # Transport enum, serial config, TLS config, port enumeration
│ │ ├── register.rs # Register types, encoding/decoding (incl. wide 32-bit values)
│ │ ├── data_source.rs # Per-point data sources (sine, counter, CSV playback…)
│ │ ├── mutation.rs # Per-point periodic mutation schedules
│ │ ├── reconnect.rs # Reconnect policy with exponential backoff
│ │ ├── error.rs # Unified ModbusError enum
│ │ ├── project.rs # .modbusproj file save/load/migrate
│ │ ├── config.rs # Connection & scan-group configuration types
│ │ └── log_collector.rs / log_entry.rs / log_helpers.rs # Thread-safe log ring buffer
│ ├── modbussim-app/ # Slave Tauri application — ModbusSlave
│ └── modbusmaster-app/ # Master Tauri application — ModbusMaster
├── frontend/ # Slave Vue 3 frontend
├── master-frontend/ # Master Vue 3 frontend
└── shared-frontend/ # Shared Vue components, composables, i18n
| Layer | Stack |
|---|---|
| Backend | Rust, Tokio (async runtime), tokio-modbus, tokio-serial, native-tls (macOS Security.framework / Linux OpenSSL / Windows SChannel), serialport |
| Frontend | Vue 3, TypeScript, Vite, @tanstack/vue-virtual |
| Desktop | Tauri 2 |
Issues and pull requests are welcome. For a code change, please make sure cargo test --workspace passes (plus the frontend type checks) before opening a PR.
See CHANGELOG.md or the Releases page.
Starting from v0.16.0, both apps check GitHub Releases on startup and prompt to install new versions. Users on pre-0.16 builds need to upgrade manually once.
The bundles are not Apple-notarized (no paid Developer Program). On first launch macOS shows "ModbusSlave / ModbusMaster cannot be opened — Apple could not verify…" with only Done and Move to Trash buttons. This is the standard macOS 15 (Sequoia) block for ad-hoc-signed apps — the app is not damaged.
How to allow it (pick one)
1. GUI path
- Double-click the
.app, see the block dialog, click Done. - Open System Settings → Privacy & Security, scroll to the bottom.
- You'll see "ModbusSlave was blocked…" — click Open Anyway and enter your password.
- The next dialog has an Open button; click it. Subsequent launches go straight through.
2. One-line Terminal
xattr -dr com.apple.quarantine "/Applications/ModbusSlave.app"
xattr -dr com.apple.quarantine "/Applications/ModbusMaster.app"Strips the quarantine flag so macOS stops blocking.
ModbusSim sends an anonymous app_started event on launch via Aptabase, so the author can see install counts, active usage, and version/OS distribution. It collects no personal data — only app version, OS, locale, and an approximate country derived from your IP (the IP itself is never stored). You can turn it off anytime via the ⓘ "About" popover in the toolbar.


