diff --git a/cores/esp32/HardwareSerial.cpp b/cores/esp32/HardwareSerial.cpp index ba8429a5dac..323482a6bbf 100644 --- a/cores/esp32/HardwareSerial.cpp +++ b/cores/esp32/HardwareSerial.cpp @@ -122,11 +122,13 @@ void serialEventRun(void) HardwareSerial::HardwareSerial(int uart_nr) : _uart_nr(uart_nr), -_uart(NULL), +_uart(NULL), _rxBufferSize(256), _txBufferSize(0), _onReceiveCB(NULL), _onReceiveErrorCB(NULL), +_onReceiveTimeout(true), +_rxTimeout(10), _eventTask(NULL) #if !CONFIG_DISABLE_HAL_LOCKS ,_lock(NULL) @@ -183,18 +185,49 @@ void HardwareSerial::onReceiveError(OnReceiveErrorCb function) HSERIAL_MUTEX_UNLOCK(); } -void HardwareSerial::onReceive(OnReceiveCb function) +void HardwareSerial::onReceive(OnReceiveCb function, bool onlyOnTimeout) { HSERIAL_MUTEX_LOCK(); // function may be NULL to cancel onReceive() from its respective task _onReceiveCB = function; + // When Rx timeout is Zero (disabled), there is only one possible option that is callback when FIFO reaches 120 bytes + _onReceiveTimeout = _rxTimeout > 0 ? onlyOnTimeout : false; + // 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(); } +// timout is calculates in time to receive UART symbols at the UART baudrate. +// the estimation is about 11 bits per symbol (SERIAL_8N1) +void HardwareSerial::setRxTimeout(uint8_t symbols_timeout) +{ + HSERIAL_MUTEX_LOCK(); + + // Zero disables timeout, thus, onReceive callback will only be called when RX FIFO reaches 120 bytes + // Any non-zero value will activate onReceive callback based on UART baudrate with about 11 bits per symbol + _rxTimeout = symbols_timeout; + if (!symbols_timeout) _onReceiveTimeout = false; // only when RX timeout is disabled, we also must disable this flag + + 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; @@ -207,14 +240,16 @@ 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->_onReceiveTimeout && event.timeout_flag) || !uart->_onReceiveTimeout) ) + uart->_onReceiveCB(); break; case UART_FIFO_OVF: log_w("UART%d FIFO Overflow. Consider adding Hardware Flow Control to your Application.", uart->_uart_nr); if(uart->_onReceiveErrorCB) uart->_onReceiveErrorCB(UART_FIFO_OVF_ERROR); break; case UART_BUFFER_FULL: - log_w("UART%d Buffer Full. Consider encreasing your buffer size of your Application.", uart->_uart_nr); + log_w("UART%d Buffer Full. Consider increasing your buffer size of your Application.", uart->_uart_nr); if(uart->_onReceiveErrorCB) uart->_onReceiveErrorCB(UART_BUFFER_FULL_ERROR); break; case UART_BREAK: @@ -317,6 +352,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); + } + HSERIAL_MUTEX_UNLOCK(); } diff --git a/cores/esp32/HardwareSerial.h b/cores/esp32/HardwareSerial.h index 4291c046b86..d886bdc5c31 100644 --- a/cores/esp32/HardwareSerial.h +++ b/cores/esp32/HardwareSerial.h @@ -73,10 +73,31 @@ 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 - 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 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) + // onlyOnTimeout parameter will define how onReceive will behave: + // Default: true -- The callback will only be called when RX Timeout happens. + // Whole stream of bytes will be ready for being read on the callback function at once. + // This option may lead to Rx Overflow depending on the Rx Buffer Size and number of bytes received in the streaming + // false -- The callback will be called when FIFO reaches 120 bytes and also on RX Timeout. + // The stream of incommig bytes will be "split" into blocks of 120 bytes on each callback. + // This option avoid any sort of Rx Overflow, but leaves the UART packet reassembling work to the Application. + void onReceive(OnReceiveCb function, bool onlyOnTimeout = true); + + // 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) - maybe usefull in some use cases + 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); @@ -140,6 +161,9 @@ class HardwareSerial: public Stream size_t _rxBufferSize; size_t _txBufferSize; OnReceiveCb _onReceiveCB; + // _onReceive and _rxTimeout have be consistent when timeout is disabled + bool _onReceiveTimeout; + uint8_t _rxTimeout; OnReceiveErrorCb _onReceiveErrorCB; TaskHandle_t _eventTask;