Skip to content

Adds C++ std::function to Serial.onReceive() #6364

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 4 commits into from
Mar 2, 2022
Merged
Show file tree
Hide file tree
Changes from all 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
159 changes: 150 additions & 9 deletions cores/esp32/HardwareSerial.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
#include "HardwareSerial.h"
#include "soc/soc_caps.h"
#include "driver/uart.h"
#include "freertos/queue.h"

#ifndef SOC_RX0
#if CONFIG_IDF_TARGET_ESP32
Expand Down Expand Up @@ -115,7 +116,131 @@ void serialEventRun(void)
}
#endif

HardwareSerial::HardwareSerial(int uart_nr) : _uart_nr(uart_nr), _uart(NULL), _rxBufferSize(256) {}
#if !CONFIG_DISABLE_HAL_LOCKS
#define HSERIAL_MUTEX_LOCK() do {} while (xSemaphoreTake(_lock, portMAX_DELAY) != pdPASS)
#define HSERIAL_MUTEX_UNLOCK() xSemaphoreGive(_lock)
#else
#define HSERIAL_MUTEX_LOCK()
#define HSERIAL_MUTEX_UNLOCK()
#endif

HardwareSerial::HardwareSerial(int uart_nr) :
_uart_nr(uart_nr),
_uart(NULL),
_rxBufferSize(256),
_onReceiveCB(NULL),
_onReceiveErrorCB(NULL),
_eventTask(NULL)
#if !CONFIG_DISABLE_HAL_LOCKS
,_lock(NULL)
#endif
{
#if !CONFIG_DISABLE_HAL_LOCKS
if(_lock == NULL){
_lock = xSemaphoreCreateMutex();
if(_lock == NULL){
log_e("xSemaphoreCreateMutex failed");
return;
}
}
#endif
}

HardwareSerial::~HardwareSerial()
{
end();
#if !CONFIG_DISABLE_HAL_LOCKS
if(_lock != NULL){
vSemaphoreDelete(_lock);
}
#endif
}


void HardwareSerial::_createEventTask(void *args)
{
// Creating UART event Task
xTaskCreate(_uartEventTask, "uart_event_task", 2048, this, configMAX_PRIORITIES - 1, &_eventTask);
if (_eventTask == NULL) {
log_e(" -- UART%d Event Task not Created!", _uart_nr);
}
}

void HardwareSerial::_destroyEventTask(void)
{
if (_eventTask != NULL) {
vTaskDelete(_eventTask);
_eventTask = NULL;
}
}

void HardwareSerial::onReceiveError(OnReceiveErrorCb function)
{
HSERIAL_MUTEX_LOCK();
// function may be NULL to cancel onReceive() from its respective task
_onReceiveErrorCB = function;
// this can be called after Serial.begin(), therefore it shall create the event task
if (function != NULL && _uart != NULL && _eventTask == NULL) {
_createEventTask(this);
}
HSERIAL_MUTEX_UNLOCK();
}

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);
}
HSERIAL_MUTEX_UNLOCK();
}

void HardwareSerial::_uartEventTask(void *args)
{
HardwareSerial *uart = (HardwareSerial *)args;
uart_event_t event;
QueueHandle_t uartEventQueue = NULL;
uartGetEventQueue(uart->_uart, &uartEventQueue);
if (uartEventQueue != NULL) {
for(;;) {
//Waiting for UART event.
if(xQueueReceive(uartEventQueue, (void * )&event, (portTickType)portMAX_DELAY)) {
switch(event.type) {
case UART_DATA:
if(uart->_onReceiveCB && uart->available() > 0) 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);
if(uart->_onReceiveErrorCB) uart->_onReceiveErrorCB(UART_BUFFER_FULL_ERROR);
break;
case UART_BREAK:
log_w("UART%d RX break.", uart->_uart_nr);
if(uart->_onReceiveErrorCB) uart->_onReceiveErrorCB(UART_BREAK_ERROR);
break;
case UART_PARITY_ERR:
log_w("UART%d parity error.", uart->_uart_nr);
if(uart->_onReceiveErrorCB) uart->_onReceiveErrorCB(UART_PARITY_ERROR);
break;
case UART_FRAME_ERR:
log_w("UART%d frame error.", uart->_uart_nr);
if(uart->_onReceiveErrorCB) uart->_onReceiveErrorCB(UART_FRAME_ERROR);
break;
default:
log_w("UART%d unknown event type %d.", uart->_uart_nr, event.type);
break;
}
}
}
}
vTaskDelete(NULL);
}

