JSON command reference
Everything in PDComm Modbus is a JSON command. In the SDK you pass it to pdcomm_modbus_call or a wrapper’s call/Call, in the headless simulator as one line of standard input, and in MCP as the arguments of a modbus_<action> tool. The C, C++ and .NET functions themselves are in the API reference.
Replies
Section titled “Replies”| Outcome | Reply |
|---|---|
| Success | {"ok":true,"result":<result>} |
| Service error | {"ok":false,"error":"<message>"}; the C ABI returns it with PDCOMM_MODBUS_SERVICE_ERROR. License errors also carry a code. |
- A missing
actionfails withAction is required; an unknown one withUnknown service action. - Member order in objects carries no meaning; replies list members in name order.
- Service errors lists the error messages.
PDU notation
Section titled “PDU notation”The pdu of request and execute and the bytes of fault accept two forms:
- A hex string:
"0300000001". Spaces between bytes are ignored ("03 00 00 00 01"). An odd number of digits fails withInvalid hex length; non-hex characters withInvalid hex. - A byte array:
[3,0,0,0,1]. Values outside 0–255 fail withExpected a byte array or hex string.
The response in results is always an array of decimal bytes, or null when there’s no response.
Commands
Section titled “Commands”capabilities
Section titled “capabilities”Returns the build’s features; takes no arguments.
| Result member | Meaning |
|---|---|
protocol |
Always "modbus" |
tcp, rtu, ascii, security |
Transports and security included in the build |
functions, diagnostics |
Supported function codes and diagnostics sub-functions |
mei13 |
"opaque_extension": MEI 13 content isn’t interpreted. |
canopen |
Always false |
project_version |
Supported project version (1) |
diagnostic_logging, logging_schema_version |
Diagnostic log support and log format version |
Returns {"config":{...},"connected":<bool>,"listen_only":<bool>,"delimiter":<ASCII end character>,"trace_count":<entries>}. When a TCP client closed its connection after a timeout or a peer close, connected is false.
configure
Section titled “configure”Replaces the whole configuration while disconnected and returns the same result as state. It also restarts the server’s diagnostics state (counters, listen-only mode). While connected it fails with Disconnect before changing configuration.
{"action":"configure","config":{"transport":"tcp","role":"client","address":"127.0.0.1:1502","port":"","unit":1,"baud":19200,"parity":"even","stop_bits":1,"timeout_ms":1000}}| Member | Required | Values | Notes |
|---|---|---|---|
transport |
Yes | tcp, rtu, ascii |
|
role |
Yes | client, server, gateway |
Use gateway with rtu or ascii. |
address |
Yes | IP:port |
TCP address; required by the format even for serial configurations. Host names aren’t accepted. |
port |
Yes | String | Serial device; "" for TCP |
unit |
Yes | 1–247 | |
baud |
Yes | 1 or more | Needs a value even for TCP. |
parity |
Yes | even, odd, none |
|
stop_bits |
Yes | 1, 2 | 2 with none parity |
timeout_ms |
Yes | 1–60000 | |
ascii_timeout_ms |
No | 1–60000, default 1000 | |
turnaround_ms |
No | 0–60000, default 100 | |
routes |
No | {"TCP unit":serial address} |
Gateway routes; keys 1–255, values 1–247 |
security |
No | Object or null |
Modbus/TCP Security; see below |
Members of the security object, for TCP configurations only:
| Member | Required | Description |
|---|---|---|
chain, private_key, trust |
Yes | PEM file paths. chain must end with the root CA. |
mode |
No | encrypted (default) or authentication_only |
hostname |
No | Name or IP the client checks in the server certificate. Default localhost |
roles |
No | Server: {"role":[function codes,...]} |
null_role |
No | Server: function codes allowed without a role. Default [] |
crls |
No | List of CRL file paths |
require_revocation |
No | When true, revocation must be checked against CRLs; needs crls. |
Unknown members fail with unknown field ... and missing required members with missing field ....
connect, disconnect
Section titled “connect, disconnect”connect opens the socket, listener or serial port and returns state. A server opened on port 0 reports its actual address in config.address. When already connected it fails with Endpoint is already connected. The SDK libraries check the license on connect and, without one, return an error whose code is license_required (see Service errors). disconnect returns {"connected":false} and succeeds even when not connected.
Has a server or gateway serve its pending requests once and returns {"handled":N}. An application that builds a server with the SDK repeats it on a worker thread; the GUI, headless simulator and MCP server repeat it themselves.
request
Section titled “request”| Argument | Description |
|---|---|
pdu |
Request PDU |
unit |
Optional, 0–255. Defaults to the configured unit. |
The result is {"response":[...]|null,"status":"success"|"exception"|"no_response","source":"wire"}. It works on a connected client only; otherwise it fails with Connect a client before sending a request. A serial client can broadcast (unit 0) writes only, and gets no_response.
execute
Section titled “execute”Takes the same arguments as request and runs against the endpoint’s own data model, with source local. In a serial configuration, a request for another unit address returns no_response, and a write to unit 0 is handled as a broadcast.
data_get
Section titled “data_get”Returns the whole data model, in the format of model_set.
data_set
Section titled “data_set”| Argument | Description |
|---|---|
table |
coils, discrete_inputs, holding_registers, input_registers |
address |
Start address |
values |
Array of values; 0 or 1 for bit tables |
Returns {"written":N}. It can change input tables too. Outside the model it fails with Address outside configured table, with wrong values Invalid data values, and with a wrong table name Unknown data table.
model_set
Section titled “model_set”Validates the whole model, replaces it in one step and returns {"updated":true}. On failure the existing model stays.
| Member | Format | Rules |
|---|---|---|
coils, discrete_inputs, holding_registers, input_registers |
{"start":N,"values":[...]} |
Start plus count must fit the 16-bit address range (Invalid table range). Bit tables hold 0 or 1. |
files |
{"file number":[words,...]} |
File numbers from 1, up to 10000 records |
fifos |
{"FIFO pointer address":[values,...]} |
Up to 31 values |
objects |
{"object ID":[bytes,...]} |
0, 1 and 2 required; 7–127 reserved; up to 244 bytes each |
server_id |
[bytes,...] |
2–251 bytes; the content of the function 17 response |
exception_status |
0–255 | The function 07 response |
individual_access |
true/false |
Allows MEI 14 individual object reads |
diagnostics, diagnostics_set
Section titled “diagnostics, diagnostics_set”diagnostics returns {"register":N,"listen_only":<bool>,"delimiter":N,"event_log":[...],"counters":{...}}, where counters holds bus_messages, communication_errors, exceptions, server_messages, no_responses, naks, busy, overruns and events.
diagnostics_set sets the diagnostic register (register, 0–65535) and the busy state (busy) for testing. With busy true, the status word of functions 11 and 12 is FFFF. configure resets both.
trace, trace_clear
Section titled “trace, trace_clear”trace returns the last 1000 entries as {"entries":[...]}, in two kinds:
| Kind | Members |
|---|---|
| Request entry | sequence, source (local, wire), unit, request, response, status. For a failed request, status holds the error message, such as Timeout. |
| Raw serial bytes | sequence, source (wire), direction (tx, rx, rx_discarded), transport, bytes, timestamp_us, timestamp_scope (host_io_batch), truncated |
rx_discarded records bytes dropped because they were left over before a request or came from another address. trace_clear returns {"cleared":true}.
project_export, project_import
Section titled “project_export, project_import”project_export returns {"kind":"pdcomm.modbus.project","version":1,"config":{...},"model":{...}}. project_import takes that object as project and returns {"loaded":true}, only while disconnected. Headless runs and scenarios describes the rules.
| Argument | Description |
|---|---|
bytes |
Raw bytes to decode, up to 4096 |
transport |
Optional: tcp, rtu or ascii. Defaults to the configured transport. |
gap_us |
Optional virtual gap between bytes fed to the RTU or ASCII decoder. Default 100 |
Returns {"source":"synthetic","frames":[...],"errors":[...]} without any I/O. The RTU decoder derives t1.5 and t3.5 from the configured baud.
serial_ports
Section titled “serial_ports”Returns {"ports":["/dev/cu.usbserial-...",...]}.
logging_configure, logging_status, logging_flush
Section titled “logging_configure, logging_status, logging_flush”Control the diagnostic log. The config of logging_configure takes enabled, level, file, max_bytes, files and forensics, and the command returns the log status. logging_flush returns {"flushed":true}. See Diagnose failures and report them.
license_install, license_status, host_id
Section titled “license_install, license_status, host_id”SDK libraries only; the headless and MCP simulators don’t have them. A license applies to the whole library, not to one endpoint, so send these to any endpoint.
| Command | Input | Result |
|---|---|---|
license_install |
text: the license text. An empty string removes the installed text. |
{"installed":N}, where N is the number of blocks with a valid signature. Without such a block it fails with the code license_invalid, and the earlier text stays. |
license_status |
None | The license status object (build_date, host_id, sources, licenses, modules, and more) |
host_id |
None | {"host_id":"PDH1-XXXX-XXXX-XXXX-XXXX"}. When the id can’t be read, it fails with the code host_id_unavailable. |
See Install and check a license for details.
shutdown
Section titled “shutdown”Headless simulator only: ends the program without a reply. The SDK and the MCP server don’t have it.