#tcp-transport #rtu #modbus-tcp #abstraction #pdu #tcp-client

rustmod-datalink

Async Modbus transport abstractions and implementations for rust-mod

1 unstable release

new 0.1.0 Feb 16, 2026

#2 in #tcp-transport


Used in 2 crates

MIT/Apache

160KB
4K SLoC

rust-mod

A modern Rust Modbus workspace for building automation and industrial integrations.

rust-mod is organized as a crate family:

  • rustmod-core: no_std-compatible protocol codec layer (PDU + TCP/RTU framing)
  • rustmod-datalink: async transport abstraction + TCP transport implementation
  • rustmod-client: high-level resilient async client API
  • rustmod-tools: CLI tools built on top of the client

Current Status

Implemented and validated:

  • Core Modbus function codes: FC01, FC02, FC03, FC04, FC05, FC06, FC15, FC16, FC22, FC23
  • Custom function-code pass-through support for vendor extensions
  • FC17 (Report Server ID) support via client API + simulator service
  • FC43/MEI 0x0E (Read Device Identification) typed client API + simulator support
  • Exception response decoding (including unknown exception codes)
  • TCP MBAP framing and RTU CRC/frame codec in rustmod-core
  • Async DataLink::exchange contract in rustmod-datalink
  • Production-usable TCP transport (ModbusTcpTransport)
  • Optional RTU serial transport (ModbusRtuTransport, rtu feature)
  • Optional native RTU serial server (ModbusRtuServer, rtu feature)
  • RTU-over-TCP server support (ModbusRtuOverTcpServer)
  • In-memory simulator service and point-model helpers for virtual device hosting
  • Simulator fixture integration tests for both client and datalink paths
  • Correlated tracing hooks and feature-gated metrics counters
  • Resilient client with timeout/retry/throttle controls
  • Blocking sync TCP facade (SyncModbusTcpClient) for non-async applications
  • Safe retry defaults (RetryPolicy::ReadOnly) to avoid duplicate writes after ambiguous failures
  • CLI binaries:
    • readholding
    • readinput
    • readcoils
    • writeholding
    • writecoil
    • scandevices

Workspace Validation

All of the following pass:

  • cargo check --workspace
  • cargo test --workspace
  • cargo check --workspace --all-features
  • cargo test --workspace --all-features
  • cargo clippy --workspace --all-features --all-targets -- -D warnings
  • cargo check -p rustmod-core --no-default-features
  • cargo test -p rustmod-core --no-default-features
  • cargo clippy --workspace --all-targets -- -D warnings

Quick Start

Build everything:

cargo build --workspace

Read holding registers from a TCP device:

cargo run -p rustmod-tools --bin readholding -- \
  --host 127.0.0.1 --port 502 --unit-id 1 --start 107 --quantity 3

Write holding register(s):

# single register (FC06)
cargo run -p rustmod-tools --bin writeholding -- \
  --host 127.0.0.1 --port 502 --unit-id 1 --start 10 --values 42

# multiple registers (FC16)
cargo run -p rustmod-tools --bin writeholding -- \
  --host 127.0.0.1 --port 502 --unit-id 1 --start 10 --values 42,43,44

Scan unit IDs:

cargo run -p rustmod-tools --bin scandevices -- \
  --host 127.0.0.1 --port 502 --unit-start 1 --unit-end 20 --timeout 300

Library Example

use rustmod_client::ModbusClient;
use rustmod_datalink::ModbusTcpTransport;

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let link = ModbusTcpTransport::connect("127.0.0.1:502").await?;
    let client = ModbusClient::new(link);

    let regs = client.read_holding_registers(1, 0x006B, 3).await?;
    println!("registers: {regs:?}");
    Ok(())
}

Notes

  • rustmod-core is no_std-compatible with std + alloc enabled by default.
  • rustmod-datalink supports TCP, optional RTU client transport, optional RTU serial server, and RTU-over-TCP server.
  • The simulator components in rustmod-datalink::sim are designed for BACnet+Modbus app simulation workflows and virtual device hosting.
  • Unknown Modbus exception codes are preserved as ExceptionCode::Unknown(u8).
  • ModbusClient::custom_request provides a compatibility path for vendor/private function codes.
  • ModbusClient::report_server_id (FC17) is available for device identity/status polling.
  • ModbusClient::read_device_identification supports typed FC43/MEI 0x0E reads.
  • ModbusClient includes typed helpers for FC22 (mask_write_register) and FC23 (read_write_multiple_registers).
  • SyncModbusTcpClient provides a blocking API over the same high-level operations.

Parity Snapshot

Relative to tokio-modbus (0.17.x), rust-mod now has comparable support for:

  • Async TCP client operations
  • Blocking TCP client facade
  • Optional async RTU client transport
  • TCP server path suitable for simulator/virtual-device hosting
  • Optional RTU serial server path
  • RTU-over-TCP server path
  • Custom function-code interoperability
  • FC17 Report Server ID access
  • FC22/FC23 typed operations
  • Typed FC43/MEI 0x0E Read Device Identification client support

Still intentionally not at parity:

  • Blocking sync RTU facade equivalent to tokio-modbus rtu-sync
  • Typed wrappers for some less-common/public function families (can still be used via custom_request)

Dependencies

~7–20MB
~148K SLoC