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
62 changes: 36 additions & 26 deletions src/helpers/ui/MomentaryButton.cpp
Original file line number Diff line number Diff line change
@@ -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;
Expand All @@ -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;
Expand All @@ -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);
}
Expand Down Expand Up @@ -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;
Expand All @@ -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;
}

Expand All @@ -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
}
}

Expand All @@ -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;
Expand Down
16 changes: 9 additions & 7 deletions src/helpers/ui/MomentaryButton.h
Original file line number Diff line number Diff line change
Expand Up @@ -2,18 +2,19 @@

#include <Arduino.h>

#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;
Expand All @@ -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;
};