void HardwareSerial::begin(unsigned long baud, uint32_t config, int8_t rxPin, int8_t txPin, bool invert, unsigned long timeout_ms, uint8_t rxfifo_full_thrhd)
{
Expand All @@ -124,6 +249,14 @@ void HardwareSerial::begin(unsigned long baud, uint32_t config, int8_t rxPin, in
return;
}

#if !CONFIG_DISABLE_HAL_LOCKS
if(_lock == NULL){
log_e("MUTEX Lock failed. Can't begin.");
return;
}
#endif

HSERIAL_MUTEX_LOCK();
// First Time or after end() --> set default Pins
if (!uartIsDriverInstalled(_uart)) {
switch (_uart_nr) {
Expand Down Expand Up @@ -176,26 +309,34 @@ void HardwareSerial::begin(unsigned long baud, uint32_t config, int8_t rxPin, in
_uart = NULL;
}
}
}

void HardwareSerial::onReceive(void(*function)(void))
{
uartOnReceive(_uart, function);
// create a task to deal with Serial Events when, for example, calling begin() twice to change the baudrate,
// or when setting the callback before calling begin()
if (_uart != NULL && (_onReceiveCB != NULL || _onReceiveErrorCB != NULL) && _eventTask == NULL) {
_createEventTask(this);
}
HSERIAL_MUTEX_UNLOCK();
}

void HardwareSerial::updateBaudRate(unsigned long baud)
{
uartSetBaudRate(_uart, baud);
}

void HardwareSerial::end(bool turnOffDebug)
void HardwareSerial::end(bool fullyTerminate)
{
if(turnOffDebug && uartGetDebug() == _uart_nr) {
uartSetDebug(0);
// default Serial.end() will completely disable HardwareSerial,
// including any tasks or debug message channel (log_x()) - but not for IDF log messages!
if(fullyTerminate) {
_onReceiveCB = NULL;
_onReceiveErrorCB = NULL;
if (uartGetDebug() == _uart_nr) {
uartSetDebug(0);
}
}
delay(10);
uartEnd(_uart);
_uart = 0;
_destroyEventTask();
}

void HardwareSerial::setDebugOutput(bool en)
Expand Down
37 changes: 32 additions & 5 deletions cores/esp32/HardwareSerial.h
Original file line number Diff line number Diff line change
Expand Up @@ -46,23 +46,40 @@
#define HardwareSerial_h

#include <inttypes.h>

#include <functional>
#include "Stream.h"
#include "esp32-hal.h"
#include "soc/soc_caps.h"
#include "HWCDC.h"

#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "freertos/semphr.h"

typedef enum {
UART_BREAK_ERROR,
UART_BUFFER_FULL_ERROR,
UART_FIFO_OVF_ERROR,
UART_FRAME_ERROR,
UART_PARITY_ERROR
} hardwareSerial_error_t;

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

class HardwareSerial: public Stream
{
public:
HardwareSerial(int uart_nr);
~HardwareSerial();

// onReceive will setup a callback for whenever UART data is received
// it will work as UART Rx interrupt
void onReceive(void(*function)(void));

// it will work as UART Rx interrupt -- Using C++ 11 std::fuction
void onReceive(OnReceiveCb function);
void onReceiveError(OnReceiveErrorCb function);

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 turnOffDebug = true);
void end(bool fullyTerminate = true);
void updateBaudRate(unsigned long baud);
int available(void);
int availableForWrite(void);
Expand Down Expand Up @@ -120,6 +137,16 @@ class HardwareSerial: public Stream
int _uart_nr;
uart_t* _uart;
size_t _rxBufferSize;
OnReceiveCb _onReceiveCB;
OnReceiveErrorCb _onReceiveErrorCB;
TaskHandle_t _eventTask;

void _createEventTask(void *args);
void _destroyEventTask(void);
static void _uartEventTask(void *args);
#if !CONFIG_DISABLE_HAL_LOCKS
SemaphoreHandle_t _lock;
#endif
};

extern void serialEventRun(void) __attribute__((weak));
Expand Down
Loading