Skip to content

Commit 4b0f51e

Browse files
committed
new class LongPressDetector
- new class LongPressDetector added to easily detect long presses of buttons - new example added to show difference between old approache and new simplified approach - corrected `lastChange()` behaviour of all derived classes (previously reported value from base class which may differ)
1 parent 240d0c8 commit 4b0f51e

File tree

6 files changed

+242
-20
lines changed

6 files changed

+242
-20
lines changed

README.md

Lines changed: 10 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -27,8 +27,9 @@ The following example sketches are included with the **Mini_Button** library:
2727

2828
- **SimpleOnOff**: Just turns the Arduino's pin 13 LED on and off.
2929
- **LongPress**: Demonstrates detecting long and short button presses.
30+
- **LongPress-LongPressDetector**: The same example as in JC_Button, but simplified using a new class `LongPressDetector`.
3031
- **UpDown**: Counts up or down, one number at a time or rapidly by holding the button down.
31-
- **UpDown-AutoRepeatButton**: Example `UpDown` from JC_Button, but made easily with `AutoRepeatButton`.
32+
- **UpDown-AutoRepeatButton**: The same example as in JC_Button, but made easily with a new class `AutoRepeatButton`.
3233
- **Toggle**: Demonstrates ToggleButton functionality.
3334

3435

@@ -60,15 +61,15 @@ Button myButton(4, 25, false, false);
6061

6162
```
6263
63-
### ToggleButton(pin, initialState, dbTime, pullup, invert)
64+
### ToggleButton(pin, initState, dbTime, pullup, invert)
6465
##### Description
6566
The constructor defines a toggle button object, which has "push-on, push-off" functionality. The initial state can be on or off. See the section, [ToggleButton Library Functions](https://github.com/JChristensen/JC_Button#togglebutton-library-functions) for functions that apply specifically to the ToggleButton object. The ToggleButton class is derived from the Button class, so all Button functions are available, but because it is inherently a more limited concept, the special ToggleButton functions will be most useful, along with `begin()` and `read()`.
6667
##### Syntax
6768
`ToggleButton(pin, initialState, dbTime, pullup, invert);`
6869
##### Required parameter
6970
**pin:** Arduino pin number that the button is connected to *(byte)*
7071
##### Optional parameters
71-
**initialState:** Initial state for the button. Defaults to off (false) if not given. *(bool)*
72+
**initState:** Initial state for the button. Defaults to off (false) if not given. *(bool)*
7273
**dbTime:** Debounce time in milliseconds, max 60000ms. Defaults to 25ms if not given. *(unsigned long)*
7374
**pullup:** *true* to enable the microcontroller's internal pull-up resistor, else *false*. Defaults to *true* if not given. *(bool)*
7475
**invert:** *false* interprets a high logic level to mean the button is pressed, *true* interprets a low level as pressed. *true* should be used when a pull-up resistor is employed, *false* for a pull-down resistor. Defaults to *true* if not given. *(bool)*
@@ -194,20 +195,18 @@ The time in milliseconds when the button last changed state *(unsigned long)*
194195
unsigned long msLastChange = myButton.lastChange();
195196
```
196197

197-
## ToggleButton Library Functions
198-
199198
### changed()
200199
##### Description
201-
Returns a boolean value (true or false) to indicate whether the toggle button changed state the last time `read()` was called.
200+
Returns a boolean value (true or false) to indicate whether the button changed state the last time `read()` was called.
202201
##### Syntax
203-
`myToggle.changed();`
202+
`myButton.changed();`
204203
##### Parameters
205204
None.
206205
##### Returns
207-
*true* if the toggle state changed, else *false* *(bool)*
206+
*true* if the button state changed, else *false* *(bool)*
208207
##### Example
209208
```c++
210-
if (myToggle.changed())
209+
if (myButton.changed())
211210
{
212211
// do something
213212
}
@@ -217,6 +216,8 @@ else
217216
}
218217
```
219218

