Skip to content

fix(ble): share sensor queue capacity proportionally under load - #313

Closed
TobiasRoeddiger wants to merge 11 commits into
codex/pr293-sensor-freshnessfrom
codex/pr293-fair-sensor-queue
Closed

TobiasRoeddiger wants to merge 11 commits into
codex/pr293-sensor-freshnessfrom
codex/pr293-fair-sensor-queue

Conversation

@TobiasRoeddiger

@TobiasRoeddiger TobiasRoeddiger commented Sep 29, 2026 •

Copy link
Copy Markdown
Member

Bursty sensor publications compete for one BLE queue. Under overload, FIFO eviction disproportionately loses PPG samples even when other sensors retain much more of their input. For example, the isolated 32-entry reference delivered 8.8% of the right earphone’s offered PPG samples during playback, versus roughly 17–19% for its other sensors.

Use a shared bounded queue that compares recent delivered/offered sample fractions. Send the least represented sensor first; on overflow, discard the oldest queued publication from the most represented sensor. Count samples using the existing sensor schemes, retain publication order within each sensor, and decay history so rate changes are handled. This changes queue scheduling only.

Stacked on #312, whose current base already contains #308. Both changes are needed for a short, fair backlog; a smaller FIFO alone does not solve the imbalance. No sensor rates, wire format, radio parameters or audio codec settings are changed by this PR.

The queue policy was tested independently at the same 32-entry capacity on the pre-batching db71350e base before tuning capacity. Both earphones were a native LEAudio pair on one Pixel 9a, with all five advertised BLE maximum settings. Each capture lasted 45 seconds after warm-up. Fractions use actual samples offered to the BLE queue; acquisition shortfalls are not counted as BLE loss. Counter and phone windows differ slightly, so percentages are approximate.

Audio condition FIFO retention spread L/R, percentage points Fair queue spread L/R, percentage points
none 24.3 / 13.1 0.6 / 0.8
playback 19.7 / 10.5 0.8 / 1.1
microphone 15.0 / 23.9 0.8 / 0.9
both 14.4 / 20.0 0.9 / 0.9

All captures retained all five streams with valid finite payloads. Quiet measurement windows had zero additional output-underrun blocks. Different connections can allocate airtime differently between ears; these fairness results are per ear, not a claim of equal left/right bandwidth or universally higher total throughput.

The default is now 32 entries in #312, allowing more transient buffering than 16. Earlier fixed-connection sizing comparisons found little sustained-throughput benefit at 32 and generally fresher readings at 16, but did not establish that 16 minimizes loss during temporary BLE stalls. Thirty-two trades some data age for additional recovery headroom; it does not solve sustained overload. The isolated policy comparison above already used 32 entries.

Earlier clean combined confirmation at 16 entries (2.2.10 + #308 + #309 + #312 + this scheduling policy):

Audio Pair sensor payload kB/s Worst-sensor estimated P95 age L/R, ms Quiet underrun increase L/R
none 35.456 66.5 / 60.5 0 / 0
playback 13.096 117.2 / 291.2 0 / 0
microphone 25.356 146.6 / 70.1 0 / 0
both 25.585 145.8 / 73.4 0 / 0

Both earphones remained a native LEAudio pair on one Pixel 9a. All five sensors used their advertised BLE maxima: IMU 100 Hz, PPG 512 Hz, bone 800 Hz, optical temperature 64 Hz, and pressure/temperature 200 Hz. Clean confirmation captures lasted 45 seconds after warm-up, with independent raw-packet decoding, all five streams, no malformed packets and no nonfinite values. Payload rates exclude packet headers. Data ages are estimates from clock probes, not display latency. Counter observations span the initial measurement read through the post-capture snapshot; that interval is slightly longer than the packet window. No debugger access occurred between those observations.

At the earlier 16-entry revision, both pristine build variants passed. At that revision, native Zephyr/Unity tests covered overflow freshness and metadata, purge/reset, bursty sensor fairness at several service rates, changing input rates, invalid input and initialization from nonzero memory. The final combined hardware source is 8c79a36b; its application source matched the earlier 16-entry PR revision; the current queue default is 32. The combined image also uses the separate radio-core configuration. Both pristine build variants also passed on the earlier PR source 835db39c. No measurement-only counters or runtime tuning controls are included.

Separate digital playback spot checks on the final combined image passed with playback alone and simultaneous audio: eight coherent 20 ms windows per ear, expected channel tones, no silent windows or full-scale clipping, and tone-fit R² ≥ 0.99. Each earphone’s microphone ISO sequence advanced at approximately 100 frames/s during the simultaneous-audio check; the phone received nonzero PCM. These checks do not certify acoustic speaker output or microphone fidelity. Earlier microphone-only output-counter increases in the actual app remain documented separately; quiet collector results do not resolve their cause.

Retained setup failures: an initial combined microphone-only attempt stalled before notification setup completed; a collector-only descriptor submission check/retry was added, and the repeated case passed. Separately, the freshness-only current-base image had one right-ear Bluetooth timeout during simultaneous-audio startup; its two repeats passed. These failed attempts are excluded from the completed-capture tables and remain recorded; their causes are not established.

Earlier actual-app confirmation on the installed 16-entry combined image: both earphones stayed paired, with all five supported BLE maximum rates verified before and after every condition. The OpenWearables app displayed live sensor graphs during each 45-second video. Small timed counter reads occurred during capture; full snapshots were outside the video.

Audio Graph changes/s Longest observed graph-change gap, ms Timed output-underrun increase L/R Before/after increase L/R
none 38.1 84.3 0 / 0 0 / 0
playback 44.4 86.4 0 / 4 0 / 4
microphone 47.6 87.0 0 / 0 0 / 0
both 39.0 83.3 0 / 5 1 / 5

Graph changes are a pixel-based responsiveness check, not source-to-screen latency. Playback recorded four additional 1 ms output-underrun blocks on the right. Simultaneous audio recorded five on the right during the timed observations; the wider before/after interval also included one on the left. Their causes are unresolved. The quiet combined collector captures had no additional blocks, so these app observations must not be described as universally glitch-free audio. The microphone-only and simultaneous app captures delivered nonzero phone PCM, with both earphone microphone sequences progressing at about 100 frames/s. These observations do not certify acoustic output or microphone fidelity.

32-entry update: merged the capacity change from #312 while retaining the existing scheduling diff. A pristine standard build passed on e1026e18, with generated capacity verified as 32. The earlier measurements above remain historical results, not a new hardware run.

@github-actions

github-actions Bot commented Sep 29, 2026 •

Copy link
Copy Markdown

✅ Unit tests passed

2 passed, 0 failed/error, 0 skipped — view workflow run

Test scenario Platform Result
sensor_component/openearable.unit.sensor_component native_sim/native/64 ✅ passed
ring_buffer/openearable.unit.ring_buffer native_sim/native/64 ✅ passed

Download the unit-test-results artifact for full Twister reports and logs.

@github-actions

Copy link
Copy Markdown

Build output available:
openearable_v2_firmware.elf.zip
openearable_v2_fota.zip

@github-actions

github-actions Bot commented Sep 29, 2026 •

Copy link
Copy Markdown

Compiler warnings

The extended-warning build completed successfully.

Application compiler warnings

None.

View this workflow run

@github-actions

github-actions Bot commented Sep 29, 2026 •

Copy link
Copy Markdown

CodeChecker static analysis

✅ No non-style issues found.

@github-actions

Copy link
Copy Markdown

Build output available:
openearable_v2_firmware.elf.zip
openearable_v2_fota.zip

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant