modbus — Modbus RTU slave protocol
This module provides a pure-Python implementation of a Modbus RTU slave that
communicates over a UART. It maintains an internal holding-register array and
responds to standard Modbus function codes 0x03 (Read Holding Registers),
0x06 (Write Single Register), and 0x10 (Write Multiple Registers).
CRC-16 (Modbus polynomial) is computed using a lookup table.
For example:
from machine import UART
from modbus import ModbusRTU
uart = UART(1, 115200, timeout=10, timeout_char=10)
modbus = ModbusRTU(uart, slave_id=0x01, register_num=30)
while True:
if modbus.any():
modbus.handle(debug=False)
class ModbusRTU
A Modbus RTU slave that reads requests from a UART, updates its internal register array, and writes the corresponding response back to the UART.
- class modbus.ModbusRTU(uart: machine.UART, slave_id: int = 0x01, register_num: int = 30)
Construct a
ModbusRTUslave.uart is a UART instance (e.g.
machine.UARTorpyb.UART) used to send and receive Modbus frames. The UART must be configured with appropriate baud rate, parity, and timeouts before being passed in.slave_id is the Modbus slave address (1-247) this instance will respond to. Frames addressed to other slave IDs are ignored.
register_num is the number of 16-bit holding registers backing this slave. Registers are stored in the
REGISTERlist and initialized to zero.
- SLAVE_ID: int
The Modbus slave address this instance will respond to. Set from the slave_id constructor argument.
- uart: machine.UART
The UART instance passed to the constructor, used for all I/O.
- register_num: int
The number of 16-bit holding registers, set from the register_num constructor argument.
- REGISTER: list[int]
List of length
register_numholding the current 16-bit register values. Reads and writes performed via incoming Modbus requests update this list. Application code may read from or write to this list directly to exchange data with the Modbus master.
- CRC16_TABLE: list[int]
Precomputed 256-entry lookup table for the Modbus CRC-16 polynomial, used by
crc16().
- any() int
Return the number of bytes currently available in the underlying UART’s receive buffer (delegates to
uart.any()). Use this to check for an incoming request before callinghandle().
- crc16(data: bytes | bytearray) bytes
Compute the Modbus CRC-16 of data using
CRC16_TABLEand return it as a 2-byte little-endianbytesobject suitable for appending to a Modbus frame.data is a
bytes/bytearray(or any iterable of integers) containing the bytes to checksum.
- handle(debug: bool = False) None
Read a single Modbus request from the UART, update the internal register array as required, and write the corresponding response back to the UART.
Supported function codes:
0x03Read Holding Registers — responds with the requested range of register values.0x06Write Single Register — writes a single register and echoes the address and value.0x10Write Multiple Registers — writes a contiguous range of registers and responds with the starting address and count.
Modbus exception responses are returned for:
Illegal Function (
0x01) — unsupported function code.Illegal Data Address (
0x02) — register index out of range.Illegal Data Value (
0x03) — byte count does not match the declared quantity of registers.
Frames whose CRC does not match or whose slave address does not match
SLAVE_IDare silently dropped.debug if
True, prints the raw request, parsed function code, generated response, and any error details to the REPL. Defaults toFalse.