Question about receiving UART serial communications and framing #13354
-
Hi, I am using a ESP32 board to receive data over UART. The sending device sends 23 Bytes every 100 ms including a 4 byte start header (F4,F3,F2,F1) and 4 byte end of message (F8,F7,F6,F5). After much trial and and error I discovered I needed to initialize my UART with the timeout keyword
I realize this is a timing issue, so I was wondering if there was a way to ensure the read waits for the beginning of a full message since the TX line is mostly quiet. It is only sending data about 25% of the time. As a work around, I am now reading 46 bytes and parsing the first full message. This might be the best way to handle it, but I wanted to ask the community. |
Beta Was this translation helpful? Give feedback.
Replies: 8 comments 9 replies
-
Great when this works for your use-case.
In my experience reading single characters (with timeout=0, checking
|
Beta Was this translation helpful? Give feedback.
-
There is as well timeout_char which defines the timeout after the first byte has been received. The default value is 0, meaning that it will not wait forever, but usually only for 1 character time. |
Beta Was this translation helpful? Give feedback.
-
The approach of @karfas is the standard way and will work. However in this instance there may be an easier way. Sending 23 chars at 9600 baud takes about 23ms, so there is a gap of about 77ms between frames. If you set while uart.read(1) is not None:
pass
# time is ~73ms from start of last frame. About 27ms later, next frame arrives.
while True:
frame = uart.read(23)
# process frame This does assume that the sender's timings are consistent and accurate. |
Beta Was this translation helpful? Give feedback.
-
I have a feeling that all those timeout based approaches are fairly unstable. Why not read byte-wise, put the byte data into a 23size ringbuffer and when receiving the ›end of frame‹ byte (the ›F5‹), check the remainder of the ringbuffer for validity. Done. |
Beta Was this translation helpful? Give feedback.
-
I suggest using the pin interrupt mechanism as an alternative solution. Here I am trying to simulate the UART communication you are trying to solve. You will need to use an extra pin, i.e. 4 wires (Tx, Rx, Gnd, Trig). ESP32-S2 is the producer and ESP32 is the consumer. ESP32-S2 script: # esp32s2 - producer
import random
from machine import UART, Pin
from time import sleep, sleep_ms
uart = UART(1, baudrate=9600) # buf=256 tx=10, rx=9
trig = Pin(12, Pin.OUT, value=0)
hdr = b'\xf4\xf3\xf2\xf1'
tal = b'\xf8\xf7\xf6\xf5'
dat = b''
while True:
#wait = random.randint(1,10); sleep(wait)
trig(0)
sleep_ms(100)
d = b''.join([random.randint(0,255).to_bytes(1, 'big') for i in range(15)])
dat = hdr + d + tal
w = uart.write(dat)
print('msg:', dat)
trig(1) # trigger data ready ESP32 script: # esp32 - consumer
from machine import UART, Pin, disable_irq, enable_irq
from time import sleep
uart = UART(1, baudrate=9600, tx=13, rx=14) # buf=256
trig = Pin(12, Pin.IN, Pin.PULL_DOWN)
hdr = b'\xf4\xf3\xf2\xf1'
tal = b'\xf8\xf7\xf6\xf5'
def get(pin):
state = disable_irq()
m=uart.read()
if m:
# print(len(m), m[4:-4])
print(len(m), m)
enable_irq(state)
irq = trig.irq(trigger=Pin.IRQ_RISING, handler=get) Here we use pin12 as the data ready pin. When a packet has been written, the ESP32-S2 signals the ESP32 by pulling pin12 high and then low, and communication is ready for the next packet. I've had good results: ESP32-S2 test output:
ESP32 test output:
Hope this helps. |
Beta Was this translation helpful? Give feedback.
-
The comments on my solution are valid. It's a "quick and dirty" approach in the spirit of the code originally posted. I think the latest iteration will work, but only if communications, once established, remain working consistently. A production grade solution would be able to re-synchronise if communications were disrupted. I would use |
Beta Was this translation helpful? Give feedback.
-
Thanks everyone for the great discussion here. There isn't much regarding UART on the forums so I hope this helps someone in the future! |
Beta Was this translation helpful? Give feedback.
-
I just want to report a buffer issue I had using ESP32 flashed with micropython 1.23. The UART was not able to transmit more than 2 characters, whatever I tried to increase tx and rx buffers, it did'nt work. Even more the bufferparameter was not accepted. When I reflashed mp v1.22 all went well regarding tx and receive buffers. This was the setup I used for uart 1 (and I use now with mp v1.22): |
Beta Was this translation helpful? Give feedback.
There was a problem with my suggestion, in that a timeout was occurring in the main loop. Try this:
This should give better timing margins than setting the timeout to 70ms.