Reading a 9-bit multidrop bus on a Pi gateway: can the UART even do that?
Food and beverage plant, old filling line. Four weighing indicators talk on a proprietary RS-485 multidrop bus from the 90s: 9600 baud, 9 data bits, where the 9th bit marks an address byte. The original PLC that polled them is dying and I want a Pi 4 with an RS-485 HAT to take over the polling and report each indicator to Hermetry as its own device.
The Hermetry part I think I understand: the Pi is a gateway and sends v1/gw/telemetry with the four indicators as children. What I don’t know is how to get 9-bit frames out of a Linux UART. pyserial only offers 5 to 8 data bits. Is this possible on a Pi at all, or do I need different hardware?
What’s the 9th bit for exactly? I’ve only ever seen 8N1.
How often do you need readings? If it’s slow, a lot of hacks become acceptable.
Once a second per indicator is plenty. David, the 9th bit tells every node “this byte is an address, wake up and check if it’s yours”. Data bytes have it at 0.
Lots of STM32s do 9-bit natively. A Blue Pill as a bridge that translates to 8-bit serial for the Pi would be the boring, reliable answer.
I’m new to the hardware side but I did serial protocol work in software for years, and you can fake this with parity. The 9th bit is sent where the parity bit would be. Linux supports “stick” parity through the CMSPAR flag: with PARENB|CMSPAR|PARODD the parity bit is always 1 (mark), without PARODD it’s always 0 (space).
So: set mark parity, write the address byte, wait for it to leave (tcdrain), switch to space parity, write the data bytes. In Python:
import termios, serial
CMSPAR = 0o10000000000
s = serial.Serial('/dev/ttyAMA0', 9600)
def set_bit9(on):
a = termios.tcgetattr(s.fd)
a[2] |= termios.PARENB | CMSPAR
a[2] = a[2] | termios.PARODD if on else a[2] & ~termios.PARODD
termios.tcsetattr(s.fd, termios.TCSADRAIN, a)
set_bit9(True); s.write(bytes([0x03])); s.flush()
set_bit9(False); s.write(bytes([0x10, 0x00]))
Receiving is the uglier half: turn on PARMRK and INPCK and a byte with the “wrong” parity arrives prefixed with 0xFF 0x00, which tells you its 9th bit. At 9600 the timing between the address and the data bytes gets a small gap from switching, most old devices don’t care. Use the PL011 UART (disable-bt), not the mini UART, the mini UART has no parity.
Then each indicator goes out as a child, something like {“scale-1”: {“values”: {“kg”: 12.45}}} on v1/gw/telemetry.
Kevin, it works. Address byte with mark, data with space, PARMRK on the receive side. Indicators 1, 2 and 4 answered first try, number 3 wanted a 2 ms gap after the address, which I added. The children appeared in Hermetry on the first v1/gw/telemetry. Keeping Jason’s STM32 idea as plan B if the line people complain.
- You must be logged in to reply to this topic.
Join the conversation
Sign in to ask and answer, like helpful posts and mark the reply that solved your problem. Every contribution earns points and levels.

