Skip to content

Defer BLE-callback display teardown and MSD update to the main loop (ESP32)#74

Closed
balloob wants to merge 1 commit into
OpenDisplay:mainfrom
balloob:fix/defer-ble-task-teardown
Closed

Defer BLE-callback display teardown and MSD update to the main loop (ESP32)#74
balloob wants to merge 1 commit into
OpenDisplay:mainfrom
balloob:fix/defer-ble-task-teardown

Conversation

@balloob

@balloob balloob commented Jul 3, 2026

Copy link
Copy Markdown
Contributor

Summary

On ESP32, onConnect() and onDisconnect() run in the BLE host task, but they performed work that races the Arduino loop() task:

  • onConnect() called updatemsdata() — I2C sensor reads and mutation of shared MSD/advertising state — concurrently with the same work in loop().
  • onDisconnect() called cleanupDirectWriteState(true)bbepSleep / SPI.end() / Wire.end() / pwrmgm(false) — while loop() may be mid bbepWriteData on the same SPI bus during a direct-write upload (the epdRefreshInProgress guard only covers the refresh phase, not the upload phase). That powers the panel down under an in-flight transfer and clears state the handler is still using.

Fix

Set pending flags in the callbacks and perform the work in loop(), mirroring the existing bleRestartAdvertisingPending deferral: onConnect sets bleConnectMsdUpdatePending, onDisconnect sets bleDirectWriteCleanupPending, and loop() acts on them right after the advertising-restart handling.

Verification

  • Compiled clean (not flashed) for esp32-N4 and nrf52840custom.

⚠️ Needs on-hardware testing. This changes concurrency timing on ESP32. Please verify: (1) connecting refreshes the MSD/advertisement as before; (2) disconnecting during a direct-write image upload cleanly aborts and powers the panel down without a crash or bus fault; (3) a normal image upload + refresh still completes. Note this touches the same onDisconnect block as #56 (touch-suspend), so the two will need a trivial merge.

…ESP32)

onConnect() and onDisconnect() run in the BLE host task. onConnect() called
updatemsdata() (I2C sensor reads + shared-state mutation) and onDisconnect()
called cleanupDirectWriteState() (bbepSleep/SPI.end/pwrmgm) directly. Both
race loop(): a disconnect firing while loop() is mid bbepWriteData powers the
panel down under an in-flight SPI transfer and clears state the handler is
still using; onConnect's updatemsdata races the loop's I2C/statics.

Set pending flags in the callbacks (like the existing bleRestartAdvertisingPending)
and do the work in loop() instead.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MgW3EZCfMkGFffseDPwV74
@balloob balloob requested a review from jonasniesner as a code owner July 3, 2026 22:13
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.

2 participants