Modbus command-line tool: ModbusBB CLI reference
Read, write, poll, scan and diagnose Modbus devices from PowerShell, cmd or bash, with JSON/CSV output and exit codes
Quick answer: ModbusBB.CLI.exe is a Modbus master/client for the Windows command line. Each command is one-shot, with the connection given inline (--tcp, --udp, --rtu-over-tcp, --rtu, --ascii). For example, ModbusBB.CLI read --tcp 192.168.1.10 --unit 1 --address 0 --count 10. Add --output json or --output csv for machine-readable output and --trace for raw hex frames. Check the exit code: 0 = OK, 1 = runtime error, 2 = usage error, 3 = license, 130 = Ctrl+C. The user manual explains the CLI step by step with examples.
Install & run
The installer puts the CLI in C:\Program Files\ModbusBB\CLI\ModbusBB.CLI.exe. Tick Add CLI to system PATH during setup to call ModbusBB.CLI from any terminal. In the portable ZIP it's CLI\ModbusBB.CLI.exe. It is a self-contained 64-bit executable, so no .NET installation is needed.
- One-shot -
ModbusBB.CLI <command> [options] <connection>connects, runs the command and exits with an exit code - Interactive - Run
ModbusBB.CLIwith no command for a prompt.connectonce, then run commands on the open connection - Help -
ModbusBB.CLI help,help <command>,help connection,help scripting,help exit-codes, andversion - License - The CLI shares the license of the GUI. In the trial, a session lasts 15 minutes. With
--no-promptor redirected input it never asks for a key. It continues in trial mode, or exits with code 3 when the trial has expired
read with --trace: TX/RX frames go to stderr, data to stdout.Commands
Addresses are always 0-based protocol addresses (register 40001 = --address 0), in decimal or 0x hex. Unit IDs are 1-247 by default. --allow-broadcast allows 0 and 248-255.
read
Read coils, discrete inputs, holding or input registers (FC01-04).
ModbusBB.CLI read [--holding|--input|--coils|--discrete] --address <addr> [--count <n>]
[--format <fmt>] [--byte-order ABCD|DCBA|BADC|CDAB] [--string-length <chars>] <connection>--holding (FC03, default), --input (FC04), --coils (FC01), --discrete (FC02), or --type / --fc 1-4. --count is registers (1-125) or bits (1-2000). The default is 10, or one value for 32/64-bit formats, and it must be a multiple of the value size. Formats: uint16 int16 hex binary float32 int32 uint32 float64 int64 uint64 string.
ModbusBB.CLI read --tcp 192.168.1.10 --unit 1 --holding --address 0 --count 10 ModbusBB.CLI read --tcp 192.168.1.10 --input --address 0x0C --format float32 --byte-order CDAB --count 4 ModbusBB.CLI read --rtu COM3 --baud 9600 --parity E --unit 5 --coils --address 0 --count 16 --output json
write
Write holding registers or coils (FC05/06/15/16).
ModbusBB.CLI write [--register|--coil] --address <addr> --value <v[,v...]> [--format <fmt>]
[--byte-order <order>] [--single|--multiple] [--dry-run] <connection>Separate values with commas. Integers accept decimal or 0x hex, and coils accept 1/0, true/false and on/off. One 16-bit value uses FC06 (a coil uses FC05), anything else FC16 (FC15). Force the choice with --single / --multiple. --dry-run prints the request frame without connecting.
ModbusBB.CLI write --tcp 192.168.1.10 --address 0x10 --value 1234 ModbusBB.CLI write --tcp 192.168.1.10 --address 100 --format float32 --byte-order CDAB --value 21.5,22.75 ModbusBB.CLI write --tcp 192.168.1.10 --coil --address 0 --value on,off,on ModbusBB.CLI write --rtu COM3 --unit 2 --address 40 --value 0xFF00 --dry-run --trace
poll
Read repeatedly and print every sample. It runs until Ctrl+C, --samples or --duration, and re-establishes the link automatically if it drops.
ModbusBB.CLI poll [read options] [--interval <ms>] [--samples <n>] [--duration <s>] [--max-errors <n>] [--no-clear] <connection>
--interval 100 ms to 1 h (default 1000). --max-errors stops with exit code 1 after n consecutive errors (0 = never, the default). The exit code is also 1 if no sample succeeded. --no-clear appends table output instead of redrawing it. --output csv writes a header and one row per sample, and --output json writes NDJSON.
ModbusBB.CLI poll --tcp 192.168.1.10 --holding --address 0 --count 4 --interval 1000 ModbusBB.CLI poll --tcp 192.168.1.10 --input --address 0 --count 2 --format float32 --output csv > log.csv ModbusBB.CLI poll --rtu COM3 --unit 1 --address 0 --count 10 --samples 60 --output json | jq .values
scan
Find responding unit IDs, or readable register ranges.
ModbusBB.CLI scan [--slaves] [--start <id>] [--end <id>] <connection> ModbusBB.CLI scan --registers [--holding|--input|--coils|--discrete] [--start <addr>] [--end <addr>] [--step <n>] <connection>
A slave scan covers IDs 1-247 (0-255 with --allow-broadcast). A device answering with a Modbus exception counts as found. A register scan probes every --step addresses (default 10, range 1-10000) from --start to --end (default 0-1000). Use a short --timeout such as 200 to speed it up. Ctrl+C prints the partial result and exits with 130.
ModbusBB.CLI scan --rtu COM3 --baud 19200 --timeout 150 --start 1 --end 32 ModbusBB.CLI scan --tcp 192.168.1.10 --registers --input --start 0 --end 2000 --step 1 --output csv
stats
Measure response time and loss. It sends n single-register reads with no retries.
ModbusBB.CLI stats [--samples <n>] [--address <addr>] [--type holding|input|coils|discrete] [--delay <ms>] <connection>
--samples 1-100000 (default 10), --delay between requests 0-60000 ms (default 100). It reports requests sent, valid responses, exception replies, timeouts, other errors, no response (loss %) and avg/min/median/p95/max response time. Loss % = requests without any response ÷ requests sent. Exception replies count as responses.
ModbusBB.CLI stats --tcp 192.168.1.10 --samples 100 --delay 0 --output json
devid
Read Device Identification (FC43 / MEI 14). Multi-part replies are followed automatically.
ModbusBB.CLI devid [--category basic|regular|extended|individual] [--object <id>] <connection> ModbusBB.CLI devid --tcp 192.168.1.10 --category regular --output json
slaveid
Report Server/Slave ID (FC17).
ModbusBB.CLI slaveid --rtu COM3 --unit 4
diag
Diagnostics (FC08). Sub-function 0 (Return Query Data) must echo the data, and a mismatch exits with code 1.
ModbusBB.CLI diag [--sub <n>] [--data <hex>] <connection> (default --sub 0 --data "A5 37") ModbusBB.CLI diag --rtu COM3 --unit 1 --sub 0 --data "12 34"
maskwrite
Mask Write Register (FC22): result = (current AND and) OR (or AND NOT and).
ModbusBB.CLI maskwrite --address <addr> --and <mask> --or <mask> <connection> ModbusBB.CLI maskwrite --tcp 192.168.1.10 --address 4 --and 0xFFF0 --or 0x0005
rw
Read/Write Multiple Registers (FC23). The write happens first.
ModbusBB.CLI rw --read-address <addr> [--read-count <n>] --write-address <addr> --value <v[,v...]> [--format <fmt>] <connection> ModbusBB.CLI rw --tcp 192.168.1.10 --read-address 0 --read-count 4 --write-address 10 --value 1,2,3
raw
Send any function code (1-127) with data and print the request/response frames. The data is the PDU after the function code, in hex. The ADU (MBAP header, or address + CRC/LRC) is added for the connection type. An exception reply is printed and exits with code 1. --dry-run shows the frame without sending it.
ModbusBB.CLI raw --tcp 192.168.1.10 --unit 1 --fc 3 --data "00 00 00 02" Request (Tcp): 00 01 00 00 00 06 01 03 00 00 00 02 Response (1.3 ms): 00 01 00 00 00 07 01 03 04 00 64 00 00 PDU: 03 04 00 64 00 00
library
Built-in device register maps: eastron-sdm120, eastron-sdm630, schneider-pm5xxx, carlo-gavazzi-em24, sunspec-common, growatt-inverter-v120, sma-inverter, abb-acs580, schneider-altivar. Addresses are 0-based. Always verify a map against your device manual.
ModbusBB.CLI library list ModbusBB.CLI library show eastron-sdm630 ModbusBB.CLI library export eastron-sdm630 sdm630.csv
workspace
workspace run connects every connection in a .mbws file that has enabled polls, starts those polls and prints every sample until Ctrl+C or --duration. Registers defined in the workspace are decoded with their name, format and scaling. --connection limits it to one connection. CSV output writes one row per value and JSON writes NDJSON. workspace show prints the contents. Old profile .json and .mbcfg files work too.
ModbusBB.CLI workspace run plant.mbws --duration 3600 --output csv > plant.csv ModbusBB.CLI workspace show plant.mbws
simulate
Run the built-in Modbus slave simulator until Ctrl+C or --duration.
ModbusBB.CLI simulate --tcp <port> | --udp <port> | --rtu <COM> | --ascii <COM> [--baud --parity --databits --stopbits]
[--unit 1,2 | 1-4] [--generator <spec>]... [--set type:addr=v,...]... [--delay <ms>] [--jitter <ms>] [--duration <s>]
Generator spec: [unit@]type:address:kind[:min:max[:periodMs[:format[:byteorder]]]]
type: holding|input|coil|discrete kind: static|ramp|sine|random|counter|toggle
ModbusBB.CLI simulate --tcp 5020 --unit 1,2 --generator "holding:0:sine:0:100:5000" --set holding:100=1,2,3
ModbusBB.CLI simulate --rtu COM4 --baud 19200 --parity E --generator "2@input:10:ramp:0:1000:10000:float32:CDAB" --verboseException injection is available in the GUI simulator only.
connect, disconnect, status, ports
In interactive mode, connect opens a connection that later commands reuse. --no-reconnect turns off automatic reconnection. As a one-shot command, connect just tests the connection. status shows the connection state, and ports lists the serial ports Windows reports.
Connection options
| Option | Meaning |
|---|---|
--tcp <host[:port]> | Modbus TCP (default port 502) |
--udp <host[:port]> | Modbus UDP (default port 502) |
--rtu-over-tcp <host:port> | RTU frames over a TCP socket (serial device servers) |
--rtu <COM> / --ascii <COM> | Modbus RTU / ASCII on a serial port |
--port <n> | TCP/UDP port (alternative to host:port) |
-b, --baud · --parity N|E|O|M|S · --databits 7|8 · --stopbits 1|1.5|2 | Serial settings (defaults 9600, N, 8, 1) |
-t, --timeout <ms> | Response timeout, 10-60000 (default 1000) |
--connect-timeout <ms> | Connect timeout, 100-60000 (default 5000) |
--retries <n> | Retries per request, 0-10 (default 3; scans and stats use 0) |
--profile <file> · --connection <name> | Take the connection from a workspace (.mbws) or old profile (.json/.mbcfg); default is the first connection |
-u, --unit <id> (alias --slave) | Unit/slave ID, 1-247 (default 1, or from the profile) |
--allow-broadcast | Allow unit 0 (broadcast) and 248-255 |
Global options
| Option | Meaning |
|---|---|
-o, --output table|json|csv | Output format (default table) |
--trace | Print raw TX/RX frames (hex) to stderr |
--verbose | Print the communication log to stderr |
-q, --quiet | Suppress the banner and informational messages |
--no-color | Disable colors (also automatic with NO_COLOR or redirected output) |
--no-prompt | Never prompt (for scripts); trial users continue in trial mode |
Output formats
Data goes to stdout. Messages, errors and --trace go to stderr, so redirecting stdout gives clean data.
Table (default)
Address Value Hex Binary ---------------------------------------- 0 84 0x0054 0000000001010100 1 0 0x0000 0000000000000000
JSON (--output json)
{
"timestamp": "2026-09-29T22:37:24.132+03:00",
"unit": 1, "function": "input", "fc": 4, "address": 0, "count": 2,
"format": "float32", "byteOrder": "ABCD", "responseMs": 13.34,
"values": [ { "address": 0, "value": 341.33667, "raw": [17322, 43800] } ]
}CSV (--output csv)
address,value,raw 10,1,0001 11,2,0002
NDJSON and CSV rows from poll
timestamp,unit,status,response_ms,error,10,11
2026-09-29T22:37:24.779+03:00,1,ok,13.02,,1,2
{"timestamp":"2026-09-29T22:37:25.394+03:00","sample":1,"unit":1,"function":"holding","address":10,"ok":true,"responseMs":14.45,"values":[{"address":10,"value":1,"raw":[1]},{"address":11,"value":2,"raw":[2]}]}Hex trace (--trace)
[22:37:26.278] TX 00 01 00 00 00 06 01 03 00 00 00 02 [22:37:26.286] RX 00 01 00 00 00 07 01 03 04 00 54 00 00
Exit codes
| Code | Meaning |
|---|---|
| 0 | Success |
| 1 | Runtime error: connection failed, timeout, Modbus exception reply, I/O error |
| 2 | Usage error: unknown command/option, missing or invalid value, value out of range |
| 3 | License error: trial expired or no valid license |
| 130 | Interrupted with Ctrl+C (poll, simulate and workspace run exit 0 when stopped with Ctrl+C) |
Scripting examples
Commands never prompt when stdin is redirected or --no-prompt is given, and colors turn off automatically when output is redirected.
PowerShell: read a float from several devices
foreach ($u in 1..5) {
$r = ModbusBB.CLI read --rtu COM3 --baud 9600 --unit $u --input --address 0 --format float32 --output json --quiet | ConvertFrom-Json
if ($LASTEXITCODE -eq 0) { "Unit $u : $($r.values[0].value)" } else { "Unit $u : no answer" }
}PowerShell: write a setpoint and verify it
$conn = @('--tcp', '192.168.1.10', '--unit', '1', '--quiet', '--no-prompt')
ModbusBB.CLI write @conn --address 100 --value 250
if ($LASTEXITCODE -ne 0) { throw "Write failed (exit $LASTEXITCODE)" }
$r = ModbusBB.CLI read @conn --address 100 --count 1 --output json | ConvertFrom-Json
if ($r.values[0].value -ne 250) { throw "Read-back mismatch: $($r.values[0].value)" }cmd.exe (batch file)
ModbusBB.CLI write --tcp 192.168.1.10 --address 100 --value 250 --quiet if errorlevel 1 echo Write failed & exit /b 1 for /L %%u in (1,1,10) do ModbusBB.CLI read --tcp 192.168.1.10 --unit %%u --address 0 --count 1 --output csv --quiet
Bash / WSL
ModbusBB.CLI read --tcp plc --address 0 --count 2 --format uint32 --output json | jq '.values[0].value'
Defaults for many commands
set MODBUSBB_OPTS=--tcp 192.168.1.10 --unit 1 (cmd) $env:MODBUSBB_OPTS = "--tcp 192.168.1.10 --unit 1" (PowerShell) ModbusBB.CLI read --address 0 --count 4
CSV logging from the command line
# One CSV row per sample, every second, for one hour ModbusBB.CLI poll --tcp 192.168.1.10 --input --address 0 --count 4 --interval 1000 --duration 3600 --output csv --quiet > log.csv # Float values from an RTU meter, stop after 5 consecutive errors ModbusBB.CLI poll --rtu COM3 --baud 9600 --parity E --unit 1 --input --address 0 --count 4 --format float32 --max-errors 5 --output csv > meter.csv # Everything a workspace polls, decoded with names and scaling (one row per value) ModbusBB.CLI workspace run plant.mbws --duration 3600 --output csv > plant.csv # NDJSON for log pipelines ModbusBB.CLI poll --tcp 192.168.1.10 --address 0 --count 10 --duration 3600 --output json > log.ndjson
Each poll row has timestamp, unit, status, response_ms, error and one column per address. Failed samples are logged with their status and error, so gaps are visible. Remember the trial's 15-minute session limit when you log for longer. A licensed copy has no limit.
Environment variables
| Variable | Effect |
|---|---|
MODBUSBB_OPTS | Default connection options used when a command has none, e.g. --tcp 192.168.1.10 --unit 1 |
MODBUSBB_PROFILE | Default --profile file |
MODBUSBB_NO_PROMPT=1 | Never prompt for a license key (same as --no-prompt) |
NO_COLOR | Disable colors |
CLI FAQ
Is there a free Modbus command-line tool for Windows?
ModbusBB CLI comes with ModbusBB. The 30-day trial includes every command, with 15 minutes per session. A $10 lifetime license removes the session limit, and the license covers both the CLI and the GUI.
Can the CLI do everything the GUI does?
No, but it covers the tasks worth scripting: read, write, poll, scan, stats, device identification (FC43), report slave ID (FC17), diagnostics (FC08), mask write (FC22), read/write multiple (FC23), raw requests, the device library, running workspaces and the simulator. The trend chart, watchdog rules and actions, register-map editing, the traffic monitor window (use --trace instead) and simulator exception injection are GUI only.
How do I detect errors in a script?
Check the exit code. 0 means success, 1 a runtime error (connection failed, timeout or Modbus exception reply), 2 a usage error, 3 a license problem and 130 Ctrl+C. In PowerShell use $LASTEXITCODE; in cmd use if errorlevel 1.
Can I see the raw Modbus frames from the CLI?
Yes. Add --trace to any command to print every TX/RX frame in hex to stderr. The raw command prints the request and response frames, and --dry-run on write and raw shows the frame without sending it.
Script your Modbus tests
The CLI is included in the ModbusBB download, next to the GUI.