fix(radio): keep the radio serviced while an e-ink refresh blocks the loop

GxEPD2 polls the panel's BUSY pin for the whole refresh (up to ~1-2s),
stalling the main loop. A TX finishing in that window left the SX126x in
standby -- deaf -- until the refresh ended; packets received meanwhile could
be lost (only the latest is readable, and a later CRC failure rejects a good
one still pending). Hook GxEPD2's busy callback to finish TX / drain RX into
a small staging queue and re-arm right away, without touching dispatch/UI.
Wired only where the display driver supports it (GxEPD2: the two e-ink Solo
boards); OLED and sim builds are unchanged.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Jakub
2026-09-24 15:21:38 +02:00
co-authored by Claude Opus 5.5
parent 81cee3846f
commit 22eba3c244
13 changed files with 184 additions and 16 deletions
+13
View File
@@ -15,6 +15,16 @@
static bool g_sim_ready = false;
#endif
#ifdef DISPLAY_HAS_BUSY_PUMP
// Run from the e-ink driver's BUSY-pin wait (DisplayDriver::setBusyPumpFn(),
// wired in setup() below) so packets landing during a refresh are pulled off
// the radio as they arrive, and a TX that finishes mid-refresh goes straight
// back to listening -- see RadioLibWrapper::pumpRecvDuringBlockingWait().
static void pumpRadioDuringDisplayBusyWait(void*) {
radio_driver.pumpRecvDuringBlockingWait();
}
#endif
// Believe it or not, this std C function is busted on some platforms!
static uint32_t _atoi(const char* sp) {
uint32_t n = 0;
@@ -293,6 +303,9 @@ void setup() {
if (disp && the_mesh.getNodePrefs())
disp->setBrightness(the_mesh.getNodePrefs()->display_brightness);
ui_task.begin(disp, &sensors, the_mesh.getNodePrefs()); // still want to pass this in as dependency, as prefs might be moved
#ifdef DISPLAY_HAS_BUSY_PUMP
if (disp) disp->setBusyPumpFn(pumpRadioDuringDisplayBusyWait, nullptr);
#endif
#endif
#ifdef NRF52_PLATFORM