219+
## ToggleButton Library Functions
220+
220221
### toggleState()
221222
##### Description
222223
Returns a boolean value (true or false) to indicate the toggle button state as of the last time `read()` was called.
Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
1+
// Mini_Button Arduino library
2+
// https://github.com/solamyl/Mini_Button
3+
// Copyright (C) 2025 by Štěpán Škrob, Copyright (C) 2018 by Jack Christensen
4+
// licensed under GNU GPL v3.0, https://www.gnu.org/licenses/gpl.html
5+
//
6+
// Example sketch demonstrating short and long button presses.
7+
//
8+
// A simple state machine where a short press of the button turns the
9+
// Arduino pin 13 LED on or off, and a long press causes the LED to
10+
// blink rapidly. Once in rapid blink mode, another long press goes
11+
// back to on/off mode.
12+
13+
#include <Mini_Button.h> // https://github.com/solamyl/Mini_Button
14+
15+
// pin assignments
16+
const byte
17+
BUTTON_PIN(7), // connect a button switch from this pin to ground
18+
LED_PIN(13); // the standard Arduino "pin 13" LED
19+
20+
const unsigned long
21+
LONG_PRESS(1000), // we define a "long press" to be 1000 milliseconds.
22+
BLINK_INTERVAL(100); // in the BLINK state, switch the LED every 100 milliseconds.
23+
24+
Button myBtn(BUTTON_PIN); // define the button
25+
LongPressDetector detectLP(&myBtn, LONG_PRESS);
26+
27+
void setup()
28+
{
29+
myBtn.begin(); // initialize the button object
30+
detectLP.begin();
31+
pinMode(LED_PIN, OUTPUT); // set the LED pin as an output
32+
}
33+
34+
// the list of possible states for the state machine. This state machine has a fixed
35+
// sequence of states, i.e. ONOFF --> BLINK --> ONOFF
36+
enum states_t {ONOFF, BLINK};
37+
38+
bool ledState; // current LED status
39+
unsigned long ms; // current time from millis()
40+
unsigned long msLast; // last time the LED was switched
41+
42+
void loop()
43+
{
44+
static states_t STATE; // current state machine state
45+
ms = millis(); // record the current time
46+
myBtn.read(); // read the button
47+
detectLP.read(); // check the button state for a long press
48+
49+
switch (STATE)
50+
{
51+
// this state watches for short and long presses, switches the LED for
52+
// short presses, and moves to the BLINK state for long presses.
53+
case ONOFF:
54+
if (detectLP.wasPressed())
55+
STATE = BLINK;
56+
// distinguish between short-press release and long-press release
57+
// LongPressDetector observes the same button so when released
58+
// then both `myBtn` and `detectLP` indicate a key release
59+
else if (myBtn.wasReleased() && !detectLP.wasReleased())
60+
switchLED();
61+
break;
62+
63+
// the fast-blink state. Watch for another long press which will cause us to
64+
// turn the LED off (as feedback to the user) and move to the ONOFF state.
65+
case BLINK:
66+
if (detectLP.wasPressed())
67+
{
68+
STATE = ONOFF;
69+
digitalWrite(LED_PIN, LOW);
70+
ledState = false;
71+
}
72+
else
73+
fastBlink();
74+
break;
75+
}
76+
}
77+
78+
// reverse the current LED state. if it's on, turn it off. if it's off, turn it on.
79+
void switchLED()
80+
{
81+
msLast = ms; // record the last switch time
82+
ledState = !ledState;
83+
digitalWrite(LED_PIN, ledState);
84+
}
85+
86+
// switch the LED on and off every BLINK_INTERVAL milliseconds.
87+
void fastBlink()
88+
{
89+
if (ms - msLast >= BLINK_INTERVAL)
90+
switchLED();
91+
}
92+

keywords.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ JC_Button KEYWORD1
99
Button KEYWORD1
1010
ToggleButton KEYWORD1
1111
AutoRepeatButton KEYWORD1
12+
LongPressDetector KEYWORD1
1213
begin KEYWORD2
1314
read KEYWORD2
1415
isPressed KEYWORD2

library.properties

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
11
name=Mini_Button
2-
version=1.3.0
2+
version=1.4.0
33
author=Štěpán Škrob, Jack Christensen
44
maintainer=Štěpán Škrob <solamy@seznam.cz>
55
sentence=Button debouncing library with minimal footprint.
6-
paragraph=Features autorepeat and two-state button. Provides robust debouncing, low-memory usage and easy API. Forked from JC_Button.
6+
paragraph=Features autorepeat, long press detection and two-state button. Provides robust debouncing, low-memory usage and easy API. Forked from JC_Button.
77
category=Signal Input/Output
88
url=https://github.com/solamyl/Mini_Button
99
architectures=*

