ESP32交替使用BLE与WiFi避免干扰的实现方案问询
解决ESP32 BLE与WiFi任务冲突的问题
你遇到的核心问题确实是ESP32的BLE和WiFi共享同一射频前端,同时操作会导致射频资源抢占,进而让其中一个功能失效。你之前用delay()的方式只是简单的顺序执行,但这种阻塞式的调度不够灵活,也没法保证射频资源的正确切换。下面给你两种可靠的解决方案,都是基于FreeRTOS的任务调度来实现的:
方案一:用FreeRTOS创建独立任务+任务同步
这种方式把BLE扫描和WiFi发送拆成两个独立的FreeRTOS任务,用任务通知来控制它们交替执行,确保同一时间只有一个任务占用射频资源。
修改后的完整代码
#include <HTTPClient.h> #include <BLEDevice.h> #include <BLEScan.h> #include <freertos/FreeRTOS.h> #include <freertos/task.h> const char* ssid = "xx"; const char* password = "xx"; int scanTime = 2; // 扫描时长(秒) BLEScan* pBLEScan; TaskHandle_t bleTaskHandle = NULL; TaskHandle_t wifiTaskHandle = NULL; // BLE回调类 class MyAdvertisedDeviceCallbacks: public BLEAdvertisedDeviceCallbacks { void onResult(BLEAdvertisedDevice advertisedDevice) { Serial.printf("Advertised Device: %s \n", advertisedDevice.toString().c_str()); } }; // BLE扫描任务 void bleScanTask(void *parameter) { BLEDevice::init(""); pBLEScan = BLEDevice::getScan(); pBLEScan->setAdvertisedDeviceCallbacks(new MyAdvertisedDeviceCallbacks()); pBLEScan->setActiveScan(true); pBLEScan->setInterval(100); pBLEScan->setWindow(99); while(1) { Serial.println("=== 开始BLE扫描 ==="); BLEScanResults foundDevices = pBLEScan->start(scanTime, false); Serial.print("Devices found: "); Serial.println(foundDevices.getCount()); pBLEScan->clearResults(); // 扫描完成后,通知WiFi任务可以执行 xTaskNotifyGive(wifiTaskHandle); // 阻塞等待WiFi任务完成的通知 ulTaskNotifyTake(pdTRUE, portMAX_DELAY); } } // WiFi发送任务 void wifiSendTask(void *parameter) { // 初始化WiFi连接 WiFi.begin(ssid, password); int retrycon = 50; while (WiFi.status() != WL_CONNECTED) { delay(500); if (--retrycon == 0) { Serial.println("RESTART"); ESP.restart(); } Serial.print("."); } Serial.print("WiFi connected with IP: "); Serial.println(WiFi.localIP()); while(1) { // 等待BLE任务的执行完成通知 ulTaskNotifyTake(pdTRUE, portMAX_DELAY); Serial.println("=== 开始WiFi数据发送 ==="); // 检查WiFi连接状态 if (WiFi.status() != WL_CONNECTED) { Serial.println("WiFi断开,尝试重连"); WiFi.reconnect(); delay(1000); if(WiFi.status() != WL_CONNECTED) { Serial.println("重连失败"); } } if(WiFi.status() == WL_CONNECTED) { HTTPClient http; http.begin("http://httpbin.org/ip"); int httpCode = http.GET(); if (httpCode > 0) { Serial.print("HTTP code "); Serial.println(httpCode); } else { Serial.println("Error on HTTP request"); } http.end(); } // WiFi任务完成后,通知BLE任务可以继续 xTaskNotifyGive(bleTaskHandle); // 可选:添加一个间隔,避免频繁切换 vTaskDelay(pdMS_TO_TICKS(3000)); } } void setup() { Serial.begin(115200); Serial.setDebugOutput(false); // 创建BLE扫描任务 xTaskCreate( bleScanTask, // 任务函数 "BLE_Scan_Task", // 任务名称 4096, // 任务栈大小 NULL, // 参数 1, // 任务优先级(WiFi任务优先级相同或稍高都可以) &bleTaskHandle // 任务句柄 ); // 创建WiFi发送任务 xTaskCreate( wifiSendTask, // 任务函数 "WiFi_Send_Task", // 任务名称 4096, // 任务栈大小 NULL, // 参数 1, // 任务优先级 &wifiTaskHandle // 任务句柄 ); } void loop() { // loop函数留空,因为所有逻辑都在FreeRTOS任务里 vTaskDelay(portMAX_DELAY); }
核心逻辑说明
- 任务分离:把BLE扫描和WiFi发送拆成两个独立任务,各自拥有自己的执行上下文
- 任务同步:用
xTaskNotifyGive()和ulTaskNotifyTake()实现两个任务的交替执行,确保同一时间只有一个任务占用射频 - 非阻塞调度:放弃了
delay()这种阻塞式的等待,改用FreeRTOS的vTaskDelay()和任务通知,让系统资源利用更高效
方案二:使用射频资源互斥锁
如果不想拆分任务,也可以用ESP-IDF提供的射频互斥锁来手动控制射频资源的访问,确保BLE和WiFi操作不会同时进行。
关键代码片段
在你的原有代码基础上,添加互斥锁的操作:
#include <esp_wifi.h> #include <esp_bt.h> // 定义射频互斥锁 SemaphoreHandle_t rfSemaphore; void setup() { // ... 原有初始化代码 ... // 创建射频互斥锁 rfSemaphore = xSemaphoreCreateMutex(); } void loop() { // 获取射频锁,执行BLE扫描 xSemaphoreTake(rfSemaphore, portMAX_DELAY); Serial.println("=== 开始BLE扫描 ==="); BLEScanResults foundDevices = pBLEScan->start(scanTime, false); Serial.print("Devices found: "); Serial.println(foundDevices.getCount()); pBLEScan->clearResults(); xSemaphoreGive(rfSemaphore); vTaskDelay(pdMS_TO_TICKS(3000)); // 获取射频锁,执行WiFi操作 xSemaphoreTake(rfSemaphore, portMAX_DELAY); Serial.println("=== 开始WiFi数据发送 ==="); if (WiFi.status() != WL_CONNECTED) { WiFi.reconnect(); Serial.println("reconnect"); } else { Serial.println("connected to WiFi"); HTTPClient http; http.begin("http://httpbin.org/ip"); int httpCode = http.GET(); if (httpCode > 0) { Serial.print("HTTP code "); Serial.println(httpCode); } else { Serial.println("Error on HTTP request"); } http.end(); } xSemaphoreGive(rfSemaphore); vTaskDelay(pdMS_TO_TICKS(10000)); }
核心逻辑说明
- 互斥锁控制:在执行BLE或WiFi操作前,先获取
rfSemaphore锁,操作完成后释放锁,确保同一时间只有一个操作占用射频资源 - 兼容原有代码结构:不需要拆分任务,适合不想修改太多代码的场景
额外注意事项
- 任务优先级:如果用方案一,两个任务的优先级建议设置为相同,避免其中一个任务长期抢占资源
- 扫描时长:BLE扫描时长不要设置过长,否则会导致WiFi任务等待时间过久
- WiFi重连逻辑:建议在WiFi任务里添加更健壮的重连机制,比如失败后间隔递增的重试
内容的提问来源于stack exchange,提问作者Pascalco
相关产品推荐
相关产品推荐

