ESP32 FreeRTOS双任务异常:sendData持续运行receiveData极少执行求助
ESP32 FreeRTOS蓝牙回显任务异常排查
我在ESP32上用FreeRTOS开发蓝牙回显功能,创建了两个任务:
- receiveData任务:从蓝牙模块接收数据并存入队列
- sendData任务:从队列读取数据回传给发送端
但出现异常:sendData任务持续高频运行,receiveData任务几乎不执行。通过在任务循环中添加Serial.println("test")调试发现,sendData中的调试语句被无限调用,receiveData中的仅被调用少数几次,功能完全不符合预期。
代码文件
main.cpp
#include <Arduino.h> #include "constants.h" #include "bluetooth.h" // utility objects Bluetooth bluetooth; /*this is the object throught which we can control the bluetooth*/ // task handlers TaskHandle_t xHandle_bluetoothSendData = NULL; /*this is the task handler for the bleutooth send data task*/ TaskHandle_t xHandle_bluetoothReceiveData = NULL; /*this is the task handler for the bleutooth receive data task*/ // Queue handlers QueueHandle_t xQueue_bluetoothSendData = NULL; /*this is the queue used for bluetooth send data queue task*/ QueueHandle_t xQueue_bluetoothRcvData = NULL; /*this is the queue used for bluetooth receive data queue task*/ // task queues contents structures typedef struct { char data[BLUETOOTH_MSG_SEND_LEN]; }bluetoothSendMsg_t; typedef struct { char data[BLUETOOTH_MSG_RCV_LEN]; }bluetoothRcvMsg_t; /** * @brief this is the task used for sending data from esp32 */ void sendData(void* pvParameters) { /*struct for sending data*/ bluetoothSendMsg_t *sendMessage; while (1) { /*get what's in the queue*/ if(xQueue_bluetoothSendData != NULL && xQueueReceive(xQueue_bluetoothSendData, &sendMessage, (TickType_t) 0) == pdPASS) { Serial.print(sendMessage->data); bluetooth.sendData(sendMessage->data); /*sending the message received back to receiver*/ } } } /** * @brief this is the task used for reveiving data by esp32 */ void receiveData(void* pvParameters) { /*for sending and receiving structs*/ bluetoothRcvMsg_t receivedMsg; bluetoothSendMsg_t *messageTobeSend; while (1) { /*receiving message form the receiver from the receiver and put it in a queue to be send back*/ strcpy(receivedMsg.data, bluetooth.getData(BLUETOOTH_MSG_RCV_LEN, 0).c_str()); Serial.print("test"); /*for some reason we received an empty message*/ if(receivedMsg.data[0] != '\0') { messageTobeSend = (bluetoothSendMsg_t*) pvPortMalloc(sizeof(bluetoothSendMsg_t)); /*make a place in the heap for this new message*/ strcpy(messageTobeSend->data, receivedMsg.data); /*copying received message to another struct that will be send back*/ Serial.print(messageTobeSend->data); xQueueSend(xQueue_bluetoothSendData, (void*) &messageTobeSend, (TickType_t) 0); // send back to the queue , non blocking if the queue is full receivedMsg.data[0] = '\0'; } } } void setup() { // put your setup code here, to run once: bluetooth.init(); /*initialization for the code*/ /*for debugging purposes*/ Serial.begin(115200); while(!Serial); /*create needed queues for inter-task communication*/ xQueue_bluetoothSendData = xQueueCreate(BLUETOOTH_SEND_DATA_QUEUE_LEN, sizeof(bluetoothSendMsg_t*)); xQueue_bluetoothRcvData = xQueueCreate(BLUETOOTH_RCV_DATA_QUEUE_LEN, sizeof(bluetoothRcvMsg_t*)); /*create needed tasks*/ xTaskCreatePinnedToCore(sendData, "Send data to bluetooth", BLUETOOTH_STACK_SEND_DATA, nullptr, tskIDLE_PRIORITY , &xHandle_bluetoothSendData, tskNO_AFFINITY); xTaskCreatePinnedToCore(receiveData, "receive dat from the bluetooth", BLUETOOTH_STACK_RCV_DATA, nullptr, tskIDLE_PRIORITY , &xHandle_bluetoothReceiveData, tskNO_AFFINITY); } void loop() { }
constants.h
#ifndef CONSTANTS_H_ #define CONSTANTS_H_ // related to the bluetooth #define BLUETOOTH_STACK_SEND_DATA 1024 // used by freeRTOS to create task for sending data #define BLUETOOTH_MSG_SEND_LEN 5 // used by queues for sending data to the task #define BLUETOOTH_SEND_DATA_QUEUE_LEN 10 // used by queues to create the length of the queue #define BLUETOOTH_STACK_RCV_DATA 1024 // used by freeRTOS to create task for sending data #define BLUETOOTH_MSG_RCV_LEN 5 // used by queues for sending data to the task #define BLUETOOTH_RCV_DATA_QUEUE_LEN 10 // used by queues to create the length of the queue #endif
bluetooth.h
#ifndef BLUETOOTH_H_ #define BLUETOOTH_H_ #include "BluetoothSerial.h" class Bluetooth { private: /*this is object used to access bluetooth utils*/ BluetoothSerial SerialBT; public: /*this is the name of the esp32 when being searched for*/ const String device_name = "ESP32-Carcotech"; /*this is the Pin number for connection with ESP for legacy devices*/ const char *pin = "1234"; /*constructor*/ Bluetooth(); /*destructor*/ ~Bluetooth(); /** * @brief used to receive a string from the bluetooth * @param dataLen: is the length of the expected data to receive from the bluetooth module * @param timeOutMS: is the timeOut to wait for if not all data received : put 0 to wait infinitely if didn't till recevie same dataLen */ String getData(int dataLen, uint32_t timeOutMS); /** * @brief used to send data over the bluetooth module * @param data: this is the data to send */ void sendData(String data); /** * @brief this is the initialization function, for some reason putting the intialization code in the * constructor results in watchdog timer to reset the esp32 over and over and over */ void init(); }; #endif
bluetooth.cpp
#include "bluetooth.h" Bluetooth::Bluetooth() { } String Bluetooth::getData(int dataLen, uint32_t timeOutMS) { // this is a counter for how many data we received int receivedCounter = 0; // this is the buffer to receive in String data; // setting the timeOut SerialBT.setTimeout(timeOutMS); // check if we should wait indefinitely or not if(timeOutMS == 0) { while(SerialBT.available()) { if(receivedCounter == dataLen) break; data += (char)SerialBT.read(); receivedCounter++; } } else { while(SerialBT.available()) { int temp = SerialBT.read(); if(receivedCounter == dataLen || temp == -1 ) break; data += (char)SerialBT.read(); receivedCounter++; } } return data + "\0"; } void Bluetooth::sendData(String data) { SerialBT.write((const uint8_t *)data.c_str(), strlen(data.c_str())); } void Bluetooth::init() { // initialization of the bluetooth SerialBT.begin(device_name); // telling the bluetooth to use that pin for connection SerialBT.setPin(pin); } Bluetooth::~Bluetooth() { }
问题排查与解决
核心问题分析
- sendData任务空循环抢占CPU:使用
xQueueReceive(..., (TickType_t)0)非阻塞读取,队列空时立刻返回并进入下一次循环,持续占用CPU时间片。两个任务优先级均为tskIDLE_PRIORITY,导致receiveData任务无法获得执行机会。 - 蓝牙接收逻辑无效:
getData函数中,timeOutMS=0时仅检查一次SerialBT.available(),无数据时直接返回空字符串,receiveData任务循环执行但实际没读到有效数据。 - 内存泄漏:sendData任务取出队列中的堆内存后未释放,长期运行会耗尽堆空间。
- 字符串越界风险:
BLUETOOTH_MSG_SEND_LEN和BLUETOOTH_MSG_RCV_LEN设为5,但字符串需要末尾\0,strcpy时若源字符串长度超过限制会导致内存越界。
修正方案
1. 改造sendData任务为阻塞式读取
将队列读取超时改为portMAX_DELAY,队列空时任务进入阻塞状态,主动让出CPU:
void sendData(void* pvParameters) { bluetoothSendMsg_t *sendMessage; while (1) { if(xQueue_bluetoothSendData != NULL && xQueueReceive(xQueue_bluetoothSendData, &sendMessage, portMAX_DELAY) == pdPASS) { Serial.print(sendMessage->data); bluetooth.sendData(sendMessage->data); vPortFree(sendMessage); // 释放堆内存,避免泄漏 } } }
2. 修复蓝牙接收逻辑
修改getData函数,timeOutMS=0时循环等待直到收到指定长度数据,同时修复原代码中重复读取的bug:
String Bluetooth::getData(int dataLen, uint32_t timeOutMS) { int receivedCounter = 0; String data; SerialBT.setTimeout(timeOutMS); if(timeOutMS == 0) { // 循环等待足够长度的数据 while(receivedCounter < dataLen) { if(SerialBT.available()) { data += (char)SerialBT.read(); receivedCounter++; } vTaskDelay(pdMS_TO_TICKS(1)); // 短延时让出CPU } } else { while(receivedCounter < dataLen) { int temp = SerialBT.read(); if(temp == -1) break; // 超时或无数据 data += (char)temp; receivedCounter++; } } return data; }
同时优化receiveData任务,添加延时并使用strncpy避免越界:
void receiveData(void* pvParameters) { bluetoothRcvMsg_t receivedMsg; bluetoothSendMsg_t *messageTobeSend; while (1) { memset(receivedMsg.data, 0, sizeof(receivedMsg.data)); String tempData = bluetooth.getData(BLUETOOTH_MSG_RCV_LEN - 1, 0); // 预留\0位置 strncpy(receivedMsg.data, tempData.c_str(), sizeof(receivedMsg.data)-1); Serial.println("test"); if(receivedMsg.data[0] != '\0') { messageTobeSend = (bluetoothSendMsg_t*) pvPortMalloc(sizeof(bluetoothSendMsg_t)); memset(messageTobeSend->data, 0, sizeof(messageTobeSend->data)); strncpy(messageTobeSend->data, receivedMsg.data, sizeof(messageTobeSend->data)-1); Serial.println(messageTobeSend->data); xQueueSend(xQueue_bluetoothSendData, &messageTobeSend, portMAX_DELAY); // 阻塞式发送避免丢数据 } vTaskDelay(pdMS_TO_TICKS(1)); // 让出CPU } }
3. 可选:调整任务优先级
若需receiveData任务优先执行,可给它设置更高优先级:
xTaskCreatePinnedToCore(sendData, "Send data to bluetooth", BLUETOOTH_STACK_SEND_DATA, nullptr, tskIDLE_PRIORITY , &xHandle_bluetoothSendData, tskNO_AFFINITY); xTaskCreatePinnedToCore(receiveData, "receive dat from the bluetooth", BLUETOOTH_STACK_RCV_DATA, nullptr, tskIDLE_PRIORITY + 1 , &xHandle_bluetoothReceiveData, tskNO_AFFINITY);
内容的提问来源于stack exchange,提问作者abdo Salm
相关产品推荐
相关产品推荐