src/Mini_Button.cpp

Lines changed: 67 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,15 @@ bool Button::read()
5454
return m_state;
5555
}
5656

57+
58+
// Initialize a button object
59+
void ToggleButton::begin()
60+
{
61+
Button::begin();
62+
m_lastChange = millis();
63+
}
64+
65+
5766
// read the button and return its state.
5867
// should be called frequently.
5968
bool ToggleButton::read()
@@ -62,10 +71,23 @@ bool ToggleButton::read()
6271

6372
if (Button::wasPressed()) {
6473
m_toggleState = !m_toggleState;
74+
m_lastChange = millis();
6575
}
6676
return m_toggleState;
6777
}
6878

79+
80+
// Initialize a button object
81+
void AutoRepeatButton::begin()
82+
{
83+
Button::begin();
84+
85+
m_virtualState = Button::isPressed();
86+
m_virtualLastState = m_virtualState;
87+
m_lastChange = millis();
88+
}
89+
90+
6991
#if 0
7092
// read the button, obtain physical state and then calculate virtual state
7193
// repeating is made by inserting fake key-releases into the stream of "pressed" states
@@ -74,35 +96,42 @@ bool AutoRepeatButton::read()
7496
{
7597
Button::read(); //read input and do the debouncing
7698

99+
uint32_t now = millis();
100+
77101
m_virtualLastState = m_virtualState;
78102
m_virtualState = Button::isPressed();
79103

80104
if (m_virtualState) {
81-
uint32_t t = millis() - lastChange();
105+
uint32_t t = now - lastChange();
82106
if (t >= m_delay) { //skip the init delay
83107
// repeating...
84108
t -= m_delay;
85109
t %= m_rate; //cycle in m_rate periods
86-
if (t < debounceTime()) //if start of the period
110+
if (t < 20/*debounceTime()*/) //if start of the period
87111
m_virtualState = false; //make a virtual key release
88112
}
89113
}
114+
if (changed()) {
115+
m_lastChange = now;
116+
}
90117
return m_virtualState;
91118
}
92119

93120
#else
94121

95-
// alternative implementation, previous may not work correctly for slower loop()
122+
// alternative implementation
123+
// previous may not work correctly for slower loop() and/or faster rates
96124
bool AutoRepeatButton::read()
97125
{
98126
Button::read(); //read input and do the debouncing
127+
uint32_t now = millis();
99128

100129
m_virtualLastState = m_virtualState;
101130
m_virtualState = Button::isPressed();
102131

103132
if (m_virtualState) {
104133
// someone is holding the button
105-
uint32_t t = millis() - lastChange(); //how long?
134+
uint32_t t = now - lastChange(); //how long?
106135
if (t >= m_delay) {
107136
// do the repeating after m_delay has elapsed
108137
t -= m_delay;
@@ -112,14 +141,48 @@ bool AutoRepeatButton::read()
112141
m_virtualState = false; //make a virtual key release
113142
m_repeatCounter++;
114143
}
144+
/*Serial.print(millis());
145+
Serial.print(": repeat=");
146+
Serial.print(m_repeatCounter);
147+
Serial.print(", cnt=");
148+
Serial.print(cnt);
149+
Serial.print(", lastState=");
150+
Serial.print(m_virtualLastState);
151+
Serial.print(", state=");
152+
Serial.print(m_virtualState);
153+
Serial.println();*/
115154
}
116155
}
117156
else {
118157
// physical button is released
119158
m_repeatCounter = 0;
120159
}
160+
if (changed()) {
161+
m_lastChange = now;
162+
}
121163
return m_virtualState;
122164
}
123165
#endif
124166

125167

168+
// Initialize a Detector object
169+
void LongPressDetector::begin()
170+
{
171+
m_state = m_buttonPtr->isPressed();
172+
m_lastState = m_state;
173+
m_lastChange = millis();
174+
}
175+
176+
177+
// Observes specified (another) button for a long press and then updates its state.
178+
// Does not any physical button input reading! Just observes the state of another class!
179+
// Call this function frequently to ensure the sketch is responsive to user input.
180+
bool LongPressDetector::read()
181+
{
182+
m_lastState = m_state;
183+
m_state = pressedFor(m_delay);
184+
if (changed()) {
185+
m_lastChange = millis();
186+
}
187+
return m_state;
188+
}

0 commit comments

Comments
 (0)