-
-
Notifications
You must be signed in to change notification settings - Fork 0
/
ButtonLib2.cpp
195 lines (169 loc) · 6.84 KB
/
ButtonLib2.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
// ====================================================================================================
//
// PushButton library version 1.0
// written 2011 - trent m. wyatt
//
// v1.01 May 4, 2022
// fixed typos in example code
//
#include <Arduino.h>
#include "ButtonLib2.h"
ButtonPressCallback bpcb = nullptr;
// ====================================================================================================
// Set up a specific input pin for use as a push button input.
// Note: The input pin will be configured to be pulled up by an internal
// 20K resistor to Vcc so fewer external parts are needed. This means
// that a digitalRead(...) of an un-pressed button will read as HIGH
// and the digitalRead(...) of a pressed button (connected to GND on
// one side and the input pin on the Arduino) will read as LOW.
//
void set_button_input(const char pin) {
pinMode(pin, INPUT); // set the button pin as an input
digitalWrite(pin, HIGH); // use the internal 20k pullup resistor to pull the pin HIGH by default
}
// ====================================================================================================
//
// Get the state of a push button input
// Returns: true if the specified button was continuously pressed, otherwise returns false.
//
// Note: The button must be continuously pressed (no jitter/makes/breaks) for at least as long as KEYDBDELAY
// (key debounce delay). This smooths out the dozens of button connections/disconnections detected at
// the speed of the CPU when the button first begins to make contact into a lower frequency responce.
//
bool button_pressed(const char pin) {
unsigned long int presstime = millis() + KEYDBDELAY;
while (!digitalRead(pin)) { // remember a pressed button returns LOW not HIGH
if (millis() >= presstime) {
return true;
}
}
return false;
}
// ====================================================================================================
//
// See if the user presses a button once, twice, or three times. Also detect whether the user has held
// down the button to indicate a "long" press. This is an enhanced button press check
// (as opposed to button_pressed(...)) in that it attempts to detect gestures.
//
char check_button_gesture(const char pin) {
if (!button_pressed(pin)) {
return NOT_PRESSED;
}
// The button is pressed.
// Time how long the user presses the button to get the intent/gesture
unsigned long int presstime = millis() + KEYLONGDELAY; // get the current time in milliseconds plus the long-press offset
while (button_pressed(pin)) {
if (millis() >= presstime) {
return SINGLE_PRESS_LONG;
}
}
// check for double-tap/press
// The user has released the button, but this might be a double-tap. Check again and decide.
presstime = millis() + ALLOWED_MULTIPRESS_DELAY;
while (millis() < presstime) {
if (button_pressed(pin)) {
presstime = millis() + KEYLONGDELAY;
while (button_pressed(pin)) {
if (millis() >= presstime) {
return DOUBLE_PRESS_LONG;
}
}
// check for triple-tap/press
// The user has released the button, but this might be a triple-tap. Check again and decide.
presstime = millis() + ALLOWED_MULTIPRESS_DELAY;
while (millis() < presstime) {
if (button_pressed(pin)) {
presstime = millis() + KEYLONGDELAY;
while (button_pressed(pin)) {
if (millis() >= presstime) {
return TRIPLE_PRESS_LONG;
}
}
return TRIPLE_PRESS_SHORT;
}
}
return DOUBLE_PRESS_SHORT;
}
}
return SINGLE_PRESS_SHORT;
}
// ====================================================================================================
//
// This wrapper function is used to allow consistent return values for back-to-back calls of
// check_button_internal(...) while the button is continuously pressed. Without this extra step
// DOUBLE_BUTTON_LONG and TRIPLE_BUTTON_LONG presses would return correctly on their first check but
// would then be reported as SINGLE_BUTTON_LONG after that if the user kept the button pressed.
//
// Additionally this function prevents the spurious reporting of a *_BUTTON_SHORT state to be reported
// after the user has let go of a button once one or more *_BUTTON_LONG states have been observed.
//
char check_button(const char pin, char& lastButtonState) {
char state = check_button_gesture(pin);
if (state & LONG_PRESS) {
if (lastButtonState & LONG_PRESS) {
state = LONG_PRESS | (lastButtonState & 0x0F);
if (nullptr != bpcb) {
bpcb(pin, state);
}
return state;
}
} else {
if (lastButtonState & LONG_PRESS) {
if (nullptr != bpcb) {
bpcb(pin, NOT_PRESSED);
}
return lastButtonState = NOT_PRESSED;
}
}
if (nullptr != bpcb) {
bpcb(pin, state);
}
return lastButtonState = state;
}
// ====================================================================================================
//
// example use:
//
// ====================================================================================================
//#define EXAMPLE_SETUP_AND_LOOP
#ifdef EXAMPLE_SETUP_AND_LOOP
// NOTE: change/define the following pin(s) based on your project/connections
#define BUTTON_PIN_1 2
char Button1State = NOT_PRESSED;
#define BUTTON_PIN_2 3
char Button2State = NOT_PRESSED;
void report_button(const char state, const char* const label = NULL) {
switch (state) {
case SINGLE_PRESS_SHORT: Serial.print(F("Single button short press")); break;
case SINGLE_PRESS_LONG: Serial.print(F("Single button long press")); break;
case DOUBLE_PRESS_SHORT: Serial.print(F("Double button short press")); break;
case DOUBLE_PRESS_LONG: Serial.print(F("Double button long press")); break;
case TRIPLE_PRESS_SHORT: Serial.print(F("Triple button short press")); break;
case TRIPLE_PRESS_LONG: Serial.print(F("Triple button long press")); break;
default:
case NOT_PRESSED:
return;
}
if (NULL != label) {
Serial.print(F(" on push button "));
Serial.print(label);
}
Serial.println();
}
void setup(void) {
Serial.begin(9600); // 115200, 2000000, whatever matches your serial monitor baud rate
uint32_t timer = millis() + 2000; // limit how long we wait on the serial port in case of no connection
while (!Serial && millis() < timer) {
// do nothing but wait until we timeout or the Serial port initializes
}
Serial.flush();
Serial.println(F("\n\nArduino Core Library - Button Library Test"));
// In this example we use two buttons but you can use only one if you want
set_button_input(BUTTON_PIN_1);
set_button_input(BUTTON_PIN_2);
}
void loop(void) {
report_button(check_button(BUTTON_PIN_1, Button1State), "1");
report_button(check_button(BUTTON_PIN_2, Button2State), "2");
}
#endif // #ifdef EXAMPLE_SETUP_AND_LOOP