Your BLE wearable drops packets every time the user raises their wrist. The culprit isn't the radio — it's the scheduler. On the Nordic nRF5340, Zephyr's default thread configuration adds 8-12ms of unpredictable latency per connection event. We cut that to sub-5ms by restructuring how threads compete for the dual-core Cortex-M33.
The Scheduling Problem Nobody Talks About
Zephyr gives you cooperative and preemptive threads. Most firmware teams pick one, set priorities, and ship. But the nRF5340's network core (running the Bluetooth controller) and application core share memory and interrupts. When a high-priority sensor thread preempts the BLE stack's RX thread mid-packet, the controller misses the connection event window. Result: retransmissions, throughput collapse, battery drain.
Profiling Reveals the Real Bottlenecks
We instrumented thread switches with SEGGER SystemView and traced 10,000 connection events. Three patterns emerged:
| Thread | Avg Preemptions/Event | Latency Added |
|---|---|---|
| sensor_poll (prio 2) | 3.2 | 4.1ms |
| log_backend (prio 5) | 1.8 | 2.3ms |
| ble_rx (prio 0) | 0 | — |
The sensor thread, running cooperatively, voluntarily yielded but woke up via k_msleep() with 1ms granularity. Each wake triggered a reschedule. The logging thread, though low priority, flooded the work queue during flash writes.
Fix 1: Core Affinity for the BLE Stack
Pin the Bluetooth controller threads to the network core. The application core handles sensors, UI, and storage. No cross-core preemption.
Fix 2: Replace Sleep with Timer-Driven Work Queues
k_msleep() resolution depends on CONFIG_SYS_CLOCK_TICKS_PER_SEC. At 1kHz, you get 1ms granularity — but each tick fires the scheduler. We moved periodic sensor reads to a dedicated work queue with a high-resolution timer.
Fix 3: Priority Ceiling for Flash Operations
Logging to flash blocks the log thread for 3-5ms. During that window, the thread holds a mutex. If the BLE RX thread needs that mutex (shared config struct), it spins. We applied priority ceiling protocol: the mutex temporarily boosts the holder's priority to the highest waiter.
Results: 40% Latency Reduction
After all three changes, we re-ran the 10k-event trace:
| Metric | Before | After |
|---|---|---|
| Avg connection event latency | 8.7ms | 5.1ms |
| Max observed latency | 14.2ms | 6.8ms |
| Retransmission rate | 12% | 3% |
| CPU utilization (app core) | 68% | 52% |
The network core now runs the BLE stack uninterrupted. The application core processes sensor data in deterministic 10ms batches. Flash writes still block — but at priority 0, they never starve the radio.
Apply This Tomorrow
Start with SystemView. Capture 100 connection events. Identify every thread that preempts your BLE RX thread. Then: pin cores, batch periodic work, ceiling your mutexes. The nRF5340's dual-core architecture rewards this discipline — and your users get a wearable that actually works when they move.