Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

UART Communication Delays and Data Loss #10420

Closed
1 task done
mightChamp opened this issue Oct 4, 2024 · 31 comments
Closed
1 task done

UART Communication Delays and Data Loss #10420

mightChamp opened this issue Oct 4, 2024 · 31 comments
Assignees
Labels
Peripheral: UART Priority: High 🗻 Issues with high priority which needs to be solved first.
Milestone

Comments

@mightChamp
Copy link

Board

ESP32 Wroom 32E 4MB Flash

Device Description

We are making an Uart Data logger, an external device send uart data, which stored in SD card.

Hardware Configuration

GPIO13- Uart TX, and GPIO14- Uart RX
Uart setup:
Baud Rate: 115200
Flow Control: Disabled
Buffer Size: 4096

Version

latest development Release Candidate (RC-X)

IDE Name

Arduino IDE

Operating System

Windows 11

Flash frequency

80Mhz

PSRAM enabled

no

Upload speed

921600

Description

We are encountering an issue with UART communication on ESP32 where data reception is delayed, and sometimes data is lost. This issue occurs intermittently and was not present in version v1.0.6.

Sketch

void setup() {
    Serial.begin(115200);  // UART initialized at 115200 baud rate
}

void loop() {
    if (Serial.available()) {
        String data = Serial.readString();
        Serial.println(data);
    }
}

Debug Message

No Debug Message

Other Steps to Reproduce

Set up UART communication between ESP32 and another device.
Send continuous data streams over UART.
Observe occasional delays and missed data.

I have checked existing issues, online documentation and the Troubleshooting Guide

  • I confirm I have checked existing issues, online documentation and Troubleshooting guide.
@mightChamp mightChamp added the Status: Awaiting triage Issue is waiting for triage label Oct 4, 2024
@lbernstone
Copy link
Contributor

The HardwareSerial library has been modified to have more capabilities for handling intermittent and burst data flows. Take a look at the OnReceive and RxTimeout examples to see how to handle high activity and sporadic flows.
@SuGlider This could definitely use some doco

@SuGlider
Copy link
Collaborator

SuGlider commented Oct 4, 2024

@mightChamp - The issue is related only to Core 3.1.0-RC1? Does it also happen with Core 3.0.5?

@SuGlider SuGlider added Peripheral: UART Priority: High 🗻 Issues with high priority which needs to be solved first. breaking-change 💥 A change that changes the API or breaks backward compatibility for users. and removed Status: Awaiting triage Issue is waiting for triage labels Oct 5, 2024
@SuGlider SuGlider moved this from Todo to Under investigation in Arduino ESP32 Core Project Roadmap Oct 5, 2024
@SuGlider SuGlider added this to the 3.1.0 milestone Oct 5, 2024
@SuGlider
Copy link
Collaborator

SuGlider commented Oct 5, 2024

This sounds related to #10397
After testing the sketch from the issue, it seems clear that IDF 4.x and 5.x UART Driver used by ESP32 Arduino HardwareSerial is not efficient and causes a huge performance issue.

The evidence is that whenever Serial0.begin() is executed, there is a significative time slot reduction to the main Arduino Task, affecting the time that loop() has to process data.

In Arduino Core 1.0.6, the UART driver was local one, faster and way more efficient.
The solution for this issue is to refactor HardwareSerial and create its own Arduino UART Driver, the same way it is done for SPI.
@me-no-dev - FYI.

@SuGlider
Copy link
Collaborator

SuGlider commented Oct 7, 2024

@mightChamp - Could you please check if PR #10428 solves this issue too?
This PR improves the loop() execution efficiency and may give it more "CPU cycles" in order to solve the data loss issue.

@mightChamp
Copy link
Author

We have tested the fix from PR #10428, but unfortunately, we are still facing the same issue.

This issue is currently blocking our development progress, and we would greatly appreciate an estimate of how long it might take to investigate or resolve this. If there are any further tests or adjustments we could try, we are ready to assist.

@SuGlider
Copy link
Collaborator

SuGlider commented Oct 7, 2024

@mightChamp - based on what @lbernstone wrote, there are a few new methods to deal with UART that were not available in 1.0.6.

