Home / Tutorials / ESP32 Tutorial / Solving BLE and Wi-Fi Concurrency on ESP32 Using FreeRTOS
pcbway
BLE WiFi concurrency using RTOS

Solving BLE and Wi-Fi Concurrency on ESP32 Using FreeRTOS

Running Bluetooth Low Energy (BLE) scanning and Wi-Fi networking at the same time on an ESP32 sounds straightforward—until it isn’t.

Many developers discover that once BLE scanning starts, their Wi-Fi web server becomes unreliable. Pages load intermittently, HTTP requests time out, or BLE scans suddenly stop when Wi-Fi traffic increases.

In this article, we’ll walk through why this happens and how using FreeRTOS (RTOS) on the ESP32 cleanly solves the concurrency problem.


The Core Problem

On ESP32, BLE and Wi-Fi share critical hardware resources:

  • The same radio
  • The same CPU
  • The same event loop when using blocking APIs

A naïve implementation often looks like this:

void loop() {
  bleScan();        // blocks
  handleHttp();     // blocks
}

This design appears to work at first, but quickly degrades under real usage.

Typical Symptoms

  • BLE scanning pauses while HTTP requests are active
  • The web server becomes unreachable during scans
  • Random socket resets
  • BLE callbacks fire late or not at all

These issues are not bugs in BLE or Wi-Fi—they are architectural problems.


Why Delays and Timers Don’t Fix It

A common attempt is to add delay() calls:

bleScan();
delay(100);

Unfortunately:

  • delay() blocks the current task
  • BLE callbacks still execute in the same context
  • Wi-Fi servicing becomes opportunistic

This approach does not guarantee fairness between BLE and Wi-Fi.

The problem is not timing — it’s task isolation.


The ESP32 Advantage: Built-in FreeRTOS

Unlike simpler microcontrollers, the ESP32 runs FreeRTOS natively, offering:

  • Preemptive multitasking
  • Two CPU cores
  • Task prioritization
  • Core pinning

Instead of fighting the scheduler, we can design with it.


High-Level Design

The solution is to split responsibilities into independent RTOS tasks, each with a single purpose.

BLE Wifi ESP32 Concurrency

Each subsystem runs independently, coordinated only through minimal shared state.


Task Responsibilities

BLE Task (Core 0)

The BLE task is responsible for:

  • Running short, non-blocking BLE scans
  • Parsing advertisement payloads
  • Updating shared state when new data arrives

Example:

void bleTask(void* param) {
  for (;;) {
    startBleScan();
    updateSharedState();
    vTaskDelay(pdMS_TO_TICKS(100));
  }
}

Key characteristics:

  • No networking code
  • No JSON formatting
  • Frequent yielding to the scheduler

HTTP Task (Core 1)

The HTTP task handles all networking:

void httpTask(void* param) {
  server.begin();

  for (;;) {
    WiFiClient client = server.available();
    if (client) {
      handleRequest(client);
      client.stop();
    }
    vTaskDelay(pdMS_TO_TICKS(10));
  }
}

This task remains responsive regardless of BLE activity.


Shared State: Keep It Simple

Shared data between tasks is intentionally minimal:

char lastData[32];
int lastSignalStrength;
volatile bool hasData;
volatile bool hasNewData;
unsigned long lastSeenMillis;

Why this works safely:

  • Single-writer pattern
  • No dynamic allocation
  • No STL containers
  • Short critical sections
  • volatile ensures cross-core visibility

This approach avoids the need for mutexes while maintaining robustness.


Handling Data Expiration (TTL)

To prevent stale data from being served indefinitely, we added a time-to-live (TTL) mechanism:

if (hasData && millis() - lastSeenMillis > DATA_TTL_MS) {
  hasData = false;
  hasNewData = false;
}

This mirrors real-world scanning behavior:

  • Data appears
  • Data ages out
  • Clients stop receiving old results

Why This Architecture Works

1. True Concurrency

BLE scanning and Wi-Fi networking never block each other.

2. Predictable Scheduling

FreeRTOS ensures both tasks get CPU time when needed.

3. Improved Stability

No more random disconnects or missed scans.

4. Easy Scalability

Additional tasks (MQTT, OTA, logging) can be added cleanly.


Results

After moving to an RTOS-based design:

  • BLE scanning runs continuously
  • The web server is always reachable
  • No socket resets under load
  • No missed BLE advertisements
  • System behavior is deterministic and production-ready

Key Takeaways

  • BLE + Wi-Fi issues on ESP32 are architectural, not library bugs
  • Blocking code in loop() does not scale
  • FreeRTOS is not optional for serious ESP32 applications
  • Separate concerns into dedicated tasks
  • Keep shared state small and explicit

Final Thoughts

If you’re building an ESP32 application that combines BLE scanning and Wi-Fi networking, embracing FreeRTOS is the turning point between a fragile prototype and a reliable system.

Once tasks are isolated and scheduled correctly, the ESP32 becomes an extremely capable concurrent platform.

Check Also

ESP32-Sensor-Log-Google-Sheets

How to Send ESP32 Sensor Data to Google Sheets (2026) 

Updated: December 30, 2025 In this tutorial, I’ll show you exactly how I send sensor …

Index