From d8c5fa83dee6afd24ca5165c61732a37f78f4082 Mon Sep 17 00:00:00 2001 From: MarekZegare4 Date: Sun, 28 Jun 2026 11:53:23 +0200 Subject: [PATCH] fix(eink): self-heal button state against the live pin; drop dead code MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The interrupt path trusted the replayed edge stream alone (btn = prev) and never sampled the live pin. Any lost edge — buffer overflow, a debounce- dropped settling edge, or a missed GPIOTE event — left prev diverged from the hardware until the next captured edge, i.e. a stuck button. After draining the queue, reconcile against digitalRead(): a no-op when the edge stream already matches, a one-call self-heal when it doesn't. Also remove dead code in applyTransition() (a local `event` that was always BUTTON_EVENT_NONE, making its cancel-on-click branch unreachable) carried over verbatim from the original inline logic. No behaviour change. Co-Authored-By: Claude Opus 4.8 --- src/helpers/ui/MomentaryButton.cpp | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/helpers/ui/MomentaryButton.cpp b/src/helpers/ui/MomentaryButton.cpp index 659a81a0..42eae4cc 100644 --- a/src/helpers/ui/MomentaryButton.cpp +++ b/src/helpers/ui/MomentaryButton.cpp @@ -125,7 +125,6 @@ bool MomentaryButton::isPressed(int level) const { void MomentaryButton::applyTransition(int btn, unsigned long at) { if (btn == prev) return; - int event = BUTTON_EVENT_NONE; // kept for parity with the original inline logic below if (isPressed(btn)) { down_at = at; } else { @@ -141,12 +140,6 @@ void MomentaryButton::applyTransition(int btn, unsigned long at) { _last_click_time = at; _pending_click = true; } - if (event == BUTTON_EVENT_CLICK && cancel) { - event = BUTTON_EVENT_NONE; - _click_count = 0; - _last_click_time = 0; - _pending_click = false; - } down_at = 0; } prev = btn; @@ -164,7 +157,14 @@ int MomentaryButton::check(bool repeat_click) { // click-duration / multi-click-window math still lines up correctly. uint8_t lvl; uint32_t at; while (popEdge(lvl, at)) applyTransition(lvl, at); - btn = prev; // edges already caught us up to the live level + // Self-heal: if an edge was ever lost (buffer overflow, a debounce-dropped + // settling edge, or a missed GPIOTE event), prev would otherwise stay + // diverged from the hardware until the next captured edge — a stuck button. + // Reconcile against the live pin; a no-op whenever the edge stream already + // matches it, which is the normal case. + int live = digitalRead(_pin); + if (live != prev) applyTransition(live, millis()); + btn = prev; } else #endif {