From 57ebd80fa73e8a11e2b08134770ba2f79148b608 Mon Sep 17 00:00:00 2001 From: MarekZegare4 Date: Sun, 28 Jun 2026 12:21:30 +0200 Subject: [PATCH] fix(eink): fall back to polling when no GPIOTE channel is free MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit begin() claimed the trampoline slot and set _isr_slot before calling attachInterrupt(), so a button that lost the GPIOTE race (only 8 channels, shared with the radio's DIO1) would take the ISR branch forever while its interrupt never fired. attachInterrupt() returns 0 when channels are exhausted — only commit the slot on success, else leave _isr_slot = -1 so check() uses the polling path. Combined with the live-pin reconcile, an exhausted button degrades cleanly to plain polling instead of misbehaving. Co-Authored-By: Claude Opus 4.8 --- src/helpers/ui/MomentaryButton.cpp | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/src/helpers/ui/MomentaryButton.cpp b/src/helpers/ui/MomentaryButton.cpp index 42eae4cc..ae27ee09 100644 --- a/src/helpers/ui/MomentaryButton.cpp +++ b/src/helpers/ui/MomentaryButton.cpp @@ -89,9 +89,17 @@ void MomentaryButton::begin() { #ifdef BUTTON_USE_INTERRUPTS for (uint8_t i = 0; i < MAX_ISR_BUTTONS; i++) { if (_isr_table[i] == nullptr) { - _isr_table[i] = this; - _isr_slot = i; - attachInterrupt(digitalPinToInterrupt(_pin), ISR_TRAMPOLINES[i], CHANGE); + // The nRF52 has only 8 GPIOTE channels, shared across every pin using + // attachInterrupt() (the radio's DIO1 takes one too). It returns 0 when + // they're exhausted — only claim the trampoline slot if a channel was + // actually allocated. Otherwise leave _isr_slot = -1 so check() uses the + // polling path: that button still works, it just won't capture edges + // that land during a blocking display refresh. Retrying another index is + // pointless — the channel pool, not the trampoline slot, is what ran out. + if (attachInterrupt(digitalPinToInterrupt(_pin), ISR_TRAMPOLINES[i], CHANGE) != 0) { + _isr_table[i] = this; + _isr_slot = i; + } break; } }