@SuGlider
Copy link
Collaborator

SuGlider commented Oct 7, 2024

I'll build a Python script + the sketch example that is here listed to find out more about the issue.
If I find out something else, I'll write about it here.

@mightChamp
Copy link
Author

@mightChamp - based on what @lbernstone wrote, there are a few new methods to deal with UART that were not available in 1.0.6.

Despite testing all examples, we encountered significant performance degradation, and also faces missing data bytes and delay issue.

@SuGlider
Copy link
Collaborator

SuGlider commented Oct 9, 2024

Despite testing all examples, we encountered significant performance degradation, and also faces missing data bytes and delay issue.

Please elaborate more about "significant performance degradation", "missing data bytes" and "delay issue".
If you could post an example (sketch + PC side script + details) for each issue, it would accelerate the resolution and also help us to validate a possible fix.

You can count on me for fixing it ASAP.

Thanks!

@mightChamp
Copy link
Author

mightChamp commented Oct 10, 2024

@SuGlider

We are experiencing a delay issue that creates the illusion of missing bytes due to timeout. Also PR #10429, reduces this delay, but still face some delay, as compared to V1.0.6.

Our project is quite large, using 1.6 MB of Flash and 35% of RAM, with two additional tasks running. The code works perfectly in v1.0.6, but we are looking to upgrade to the latest SDK. Due to the size and complexity of the code, providing a simplified version for testing is challenging.

@SuGlider
Copy link
Collaborator

@mightChamp - Could you please test it using this sketch:

void setup() {
    Serial.begin(115200);  // UART initialized at 115200 baud rate
    // settings used by Core 1.0.6:
    Serial.setRxFIFOFull(1); // it will push every single byte from FIFO directly to the Arduino RX Buffer
    Serial.setRxTimeout(2); // RX Timeout is set to 2 symbols (at 115200 bps, it is about 18us)
}

void loop() {
    if (Serial.available()) {
        String data = Serial.readString();
        Serial.println(data);
    }
}

@rftafas rftafas modified the milestones: 3.1.0, 3.1.1 Jan 6, 2025
@rftafas rftafas modified the milestones: 3.1.1, 3.2.0 Jan 14, 2025
@EricHarbers
Copy link

EricHarbers commented Mar 6, 2025

I had the same issue.
I have solved it, by setting the following functions:
Serial.setRxFIFOFull(60);
Serial.setRxTimeout(60);

This works well on BSP version 3.0.3. The println function will then write the value of 60. And the connection will not loose data anymore.

For the latest BSP version now (V 3.1.3), the value of 60 wouldn't work anymore. Then the println function will also return random numbers, (and the connection will loose data).

Please can anyone repair this?

@EricHarbers
Copy link

Update:
I've checked some more.
It seems that the value for Serial.setRxFIFOFull could be 120. That is valid.
But for Serial.setRxTimeout, the maximum value is 101. If you set the timeout to more than that, data will be lost.

@EricHarbers
Copy link

The above works for esp32 V3.0.3.
For V3.1.3, it will not work at all!!

@Parsaabasi Parsaabasi removed this from the 3.2.0 milestone Mar 10, 2025
@Parsaabasi Parsaabasi added this to the 3.3.0 milestone Mar 10, 2025
@Jason2866
Copy link
Collaborator

@EricHarbers Could you test with core 3.2.0-rc2?

@Jason2866 Jason2866 removed the breaking-change 💥 A change that changes the API or breaks backward compatibility for users. label Mar 13, 2025
@Jason2866
Copy link
Collaborator

@SuGlider Can you have a look, if the issue is still in 3.2.0-rc2 it would be good if it could be fixed before release of 3.2.0

@EricHarbers
Copy link

@Jason2866 I've checked with core 3.2.0-RC2, but the the problem is still there. The version again does not read stable block sizes.
Let me know if I can check more for you.

@EricHarbers
Copy link

@Jason2866 A problem will also be, that you have to always call the onReceiveError() function to install the error callback function. Even if the function is empty. Not calling the onReceiveError() function lets the application crash.

@Jason2866
Copy link
Collaborator

@EricHarbers Thx for the testing. SuGlider is the expert and will have for sure a look. I am only helping a bit to track the issues.

