fix(eink): self-heal button state against the live pin; drop dead code

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 <noreply@anthropic.com>
This commit is contained in:
MarekZegare4
2026-06-28 11:53:23 +02:00
parent 1387cc9507
commit d8c5fa83de

View File

@@ -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
{