Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
72 changes: 50 additions & 22 deletions src/helpers/ui/MomentaryButton.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

#define MULTI_CLICK_WINDOW_MS 280

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, int debounce_ms) {
_pin = pin;
_reverse = reverse;
_pull = pulldownup;
Expand All @@ -15,9 +15,12 @@ MomentaryButton::MomentaryButton(int8_t pin, int long_press_millis, bool reverse
_last_click_time = 0;
_multi_click_window = multiclick ? MULTI_CLICK_WINDOW_MS : 0;
_pending_click = false;
_debounce_ms = debounce_ms;
_last_debounce_time = 0;
_last_read = prev;
}

MomentaryButton::MomentaryButton(int8_t pin, int long_press_millis, int analog_threshold) {
MomentaryButton::MomentaryButton(int8_t pin, int long_press_millis, int analog_threshold, int debounce_ms) {
_pin = pin;
_reverse = false;
_pull = false;
Expand All @@ -30,6 +33,9 @@ MomentaryButton::MomentaryButton(int8_t pin, int long_press_millis, int analog_t
_last_click_time = 0;
_multi_click_window = MULTI_CLICK_WINDOW_MS;
_pending_click = false;
_debounce_ms = debounce_ms;
_last_debounce_time = 0;
_last_read = prev;
}

void MomentaryButton::begin() {
Expand Down Expand Up @@ -62,36 +68,57 @@ bool MomentaryButton::isPressed(int level) const {
}
}

void MomentaryButton::setDebounceMs(int ms) {
if (ms < 0) ms = 0;
_debounce_ms = ms;
}

int MomentaryButton::getDebounceMs() const {
return _debounce_ms;
}

int MomentaryButton::check(bool repeat_click) {
if (_pin < 0) return BUTTON_EVENT_NONE;

int event = BUTTON_EVENT_NONE;
int btn = _threshold > 0 ? (analogRead(_pin) < _threshold) : digitalRead(_pin);
if (btn != prev) {
if (isPressed(btn)) {
down_at = millis();
} 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
int raw_btn = _threshold > 0 ? (analogRead(_pin) < _threshold) : digitalRead(_pin);

// debounce: detect changes and wait for stability
if (raw_btn != _last_read) {
_last_debounce_time = millis();
_last_read = raw_btn;
}

int btn = raw_btn; // use the latest raw reading for logic below

// Only accept the state change if it has been stable for _debounce_ms
if ((unsigned long)(millis() - _last_debounce_time) >= (unsigned long)_debounce_ms) {
if (btn != prev) {
if (isPressed(btn)) {
down_at = millis();
} 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 {
_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;
_click_count = 0;
_last_click_time = 0;
_pending_click = false;
if (event == BUTTON_EVENT_CLICK && cancel) {
event = BUTTON_EVENT_NONE;
_click_count = 0;
_last_click_time = 0;
_pending_click = false;
}
down_at = 0;
}
down_at = 0;
prev = btn;
}
prev = btn;
}
if (!isPressed(btn) && cancel) { // always clear the pending 'cancel' once button is back in UP state
cancel = 0;
Expand Down Expand Up @@ -133,6 +160,7 @@ int MomentaryButton::check(bool repeat_click) {
break;
default:
// For 4+ clicks, treat as triple click?

event = BUTTON_EVENT_TRIPLE_CLICK;
break;
}
Expand Down
14 changes: 12 additions & 2 deletions src/helpers/ui/MomentaryButton.h
Original file line number Diff line number Diff line change
Expand Up @@ -20,14 +20,24 @@ class MomentaryButton {
int _multi_click_window;
bool _pending_click;

// Debounce support
unsigned long _last_debounce_time;
int _last_read;
int _debounce_ms; // debounce interval in milliseconds

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, int analog_threshold);
// debounce_ms: milliseconds of stable state required to accept changes (default 30ms)
MomentaryButton(int8_t pin, int long_press_mills=0, bool reverse=false, bool pulldownup=false, bool multiclick=true, int debounce_ms=30);
MomentaryButton(int8_t pin, int long_press_mills, int analog_threshold, int debounce_ms=30);
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)
uint8_t getPin() { return _pin; }
bool isPressed() const;

// Debounce control
void setDebounceMs(int ms);
int getDebounceMs() const;
};