Skip to content

Adds HardwareSerial::setRxTimeout() #6397

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

Merged
merged 18 commits into from
Mar 28, 2022
Merged
Show file tree
Hide file tree
Changes from 10 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
37 changes: 34 additions & 3 deletions cores/esp32/HardwareSerial.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -128,7 +128,8 @@ HardwareSerial::HardwareSerial(int uart_nr) :
_uart_nr(uart_nr),
_uart(NULL),
_rxBufferSize(256),
_onReceiveCB(NULL),
_onReceiveCB(NULL),
_rxTimeout(10),
_onReceiveErrorCB(NULL),
_eventTask(NULL)
#if !CONFIG_DISABLE_HAL_LOCKS
Expand Down Expand Up @@ -191,13 +192,37 @@ void HardwareSerial::onReceive(OnReceiveCb function)
HSERIAL_MUTEX_LOCK();
// function may be NULL to cancel onReceive() from its respective task
_onReceiveCB = function;

// this can be called after Serial.begin(), therefore it shall create the event task
if (function != NULL && _uart != NULL && _eventTask == NULL) {
_createEventTask(this);
_createEventTask(this); // Create event task
}
HSERIAL_MUTEX_UNLOCK();
}

void HardwareSerial::setRxTimeout(uint8_t symbols_timeout)
{
HSERIAL_MUTEX_LOCK();

_rxTimeout = symbols_timeout;

if(_uart != NULL) uart_set_rx_timeout(_uart_nr, _rxTimeout); // Set new timeout

HSERIAL_MUTEX_UNLOCK();
}

void HardwareSerial::eventQueueReset()
{
QueueHandle_t uartEventQueue = NULL;
if (_uart == NULL) {
return;
}
uartGetEventQueue(_uart, &uartEventQueue);
if (uartEventQueue != NULL) {
xQueueReset(uartEventQueue);
}
}

void HardwareSerial::_uartEventTask(void *args)
{
HardwareSerial *uart = (HardwareSerial *)args;
Expand All @@ -210,7 +235,7 @@ void HardwareSerial::_uartEventTask(void *args)
if(xQueueReceive(uartEventQueue, (void * )&event, (portTickType)portMAX_DELAY)) {
switch(event.type) {
case UART_DATA:
if(uart->_onReceiveCB && uart->available() > 0) uart->_onReceiveCB();
if(uart->_onReceiveCB && uart->available() > 0) uart->_onReceiveCB(event.timeout_flag);
break;
case UART_FIFO_OVF:
log_w("UART%d FIFO Overflow. Consider adding Hardware Flow Control to your Application.", uart->_uart_nr);
Expand Down Expand Up @@ -320,6 +345,12 @@ void HardwareSerial::begin(unsigned long baud, uint32_t config, int8_t rxPin, in
if (_uart != NULL && (_onReceiveCB != NULL || _onReceiveErrorCB != NULL) && _eventTask == NULL) {
_createEventTask(this);
}

// Set UART RX timeout
if (_uart != NULL) {
uart_set_rx_timeout(_uart_nr, _rxTimeout);
}

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This shall not be here. It shall be only in the setRxTimeout(...) API function.
It is enforcing timeout usage to the sketch. That shall be optional.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As I said before. The timeout behaviour is the default as per IDF (10 symbols defined in UART_TOUT_THRESH_DEFAULT). I think this is needed because the user could call setRxTimeout before or after the begin() . This is the same principle as you took with onReceive(). This is because uart_set_rx_timeout should be called once the uart is initialized.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

OK. I agree. Let's set the default always.

HSERIAL_MUTEX_UNLOCK();
}

Expand Down
24 changes: 21 additions & 3 deletions cores/esp32/HardwareSerial.h
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ typedef enum {
UART_PARITY_ERROR
} hardwareSerial_error_t;

typedef std::function<void(void)> OnReceiveCb;
typedef std::function<void(bool)> OnReceiveCb;
typedef std::function<void(hardwareSerial_error_t)> OnReceiveErrorCb;

class HardwareSerial: public Stream
Expand All @@ -73,10 +73,27 @@ class HardwareSerial: public Stream
HardwareSerial(int uart_nr);
~HardwareSerial();

// onReceive will setup a callback for whenever UART data is received
// it will work as UART Rx interrupt -- Using C++ 11 std::fuction
// onReceive will setup a callback that will be called whenever an UART interruption occurs (UART_INTR_RXFIFO_FULL or UART_INTR_RXFIFO_TOUT)
// UART_INTR_RXFIFO_FULL interrupt triggers at UART_FULL_THRESH_DEFAULT bytes received (defined as 120 bytes by default in IDF)
// UART_INTR_RXFIFO_TOUT interrupt triggers at UART_TOUT_THRESH_DEFAULT symbols passed without any reception (defined as 10 symbos by default in IDF)
// The prototype of the callback function passed must be as defined by OnReceiveCb (void callbackFunction(bool timeout))
// The bool parameter in the callback function prototype informs the callback function if the callback was called because a timeout (true) or because the RX FIFO reached 120 bytes (false)
// param function is the callback to be called after reception timeout
void onReceive(OnReceiveCb function);

// setRxTimeout sets the timeout after which onReceive callback will be called (after receiving data, it waits for this time of UART rx inactivity to call the callback fnc)
// param symbols_timeout defines a timeout threshold in uart symbol periods. Setting 0 symbol timeout disables the callback call by timeout.
// Maximum timeout setting is calculacted automatically by IDF. If set above the maximum, it is ignored and an error is printed on Serial0 (check console).
// Examples: Maximum for 11 bits symbol is 92 (SERIAL_8N2, SERIAL_8E1, SERIAL_8O1, etc), Maximum for 10 bits symbol is 101 (SERIAL_8N1).
// For example symbols_timeout=1 defines a timeout equal to transmission time of one symbol (~11 bit) on current baudrate.
// For a baudrate of 9600, SERIAL_8N1 (10 bit symbol) and symbols_timeout = 3, the timeout would be 3 / (9600 / 10) = 3.125 ms
void setRxTimeout(uint8_t symbols_timeout);

// onReceive will be called on error events (see hardwareSerial_error_t)
void onReceiveError(OnReceiveErrorCb function);

// eventQueueReset clears all events in the queue (the events that trigger onReceive and onReceiveError)
void eventQueueReset();

void begin(unsigned long baud, uint32_t config=SERIAL_8N1, int8_t rxPin=-1, int8_t txPin=-1, bool invert=false, unsigned long timeout_ms = 20000UL, uint8_t rxfifo_full_thrhd = 112);
void end(bool fullyTerminate = true);
Expand Down Expand Up @@ -138,6 +155,7 @@ class HardwareSerial: public Stream
uart_t* _uart;
size_t _rxBufferSize;
OnReceiveCb _onReceiveCB;
uint8_t _rxTimeout;
OnReceiveErrorCb _onReceiveErrorCB;
TaskHandle_t _eventTask;

Expand Down