Skip to content

BMCLI Modbus RTU, ASCII, and TCP

Reference version: BMCLI 1.6.3. Command names, parameters and machine-readable fields retain their source spelling.

The modbus command category is the Modbus Client interface. Gateway projects also support a local TCP/RTU/ASCII Server mirror. The Client supports FC01, FC02, FC03, FC04, FC05, FC06, FC15, FC16, FC20, FC21, and FC24. Addresses are always zero-based protocol addresses; a vendor display reference such as 40001 must be converted according to that vendor’s documentation. All modbus commands produce structured data and require --format=json or --format=jsonl; use JSON for bounded commands and REST responses.

Use runtime Help as the exact option contract:

bmcli help modbus --format=json
bmcli help modbus read --format=json
bmcli help modbus resource read --format=json
bmcli help modbus poll start --format=json

Explicit raw access accepts either a logical --channel imported into the bench or a complete endpoint. A TCP example is:

bmcli modbus read --transport=tcp --host=127.0.0.1 --unit-id=1 --table=holding-registers --address=0 --count=2 --format=json

Serial endpoints require --transport=rtu|ascii, --port, --baud, --data-bits, --parity, --stop-bits, and --unit-id. RTU requires eight data bits. BMCLI validates CRC/LRC, Unit ID, function code, transaction ID, response length, exception responses, timeout, and protocol quantity limits. Read operations may be retried by a supervising application; writes are not automatically retried because a timeout leaves completion ambiguous. modbus status only opens the selected transport. Its transport_open=true, slave_checked=false result does not prove that a Unit ID responds; issue a bounded read of a known address for that check.

BMCLI reads the Linux Foundation EdgeX Modbus Device Profile subset needed for deviceResources and deviceCommands, including value type, read/write access, scale, offset, unit, range, mappings, byte/word order, and Modbus attributes. Unknown YAML/JSON fields are skipped. This is a tolerant read-only importer, not an EdgeX editor. The official EdgeX table spelling is DISCRETES_INPUT; the older DISCRETE_INPUTS spelling is accepted only as a compatibility alias. EdgeX Device Definitions may bind modbus-tcp, modbus-rtu, or modbus-ascii. Command writes accept an exact raw mapping key or a unique, case-sensitive published label; an exact raw key takes precedence. Reads retain the raw key and engineering value and add mapped when a mapping matches. Label ambiguity, an unknown label/type mismatch, and range failures are validated for every operation before opening the transport, so they send zero requests. Mapping keys remain exact raw text through integer encoding, including signed and unsigned 64-bit values beyond IEEE-754’s exact integer range. Limits still apply to the derived engineering value, and byte/word ordering is applied once to the exact raw value.

For modbus-tcp, Address contains only the host (a hostname, IPv4 address, or bare IPv6 address) and Port is a required integer in 1..65535. Address:port and [IPv6]:port forms are rejected at import with a field-level diagnostic instead of being accepted and later combined with another/default port. BMCLI does not auto-correct this invalid EdgeX shape.

bmcli database load --file=boiler-profile.yaml --type=modbus-edgex --name=boiler --format=json
bmcli bench import --file=devices.yaml --source-format=edgex-modbus --device=Boiler-RTU --format=json
bmcli bench import --file=devices.yaml --source-format=edgex-modbus --device=Boiler-RTU --apply --format=json
bmcli modbus resource read --database=boiler --resource=Temperature --channel=boiler-rtu --format=json

bench import is dry-run by default and never guesses a COM/tty path, host, Unit ID, device selection, or collision replacement. Use --replace only with an explicitly reviewed --apply operation. Profiles share the generic typed database lifecycle and unload with database unload.

Writing a resource with EdgeX access W sends exactly one write and does not attempt a readback that the profile declares unavailable. Access RW retains write verification. If a multi-resource command fails after earlier writes, the structured error identifies failed_resource, failed_operation, completed_operations, and whether a write may already have completed; callers must not blindly retry the whole command.

A daemon-owned poll job reads one EdgeX resource or command immediately and then at a requested interval. Each job retains at most 256 complete dynamically sized samples and 1 MiB in memory, and reports sequence, quality, freshness/stale state, errors, gaps, and missed deadlines. The daemon owns one persistent session per physical endpoint; poll jobs, one-shot CLI operations, and Web calls serialize transactions through it. A different configuration for an already-owned physical endpoint fails explicitly. The session reconnects after an I/O/protocol failure. --on-change suppresses unchanged good values but retains errors.

bmcli modbus poll start --job=boiler-temp --interval=500 --database=boiler --resource=Temperature --channel=boiler-rtu --format=json
bmcli modbus value latest --job=boiler-temp --format=json
bmcli modbus value history --job=boiler-temp --count=100 --format=json
bmcli modbus value history --job=boiler-temp --after=120 --count=100 --format=json
bmcli modbus poll stop --job=boiler-temp --format=json

The typed Web API exposes raw operations, EdgeX resources/commands, poll lifecycle, latest/history, and SSE updates under /api/v1/modbus. The bundled engineering page provides endpoint inspection, polling, value, quality and history visualization. History returns oldest_sequence, latest_sequence, next_after, and gaps. SSE uses the sample sequence as its event ID, drains every retained sample in order, resumes from Last-Event-ID, and emits an explicit gap event if that cursor has expired. Runtime OpenAPI is authoritative. Poll state and history are intentionally process-local and are deleted when stopped or when the daemon exits; durable storage belongs to a surrounding application service.


Source of truth: BMCLI 1.6.3 repository documentation. Run bmcli help <category> <action> --format=json for the exact contract of the installed version.