Skip to content

Commit 6345350

Browse files
authored
Adds a new example: onReceiveExample.ino (#9415)
* feat: onReceiveExample.ino Adds a new example that uses HardwareSerial::onReceive(). The example demosntrates how to read all the data sent to UART0, considering that the end of transmission is defined by a period of time with UART in idle state. * fix: onReceiveExample.ino Fixes typos * feat: add explanation header * fix: mutex release * fix: add Mutex verification * feat: Mutex error message * feat: Mutex NULL testing
1 parent 9c0d59f commit 6345350

File tree

1 file changed

+124
-0
lines changed

1 file changed

+124
-0
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,124 @@
1+
/*
2+
3+
This Sketch demonstrates how to use onReceive(callbackFunc) with HardwareSerial
4+
5+
void HardwareSerial::onReceive(OnReceiveCb function, bool onlyOnTimeout = false)
6+
7+
It is possible to register an UART callback function that will be called
8+
every time that UART receives data and an associated interrupt is generated.
9+
10+
In summary, HardwareSerial::onReceive() works like an RX Interrupt callback, that can be adjusted
11+
using HardwareSerial::setRxFIFOFull() and HardwareSerial::setRxTimeout().
12+
13+
OnReceive will be called, while receiving a stream of data, when every 120 bytes are received (default FIFO Full),
14+
which may not help in case that the application needs to get all data at once before processing it.
15+
Therefore, a way to make it work is by detecting the end of a stream transmission. This can be based on a protocol
16+
or based on timeout with the UART line in idle (no data received - this is the case of this example).
17+
18+
In some cases, it is necessary to wait for receiving all the data before processing it and parsing the
19+
UART input. This example demonstrates a way to create a String with all data received from UART0 and
20+
signaling it using a Mutex for another task to process it. This example uses a timeout of 500ms as a way to
21+
know when the reception of data has finished.
22+
23+
The onReceive() callback is called whenever the RX ISR is triggered.
24+
It can occur because of two possible events:
25+
26+
1- UART FIFO FULL: it happens when internal UART FIFO reaches a certain number of bytes.
27+
Its full capacity is 127 bytes. The FIFO Full threshold for the interrupt can be changed
28+
using HardwareSerial::setRxFIFOFull(uint8_t fifoFull).
29+
Default FIFO Full Threshold is set in the UART initialization using HardwareSerial::begin()
30+
This will depend on the baud rate used when begin() is executed.
31+
For a baud rate of 115200 or lower, it it just 1 byte, mimicking original Arduino UART driver.
32+
For a baud rate over 115200 it will be 120 bytes for higher performance.
33+
Anyway, it can be changed by the application at any time.
34+
35+
2- UART RX Timeout: it happens, based on a timeout equivalent to a number of symbols at
36+
the current baud rate. If the UART line is idle for this timeout, it will raise an interrupt.
37+
This time can be changed by HardwareSerial::setRxTimeout(uint8_t rxTimeout)
38+
39+
When any of those two interrupts occur, IDF UART driver will copy FIFO data to its internal
40+
RingBuffer and then Arduino can read such data. At the same time, Arduino Layer will execute
41+
the callback function defined with HardwareSerial::onReceive().
42+
43+
<bool onlyOnTimeout> parameter (default false) can be used by the application to tell Arduino to
44+
only execute the callback when the second event above happens (Rx Timeout). At this time all
45+
received data will be available to be read by the Arduino application. But if the number of
46+
received bytes is higher than the FIFO space, it will generate an error of FIFO overflow.
47+
In order to avoid such problem, the application shall set an appropriate RX buffer size using
48+
HardwareSerial::setRxBufferSize(size_t new_size) before executing begin() for the Serial port.
49+
*/
50+
51+
52+
// this will make UART0 work in any case (using or not USB)
53+
#if ARDUINO_USB_CDC_ON_BOOT
54+
#define UART0 Serial0
55+
#else
56+
#define UART0 Serial
57+
#endif
58+
59+
// global variable to keep the results from onReceive()
60+
String uart_buffer = "";
61+
// a pause of a half second in the UART transmission is considered the end of transmission.
62+
const uint32_t communicationTimeout_ms = 500;
63+
64+
// Create a mutex for the access to uart_buffer
65+
// only one task can read/write it at a certain time
66+
SemaphoreHandle_t uart_buffer_Mutex = NULL;
67+
68+
// UART_RX_IRQ will be executed as soon as data is received by the UART
69+
// This is a callback function executed from a high priority
70+
// task created when onReceive() is used
71+
void UART0_RX_CB() {
72+
// take the mutex, waits forever until loop() finishes its processing
73+
if (xSemaphoreTake(uart_buffer_Mutex, portMAX_DELAY)) {
74+
uint32_t now = millis(); // tracks timeout
75+
while ((millis() - now) < communicationTimeout_ms) {
76+
if (UART0.available()) {
77+
uart_buffer += (char) UART0.read();
78+
now = millis(); // reset the timer
79+
}
80+
}
81+
// releases the mutex for data processing
82+
xSemaphoreGive(uart_buffer_Mutex);
83+
}
84+
}
85+
86+
// setup() and loop() are functions executed by a low priority task
87+
// Therefore, there are 2 tasks running when using onReceive()
88+
void setup() {
89+
UART0.begin(115200);
90+
91+
// creates a mutex object to control access to uart_buffer
92+
uart_buffer_Mutex = xSemaphoreCreateMutex();
93+
if(uart_buffer_Mutex == NULL) {
94+
log_e("Error creating Mutex. Sketch will fail.");
95+
while(true) {
96+
UART0.println("Mutex error (NULL). Program halted.");
97+
delay(2000);
98+
}
99+
}
100+
101+
UART0.onReceive(UART0_RX_CB); // sets the callback function
102+
UART0.println("Send data to UART0 in order to activate the RX callback");
103+
}
104+
105+
uint32_t counter = 0;
106+
void loop() {
107+
if (uart_buffer.length() > 0) {
108+
// signals that the onReceive function shall not change uart_buffer while processing
109+
if (xSemaphoreTake(uart_buffer_Mutex, portMAX_DELAY)) {
110+
// process the received data from UART0 - example, just print it beside a counter
111+
UART0.print("[");
112+
UART0.print(counter++);
113+
UART0.print("] [");
114+
UART0.print(uart_buffer.length());
115+
UART0.print(" bytes] ");
116+
UART0.println(uart_buffer);
117+
uart_buffer = ""; // reset uart_buffer for the next UART reading
118+
// releases the mutex for more data to be received
119+
xSemaphoreGive(uart_buffer_Mutex);
120+
}
121+
}
122+
UART0.println("Sleeping for 1 second...");
123+
delay(1000);
124+
}

0 commit comments

Comments
 (0)