You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.07 18:17:26