-
Notifications
You must be signed in to change notification settings - Fork 0
/
Buttons.h
503 lines (420 loc) · 15.1 KB
/
Buttons.h
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
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
#ifndef Buttons
#define Buttons
#include <stdio.h>
#include <Arduino.h>
#include <type_traits>
enum ButtonType{
ONLYPRESS,
ONLYHOLD,
BOTH
};
enum PressType{
INDETERM,
PRESS,
HOLD
};
// Class to track the events of the button
class PressEvent{
PressType type;
int amount;
unsigned long time;
unsigned long waittime;
int triggd;
public:
PressEvent(PressType t = INDETERM, int a = 0, unsigned long time = 0 , bool triggd = false);
unsigned long eventtime() const; // Get the time of the event
int triggered() const; // Get the triggered
// status of the event
void settype(PressType t); // Set the type of the event
void setamount(int a); // Set the amount of the event
void settime(unsigned long t); // Set the time of the event
void settriggd(int t); // Set the event as triggered
void sethold(int amount, unsigned long time); // Set the event as hold
bool istriggered() const; // Check if the hold event
// has been triggered
bool isindeterm() const; // Check if the event is
// indeterminate
int ispress(PressType t) const; // Check if the event is of type
// t and amount a
};
#include <Buttons.h>
PressEvent::PressEvent(PressType t,
int a ,
unsigned long time,
bool triggd):
type(t),
amount(a),
time(time),
triggd(triggd)
{};
unsigned long PressEvent::eventtime() const{
return time;
};
int PressEvent::triggered() const{
return triggd;
};
int PressEvent::ispress(PressType t) const{
if (type == t){
return amount;
}
return 0;
};
void PressEvent::settype(PressType t){
if ( t != type){
triggd = false;
type = t;
time = millis();
};
};
void PressEvent::setamount(int a){
amount = a;
};
void PressEvent::settime(unsigned long t){
time = t;
};
void PressEvent::settriggd(int t){
triggd = t;
};
void PressEvent::sethold(int amount, unsigned long time){
if (type != HOLD){
settype(HOLD);
setamount(amount);
settime(time);
}
};
bool PressEvent::istriggered() const{
return triggd == 2;
};
bool PressEvent::isindeterm() const{
return type == INDETERM;
};
template <ButtonType T = BOTH>
class Button{
private:
int pin; // Pin number of the button
int steadyState; // Steady State of the button
int lastRawState; // Last state of the button
unsigned long lastRawTime; // Time of last change
unsigned long steadyTime; // Time when the state became steady
unsigned long steadyDelay; // Jitter delay
bool stateRead; // To check if some state has already been read
// To prevent multiple reads of the same state
int activeState; // Active state of the button, HIGH or LOW
unsigned long pressWindow; // Time window to check for another press
unsigned long holdWindow; // Time window for which the button
// is considered held
unsigned long holdTime; // Time for which the button has been held
int maxPresses; // Maximum number of consecutive presses
// that can be registered
unsigned long retrigTime; // Time to retrigger the button
unsigned long manualRetrigTime; // Time to manually retrigger the button
int stableEvents; // How many press events have been recorded before
PressEvent pevent; // Last event recorded
public:
Button(int pin, int maxPresses = 3, unsigned long retrigTime = 500,
unsigned long steadyDelay = 7, int activeState = LOW,
unsigned long pressWindow = 225, unsigned long holdWindow = 300);
void pullUpMode() const { pinMode(pin, INPUT_PULLUP);};
int rawRead() const;
void steadyRead();
int getState() const;
// Setters
void setSteadyDelay(const unsigned long newdelay);
void setPressWindow(const unsigned long newwindow);
void setHoldWindow(const unsigned long newwindow);
void setMaxPresses(const int newmax);
void setRetrigTime(const unsigned long newtime);
void setActiveState(const int newstate);
bool isInactive() const;
bool isActive() const;
bool isWaiting() const;
bool previousWaitingState() const;
template <ButtonType U = T>
typename std::enable_if<U == ONLYHOLD || U == BOTH, bool>::type
wasHeld() const;
bool nopress() const; // Check if the button is not pressed
template <ButtonType U = T> // Don't compile
typename std::enable_if<U == ONLYPRESS || U == BOTH, int>::type
press() const; // Check if button is pressed num times
template <ButtonType U = T>
typename std::enable_if<U == ONLYHOLD || U == BOTH, int>::type
holdSingle(); // Check if button is held, but only once
template <ButtonType U = T>
typename std::enable_if<U == ONLYHOLD || U == BOTH, int>::type
holdRepeat(); // Check if button is held, but repeated
template <ButtonType U = T>
typename std::enable_if<U == ONLYHOLD || U == BOTH, int>::type
held() const; // Check if the button is held
void monitorPress();
void showEvent() const;
};
template <ButtonType T>
Button<T>::Button(int pin, int maxPresses,
unsigned long retrigTime, unsigned long steadyDelay,
int activeState, unsigned long pressWindow,
unsigned long holdWindow):
pin(pin),
steadyState(HIGH),
lastRawState(HIGH),
lastRawTime(0),
steadyTime(0),
steadyDelay(steadyDelay),
stateRead(false),
activeState(LOW),
pressWindow(pressWindow),
holdWindow(holdWindow),
holdTime(0),
maxPresses(maxPresses),
retrigTime(retrigTime),
manualRetrigTime(0),
stableEvents(0),
pevent(PressEvent())
{
pinMode(pin, INPUT); // Set the pin as input
};
template <ButtonType T>
int Button<T>::rawRead() const{
return digitalRead(pin);
};
template <ButtonType T>
void Button<T>::steadyRead(){
int readstate = rawRead();
if(readstate != lastRawState){ // if state changes
lastRawState = readstate; // save the new state
lastRawTime = millis(); // note the time
}
else if (millis() - lastRawTime > steadyDelay){
if (readstate != steadyState){ // If the state has been steady for long enough
steadyState = readstate;
stateRead = false;
steadyTime = millis();
}
}
};
template <ButtonType T>
int Button<T>::getState() const{
return steadyState;
};
template <ButtonType T>
void Button<T>::setSteadyDelay(const unsigned long newdelay){
steadyDelay = newdelay;
};
template <ButtonType T>
void Button<T>::setPressWindow(const unsigned long newwindow){
pressWindow = newwindow;
};
template <ButtonType T>
void Button<T>::setHoldWindow(const unsigned long newwindow){
holdWindow = newwindow;
};
template <ButtonType T>
void Button<T>::setMaxPresses(const int newmax){
maxPresses = newmax;
};
template <ButtonType T>
void Button<T>::setRetrigTime(const unsigned long newtime){
retrigTime = newtime;
};
template <ButtonType T>
void Button<T>::setActiveState(const int newstate){
activeState = newstate;
};
template <ButtonType T>
bool Button<T>::isInactive() const{
return getState() == !activeState && stableEvents == 0;
}
template <ButtonType T>
bool Button<T>::isActive() const{
return getState() == activeState;
};
template <ButtonType T>
bool Button<T>::isWaiting() const{
return stableEvents != 0;
};
template <ButtonType T>
bool Button<T>::previousWaitingState() const{
return stableEvents % 2 != 0;
};
template <ButtonType T>
template <ButtonType U>
typename std::enable_if<U == ONLYHOLD || U == BOTH, bool>::type
Button<T>::wasHeld() const{
// return holdTime != 0;
return pevent.ispress(HOLD);
};
template <ButtonType T>
bool Button<T>::nopress() const{
return pevent.isindeterm();
};
template <ButtonType T>
template <ButtonType U>
typename std::enable_if<U == ONLYPRESS || U == BOTH, int>::type
Button<T>::press() const{
return pevent.ispress(PRESS);
};
// This works as follows:
// If the button is held,
// Monitorbutton will only have one cycle where triggered is 1 before setting
// it to 2. When it is one it will register a hold. 2 is the waiting state
// which will be set to 1 after the retrigger time has passed.
template <ButtonType T>
template <ButtonType U>
typename std::enable_if<U == ONLYHOLD || U == BOTH, int>::type
Button<T>::holdSingle(){
if (pevent.triggered() == 1)
{
manualRetrigTime = 4294967295;
return pevent.ispress(HOLD);
};
return 0;
};
template <ButtonType T>
template <ButtonType U>
typename std::enable_if<U == ONLYHOLD || U == BOTH, int>::type
Button<T>::holdRepeat(){
if (pevent.triggered() == 1)
{
return pevent.ispress(HOLD);
};
return 0;
};
template <ButtonType T>
template <ButtonType U>
typename std::enable_if<U == ONLYHOLD || U == BOTH, int>::type
Button<T>::held() const{
return pevent.ispress(HOLD);
};
template <ButtonType T>
void Button<T>::monitorPress(){
steadyRead();
if (isInactive()) { // If button is inactive
goto indeterm_event;
}
if (!isWaiting() && isActive()){ // First press after inactivity
stableEvents = 1;
goto indeterm_event;
}
if (isWaiting()){
if (isActive()){ // Is waiting and now active
if(previousWaitingState()){ // Case: Is active was active
if (T == ONLYHOLD || T == BOTH){
// If it was still active, check if it is a hold
unsigned long this_time = millis();
if (this_time - steadyTime > holdWindow ){
// If long enough, it's held
// and it was not already
// triggered
if (pevent.triggered() == 0){ // If not already triggered
int pamount = stableEvents/2 + 1 ; //
pevent.sethold(pamount, this_time);
pevent.settriggd(1);
// holdTime = this_time; // is this still neccesary?
}
else if (pevent.triggered() == 1){ // If already triggered
pevent.settriggd(2);
pevent.settime(millis());
}
else{ // pevent.triggered() == 2
if (manualRetrigTime == 0){ // If not manually retriggered
// just wait for the retrigger time
if (millis() - pevent.eventtime() > retrigTime)
pevent.settriggd(1);
}
else{ // If manually retriggered use that time once
if (millis() - pevent.eventtime() > manualRetrigTime){
manualRetrigTime = 0;
pevent.settriggd(1);
}
}
}
return;
}
else{
goto indeterm_event;
}
}
else{ // If hold doesn't need to be checked, just wait when held
goto indeterm_event;
}
}
else{ // Case: Is active was inactive
stableEvents++;
goto indeterm_event;
}
}
else{ // Is waiting, and currently inactive
if (previousWaitingState()){ // Case: Is inactive was active
if constexpr (T == ONLYHOLD || T == BOTH){
if (wasHeld()){ // If it was held, we're done
manualRetrigTime = 0;
stableEvents = 0;
goto indeterm_event;
}
}
// holdTime = 0;
if (T == ONLYPRESS || T == BOTH){
if (stableEvents == maxPresses*2-1){ // If max presses reached
// Immediately terminate
pevent.settype(PRESS);
pevent.setamount(maxPresses);
stableEvents = 0;
return;
}
}
else{
if (stableEvents == maxPresses*2-1){ // If max presses reached
// Immediately terminate
stableEvents = 0;
goto indeterm_event;
}
}
stableEvents++;
goto indeterm_event;
}
else{ // Case: Is inactive was inactive
if (T == ONLYPRESS || T == BOTH){
unsigned long this_time = millis();
if (this_time - steadyTime > pressWindow){ // Press window passed
int pamount = stableEvents/2;
pevent.settype(PRESS);
pevent.setamount(pamount);
stableEvents = 0;
return;
}
}
else{
unsigned long this_time = millis();
if (this_time - steadyTime > pressWindow){ // Press window passed
stableEvents = 0;
}
}
// ELSE
goto indeterm_event;
}
}
}
return;
indeterm_event:{
if (!pevent.isindeterm()){
pevent.settype(INDETERM);
pevent.settime(millis());
pevent.settriggd(0);
}
return;
}
};
template <ButtonType T>
void Button<T>::showEvent() const{
if (pevent.isindeterm()){
Serial.println("Indeterminate");
}
else if (pevent.ispress(PRESS)){
Serial.print("Press: ");
Serial.println(pevent.ispress(PRESS));
}
else if (pevent.ispress(HOLD)){
Serial.print("Hold: ");
Serial.println(pevent.ispress(HOLD));
}
};
#endif