Mini note on robust serial communication with microcontrollers
04 Jun 2021 - tsp
Last update 04 Jun 2021
3 mins
Everyone who has built custom electronics - be it for home automation, in the lab
or for other purposes - knows this problem. You have a rather convenient serial
interface that is easily accessible - just set the Baud rate, start and stop bits
as well as parity bit configuration and youβre good to go - most of the time with
hardware support. You can use USARTs for different communication protocols like
RS232, similar TTL and CMOS level interfaces, RS485, etc.
Usually it works great - until some noise brings your devices out of sync. This
usually happens for sure after some time of operation even on small scale
systems but is happening more often in noisy and large scale environments. So the
question arrises - how to handle that problem?
Well basically when using the UART itβs pretty simple. Since the UART does not
transmit any clock signal one can at least be sure that it will finish to receive
a full word of data - it will just contain garbage. So to bring the clocks back
into sync is rather simple by just introducing some delay and then starting
over with the next start bit. Getting into sync with the higher level logic
is somewhat more problematic since one has to detect the invalid reception of
additional bytes which might be a problem when using TLV encoded packets
as well as determining the point in the data stream that the next message
starts.
TL;DR:
- Checksums are not optional for any non short term experimental setup
- One has to use some kind of start marker
The simple solution - using packets
The simplest solution for the problem lies in the usage of packets. Each
packet should include a start of packet marker. If you do have a byte sequence
that will never occure anywhere else in your data packet you could easily use
this packet. If not you can use it in conjunction with a checksum.
The idea is rather simple:
- Fill every received byte into a ringbuffer that can keep at least one full
data packet
- After each received byte check if you could have received a start of packet
marker and already would have received the full packet. If not continue
receiving
- Calculate the checksum and verify if you have received a valid packet. Rather
common checksums are either CRC-16 , CRC-32 or if itβs only a somewhat unproblematic
solution the usage of a simple
xor
based checksum (a packet is valid if
all bytes exclusively orβed together are 0x00
- this checksum can be calculated
by leaving out the checksum field and xor
all bytes together).
Ensuring the next packet will be most likely received
Since Iβm sometimes using ESP8266
and ESP32
USARTs where microcontrollers
are transmitting some ROM bootloader information during boot or are working in
noisy environments and want to ensure the next packet is most likely received one
can add another procedure: Simply clobbering the line with some data bytes that
are not containing a start of packet marker. This finished previous packets for
sure - hopefully with an invalid checksum. One just has to transmit at least
as many bytes as maximum packet size minus one. The next start of packet will
then be received correctly.
To ensure packets are received one should really transmit an acknowledge after
every data packet. In case the acknowledge is not received during a timeout
the jabber procedure described above should be used and then the packet should
be sent another time after a short timeout that allows the USARTs to finish
the current word reception timeslice for sure.
This article is tagged: