Pi as Modbus RTU master for six PIC slaves on one RS-485 line: replies collide and get garbled
Small retrofit: six old PIC18 boards on a mixing line, each one a Modbus RTU slave (IDs 11 to 16, 38400 8N1) on one RS-485 pair, about 40 m. A Raspberry Pi 4 polls them and reports into Hermetry.
Polling them one at a time from a laptop works. From the Pi, polling all six in a loop, about one in five replies is garbage or a CRC error, worse at the end of the line. Pi side is the onboard UART with a MAX3485, DE/RE driven by a GPIO from my Python code before and after each write.
pymodbus.exceptions.ModbusIOException: Modbus Error: [Input/Output] No Response received from the remote unit/Unable to decode response
CRC check failed: 0x5a3e != 0x1b07
Is this a timing issue with the GPIO direction switching?
Termination and bias resistors? 40 m at 38400 without them can do exactly this.
120 R at both ends. No bias resistors anywhere, I’d assumed the PIC boards had them.
Not my field, but our building contractor had similar issues on a long cable and it turned out to be grounding. Just in case.
Yes, it’s the GPIO timing, and the missing bias doesn’t help. I had almost the same line on six pump controllers.
Driving DE/RE from Python is too slow and too jittery. After the write, your code switches back to receive only when Linux gets round to it. At 38400 a slave can start answering within a couple of milliseconds, so the start of the reply is lost or you’re still driving the line while the slave talks. Fix it in hardware: a USB-RS485 adapter with automatic direction control (FTDI-based ones are good), or let the kernel do it on the UART with rs485 mode. The adapter is the five-minute fix.
Then the line itself:
- Bias: one pull-up on A and one pull-down on B, only once on the bus (560 R to 1 k is typical), so the idle line is defined.
- Common ground wire between all nodes.
- Give pymodbus a sane timeout and a pause between slaves, some old firmware needs a breather.
client = ModbusSerialClient(port="/dev/ttyUSB0", baudrate=38400, parity="N",
stopbits=1, bytesize=8, timeout=0.3, retries=2)
for unit in range(11, 17):
rr = client.read_holding_registers(0, count=4, slave=unit)
time.sleep(0.02)
Then report all six as children of the Pi on v1/gw/telemetry, keyed by name («mixer-11» and so on). Each one gets its own device in Hermetry, and you can put a Device offline rule on their profile.
USB-RS485 with auto direction plus 680 R bias resistors at the Pi end: zero CRC errors over 10,000 polls. Children show up in Hermetry as mixer-11 to mixer-16. Ciaran, thanks.
- Debes estar registrado para responder a este debate.
Únete a la conversación
Inicia sesión para preguntar y responder, dar me gusta a las publicaciones útiles y marcar la respuesta que resolvió tu problema. Cada aportación suma puntos y niveles.

