ESP32 FreeRTOS avoiding deadlock, etc.

blim_acs
Posts: 4
Joined: Fri Jan 05, 2024 8:38 pm

ESP32 FreeRTOS avoiding deadlock, etc.

Postby blim_acs » Thu Mar 21, 2024 9:40 pm

Hi,

First time using a RTOS and I was wondering what your suggestion on what synchronization techniques you would use in my situation.

My application is based on the RA6E2 - bt_spp_initiator example. A device is connected to the ESP32 through serial and the ESP connects to a printer over BT classic. There is a task that handles UART RXs and when a specific header byte is sent to the ESP, all device messages need to be transmitted to the printer over bluetooth. This requires repeated calls to esp_spp_write but that would need the parameter 'cong' to equal false for the callback event ESP_SPP_WRITE_EVT. If the parameter isn't false, I would need to wait for the callback event ESP_SPP_CONG_EVT and for its parameter 'cong' to equal false.

I was planning to implement a task just for bluetooth TXs, a mutex that modifies a flag whenever the header byte is received and after it transmit the message to the printer, and group events to signal to the BT TX task when the 'cong' parameter of the callback are false.

Is this approach okay or is there a better method?

code snippet:

Code: Select all

#define BLUETOOTH_READY_BIT  (1 << 0)
SemaphoreHandle_t dataMutex;
static EventGroupHandle_t bluetoothEventGroup;

//uart rx task
static void console_uart_task(void *pvParameters)
{
    uart_event_t event;
    spp_msg_prs_cb_t *parser = &spp_msg_parser;
    spp_msg_parser_reset_state(parser);
    spp_msg_parser_register_callback(parser, spp_msg_handler);

    for (;;) {
        //Waiting for UART event.
        if (xQueueReceive(uart_queue, (void * )&event, (TickType_t)portMAX_DELAY)) {
            switch (event.type) {
                //Event of UART receving data
                case UART_DATA:
                {
                	memset(tmp, 0, TMP_BUF_LEN);
                	memset(tmp_buf, 0, TMP_BUF_LEN);
                    len = uart_read_bytes(CONSOLE_UART_NUM, tmp_buf, TMP_BUF_LEN, 0);
//                    printf("len: [%d]\r\n", len);
                    // if data mode, send all msgs to bt device
                    if (tmp_buf[len - 3] == 0x00 && tmp_buf[len - 2] == 0x1b && tmp_buf[len - 1] == 0x79)
                    {

                    	//set flag/semaphore TODO: ---------------------------------------
                    	xSemaphoreTake(dataMutex, portMAX_DELAY);
                    	if (num_connected_dev && num_connected_dev <= MAX_CONNECT_DEV)
                    	{
                    		esp_spp_write(connect_id[num_connected_dev - 1], len, &tmp_buf);
                    		bt_tx_flag = 1;
//                    		printf("%c%c%c", special_msg2[0], special_msg2[1], special_msg2[2]);
                    	}
                    	xSemaphoreGive(dataMutex);
                    	//----------------------------------------------------------------


//RELEVANT EVENTS FROM BT CALLBACK
    case ESP_SPP_WRITE_EVT:
    	if (param->write.cong)
    	{
    		//wait for cong event
    	} else if (bt_tx_flag && !param->write.cong)
    	{
    		xEventGroupSetBitsFromISR(bluetoothEventGroup, BLUETOOTH_READY_BIT, &xHigherPriorityTaskWoken);
    	}
        break;
    case ESP_SPP_CONG_EVT:
    	if (param->cong.cong)
    	{
    		//wait for .cong = false
    	} else if (bt_tx_flag && !param->cong.cong)
    	{
    		xEventGroupSetBitsFromISR(bluetoothEventGroup, BLUETOOTH_READY_BIT, &xHigherPriorityTaskWoken);
    	}
        break;


//BT task
void bluetooth_tx(void *pvParameters)
{
	while (1)
	{
		EventBits_t bits = xEventGroupWaitBits(bluetoothEventGroup, BLUETOOTH_READY_BIT, pdTRUE, pdFALSE, portMAX_DELAY);
		if (bits && BLUETOOTH_READY_BIT)
		{
			//send over bt to printer
			esp_spp_write(connect_id[num_connected_dev - 1], len, &tmp_buf);
			xEventGroupClearBits(bluetoothEventGroup, BLUETOOTH_READY_BIT);
			xSemaphoreTake(dataMutex, portMAX_DELAY);
			bt_tx_flag = 0;
			xSemaphoreGive();
		}
	}
}


Initialization
    bluetoothEventGroup = xEventGroupCreate();
    dataMutex = xSemaphoreCreateMutex();
    xTaskCreate(bluetooth_tx, "bt_tx", 4096, NULL, 8, NULL);

liaifat85
Posts: 200
Joined: Wed Dec 06, 2023 2:46 pm

Re: ESP32 FreeRTOS avoiding deadlock, etc.

Postby liaifat85 » Fri Mar 22, 2024 10:03 am

Proper resource management is essential. You have to clear event group bits (xEventGroupClearBits) after handling them and correctly using mutexes to protect shared data.

MicroController
Posts: 1725
Joined: Mon Oct 17, 2022 7:38 pm
Location: Europe, Germany

Re: ESP32 FreeRTOS avoiding deadlock, etc.

Postby MicroController » Fri Mar 22, 2024 11:20 am

When I implemented UART<->SPP, I did so using two ring buffers (RX+TX) (like https://docs.espressif.com/projects/esp ... ng-buffers), and one task for each direction. UART-RX task puts data into a ring buffer, and starts a BT transmission if needed. The BT event handler is used to go on transmitting until the ring buffer is empty, including cong handling, and to put data received via BT into the buffer for the UART-TX task.

The UART-RX task blocks on reading from the UART, the UART-TX task blocks on reading from its ring buffer, and the ring buffer takes care of much of the synchronization between the tasks and the BT callback while also providing buffering for cases of fluctuating (BT) bandwidth/latency.

- Thinking about it, the UART-TX task & buffer are probably not necessary as the UART driver can already provide an internal ring buffer. Using the UART driver's buffers and events, the RX task could be obsolete too; one could just trigger a BT transmission upon receiving a data event from the UART if there isn't one running already.

Who is online

Users browsing this forum: Majestic-12 [Bot] and 333 guests