Page 1 of 1

Understanding UART receive buffering

Posted: Mon Nov 14, 2022 8:45 pm
by wolfbert
Hi,

I'm receiving a 262 282 byte data packet (sent in one piece) on an ESP32 (2400 baud, 8N1) like so:

Code: Select all

void setup()
{
    Serial.begin(115200);

    Serial2.setRxBufferSize(512);
    Serial2.begin(2400);
}

void loop()
{
    uint8_t buffer[512];
    size_t bytesRead;

    while(!Serial2.available());
    bytesRead = Serial2.read(buffer, sizeof(buffer));

    Serial.printf("\nBytes received: %u\n", bytesRead);
}
Data is received in chunks of 120, 120 and 42 bytes.

I understand that the ESP32 has a hardware receive buffer and that an interrupt is triggered when 120 bytes have been received (or after a timeout). What I don't understand is the purpose of the internal RxRingBuffer which is initialized to 256 bytes (or 512 above). Is there a way to actually use this buffer?

Re: Understanding UART receive buffering

Posted: Tue Nov 15, 2022 2:28 am
by ESP_Sprite
The logic is that buffering needs to happen when you receive data at a fast speed (way faster than 2400 baud) and your user code is doing other things at a particular point in time (e.g. servicing WiFi); the ringbuffer acts as an extra buffer to temporarily hold the data until your CPU can process it. Both the FIFO and the CPU are not intended to 'depacketize' your incoming bytes: if you need to detect packet boundaries, you'd need to do that in user code as the driver sees the incoming UART data as simply a stream of bytes.

Re: Understanding UART receive buffering

Posted: Tue Nov 15, 2022 9:52 am
by wolfbert
Thank you for your explanation. It made me rethink my assumptions (I was used to AVR controllers), and I believe I've figured it out.

The ring buffer is of course used. But, while a transmission is ongoing, and with default settings, the first non-zero result of available() will be 120 - the first time the HW buffer is copied over. Same as on AVR actually, just that the HW buffer there is just a single byte, so data becomes immediately available in the ring buffer.

What fooled me was that I believed read() was blocking (so why stop at 120?), when in fact it was not.

By the way, to assemble the whole message (at least in my example) in Arduino, one could use Stream::readBytes(). It has its own timeout and calls HardwareSerial::read() repeatedly until the message is in (assuming one knows what to expect).

Case closed, thanks for your support.