@SuGlider
Copy link
Collaborator

I have tested it. It works fine with Arduino Core 3.1.3 or using master branch.
readString() is a Stream method, therefore, it has timeout of 1 second. It can be seen in the output time stamp
It will read all data stream and only return after 1 second after not reading anything.

There is no data loss.
I tested it sending data through the Serial Monitor, using 20 byte like this: 0123456789abcdefghij with no nl/cr at the end.
Tested sending 20, 100, 500, 2000 and 5000 bytes at once. Check the output and timestamps.

100 bytes test can use this input: 0123456789abcdefghij0123456789abcdefghij0123456789abcdefghij0123456789abcdefghij0123456789abcdefghij

void setup() {
    Serial.begin(115200);  // UART initialized at 115200 baud rate
}

void loop() {
    if (Serial.available()) {
        Serial.println("got a data strem");
        String data = Serial.readString();
        Serial.printf("\r\nRead %d bytes.\r\n", data.length());
        uint32_t i = 0;
        uint32_t dataLen = data.length();
         while (i < dataLen) {
          int r = dataLen > 50 ? 50 : dataLen;
          for (int j = 0; j < r; j++) {
            Serial.print((char) *(data.c_str() + i + j));
          }
          i += r;
          Serial.println();
        }
    }
}

Output:

