From a528514aa33bf7c1df5566130c5ab3541878cbd6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dobri=C8=99an=20Rare=C8=99=20Florin?= Date: Fri, 15 May 2026 10:15:48 +0300 Subject: [PATCH] fix: re-introduce debounce logic in MomentaryButton for NewUI De-bounce guard was present in OrigUI but lost during the NewUI / MomentaryButton refactor. Adds a 50ms edge filter (_last_edge_ms) inside check() to suppress electrical noise before the multi-click window logic runs. Fixes #2539. --- src/helpers/ui/MomentaryButton.cpp | 62 +++++++++++++++++------------- src/helpers/ui/MomentaryButton.h | 16 ++++---- 2 files changed, 45 insertions(+), 33 deletions(-) diff --git a/src/helpers/ui/MomentaryButton.cpp b/src/helpers/ui/MomentaryButton.cpp index 9d01e5b011..2931d94660 100644 --- a/src/helpers/ui/MomentaryButton.cpp +++ b/src/helpers/ui/MomentaryButton.cpp @@ -1,12 +1,15 @@ #include "MomentaryButton.h" -#define MULTI_CLICK_WINDOW_MS 280 +#define MULTI_CLICK_WINDOW_MS 280 +#define DEBOUNCE_MS 50 -MomentaryButton::MomentaryButton(int8_t pin, int long_press_millis, bool reverse, bool pulldownup, bool multiclick) { +MomentaryButton::MomentaryButton(int8_t pin, int long_press_millis, bool reverse, bool pulldownup, + bool multiclick) { + _last_edge_ms = 0; _pin = pin; _reverse = reverse; _pull = pulldownup; - down_at = 0; + down_at = 0; prev = _reverse ? HIGH : LOW; cancel = 0; _long_millis = long_press_millis; @@ -18,6 +21,7 @@ MomentaryButton::MomentaryButton(int8_t pin, int long_press_millis, bool reverse } MomentaryButton::MomentaryButton(int8_t pin, int long_press_millis, int analog_threshold) { + _last_edge_ms = 0; _pin = pin; _reverse = false; _pull = false; @@ -38,7 +42,7 @@ void MomentaryButton::begin() { } } -bool MomentaryButton::isPressed() const { +bool MomentaryButton::isPressed() const { int btn = _threshold > 0 ? (analogRead(_pin) < _threshold) : digitalRead(_pin); return isPressed(btn); } @@ -67,21 +71,26 @@ int MomentaryButton::check(bool repeat_click) { int event = BUTTON_EVENT_NONE; int btn = _threshold > 0 ? (analogRead(_pin) < _threshold) : digitalRead(_pin); + if (btn != prev) { + uint32_t now = millis(); + if ((uint32_t)(now - _last_edge_ms) < DEBOUNCE_MS) goto skip_edge; + _last_edge_ms = now; + if (isPressed(btn)) { - down_at = millis(); + down_at = now; } else { // button UP if (_long_millis > 0) { - if (down_at > 0 && (unsigned long)(millis() - down_at) < _long_millis) { // only a CLICK if still within the long_press millis - _click_count++; - _last_click_time = millis(); - _pending_click = true; - } - } else { + if (down_at > 0 && (unsigned long)(millis() - down_at) < _long_millis) { _click_count++; _last_click_time = millis(); _pending_click = true; + } + } else { + _click_count++; + _last_click_time = millis(); + _pending_click = true; } if (event == BUTTON_EVENT_CLICK && cancel) { event = BUTTON_EVENT_NONE; @@ -92,8 +101,9 @@ int MomentaryButton::check(bool repeat_click) { down_at = 0; } prev = btn; + skip_edge:; } - if (!isPressed(btn) && cancel) { // always clear the pending 'cancel' once button is back in UP state + if (!isPressed(btn) && cancel) { // always clear the pending 'cancel' once button is back in UP state cancel = 0; } @@ -112,7 +122,7 @@ int MomentaryButton::check(bool repeat_click) { if (down_at > 0 && repeat_click) { unsigned long diff = (unsigned long)(millis() - down_at); if (diff >= 700) { - event = BUTTON_EVENT_CLICK; // wait 700 millis before repeating the click events + event = BUTTON_EVENT_CLICK; // wait 700 millis before repeating the click events } } @@ -122,19 +132,19 @@ int MomentaryButton::check(bool repeat_click) { return event; } switch (_click_count) { - case 1: - event = BUTTON_EVENT_CLICK; - break; - case 2: - event = BUTTON_EVENT_DOUBLE_CLICK; - break; - case 3: - event = BUTTON_EVENT_TRIPLE_CLICK; - break; - default: - // For 4+ clicks, treat as triple click? - event = BUTTON_EVENT_TRIPLE_CLICK; - break; + case 1: + event = BUTTON_EVENT_CLICK; + break; + case 2: + event = BUTTON_EVENT_DOUBLE_CLICK; + break; + case 3: + event = BUTTON_EVENT_TRIPLE_CLICK; + break; + default: + // For 4+ clicks, treat as triple click? + event = BUTTON_EVENT_TRIPLE_CLICK; + break; } _click_count = 0; _last_click_time = 0; diff --git a/src/helpers/ui/MomentaryButton.h b/src/helpers/ui/MomentaryButton.h index 358a343b0f..6c9e66aa43 100644 --- a/src/helpers/ui/MomentaryButton.h +++ b/src/helpers/ui/MomentaryButton.h @@ -2,18 +2,19 @@ #include -#define BUTTON_EVENT_NONE 0 -#define BUTTON_EVENT_CLICK 1 -#define BUTTON_EVENT_LONG_PRESS 2 +#define BUTTON_EVENT_NONE 0 +#define BUTTON_EVENT_CLICK 1 +#define BUTTON_EVENT_LONG_PRESS 2 #define BUTTON_EVENT_DOUBLE_CLICK 3 #define BUTTON_EVENT_TRIPLE_CLICK 4 class MomentaryButton { + uint32_t _last_edge_ms; int8_t _pin; int8_t prev, cancel; bool _reverse, _pull; int _long_millis; - int _threshold; // analog mode + int _threshold; // analog mode unsigned long down_at; uint8_t _click_count; unsigned long _last_click_time; @@ -23,11 +24,12 @@ class MomentaryButton { bool isPressed(int level) const; public: - MomentaryButton(int8_t pin, int long_press_mills=0, bool reverse=false, bool pulldownup=false, bool multiclick=true); + MomentaryButton(int8_t pin, int long_press_mills = 0, bool reverse = false, bool pulldownup = false, + bool multiclick = true); MomentaryButton(int8_t pin, int long_press_mills, int analog_threshold); void begin(); - int check(bool repeat_click=false); // returns one of BUTTON_EVENT_* - void cancelClick(); // suppress next BUTTON_EVENT_CLICK (if already in DOWN state) + int check(bool repeat_click = false); // returns one of BUTTON_EVENT_* + void cancelClick(); // suppress next BUTTON_EVENT_CLICK (if already in DOWN state) uint8_t getPin() { return _pin; } bool isPressed() const; };