Modbus-Impl (RTU) for rp-usb-serial & rp-pio-serial
A small no_std Modbus RTU helper library designed to run on embedded Rust targets (e.g. RP2040/RP2350) and work with rp-usb-serial USB CDC link & rp-pio-serial PIO-based software serial for RP2040 & RP2350 using arbitrary GPIO pins. It currently focuses on Modbus function codes 01, 02, 03, and 04 (read operations) and builds valid Modbus RTU frames including CRC16.
What it does
At runtime the library processes fixed-length Modbus requests carried over a byte-stream transport (USB CDC). For each incoming request frame it:
- Validates CRC16 (Modbus polynomial 0xA001, init 0xFFFF)
- Parses the request fields:
- Unit ID
- Function code (0x01/0x02/0x03/0x04/0x05/0x06)
- Start address
- Quantity
- Checks address range using
is_valid(addr) - Builds one of:
- A normal response frame for the requested function, or
- An exception response frame:
- Function | 0x80
- Exception code (ILLEGAL_FUNCTION / ILLEGAL_DATA_ADDRESS / ILLEGAL_DATA_VALUE)
- CRC16
Supported Modbus Functions
- 0x01 Read Coils
- 0x02 Read Discrete Inputs
- 0x03 Read Holding Registers (16-bit registers, big-endian in the payload)
- 0x04 Read Input Registers (16-bit registers, big-endian in the payload)
- 0x05 Write Single Coil
- 0x06 Write Single Register
Coils/inputs are packed into bytes using Modbus rules (LSB-first bit packing).
Data Model
The library defines interfaces so you can plug in your own memory map:
RegisterRead
Used for 16-bit register based functions (FC03/FC04):
get(addr: u16) -> u16is_valid(addr: u16) -> bool
RegisterWrite
Used for 16-bit register based functions (FC06)
- 'set_reg(addr: u16, val: u16)`
BitRead
Used for bit based functions (FC01/FC02):
get(addr: u16) -> boolis_valid(addr: u16) -> bool
BitWrite
Used for 16 bit based functions (FC05)
set_bit(addr: u16, val: bool)
It also provides basic storage types that implement these traits:
Hreg<N>for Holding Registers (FC03/FC06)Ireg<N>for Input Registers (FC04)Coil<N>for Coils (FC01/FC05)Ists<N>for Discrete Inputs (FC02)
Implementation Overview
Key components:
-
crc16_modbus(data: &[u8]) -> u16
Implements Modbus RTU CRC16. -
Frame parsing
- Requests are assumed to be 8 bytes long (standard RTU frame for function 01/02/03/04 read requests, 05/06 write requests).
parse_pdu()dispatch supports multiple function codes.
-
Response builders
build_resp_bit_reads()builds FC01/FC02 responses.build_resp_regs()builds FC03/FC04 responses.build_exception_resp()builds exception responses.
-
ModbusCtx::pharse_pdu()The main entry that takes a request frame and outputs either a response or an exception.
Typical Usage Flow
In your main loop you typically:
- Receive bytes from
rp-usb-serialinto an 8-byte buffer. - Call:
ctx.pharse_pdu::<MAX_QTY>(&req8, &mut resp_buf, &mut exc_buf)
- Send the resulting frame back with:
RpUsbConsole::write(&resp_or_exc[..len])
Example for use
rp2350-pio-soft-serial-modbus-example
Notes / Limitations
- This library is RTU-focused but transport-agnostic: it assumes requests arrive as a byte stream and are accumulated into exact 8-byte frames.
- Only read functions are implemented (01/02/03/04).
- Write functions now include 05/06
- Bit packing follows Modbus LSB-first conventions.