17:29:26.623 -> rst:0x10 (RTCWDT_RTC_RESET),boot:0x13 (SPI_FAST_FLASH_BOOT)
17:29:26.623 -> configsip: 0, SPIWP:0xee
17:29:26.623 -> clk_drv:0x00,q_drv:0x00,d_drv:0x00,cs0_drv:0x00,hd_drv:0x00,wp_drv:0x00
17:29:26.623 -> mode:DIO, clock div:1
17:29:26.623 -> load:0x3fff0030,len:4888
17:29:26.623 -> load:0x40078000,len:16516
17:29:26.623 -> load:0x40080400,len:4
17:29:26.623 -> load:0x40080404,len:3476
17:29:26.623 -> entry 0x400805b4
17:29:33.863 -> got a data strem
17:29:34.848 -> 
17:29:34.848 -> Read 20 bytes.
17:29:34.848 -> 0123456789abcdefghij
17:29:45.627 -> got a data strem
17:29:46.644 -> 
17:29:46.644 -> Read 100 bytes.
17:29:46.644 -> 0123456789abcdefghij0123456789abcdefghij0123456789
17:29:46.644 -> abcdefghij0123456789abcdefghij0123456789abcdefghij
17:29:57.890 -> got a data strem
17:29:58.941 -> 
17:29:58.941 -> Read 500 bytes.
17:29:58.941 -> 0123456789abcdefghij0123456789abcdefghij0123456789
17:29:58.941 -> abcdefghij0123456789abcdefghij0123456789abcdefghij
17:29:58.941 -> 0123456789abcdefghij0123456789abcdefghij0123456789
17:29:58.941 -> abcdefghij0123456789abcdefghij0123456789abcdefghij
17:29:58.974 -> 0123456789abcdefghij0123456789abcdefghij0123456789
17:29:58.974 -> abcdefghij0123456789abcdefghij0123456789abcdefghij
17:29:58.974 -> 0123456789abcdefghij0123456789abcdefghij0123456789
17:29:58.974 -> abcdefghij0123456789abcdefghij0123456789abcdefghij
17:29:58.974 -> 0123456789abcdefghij0123456789abcdefghij0123456789
17:29:58.974 -> abcdefghij0123456789abcdefghij0123456789abcdefghij
17:30:04.532 -> got a data strem
17:30:05.599 -> 
17:30:05.599 -> Read 2000 bytes.
17:30:05.599 -> 0123456789abcdefghij0123456789abcdefghij0123456789
17:30:05.599 -> abcdefghij0123456789abcdefghij0123456789abcdefghij
17:30:05.599 -> 0123456789abcdefghij0123456789abcdefghij0123456789
17:30:05.632 -> abcdefghij0123456789abcdefghij0123456789abcdefghij
17:30:05.632 -> 0123456789abcdefghij0123456789abcdefghij0123456789
17:30:05.632 -> abcdefghij0123456789abcdefghij0123456789abcdefghij
17:30:05.632 -> 0123456789abcdefghij0123456789abcdefghij0123456789
17:30:05.632 -> abcdefghij0123456789abcdefghij0123456789abcdefghij
17:30:05.632 -> 0123456789abcdefghij0123456789abcdefghij0123456789
17:30:05.632 -> abcdefghij0123456789abcdefghij0123456789abcdefghij
17:30:05.632 -> 0123456789abcdefghij0123456789abcdefghij0123456789
17:30:05.665 -> abcdefghij0123456789abcdefghij0123456789abcdefghij
17:30:05.665 -> 0123456789abcdefghij0123456789abcdefghij0123456789
17:30:05.665 -> abcdefghij0123456789abcdefghij0123456789abcdefghij
17:30:05.665 -> 0123456789abcdefghij0123456789abcdefghij0123456789
17:30:05.665 -> abcdefghij0123456789abcdefghij0123456789abcdefghij
17:30:05.665 -> 0123456789abcdefghij0123456789abcdefghij0123456789
17:30:05.665 -> abcdefghij0123456789abcdefghij0123456789abcdefghij
17:30:05.699 -> 0123456789abcdefghij0123456789abcdefghij0123456789
17:30:05.699 -> abcdefghij0123456789abcdefghij0123456789abcdefghij
17:30:05.699 -> 0123456789abcdefghij0123456789abcdefghij0123456789
17:30:05.699 -> abcdefghij0123456789abcdefghij0123456789abcdefghij
17:30:05.699 -> 0123456789abcdefghij0123456789abcdefghij0123456789
17:30:05.699 -> abcdefghij0123456789abcdefghij0123456789abcdefghij
17:30:05.699 -> 0123456789abcdefghij0123456789abcdefghij0123456789
17:30:05.699 -> abcdefghij0123456789abcdefghij0123456789abcdefghij
17:30:05.749 -> 0123456789abcdefghij0123456789abcdefghij0123456789
17:30:05.749 -> abcdefghij0123456789abcdefghij0123456789abcdefghij
17:30:05.749 -> 0123456789abcdefghij0123456789abcdefghij0123456789
17:30:05.749 -> abcdefghij0123456789abcdefghij0123456789abcdefghij
17:30:05.749 -> 0123456789abcdefghij0123456789abcdefghij0123456789
17:30:05.749 -> abcdefghij0123456789abcdefghij0123456789abcdefghij
17:30:05.749 -> 0123456789abcdefghij0123456789abcdefghij0123456789
17:30:05.749 -> abcdefghij0123456789abcdefghij0123456789abcdefghij
17:30:05.749 -> 0123456789abcdefghij0123456789abcdefghij0123456789
17:30:05.749 -> abcdefghij0123456789abcdefghij0123456789abcdefghij
17:30:05.749 -> 0123456789abcdefghij0123456789abcdefghij0123456789
17:30:05.782 -> abcdefghij0123456789abcdefghij0123456789abcdefghij
17:30:05.782 -> 0123456789abcdefghij0123456789abcdefghij0123456789
17:30:05.782 -> abcdefghij0123456789abcdefghij0123456789abcdefghij
17:30:12.907 -> got a data strem
17:30:13.991 -> 
17:30:13.991 -> Read 5000 bytes.
17:30:13.991 -> 0123456789abcdefghij0123456789abcdefghij0123456789
17:30:13.991 -> abcdefghij0123456789abcdefghij0123456789abcdefghij
17:30:13.991 -> 0123456789abcdefghij0123456789abcdefghij0123456789
17:30:13.991 -> abcdefghij0123456789abcdefghij0123456789abcdefghij
17:30:13.991 -> 0123456789abcdefghij0123456789abcdefghij0123456789
17:30:13.991 -> abcdefghij0123456789abcdefghij0123456789abcdefghij
17:30:13.991 -> 0123456789abcdefghij0123456789abcdefghij0123456789
17:30:14.041 -> abcdefghij0123456789abcdefghij0123456789abcdefghij
17:30:14.041 -> 0123456789abcdefghij0123456789abcdefghij0123456789
17:30:14.041 -> abcdefghij0123456789abcdefghij0123456789abcdefghij
17:30:14.041 -> 0123456789abcdefghij0123456789abcdefghij0123456789
17:30:14.041 -> abcdefghij0123456789abcdefghij0123456789abcdefghij
17:30:14.041 -> 0123456789abcdefghij0123456789abcdefghij0123456789
17:30:14.041 -> abcdefghij0123456789abcdefghij0123456789abcdefghij
17:30:14.041 -> 0123456789abcdefghij0123456789abcdefghij0123456789
17:30:14.041 -> abcdefghij0123456789abcdefghij0123456789abcdefghij
17:30:14.041 -> 0123456789abcdefghij0123456789abcdefghij0123456789
17:30:14.041 -> abcdefghij0123456789abcdefghij0123456789abcdefghij
17:30:14.074 -> 0123456789abcdefghij0123456789abcdefghij0123456789
17:30:14.074 -> abcdefghij0123456789abcdefghij0123456789abcdefghij
17:30:14.074 -> 0123456789abcdefghij0123456789abcdefghij0123456789
17:30:14.074 -> abcdefghij0123456789abcdefghij0123456789abcdefghij
17:30:14.074 -> 0123456789abcdefghij0123456789abcdefghij0123456789
17:30:14.074 -> abcdefghij0123456789abcdefghij0123456789abcdefghij
17:30:14.074 -> 0123456789abcdefghij0123456789abcdefghij0123456789
17:30:14.074 -> abcdefghij0123456789abcdefghij0123456789abcdefghij
17:30:14.108 -> 0123456789abcdefghij0123456789abcdefghij0123456789
17:30:14.108 -> abcdefghij0123456789abcdefghij0123456789abcdefghij
17:30:14.108 -> 0123456789abcdefghij0123456789abcdefghij0123456789
17:30:14.108 -> abcdefghij0123456789abcdefghij0123456789abcdefghij
17:30:14.108 -> 0123456789abcdefghij0123456789abcdefghij0123456789
17:30:14.108 -> abcdefghij0123456789abcdefghij0123456789abcdefghij
17:30:14.108 -> 0123456789abcdefghij0123456789abcdefghij0123456789
17:30:14.142 -> abcdefghij0123456789abcdefghij0123456789abcdefghij
17:30:14.142 -> 0123456789abcdefghij0123456789abcdefghij0123456789
17:30:14.142 -> abcdefghij0123456789abcdefghij0123456789abcdefghij
17:30:14.142 -> 0123456789abcdefghij0123456789abcdefghij0123456789
17:30:14.142 -> abcdefghij0123456789abcdefghij0123456789abcdefghij
17:30:14.142 -> 0123456789abcdefghij0123456789abcdefghij0123456789
17:30:14.142 -> abcdefghij0123456789abcdefghij0123456789abcdefghij
17:30:14.142 -> 0123456789abcdefghij0123456789abcdefghij0123456789
17:30:14.190 -> abcdefghij0123456789abcdefghij0123456789abcdefghij
17:30:14.190 -> 0123456789abcdefghij0123456789abcdefghij0123456789
17:30:14.190 -> abcdefghij0123456789abcdefghij0123456789abcdefghij
17:30:14.190 -> 0123456789abcdefghij0123456789abcdefghij0123456789
17:30:14.190 -> abcdefghij0123456789abcdefghij0123456789abcdefghij
17:30:14.190 -> 0123456789abcdefghij0123456789abcdefghij0123456789
17:30:14.190 -> abcdefghij0123456789abcdefghij0123456789abcdefghij
17:30:14.190 -> 0123456789abcdefghij0123456789abcdefghij0123456789
17:30:14.190 -> abcdefghij0123456789abcdefghij0123456789abcdefghij
17:30:14.190 -> 0123456789abcdefghij0123456789abcdefghij0123456789
17:30:14.190 -> abcdefghij0123456789abcdefghij0123456789abcdefghij
17:30:14.224 -> 0123456789abcdefghij0123456789abcdefghij0123456789
17:30:14.224 -> abcdefghij0123456789abcdefghij0123456789abcdefghij
17:30:14.224 -> 0123456789abcdefghij0123456789abcdefghij0123456789
17:30:14.224 -> abcdefghij0123456789abcdefghij0123456789abcdefghij
17:30:14.224 -> 0123456789abcdefghij0123456789abcdefghij0123456789
17:30:14.224 -> abcdefghij0123456789abcdefghij0123456789abcdefghij
17:30:14.224 -> 0123456789abcdefghij0123456789abcdefghij0123456789
17:30:14.257 -> abcdefghij0123456789abcdefghij0123456789abcdefghij
17:30:14.257 -> 0123456789abcdefghij0123456789abcdefghij0123456789
17:30:14.257 -> abcdefghij0123456789abcdefghij0123456789abcdefghij
17:30:14.257 -> 0123456789abcdefghij0123456789abcdefghij0123456789
17:30:14.257 -> abcdefghij0123456789abcdefghij0123456789abcdefghij
17:30:14.257 -> 0123456789abcdefghij0123456789abcdefghij0123456789
17:30:14.257 -> abcdefghij0123456789abcdefghij0123456789abcdefghij
17:30:14.257 -> 0123456789abcdefghij0123456789abcdefghij0123456789
17:30:14.306 -> abcdefghij0123456789abcdefghij0123456789abcdefghij
17:30:14.306 -> 0123456789abcdefghij0123456789abcdefghij0123456789
17:30:14.306 -> abcdefghij0123456789abcdefghij0123456789abcdefghij
17:30:14.306 -> 0123456789abcdefghij0123456789abcdefghij0123456789
17:30:14.306 -> abcdefghij0123456789abcdefghij0123456789abcdefghij
17:30:14.306 -> 0123456789abcdefghij0123456789abcdefghij0123456789
17:30:14.306 -> abcdefghij0123456789abcdefghij0123456789abcdefghij
17:30:14.306 -> 0123456789abcdefghij0123456789abcdefghij0123456789
17:30:14.306 -> abcdefghij0123456789abcdefghij0123456789abcdefghij
17:30:14.306 -> 0123456789abcdefghij0123456789abcdefghij0123456789
17:30:14.341 -> abcdefghij0123456789abcdefghij0123456789abcdefghij
17:30:14.341 -> 0123456789abcdefghij0123456789abcdefghij0123456789
17:30:14.341 -> abcdefghij0123456789abcdefghij0123456789abcdefghij
17:30:14.341 -> 0123456789abcdefghij0123456789abcdefghij0123456789
17:30:14.341 -> abcdefghij0123456789abcdefghij0123456789abcdefghij
17:30:14.341 -> 0123456789abcdefghij0123456789abcdefghij0123456789
17:30:14.341 -> abcdefghij0123456789abcdefghij0123456789abcdefghij
17:30:14.341 -> 0123456789abcdefghij0123456789abcdefghij0123456789
17:30:14.375 -> abcdefghij0123456789abcdefghij0123456789abcdefghij
17:30:14.375 -> 0123456789abcdefghij0123456789abcdefghij0123456789
17:30:14.375 -> abcdefghij0123456789abcdefghij0123456789abcdefghij
17:30:14.375 -> 0123456789abcdefghij0123456789abcdefghij0123456789
17:30:14.375 -> abcdefghij0123456789abcdefghij0123456789abcdefghij
17:30:14.375 -> 0123456789abcdefghij0123456789abcdefghij0123456789
17:30:14.375 -> abcdefghij0123456789abcdefghij0123456789abcdefghij
17:30:14.375 -> 0123456789abcdefghij0123456789abcdefghij0123456789
17:30:14.408 -> abcdefghij0123456789abcdefghij0123456789abcdefghij
17:30:14.408 -> 0123456789abcdefghij0123456789abcdefghij0123456789
17:30:14.408 -> abcdefghij0123456789abcdefghij0123456789abcdefghij
17:30:14.408 -> 0123456789abcdefghij0123456789abcdefghij0123456789
17:30:14.408 -> abcdefghij0123456789abcdefghij0123456789abcdefghij
17:30:14.408 -> 0123456789abcdefghij0123456789abcdefghij0123456789
17:30:14.408 -> abcdefghij0123456789abcdefghij0123456789abcdefghij

