From 021dad8e1f920998efc91eb45e5e49a724af7d9f Mon Sep 17 00:00:00 2001 From: me-no-dev Date: Tue, 28 Aug 2018 20:54:22 +0200 Subject: [PATCH 1/3] TX Flow Control and Code cleanup --- .../BluetoothSerial/src/BluetoothSerial.cpp | 299 +++++++++++++----- 1 file changed, 222 insertions(+), 77 deletions(-) diff --git a/libraries/BluetoothSerial/src/BluetoothSerial.cpp b/libraries/BluetoothSerial/src/BluetoothSerial.cpp index 97f188673df..fb769ced0c3 100644 --- a/libraries/BluetoothSerial/src/BluetoothSerial.cpp +++ b/libraries/BluetoothSerial/src/BluetoothSerial.cpp @@ -40,11 +40,128 @@ #include "esp32-hal-log.h" #endif -const char * _spp_server_name = "ESP32_SPP_SERVER"; +const char * _spp_server_name = "ESP32SPP"; -#define QUEUE_SIZE 256 +#define RX_QUEUE_SIZE 512 +#define TX_QUEUE_SIZE 32 static uint32_t _spp_client = 0; -static xQueueHandle _spp_queue = NULL; +static xQueueHandle _spp_rx_queue = NULL; +static xQueueHandle _spp_tx_queue = NULL; +static TaskHandle_t _spp_task_handle = NULL; +static EventGroupHandle_t _spp_event_group = NULL; + +#define SPP_RUNNING 0x01 +#define SPP_CONNECTED 0x02 +#define SPP_CONGESTED 0x04 + +typedef struct { + size_t len; + uint8_t * data; +} spp_packet_t; + +static esp_err_t _spp_queue_packet(uint8_t *data, size_t len){ + if(!data || !len){ + log_w("No data provided"); + return ESP_FAIL; + } + spp_packet_t * packet = (spp_packet_t*)malloc(sizeof(spp_packet_t)); + if(!packet){ + log_w("SPP TX Packet Malloc Failed!"); + return ESP_FAIL; + } + packet->len = len; + packet->data = NULL; + uint8_t * pdata = (uint8_t*)malloc(len); + if(!pdata){ + log_w("SPP TX Packet Data Malloc Failed!"); + free(packet); + return ESP_FAIL; + } + memcpy(pdata, data, len); + packet->data = pdata; + if (xQueueSend(_spp_tx_queue, &packet, portMAX_DELAY) != pdPASS) { + log_w("SPP TX Queue Send Failed!"); + free(packet->data); + free(packet); + return ESP_FAIL; + } + return ESP_OK; +} + +const uint16_t SPP_TX_MAX = 330; +static uint8_t _spp_tx_buffer[SPP_TX_MAX]; +static uint16_t _spp_tx_buffer_len = 0; + +static bool _spp_send_buffer(){ + if((xEventGroupWaitBits(_spp_event_group, SPP_CONGESTED, pdFALSE, pdTRUE, portMAX_DELAY) & SPP_CONGESTED)){ + esp_err_t err = esp_spp_write(_spp_client, _spp_tx_buffer_len, _spp_tx_buffer); + if(err != ESP_OK){ + log_e("SPP Write Failed! [0x%X]", err); + return false; + } + return true; + } + return false; +} + +static void _spp_tx_task(void * arg){ + spp_packet_t *packet = NULL; + size_t len = 0, to_send = 0; + uint8_t * data = NULL; + for (;;) { + if(_spp_tx_queue && xQueueReceive(_spp_tx_queue, &packet, portMAX_DELAY) == pdTRUE && packet){ + if(packet->len <= (SPP_TX_MAX - _spp_tx_buffer_len)){ + memcpy(_spp_tx_buffer+_spp_tx_buffer_len, packet->data, packet->len); + _spp_tx_buffer_len+=packet->len; + free(packet->data); + free(packet); + packet = NULL; + if(SPP_TX_MAX == _spp_tx_buffer_len || uxQueueMessagesWaiting(_spp_tx_queue) == 0){ + _spp_send_buffer(); + _spp_tx_buffer_len = 0; + delay(2); + } + } else { + len = packet->len; + data = packet->data; + to_send = SPP_TX_MAX - _spp_tx_buffer_len; + memcpy(_spp_tx_buffer+_spp_tx_buffer_len, data, to_send); + _spp_tx_buffer_len = SPP_TX_MAX; + data += to_send; + len -= to_send; + _spp_send_buffer(); + _spp_tx_buffer_len = 0; + delay(2); + while(len >= SPP_TX_MAX){ + memcpy(_spp_tx_buffer, data, SPP_TX_MAX); + _spp_tx_buffer_len = SPP_TX_MAX; + data += SPP_TX_MAX; + len -= SPP_TX_MAX; + _spp_send_buffer(); + _spp_tx_buffer_len = 0; + delay(2); + } + if(len){ + memcpy(_spp_tx_buffer, data, len); + _spp_tx_buffer_len += len; + free(packet->data); + free(packet); + packet = NULL; + if(uxQueueMessagesWaiting(_spp_tx_queue) == 0){ + _spp_send_buffer(); + _spp_tx_buffer_len = 0; + delay(2); + } + } + } + } else { + log_e("Something went horribly wrong"); + } + } + vTaskDelete(NULL); + _spp_task_handle = NULL; +} + static void esp_spp_cb(esp_spp_cb_event_t event, esp_spp_cb_param_t *param) { @@ -54,43 +171,63 @@ static void esp_spp_cb(esp_spp_cb_event_t event, esp_spp_cb_param_t *param) log_i("ESP_SPP_INIT_EVT"); esp_bt_gap_set_scan_mode(ESP_BT_SCAN_MODE_CONNECTABLE_DISCOVERABLE); esp_spp_start_srv(ESP_SPP_SEC_NONE, ESP_SPP_ROLE_SLAVE, 0, _spp_server_name); + xEventGroupSetBits(_spp_event_group, SPP_RUNNING); break; - case ESP_SPP_DISCOVERY_COMP_EVT://discovery complete - log_i("ESP_SPP_DISCOVERY_COMP_EVT"); - break; - case ESP_SPP_OPEN_EVT://Client connection open - log_i("ESP_SPP_OPEN_EVT"); + + case ESP_SPP_SRV_OPEN_EVT://Server connection open + _spp_client = param->open.handle; + xEventGroupSetBits(_spp_event_group, SPP_CONNECTED); + log_i("ESP_SPP_SRV_OPEN_EVT"); break; + case ESP_SPP_CLOSE_EVT://Client connection closed _spp_client = 0; + xEventGroupClearBits(_spp_event_group, SPP_CONNECTED); log_i("ESP_SPP_CLOSE_EVT"); break; - case ESP_SPP_START_EVT://server started - log_i("ESP_SPP_START_EVT"); + + case ESP_SPP_CONG_EVT://connection congestion status changed + if(param->cong.cong){ + xEventGroupClearBits(_spp_event_group, SPP_CONGESTED); + } else { + xEventGroupSetBits(_spp_event_group, SPP_CONGESTED); + } + log_v("ESP_SPP_CONG_EVT: %s", param->cong.cong?"CONGESTED":"FREE"); break; - case ESP_SPP_CL_INIT_EVT://client initiated a connection - log_i("ESP_SPP_CL_INIT_EVT"); + + case ESP_SPP_WRITE_EVT://write operation completed + if(param->write.cong){ + xEventGroupClearBits(_spp_event_group, SPP_CONGESTED); + } + log_v("ESP_SPP_WRITE_EVT: %u %s", param->write.len, param->write.cong?"CONGESTED":"FREE"); break; + case ESP_SPP_DATA_IND_EVT://connection received data log_v("ESP_SPP_DATA_IND_EVT len=%d handle=%d", param->data_ind.len, param->data_ind.handle); //esp_log_buffer_hex("",param->data_ind.data,param->data_ind.len); //for low level debug - if (_spp_queue != NULL){ - for (int i = 0; i < param->data_ind.len; i++) - xQueueSend(_spp_queue, param->data_ind.data + i, (TickType_t)0); - } else { - log_e("SerialQueueBT ERROR"); + if (_spp_rx_queue != NULL){ + for (int i = 0; i < param->data_ind.len; i++){ + if(xQueueSend(_spp_rx_queue, param->data_ind.data + i, (TickType_t)0) != pdTRUE){ + log_e("RX Full! Discarding %u bytes", param->data_ind.len - i); + break; + } + } } break; - case ESP_SPP_CONG_EVT://connection congestion status changed - log_i("ESP_SPP_CONG_EVT"); + + //should maybe delete those. + case ESP_SPP_DISCOVERY_COMP_EVT://discovery complete + log_i("ESP_SPP_DISCOVERY_COMP_EVT"); break; - case ESP_SPP_WRITE_EVT://write operation completed - log_v("ESP_SPP_WRITE_EVT"); + case ESP_SPP_OPEN_EVT://Client connection open + log_i("ESP_SPP_OPEN_EVT"); break; - case ESP_SPP_SRV_OPEN_EVT://Server connection open - _spp_client = param->open.handle; - log_i("ESP_SPP_SRV_OPEN_EVT"); + case ESP_SPP_START_EVT://server started + log_i("ESP_SPP_START_EVT"); + break; + case ESP_SPP_CL_INIT_EVT://client initiated a connection + log_i("ESP_SPP_CL_INIT_EVT"); break; default: break; @@ -99,41 +236,69 @@ static void esp_spp_cb(esp_spp_cb_event_t event, esp_spp_cb_param_t *param) static bool _init_bt(const char *deviceName) { + if(!_spp_event_group){ + _spp_event_group = xEventGroupCreate(); + if(!_spp_event_group){ + log_e("SPP Event Group Create Failed!"); + return false; + } + xEventGroupClearBits(_spp_event_group, 0xFFFFFF); + xEventGroupSetBits(_spp_event_group, SPP_CONGESTED); + //((xEventGroupGetBits(_spp_event_group) & SPP_CONGESTED) == SPP_CONGESTED); + //((xEventGroupWaitBits(_spp_event_group, SPP_CONGESTED, pdFALSE, pdTRUE, portMAX_DELAY) & SPP_CONGESTED) == SPP_CONGESTED); + } + if (_spp_rx_queue == NULL){ + _spp_rx_queue = xQueueCreate(RX_QUEUE_SIZE, sizeof(uint8_t)); //initialize the queue + if (_spp_rx_queue == NULL){ + log_e("RX Queue Create Failed"); + return false; + } + } + if (_spp_tx_queue == NULL){ + _spp_tx_queue = xQueueCreate(TX_QUEUE_SIZE, sizeof(spp_packet_t*)); //initialize the queue + if (_spp_tx_queue == NULL){ + log_e("TX Queue Create Failed"); + return false; + } + } + if(!_spp_task_handle){ + xTaskCreate(_spp_tx_task, "spp_tx", 4096, NULL, 2, &_spp_task_handle); + if(!_spp_task_handle){ + log_e("Network Event Task Start Failed!"); + return false; + } + } + if (!btStarted() && !btStart()){ - log_e("%s initialize controller failed\n", __func__); + log_e("initialize controller failed"); return false; } esp_bluedroid_status_t bt_state = esp_bluedroid_get_status(); if (bt_state == ESP_BLUEDROID_STATUS_UNINITIALIZED){ if (esp_bluedroid_init()) { - log_e("%s initialize bluedroid failed\n", __func__); + log_e("initialize bluedroid failed"); return false; } } if (bt_state != ESP_BLUEDROID_STATUS_ENABLED){ if (esp_bluedroid_enable()) { - log_e("%s enable bluedroid failed\n", __func__); + log_e("enable bluedroid failed"); return false; } } if (esp_spp_register_callback(esp_spp_cb) != ESP_OK){ - log_e("%s spp register failed\n", __func__); + log_e("spp register failed"); return false; } if (esp_spp_init(ESP_SPP_MODE_CB) != ESP_OK){ - log_e("%s spp init failed\n", __func__); + log_e("spp init failed"); return false; } - _spp_queue = xQueueCreate(QUEUE_SIZE, sizeof(uint8_t)); //initialize the queue - if (_spp_queue == NULL){ - log_e("%s Queue creation error\n", __func__); - return false; - } esp_bt_dev_set_device_name(deviceName); // the default BTA_DM_COD_LOUDSPEAKER does not work with the macOS BT stack @@ -142,7 +307,7 @@ static bool _init_bt(const char *deviceName) cod.minor = 0b000100; cod.service = 0b00000010110; if (esp_bt_gap_set_cod(cod, ESP_BT_INIT_COD) != ESP_OK) { - log_e("%s set cod failed\n", __func__); + log_e("set cod failed"); return false; } @@ -159,6 +324,15 @@ static bool _stop_bt() esp_bluedroid_deinit(); btStop(); } + vTaskDelete(_spp_task_handle); + vEventGroupDelete(_spp_event_group); + vQueueDelete(_spp_rx_queue); + vQueueDelete(_spp_tx_queue);//todo: free all queued packets + _spp_client = 0; + _spp_rx_queue = NULL; + _spp_tx_queue = NULL; + _spp_task_handle = NULL; + _spp_event_group = NULL; return true; } @@ -175,7 +349,6 @@ BluetoothSerial::BluetoothSerial() BluetoothSerial::~BluetoothSerial(void) { _stop_bt(); - vQueueDelete(_spp_queue); } bool BluetoothSerial::begin(String localName) @@ -188,60 +361,39 @@ bool BluetoothSerial::begin(String localName) int BluetoothSerial::available(void) { - if (!_spp_client || _spp_queue == NULL){ + if (_spp_rx_queue == NULL){ return 0; } - return uxQueueMessagesWaiting(_spp_queue); + return uxQueueMessagesWaiting(_spp_rx_queue); } int BluetoothSerial::peek(void) { - if (available()){ - if (!_spp_client || _spp_queue == NULL){ - return 0; - } - - uint8_t c; - if (xQueuePeek(_spp_queue, &c, 0)){ - return c; - } + uint8_t c; + if (_spp_rx_queue && xQueuePeek(_spp_rx_queue, &c, 0)){ + return c; } return -1; } bool BluetoothSerial::hasClient(void) { - if (_spp_client) - return true; - - return false; + return _spp_client > 0; } int BluetoothSerial::read(void) { - if (available()){ - if (!_spp_client || _spp_queue == NULL){ - return 0; - } - uint8_t c; - if (xQueueReceive(_spp_queue, &c, 0)){ - return c; - } + uint8_t c = 0; + if (_spp_rx_queue && xQueueReceive(_spp_rx_queue, &c, 0)){ + return c; } - return 0; + return -1; } size_t BluetoothSerial::write(uint8_t c) { - if (!_spp_client){ - return 0; - } - - uint8_t buffer[1]; - buffer[0] = c; - esp_err_t err = esp_spp_write(_spp_client, 1, buffer); - return (err == ESP_OK) ? 1 : 0; + return write(&c, 1); } size_t BluetoothSerial::write(const uint8_t *buffer, size_t size) @@ -249,24 +401,17 @@ size_t BluetoothSerial::write(const uint8_t *buffer, size_t size) if (!_spp_client){ return 0; } - - esp_err_t err = esp_spp_write(_spp_client, size, (uint8_t *)buffer); - return (err == ESP_OK) ? size : 0; + return (_spp_queue_packet((uint8_t *)buffer, size) == ESP_OK) ? size : 0; } void BluetoothSerial::flush() { - if (_spp_client){ - int qsize = available(); - uint8_t buffer[qsize]; - esp_spp_write(_spp_client, qsize, buffer); - } + while(read() >= 0){} } void BluetoothSerial::end() { _stop_bt(); - vQueueDelete(_spp_queue); } #endif From a92bbec55f219eaf910f7188fa1aad5b8d01053b Mon Sep 17 00:00:00 2001 From: me-no-dev Date: Thu, 30 Aug 2018 12:12:37 +0200 Subject: [PATCH 2/3] Use semaphore instead of delay TX functionality is done. --- .../BluetoothSerial/src/BluetoothSerial.cpp | 54 ++++++++++++------- 1 file changed, 36 insertions(+), 18 deletions(-) diff --git a/libraries/BluetoothSerial/src/BluetoothSerial.cpp b/libraries/BluetoothSerial/src/BluetoothSerial.cpp index fb769ced0c3..7d9d001b7d4 100644 --- a/libraries/BluetoothSerial/src/BluetoothSerial.cpp +++ b/libraries/BluetoothSerial/src/BluetoothSerial.cpp @@ -47,6 +47,7 @@ const char * _spp_server_name = "ESP32SPP"; static uint32_t _spp_client = 0; static xQueueHandle _spp_rx_queue = NULL; static xQueueHandle _spp_tx_queue = NULL; +static SemaphoreHandle_t _spp_tx_done = NULL; static TaskHandle_t _spp_task_handle = NULL; static EventGroupHandle_t _spp_event_group = NULL; @@ -99,6 +100,11 @@ static bool _spp_send_buffer(){ log_e("SPP Write Failed! [0x%X]", err); return false; } + _spp_tx_buffer_len = 0; + if(xSemaphoreTake(_spp_tx_done, portMAX_DELAY) != pdTRUE){ + log_e("SPP Ack Failed!"); + return false; + } return true; } return false; @@ -118,8 +124,6 @@ static void _spp_tx_task(void * arg){ packet = NULL; if(SPP_TX_MAX == _spp_tx_buffer_len || uxQueueMessagesWaiting(_spp_tx_queue) == 0){ _spp_send_buffer(); - _spp_tx_buffer_len = 0; - delay(2); } } else { len = packet->len; @@ -130,16 +134,12 @@ static void _spp_tx_task(void * arg){ data += to_send; len -= to_send; _spp_send_buffer(); - _spp_tx_buffer_len = 0; - delay(2); while(len >= SPP_TX_MAX){ memcpy(_spp_tx_buffer, data, SPP_TX_MAX); _spp_tx_buffer_len = SPP_TX_MAX; data += SPP_TX_MAX; len -= SPP_TX_MAX; _spp_send_buffer(); - _spp_tx_buffer_len = 0; - delay(2); } if(len){ memcpy(_spp_tx_buffer, data, len); @@ -149,8 +149,6 @@ static void _spp_tx_task(void * arg){ packet = NULL; if(uxQueueMessagesWaiting(_spp_tx_queue) == 0){ _spp_send_buffer(); - _spp_tx_buffer_len = 0; - delay(2); } } } @@ -199,6 +197,7 @@ static void esp_spp_cb(esp_spp_cb_event_t event, esp_spp_cb_param_t *param) if(param->write.cong){ xEventGroupClearBits(_spp_event_group, SPP_CONGESTED); } + xSemaphoreGive(_spp_tx_done);//we can try to send another packet log_v("ESP_SPP_WRITE_EVT: %u %s", param->write.len, param->write.cong?"CONGESTED":"FREE"); break; @@ -244,8 +243,6 @@ static bool _init_bt(const char *deviceName) } xEventGroupClearBits(_spp_event_group, 0xFFFFFF); xEventGroupSetBits(_spp_event_group, SPP_CONGESTED); - //((xEventGroupGetBits(_spp_event_group) & SPP_CONGESTED) == SPP_CONGESTED); - //((xEventGroupWaitBits(_spp_event_group, SPP_CONGESTED, pdFALSE, pdTRUE, portMAX_DELAY) & SPP_CONGESTED) == SPP_CONGESTED); } if (_spp_rx_queue == NULL){ _spp_rx_queue = xQueueCreate(RX_QUEUE_SIZE, sizeof(uint8_t)); //initialize the queue @@ -261,6 +258,15 @@ static bool _init_bt(const char *deviceName) return false; } } + if(_spp_tx_done == NULL){ + _spp_tx_done = xSemaphoreCreateBinary(); + if (_spp_tx_done == NULL){ + log_e("TX Semaphore Create Failed"); + return false; + } + xSemaphoreTake(_spp_tx_done, 0); + } + if(!_spp_task_handle){ xTaskCreate(_spp_tx_task, "spp_tx", 4096, NULL, 2, &_spp_task_handle); if(!_spp_task_handle){ @@ -324,15 +330,27 @@ static bool _stop_bt() esp_bluedroid_deinit(); btStop(); } - vTaskDelete(_spp_task_handle); - vEventGroupDelete(_spp_event_group); - vQueueDelete(_spp_rx_queue); - vQueueDelete(_spp_tx_queue);//todo: free all queued packets _spp_client = 0; - _spp_rx_queue = NULL; - _spp_tx_queue = NULL; - _spp_task_handle = NULL; - _spp_event_group = NULL; + if(_spp_task_handle){ + vTaskDelete(_spp_task_handle); + _spp_task_handle = NULL; + } + if(_spp_event_group){ + vEventGroupDelete(_spp_event_group); + _spp_event_group = NULL; + } + if(_spp_rx_queue){ + vQueueDelete(_spp_rx_queue); + _spp_rx_queue = NULL; + } + if(_spp_tx_queue){ + vQueueDelete(_spp_tx_queue);//todo: free all queued packets + _spp_tx_queue = NULL; + } + if (_spp_tx_done) { + vSemaphoreDelete(_spp_tx_done); + _spp_tx_done = NULL; + } return true; } From 7dd5b05a8ccccc6df23680cada5f8909d380505d Mon Sep 17 00:00:00 2001 From: me-no-dev Date: Thu, 6 Sep 2018 23:36:58 +0200 Subject: [PATCH 3/3] Use single buffer and empty queue on exit --- .../BluetoothSerial/src/BluetoothSerial.cpp | 25 ++++++++----------- 1 file changed, 10 insertions(+), 15 deletions(-) diff --git a/libraries/BluetoothSerial/src/BluetoothSerial.cpp b/libraries/BluetoothSerial/src/BluetoothSerial.cpp index 7d9d001b7d4..61a1cbdd4ab 100644 --- a/libraries/BluetoothSerial/src/BluetoothSerial.cpp +++ b/libraries/BluetoothSerial/src/BluetoothSerial.cpp @@ -57,7 +57,7 @@ static EventGroupHandle_t _spp_event_group = NULL; typedef struct { size_t len; - uint8_t * data; + uint8_t data[]; } spp_packet_t; static esp_err_t _spp_queue_packet(uint8_t *data, size_t len){ @@ -65,24 +65,15 @@ static esp_err_t _spp_queue_packet(uint8_t *data, size_t len){ log_w("No data provided"); return ESP_FAIL; } - spp_packet_t * packet = (spp_packet_t*)malloc(sizeof(spp_packet_t)); + spp_packet_t * packet = (spp_packet_t*)malloc(sizeof(spp_packet_t) + len); if(!packet){ log_w("SPP TX Packet Malloc Failed!"); return ESP_FAIL; } packet->len = len; - packet->data = NULL; - uint8_t * pdata = (uint8_t*)malloc(len); - if(!pdata){ - log_w("SPP TX Packet Data Malloc Failed!"); - free(packet); - return ESP_FAIL; - } - memcpy(pdata, data, len); - packet->data = pdata; + memcpy(packet->data, data, len); if (xQueueSend(_spp_tx_queue, &packet, portMAX_DELAY) != pdPASS) { log_w("SPP TX Queue Send Failed!"); - free(packet->data); free(packet); return ESP_FAIL; } @@ -119,7 +110,6 @@ static void _spp_tx_task(void * arg){ if(packet->len <= (SPP_TX_MAX - _spp_tx_buffer_len)){ memcpy(_spp_tx_buffer+_spp_tx_buffer_len, packet->data, packet->len); _spp_tx_buffer_len+=packet->len; - free(packet->data); free(packet); packet = NULL; if(SPP_TX_MAX == _spp_tx_buffer_len || uxQueueMessagesWaiting(_spp_tx_queue) == 0){ @@ -144,7 +134,6 @@ static void _spp_tx_task(void * arg){ if(len){ memcpy(_spp_tx_buffer, data, len); _spp_tx_buffer_len += len; - free(packet->data); free(packet); packet = NULL; if(uxQueueMessagesWaiting(_spp_tx_queue) == 0){ @@ -204,6 +193,7 @@ static void esp_spp_cb(esp_spp_cb_event_t event, esp_spp_cb_param_t *param) case ESP_SPP_DATA_IND_EVT://connection received data log_v("ESP_SPP_DATA_IND_EVT len=%d handle=%d", param->data_ind.len, param->data_ind.handle); //esp_log_buffer_hex("",param->data_ind.data,param->data_ind.len); //for low level debug + //ets_printf("r:%u\n", param->data_ind.len); if (_spp_rx_queue != NULL){ for (int i = 0; i < param->data_ind.len; i++){ @@ -341,10 +331,15 @@ static bool _stop_bt() } if(_spp_rx_queue){ vQueueDelete(_spp_rx_queue); + //ToDo: clear RX queue when in packet mode _spp_rx_queue = NULL; } if(_spp_tx_queue){ - vQueueDelete(_spp_tx_queue);//todo: free all queued packets + spp_packet_t *packet = NULL; + while(xQueueReceive(_spp_tx_queue, &packet, 0) == pdTRUE){ + free(packet); + } + vQueueDelete(_spp_tx_queue); _spp_tx_queue = NULL; } if (_spp_tx_done) {