RS-485 and Modbus
RS-485 uses a shared differential bus where multiple devices communicate over two wires. Unlike RS-232’s point-to-point model, RS-485 is half-duplex — only one device transmits at a time while all others listen. This makes it the standard for industrial protocols like Modbus RTU.
All of the operations described in this guide are performed through your MCP client. Ask the assistant to open ports, switch modes, scan for devices, and transact on the bus. The tool call notation below shows exactly what the assistant calls on your behalf.
Switching to RS-485 mode
Section titled “Switching to RS-485 mode”Ports open in RS-232 mode by default. Switch to RS-485 mode before using any RS-485 tools.
// open_serial_port(port="/dev/ttyUSB0", baudrate=9600){ "success": true, "port": "/dev/ttyUSB0", "mode": "rs232", "baudrate": 9600}
// set_port_mode(port="/dev/ttyUSB0", mode="rs485"){ "success": true, "port": "/dev/ttyUSB0", "previous_mode": "rs232", "current_mode": "rs485", "mode_tools": [ "set_rs485_mode", "rs485_transact", "rs485_scan_addresses", "check_rs485_support" ]}Once in RS-485 mode, the RS-232-specific tools (get_modem_lines, pulse_line, etc.) are unavailable. Common tools like read_serial, write_serial, and configure_serial work in both modes.
Configuring RS-485 hardware
Section titled “Configuring RS-485 hardware”If your USB-serial adapter or UART has hardware RS-485 support, set_rs485_mode configures the driver to automatically toggle the TX enable (DE/RE) line during transmission. This eliminates the need for manual RTS toggling.
// set_rs485_mode(// port="/dev/ttyUSB0",// enabled=true,// rts_level_for_tx=true,// rts_level_for_rx=false,// delay_before_tx=0.0,// delay_before_rx=0.0// ){ "success": true, "port": "/dev/ttyUSB0", "rs485_enabled": true, "rts_level_for_tx": true, "rts_level_for_rx": false, "delay_before_tx": 0.0, "delay_before_rx": 0.0, "loopback": false}Parameters explained:
| Parameter | Default | Purpose |
|---|---|---|
rts_level_for_tx | true | RTS state when transmitting (HIGH enables the driver on most RS-485 transceivers) |
rts_level_for_rx | false | RTS state when receiving (LOW disables the driver, allows listening) |
delay_before_tx | 0.0 | Seconds to wait after asserting TX enable before sending data |
delay_before_rx | 0.0 | Seconds to wait after transmission before switching to receive |
loopback | false | Echo transmitted data back (for testing) |
Checking hardware support
Section titled “Checking hardware support”Not all USB-serial adapters support hardware RS-485. Use check_rs485_support to find out what your hardware can do before configuring.
// check_rs485_support(port="/dev/ttyUSB0"){ "success": true, "port": "/dev/ttyUSB0", "driver": "ftdi_sio", "chip": "FT232R", "hardware_rs485": true, "software_fallback": true, "kernel_rs485_ioctl": true, "notes": [ "FTDI chips have hardware RS-485 auto-direction", "Kernel TIOCSRS485 ioctl supported" ], "recommendation": "Use set_rs485_mode() for automatic DE/RE control"}This tool queries the kernel driver and USB device information to determine:
- Which driver is loaded (ftdi_sio, cp210x, ch341, pl2303)
- Whether the chip supports hardware DE/RE toggling
- Whether the kernel TIOCSRS485 ioctl is available
- A recommendation for how to proceed
Hardware support by adapter
Section titled “Hardware support by adapter”| Chip | Hardware RS-485 | Notes |
|---|---|---|
| FTDI (FT232R, FT2232, etc.) | Yes | Automatic direction control via driver |
| CP2105 / CP2108 | Yes | Hardware support in these models |
| CP2102 | No | Software RTS control required |
| CH340 / CH341 | No | Timing may be unreliable at high baud rates |
| PL2303 | No | Software RTS control required |
| Native UART (ttyS, ttyAMA) | Depends | Check if RS-485 transceiver is connected |
Half-duplex transactions
Section titled “Half-duplex transactions”The rs485_transact tool handles the complete send-then-receive cycle that RS-485 communication requires. It manages TX/RX turnaround timing automatically, whether your hardware supports it natively or needs software RTS toggling.
// rs485_transact(// port="/dev/ttyUSB0",// data="\x01\x03\x00\x00\x00\x01\x84\x0A",// response_timeout=1.0,// response_length=7// ){ "success": true, "port": "/dev/ttyUSB0", "bytes_sent": 8, "data_sent": "\u0001\u0003\u0000\u0000\u0000\u0001\u0084\n", "response": "\u0001\u0003\u0002\u0000\u0005\u0085\u0085", "response_bytes": 7, "response_hex": "01030200058585", "hardware_rs485": true}When hardware RS-485 is not configured, rs485_transact falls back to manual RTS control:
- Asserts RTS HIGH (enable driver / TX mode)
- Sends the data and waits for transmission to complete
- Drops RTS LOW (disable driver / RX mode)
- Waits for the turnaround delay
- Reads the response
The turnaround_delay parameter (default 5ms) controls the pause between sending and listening. Increase it for slow devices or long cable runs.
Scanning for devices
Section titled “Scanning for devices”rs485_scan_addresses probes a range of addresses and reports which ones respond. This is the fastest way to discover what is connected to a bus.
// rs485_scan_addresses(// port="/dev/ttyUSB0",// start_address=1,// end_address=30,// probe_template="{addr:02x}03000001",// response_timeout=0.1// ){ "success": true, "port": "/dev/ttyUSB0", "addresses_scanned": 30, "devices_found": 3, "responding_addresses": [ {"address": 1, "response_length": 7, "response_hex": "01030200058585"}, {"address": 5, "response_length": 7, "response_hex": "05030200124478"}, {"address": 16, "response_length": 7, "response_hex": "10030200009930"} ], "hardware_rs485": true}The probe_template uses Python’s string formatting. The placeholder {addr} is replaced with the current address being scanned. The default template {addr:02x}03000001 formats the address as a two-character hex string followed by a Modbus “read holding register” frame.
Practical Modbus RTU workflow
Section titled “Practical Modbus RTU workflow”This example walks through a complete Modbus RTU session: discovering devices, then reading a holding register.
-
Open the port and switch to RS-485 mode
Modbus RTU typically runs at 9600 baud with 8N1 framing.
// open_serial_port(port="/dev/ttyUSB0", baudrate=9600)// set_port_mode(port="/dev/ttyUSB0", mode="rs485") -
Configure hardware RS-485 (if supported)
If your adapter supports hardware direction control, enable it. If not,
rs485_transacthandles RTS toggling in software.// set_rs485_mode(port="/dev/ttyUSB0", enabled=true, rts_level_for_tx=true, rts_level_for_rx=false) -
Scan the bus for responding devices
Scan addresses 1 through 247 (the Modbus valid range). Use a short timeout per address to keep the scan fast.
// rs485_scan_addresses(// port="/dev/ttyUSB0",// start_address=1,// end_address=247,// response_timeout=0.1// ){"success": true,"addresses_scanned": 247,"devices_found": 2,"responding_addresses": [{"address": 1, "response_length": 7, "response_hex": "01030200058585"},{"address": 10, "response_length": 7, "response_hex": "0a030200003071"}]} -
Read a holding register from a device
Construct a Modbus RTU “Read Holding Registers” frame for address 1, register 0, quantity 1. The frame format is:
[address][function 0x03][start_hi][start_lo][qty_hi][qty_lo][CRC_lo][CRC_hi].// rs485_transact(// port="/dev/ttyUSB0",// data="\x01\x03\x00\x00\x00\x01\x84\x0A",// response_length=7,// encoding="latin-1"// ){"success": true,"bytes_sent": 8,"response_hex": "01030200058585","response_bytes": 7,"hardware_rs485": true}The response
01 03 02 00 05 85 85decodes as: address 1, function 3, 2 bytes of data, value 0x0005 (decimal 5), followed by a CRC. -
Close the port when finished
// close_serial_port(port="/dev/ttyUSB0")
Understanding probe_template format
Section titled “Understanding probe_template format”The probe_template parameter in rs485_scan_addresses is a Python format string. It supports the {addr} placeholder with standard format specifiers:
| Template | Address 16 becomes | Use case |
|---|---|---|
{addr:02x}03000001 | 1003000001 | Modbus-style hex address prefix |
{addr:d} | 16 | Decimal address prefix |
{addr:c} | (ASCII char 16) | Single-byte binary address |
If the template does not contain {addr, the raw address byte is prepended automatically. This means a template of 03000001 with address 16 sends byte 0x10 followed by 03000001.
Software vs hardware RS-485
Section titled “Software vs hardware RS-485”When check_rs485_support reports that your adapter lacks hardware RS-485, mcserial uses software emulation:
- Before transmitting, RTS is asserted HIGH to enable the transceiver’s driver
- After transmission completes, RTS is dropped LOW to switch back to receive mode
- A configurable turnaround delay prevents missed response bytes
Software emulation works reliably at baud rates up to about 115200. At higher speeds, the timing of the RTS toggle may not be precise enough, causing the first byte of a response to be corrupted or missed. If you need high-speed RS-485, use an adapter with hardware support (FTDI-based adapters are widely available and well-supported).