@EricHarbers
Copy link

EricHarbers commented Mar 13, 2025

#define BAUD_RATE 230400

void receiveSerial();
void receiveErrorSerial(hardwareSerial_error_t e);

uint8_t ReceiveBuffer[1024];
unsigned int uartBaudrate;

void setup() {
// put your setup code here, to run once:
uartBaudrate = BAUD_RATE;

Serial.begin(uartBaudrate);
Serial1.begin(uartBaudrate, SERIAL_8N1, 26, 27);

// the 2 lines below will work properly for esp32 BSP V3.0.3
// for the version V3.1.3, it will not work at all
Serial.setRxFIFOFull(120);

Serial.setRxTimeout(50); // max value 101 on core 3.0.3

Serial.onReceive(bluetooth_receiveSerial); // bluetooth_receiveSerial
Serial.onReceiveError(bluetooth_receiveErrorSerial);
}

void receiveSerial() {
static int col = 0;
size_t len;

while ((len = Serial.read(ReceiveBuffer, sizeof(ReceiveBuffer))) > 0) {
SerialBT.write(ReceiveBuffer, len);
Serial1.printf("%5u", len);
if (++col == 25) {
Serial1.printf("\r\n");
col = 0;
}
}
}

void receiveErrorSerial(hardwareSerial_error_t e) {
// Serial1.printf("Error %X\r\n", e);
}

