From b71b46fd842ac9cc9076283049b5f76b6a621045 Mon Sep 17 00:00:00 2001 From: Jakub <106778416+MarekZegare4@users.noreply.github.com> Date: Thu, 27 Aug 2026 10:22:50 +0200 Subject: [PATCH] fix(ui): debounce the Hall sensor poll against reed-switch contact bounce pollHallSensor() acted on the raw pin reading immediately, unlike every other physical-input path in this file (MomentaryButton, pollCardKB()'s own last-raw edge check). A cheap mechanical reed switch -- one of the two sensor types the docs explicitly recommend wiring here, alongside a solid-state Hall IC -- can chatter for a few ms while the magnet crosses the trigger distance, so a poll every loop() tick during that window could flip _locked and fire _display->turnOff()/turnOn() repeatedly in that short span: wasted work on any panel, and a real cost on e-ink where each is a slow full-panel operation. A raw reading now has to hold steady for HALL_DEBOUNCE_MS (25ms, same threshold as MomentaryButton's ISR_DEBOUNCE_MS) before it replaces _hall_magnet_present and triggers the lock/unlock actions. Co-Authored-By: Claude Sonnet 5 --- examples/companion_radio/ui-new/UITask.cpp | 19 ++++++++++++++++--- examples/companion_radio/ui-new/UITask.h | 7 +++++++ 2 files changed, 23 insertions(+), 3 deletions(-) diff --git a/examples/companion_radio/ui-new/UITask.cpp b/examples/companion_radio/ui-new/UITask.cpp index a6e6f9de..fb4163e0 100644 --- a/examples/companion_radio/ui-new/UITask.cpp +++ b/examples/companion_radio/ui-new/UITask.cpp @@ -2255,11 +2255,24 @@ void UITask::pollCardKB() { // there's nothing to show), opening unlocks and wakes it, with no combo or // keypress either way. Independent of Auto-lock (Settings > Display), which is // a timeout-driven setting -- this is a direct physical event. +// +// Debounced against a mechanical reed switch chattering for a few ms as the +// magnet crosses the trigger distance -- a raw flip only becomes the new +// _hall_magnet_present once it's been steady for HALL_DEBOUNCE_MS, so a bounce +// can't fire the lock/unlock actions (each including a full display +// off/on -- slow and disruptive on e-ink) more than once per real transition. void UITask::pollHallSensor() { #if defined(PIN_HALL_SENSOR) - bool present = HALL_ACTIVE_HIGH ? (digitalRead(PIN_HALL_SENSOR) == HIGH) - : (digitalRead(PIN_HALL_SENSOR) == LOW); - if (present == _hall_magnet_present) return; + bool raw = HALL_ACTIVE_HIGH ? (digitalRead(PIN_HALL_SENSOR) == HIGH) + : (digitalRead(PIN_HALL_SENSOR) == LOW); + if (raw != _hall_candidate) { + _hall_candidate = raw; + _hall_candidate_since = millis(); + } + if (_hall_candidate == _hall_magnet_present) return; // no debounced change yet + if (millis() - _hall_candidate_since < HALL_DEBOUNCE_MS) return; // not steady long enough + + bool present = _hall_candidate; _hall_magnet_present = present; if (present) { // cover closed diff --git a/examples/companion_radio/ui-new/UITask.h b/examples/companion_radio/ui-new/UITask.h index a2ce2411..995dd046 100644 --- a/examples/companion_radio/ui-new/UITask.h +++ b/examples/companion_radio/ui-new/UITask.h @@ -219,6 +219,13 @@ class UITask : public AbstractUITask { #define HALL_ACTIVE_HIGH 0 #endif bool _hall_magnet_present = false; + // Contact-bounce guard for a mechanical reed switch (a Hall-effect IC reads + // clean, but the docs recommend either): a raw reading only replaces + // _hall_magnet_present once it's held steady for HALL_DEBOUNCE_MS, same + // threshold and reasoning as MomentaryButton's ISR_DEBOUNCE_MS. + static const uint32_t HALL_DEBOUNCE_MS = 25; + bool _hall_candidate = false; + uint32_t _hall_candidate_since = 0; #endif void pollHallSensor();