Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Debouncing in one point. #118

Merged
merged 6 commits into from
May 7, 2023
Merged
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
3 changes: 2 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
.DS_Store
.idea/
.vscode
_*
_*
build
63 changes: 27 additions & 36 deletions src/OneButton.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
*
* @author Matthias Hertel, https://www.mathertel.de
* @Copyright Copyright (c) by Matthias Hertel, https://www.mathertel.de.
* Ihor Nehrutsa, [email protected]
*
* This work is licensed under a BSD style license. See
* http://www.mathertel.de/License.aspx
Expand Down Expand Up @@ -36,7 +37,6 @@ OneButton::OneButton()
*/
OneButton::OneButton(const int pin, const boolean activeLow, const bool pullupActive)
{
// OneButton();
_pin = pin;

if (activeLow) {
Expand All @@ -46,36 +46,36 @@ OneButton::OneButton(const int pin, const boolean activeLow, const bool pullupAc
} else {
// the button connects the input pin to VCC when pressed.
_buttonPressed = HIGH;
} // if
}

if (pullupActive) {
// use the given pin as input and activate internal PULLUP resistor.
pinMode(pin, INPUT_PULLUP);
} else {
// use the given pin as input
pinMode(pin, INPUT);
} // if
}
} // OneButton


// explicitly set the number of millisec that have to pass by before a click is assumed stable.
void OneButton::setDebounceTicks(const int ticks)
void OneButton::setDebounceTicks(const unsigned int ms)
{
_debounceTicks = ticks;
_debounce_ms = ms;
} // setDebounceTicks


// explicitly set the number of millisec that have to pass by before a click is detected.
void OneButton::setClickTicks(const int ticks)
void OneButton::setClickTicks(const unsigned int ms)
{
_clickTicks = ticks;
_click_ms = ms;
} // setClickTicks


// explicitly set the number of millisec that have to pass by before a long button press is detected.
void OneButton::setPressTicks(const int ticks)
void OneButton::setPressTicks(const unsigned int ms)
{
_pressTicks = ticks;
_press_ms = ms;
} // setPressTicks


Expand Down Expand Up @@ -176,7 +176,6 @@ void OneButton::attachDuringLongPress(parameterizedCallbackFunction newFunction,
void OneButton::reset(void)
{
_state = OneButton::OCS_INIT;
_lastState = OneButton::OCS_INIT;
_nClicks = 0;
_startTime = 0;
}
Expand All @@ -190,23 +189,32 @@ int OneButton::getNumberClicks(void)


/**
* @brief Check input of the configured pin and then advance the finite state
* machine (FSM).
* @brief Check input of the configured pin,
* debounce input pin level and then
* advance the finite state machine (FSM).
*/
void OneButton::tick(void)
{
if (_pin >= 0) {
tick(digitalRead(_pin) == _buttonPressed);
int pinLevel = digitalRead(_pin);
now = millis(); // current (relative) time in msecs.
if (_lastDebouncePinLevel == pinLevel) {
if ((now - _lastDebounceTime) >= _debounce_ms) {
tick(pinLevel == _buttonPressed); // pinLevel is debounced here
}
} else {
_lastDebouncePinLevel = pinLevel;
_lastDebounceTime = now;
}
}
}
} // tick()


/**
* @brief Advance to a new state and save the last one to come back in cas of bouncing detection.
*/
void OneButton::_newState(stateMachine_t nextState)
{
_lastState = _state;
_state = nextState;
} // _newState()