void loop() {
// put your main code here, to run repeatedly:
}

////////////////////////
Serial.setRxTimeout(50); will set the timeout
For core 3.0.3 the above code works fine if you send a data stream at 230400 baud to the serial channel
The output on Serial1 will be:
120 120 120 120 120 120 120 120 120 120 120 120 120 120 120 120 120 120 120 120 120 120 120 120 120
120 120 120 120 120 120 120 120 120 120 120 120 120 120 120 120 120 120 120 120 120 120 120 120 120
120 120 120 120 120 120 120 120 120 120 120 120 120 120 120 120 120 120 120 120 120 120 120 120 120
120 120 120 120 120 120 120 120 120 120 120 120 120 120 120 120 120 120 120 120 120 120 120 120 120

The 120 is the 120 bytes set in the setRxFIFOFull function

With core 3.1.3 and newer, the output will be random, eg:
12 8 13 12 4 378 21 5 8, etc...

Most values will be very small releated to the setting of 120 set with the setRxFIFOFull function.
Sometimes a big byte count will be displayed, eg 378. When this is the case, also data will be lost.
It doesn't occur with a couple of hundreths of bytes, but with thousands of bytes.
Our application will receive the data, and this should be without any byte lost.

It seems that the Serial.setRxTimeout() function will not work properly.

@SuGlider
Copy link
Collaborator

The issue is that HardwareSerial::onReceive() works like an UART ISR.
I see that it is used to write to BT Serial. This operation take time and it may lose incoming UART data.

Any ISR shall do the minimal necessary to just get the UART data.
All other operation shall take place in a separated task, such as loop(), or something else created for that need.

It is necessary to change the code.

@SuGlider
Copy link
Collaborator

This issue is cross related to #10314 (comment)

@EricHarbers
Copy link

@SuGlider How can you explain that the code with V3.0.3 and below will receive nice data chunks of 120 bytes, set by the setRxFIFOFull function, and not loose data at any time but for V3.1.3 it will read random length blocks, short and very long, and loose data.
Imho it is related to the RxTimeout, wich doesn't work properly for V3.1.3.

But, i've a workaround for this problem: I'll use V3.0.3 and everything works fine.
It's up to you what you do with the RxTimeout problem.

@EricHarbers
Copy link

@SuGlider I've test it with a disabled BT write call, and it will output the same random lengthes, for V3.1.3

@EricHarbers
Copy link

EricHarbers commented Mar 14, 2025

@SuGlider I've now called my receiveSerial() function, which calls the BT write(), from within the loop() function, and it works the same for V3.1.3. It also looses data regularly.
The way V3.0.3 works, with big chunks of data, seems much better to work.
Btw, I did all tests with V3.2.0-RC2.

@EricHarbers
Copy link