Expand All @@ -216,7 +224,6 @@ void OneButton::_newState(stateMachine_t nextState)
*/
void OneButton::tick(bool activeLevel)
{
unsigned long now = millis(); // current (relative) time in msecs.
unsigned long waitTime = (now - _startTime);

// Implementation of the state machine
Expand All @@ -233,15 +240,11 @@ void OneButton::tick(bool activeLevel)
case OneButton::OCS_DOWN:
// waiting for level to become inactive.

if ((!activeLevel) && (waitTime < _debounceTicks)) {
// button was released to quickly so I assume some bouncing.
_newState(_lastState);

} else if (!activeLevel) {
if (!activeLevel) {
_newState(OneButton::OCS_UP);
_startTime = now; // remember starting time

} else if ((activeLevel) && (waitTime > _pressTicks)) {
} else if ((activeLevel) && (waitTime > _press_ms)) {
if (_longPressStartFunc) _longPressStartFunc();
if (_paramLongPressStartFunc) _paramLongPressStartFunc(_longPressStartFuncParam);
_newState(OneButton::OCS_PRESS);
Expand All @@ -251,15 +254,9 @@ void OneButton::tick(bool activeLevel)
case OneButton::OCS_UP:
// level is inactive

if ((activeLevel) && (waitTime < _debounceTicks)) {
// button was pressed to quickly so I assume some bouncing.
_newState(_lastState); // go back

} else if (waitTime >= _debounceTicks) {
// count as a short button down
_nClicks++;
_newState(OneButton::OCS_COUNT);
} // if
break;

case OneButton::OCS_COUNT:
Expand All @@ -270,7 +267,7 @@ void OneButton::tick(bool activeLevel)
_newState(OneButton::OCS_DOWN);
_startTime = now; // remember starting time

} else if ((waitTime > _clickTicks) || (_nClicks == _maxClicks)) {
} else if ((waitTime >= _click_ms) || (_nClicks == _maxClicks)) {
// now we know how many clicks have been made.

if (_nClicks == 1) {
Expand All @@ -294,7 +291,7 @@ void OneButton::tick(bool activeLevel)
break;

case OneButton::OCS_PRESS:
// waiting for menu pin being release after long press.
// waiting for pin being release after long press.

if (!activeLevel) {
_newState(OneButton::OCS_PRESSEND);
Expand All @@ -310,15 +307,9 @@ void OneButton::tick(bool activeLevel)
case OneButton::OCS_PRESSEND:
// button was released.

if ((activeLevel) && (waitTime < _debounceTicks)) {
// button was released to quickly so I assume some bouncing.
_newState(_lastState); // go back

} else if (waitTime >= _debounceTicks) {
if (_longPressStopFunc) _longPressStopFunc();
if (_paramLongPressStopFunc) _paramLongPressStopFunc(_longPressStopFuncParam);
reset();
}
break;

default:
Expand Down
45 changes: 30 additions & 15 deletions src/OneButton.h
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
// 26.09.2018 Initialization moved into class declaration.
// 26.09.2018 Jay M Ericsson: compiler warnings removed.
// 29.01.2020 improvements from ShaggyDog18
// 07.05.2023 Debouncing in one point. #118
// -----

#ifndef OneButton_h
Expand Down Expand Up @@ -53,17 +54,22 @@ class OneButton
/**
* set # millisec after safe click is assumed.
*/
void setDebounceTicks(const int ticks);
void setDebounceTicks(const unsigned int ms); // obsolete
void setDebounceMs(const unsigned int ms) { setDebounceTicks(ms); };

/**
* set # millisec after single click is assumed.
*/
void setClickTicks(const int ticks);
void setClickTicks(const unsigned int ms); // obsolete
void setClickMs(const unsigned int ms) { setClickTicks(ms); };

/**
* set # millisec after press is assumed.
*/
void setPressTicks(const int ticks);
void setPressTicks(const unsigned int ms); // obsolete
void setPressMs(const unsigned int ms) { setPressTicks(ms); };

// ----- Attach events functions -----

/**
* Attach an event to be called when a single click is detected.
Expand Down Expand Up @@ -115,15 +121,16 @@ class OneButton
*/
void tick(void);


/**
* @brief Call this function every time the input level has changed.
* Using this function no digital input pin is checked because the current
* level is given by the parameter.
* Run the finite state machine (FSM) using the given level.
*/
void tick(bool level);


public:
/**
* Reset the button state machine.
*/
Expand All @@ -149,12 +156,14 @@ class OneButton


private:
int _pin = 0; // hardware pin number.
unsigned int _debounceTicks = 50; // number of ticks for debounce times.
unsigned int _clickTicks = 400; // number of msecs before a click is detected.
unsigned int _pressTicks = 800; // number of msecs before a long button press is detected
int _pin = -1; // hardware pin number.
unsigned int _debounce_ms = 50; // number of msecs for debounce times.
unsigned int _click_ms = 400; // number of msecs before a click is detected.
unsigned int _press_ms = 800; // number of msecs before a long button press is detected

int _buttonPressed = 0;
int _buttonPressed = 0; // this is the level of the input pin when the button is pressed.
// LOW if the button connects the input pin to GND when pressed.
// HIGH if the button connects the input pin to VCC when pressed.

// These variables will hold functions acting as event source.
callbackFunction _clickFunc = NULL;
Expand Down Expand Up @@ -188,25 +197,31 @@ class OneButton
// define FiniteStateMachine
enum stateMachine_t : int {
OCS_INIT = 0,
OCS_DOWN = 1,
OCS_UP = 2,
OCS_DOWN = 1, // button is down
OCS_UP = 2, // button is up
OCS_COUNT = 3,
OCS_PRESS = 6,
OCS_PRESS = 6, // button is hold down
OCS_PRESSEND = 7,
UNKNOWN = 99
};

/**
* Advance to a new state and save the last one to come back in cas of bouncing detection.
* Advance to a new state.
*/
void _newState(stateMachine_t nextState);

stateMachine_t _state = OCS_INIT;
stateMachine_t _lastState = OCS_INIT; // used for debouncing

int _lastDebouncePinLevel = -1; // used for pin debouncing
unsigned long _lastDebounceTime = 0; // millis()
unsigned long now = 0; // millis()

unsigned long _startTime = 0; // start of current input change to checking debouncing
int _nClicks = 0; // count the number of clicks with this variable
int _maxClicks = 1; // max number (1, 2, multi=3) of clicks of interest by registration of event functions.

public:
int pin() const { return _pin; };
stateMachine_t state() const { return _state; };
};

#endif