EricHarbers commented Mar 14, 2025

@SuGlider There are 2 ways of using a UART:
1- Process data asap when there is data to process. This is the way V3.1.3 and newer does. But this is a time consuming way for the controller, because it generates many interrupts. For low and medium speeds this works fine, because the data latency is low.
2- Let the UART do the work and fill the hardware FIFO until the full level, and then process all data in the FIFO in one chunk. This is a more relaxed way for the controller, because it will generate less interrupts. This is the way V3.0.3 does. This allows a higher throughput of data. It will work for high speed. But the data latency is higher, because it has to read a full chunk before an interrupt will be given.

I can understand that your engineers have decided to change the way of processing between V3.0.3 and V3.1.3, from type 2 to type 1 because the latency is lower.
But for our application this low latency is not needed, only a high throughput of data at high speed. We need the type 2 way of processing.

Can't you make something in your software, maybe Serial.setRxFIFOFull(0), so that someone can choose between both options?
For now, we can only use V3.0.3, because it uses type 2 of processing.

@SuGlider
Copy link
Collaborator

@EricHarbers - I'd like to make a suggestion that may solve the issue you are facing, in case that it applies to your application.

void onReceive(OnReceiveCb function, bool onlyOnTimeout = false) has two parameters.
The first is the callback function, which in the example, is bluetooth_receiveSerial()
The second parameter tells the UART data processing Task to only activate the callback function whenever the receiving data flow has finished within the RX Timeout defined with setRxTimeout(uint8_t symbols_timeout).

This can be used with setRxBufferSize(size_t new_size) to make sure that all the data burst will fit in the UART buffer.
Note that it is necessary to set the RX Buffer Size before calling Serial.begin()

In that case, Serial.available() will return the lenght of the data stream that was received and it can be copied or sent to some other interface at once.

Something like that:

#define BAUD_RATE 230400
#define RX_BUFFER_SIZE 1024

void receiveSerial();
void receiveErrorSerial(hardwareSerial_error_t e);

uint8_t ReceiveBuffer[RX_BUFFER_SIZE];
unsigned int uartBaudrate;

void setup() {
  // put your setup code here, to run once:
  uartBaudrate = BAUD_RATE;
  Serial.setRxBufferSize(RX_BUFFER_SIZE * 2);
  Serial.begin(uartBaudrate);
  Serial1.begin(uartBaudrate, SERIAL_8N1, 26, 27);

  // the 2 lines below will work properly for esp32 BSP V3.0.3
  // for the version V3.1.3, it will not work at all
  Serial.setRxFIFOFull(120);

  Serial.setRxTimeout(50); // max value 101 on core 3.0.3

  Serial.onReceive(bluetooth_receiveSerial, true); // callback is called only on RX Timeout
  Serial.onReceiveError(bluetooth_receiveErrorSerial);
}

void bluetooth_receiveSerial() {
  static int col = 0;
  size_t len;
  // given that the callback is called at the end of the transmission, it will read all data that has arrived into UART
  while ((len = Serial.read(ReceiveBuffer, sizeof(ReceiveBuffer))) > 0) {
    SerialBT.write(ReceiveBuffer, len);
    Serial1.printf("%5u", len);
    if (++col == 25) {
      Serial1.printf("\r\n");
      col = 0;
    }
  }
}

void bluetooth_receiveErrorSerial(hardwareSerial_error_t e) {
  // Serial1.printf("Error %X\r\n", e);
}

void loop() {
  // put your main code here, to run repeatedly:
}

@EricHarbers
Copy link

@SuGlider This works for me! There is no data loss now.
The main problem was, that I did call the Serial.setRxBufferSize() after the Serial.begin() call. That caused the data loss, because the default RxBuffer was too small for my application.

@Jason2866
Copy link
Collaborator

@EricHarbers Can we consider as solved and close this issue?

@EricHarbers
Copy link

@SuGlider I agree with that.

@github-project-automation github-project-automation bot moved this from Under investigation to Done in Arduino ESP32 Core Project Roadmap Mar 17, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Peripheral: UART Priority: High 🗻 Issues with high priority which needs to be solved first.
Projects
Development

Successfully merging a pull request may close this issue.

7 participants