From 3f97b12d215c18527095a0238de64b67fcf4c729 Mon Sep 17 00:00:00 2001 From: IhorNehrutsa Date: Sat, 6 May 2023 16:46:34 +0300 Subject: [PATCH 1/5] Debounsing in one point. --- src/OneButton.cpp | 16 ++++++++++++---- src/OneButton.h | 23 +++++++++++++++++++---- 2 files changed, 31 insertions(+), 8 deletions(-) diff --git a/src/OneButton.cpp b/src/OneButton.cpp index 38dd9c7..e4df5ff 100644 --- a/src/OneButton.cpp +++ b/src/OneButton.cpp @@ -190,13 +190,22 @@ int OneButton::getNumberClicks(void) /** - * @brief Check input of the configured pin and then advance the finite state + * @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) >= _debounceTicks) { + tick(pinLevel == _buttonPressed); // pinLevel is debounced here + } + } else { + _lastDebouncePinLevel = pinLevel; + _lastDebounceTime = now; + } } } @@ -216,7 +225,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 @@ -294,7 +302,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); diff --git a/src/OneButton.h b/src/OneButton.h index b1e4a8a..e2e1906 100644 --- a/src/OneButton.h +++ b/src/OneButton.h @@ -116,14 +116,17 @@ class OneButton void tick(void); +private: /** * @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. */ @@ -154,7 +157,10 @@ class OneButton 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 _buttonPressed; + int _buttonPressed; // 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; @@ -204,9 +210,18 @@ class OneButton stateMachine_t _state = OCS_INIT; stateMachine_t _lastState = OCS_INIT; // used for debouncing - unsigned long _startTime; // start of current input change to checking debouncing - int _nClicks; // 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. + 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. + + int _lastDebouncePinLevel = -1; // used for debouncing + unsigned long _lastDebounceTime = 0; // millis() + unsigned long now = 0; // millis() + +public: + int pin() const { return _pin; }; + stateMachine_t state() const { return _state; }; + }; #endif From 1d5f87171c5fe56448600403c2471311feec727a Mon Sep 17 00:00:00 2001 From: IhorNehrutsa Date: Sun, 7 May 2023 02:54:42 +0300 Subject: [PATCH 2/5] Update .gitignore --- .gitignore | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index 4048809..32985b4 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,5 @@ .DS_Store .idea/ .vscode -_* \ No newline at end of file +_* +build From 3783945e8cb79ca1f36fb52e278ad4b82db887f0 Mon Sep 17 00:00:00 2001 From: IhorNehrutsa Date: Sun, 7 May 2023 03:03:04 +0300 Subject: [PATCH 3/5] FSM correction --- src/OneButton.cpp | 91 ++++++++++++++++++----------------------------- src/OneButton.h | 38 +++++++++++--------- 2 files changed, 56 insertions(+), 73 deletions(-) diff --git a/src/OneButton.cpp b/src/OneButton.cpp index e4df5ff..2fe770a 100644 --- a/src/OneButton.cpp +++ b/src/OneButton.cpp @@ -6,6 +6,7 @@ * * @author Matthias Hertel, https://www.mathertel.de * @Copyright Copyright (c) by Matthias Hertel, https://www.mathertel.de. + * Ihor Nehrutsa, Ihor.Nehrutsa@gmail.com * * This work is licensed under a BSD style license. See * http://www.mathertel.de/License.aspx @@ -36,7 +37,6 @@ OneButton::OneButton() */ OneButton::OneButton(const int pin, const boolean activeLow, const bool pullupActive) { - // OneButton(); _pin = pin; if (activeLow) { @@ -46,7 +46,7 @@ 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. @@ -54,36 +54,36 @@ OneButton::OneButton(const int pin, const boolean activeLow, const bool pullupAc } 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; -} // setDebounceTicks + _debounce_ms = ms; +} // 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; -} // setClickTicks + _click_ms = ms; +} // 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; -} // setPressTicks + _press_ms = ms; +} // save function for click event void OneButton::attachClick(callbackFunction newFunction) { _clickFunc = newFunction; -} // attachClick +} // save function for parameterized click event @@ -91,7 +91,7 @@ void OneButton::attachClick(parameterizedCallbackFunction newFunction, void *par { _paramClickFunc = newFunction; _clickFuncParam = parameter; -} // attachClick +} // save function for doubleClick event @@ -99,7 +99,7 @@ void OneButton::attachDoubleClick(callbackFunction newFunction) { _doubleClickFunc = newFunction; _maxClicks = max(_maxClicks, 2); -} // attachDoubleClick +} // save function for parameterized doubleClick event @@ -108,7 +108,7 @@ void OneButton::attachDoubleClick(parameterizedCallbackFunction newFunction, voi _paramDoubleClickFunc = newFunction; _doubleClickFuncParam = parameter; _maxClicks = max(_maxClicks, 2); -} // attachDoubleClick +} // save function for multiClick event @@ -116,7 +116,7 @@ void OneButton::attachMultiClick(callbackFunction newFunction) { _multiClickFunc = newFunction; _maxClicks = max(_maxClicks, 100); -} // attachMultiClick +} // save function for parameterized MultiClick event @@ -125,14 +125,14 @@ void OneButton::attachMultiClick(parameterizedCallbackFunction newFunction, void _paramMultiClickFunc = newFunction; _multiClickFuncParam = parameter; _maxClicks = max(_maxClicks, 100); -} // attachMultiClick +} // save function for longPressStart event void OneButton::attachLongPressStart(callbackFunction newFunction) { _longPressStartFunc = newFunction; -} // attachLongPressStart +} // save function for parameterized longPressStart event @@ -140,14 +140,14 @@ void OneButton::attachLongPressStart(parameterizedCallbackFunction newFunction, { _paramLongPressStartFunc = newFunction; _longPressStartFuncParam = parameter; -} // attachLongPressStart +} // save function for longPressStop event void OneButton::attachLongPressStop(callbackFunction newFunction) { _longPressStopFunc = newFunction; -} // attachLongPressStop +} // save function for parameterized longPressStop event @@ -155,14 +155,14 @@ void OneButton::attachLongPressStop(parameterizedCallbackFunction newFunction, v { _paramLongPressStopFunc = newFunction; _longPressStopFuncParam = parameter; -} // attachLongPressStop +} // save function for during longPress event void OneButton::attachDuringLongPress(callbackFunction newFunction) { _duringLongPressFunc = newFunction; -} // attachDuringLongPress +} // save function for parameterized during longPress event @@ -170,13 +170,12 @@ void OneButton::attachDuringLongPress(parameterizedCallbackFunction newFunction, { _paramDuringLongPressFunc = newFunction; _duringLongPressFuncParam = parameter; -} // attachDuringLongPress +} void OneButton::reset(void) { _state = OneButton::OCS_INIT; - _lastState = OneButton::OCS_INIT; _nClicks = 0; _startTime = 0; } @@ -199,7 +198,7 @@ void OneButton::tick(void) int pinLevel = digitalRead(_pin); now = millis(); // current (relative) time in msecs. if (_lastDebouncePinLevel == pinLevel) { - if ((now - _lastDebounceTime) >= _debounceTicks) { + if ((now - _lastDebounceTime) >= _debounce_ms) { tick(pinLevel == _buttonPressed); // pinLevel is debounced here } } else { @@ -215,9 +214,8 @@ void OneButton::tick(void) */ void OneButton::_newState(stateMachine_t nextState) { - _lastState = _state; _state = nextState; -} // _newState() +} /** @@ -235,39 +233,29 @@ void OneButton::tick(bool activeLevel) _newState(OneButton::OCS_DOWN); _startTime = now; // remember starting time _nClicks = 0; - } // if + } break; 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); - } // if + } break; 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: @@ -278,7 +266,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) { @@ -295,10 +283,10 @@ void OneButton::tick(bool activeLevel) // this was a multi click sequence. if (_multiClickFunc) _multiClickFunc(); if (_paramMultiClickFunc) _paramMultiClickFunc(_multiClickFuncParam); - } // if + } reset(); - } // if + } break; case OneButton::OCS_PRESS: @@ -312,30 +300,21 @@ void OneButton::tick(bool activeLevel) // still the button is pressed if (_duringLongPressFunc) _duringLongPressFunc(); if (_paramDuringLongPressFunc) _paramDuringLongPressFunc(_duringLongPressFuncParam); - } // if + } break; 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: // unknown state detected -> reset state machine _newState(OneButton::OCS_INIT); break; - } // if + } } // OneButton.tick() - - -// end. diff --git a/src/OneButton.h b/src/OneButton.h index e2e1906..9c1b657 100644 --- a/src/OneButton.h +++ b/src/OneButton.h @@ -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 @@ -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. @@ -152,10 +158,10 @@ class OneButton private: - int _pin; // 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; // 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. @@ -194,30 +200,28 @@ 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 holded 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. - int _lastDebouncePinLevel = -1; // used for debouncing - unsigned long _lastDebounceTime = 0; // millis() - unsigned long now = 0; // millis() - public: int pin() const { return _pin; }; stateMachine_t state() const { return _state; }; From a276e7f9c8963ebcd304c609018fa6f71cce87d8 Mon Sep 17 00:00:00 2001 From: Matthias Hertel Date: Sun, 7 May 2023 12:15:25 +0200 Subject: [PATCH 4/5] minor changesin comments reverted --- src/OneButton.cpp | 54 +++++++++++++++++++++++++---------------------- src/OneButton.h | 2 +- 2 files changed, 30 insertions(+), 26 deletions(-) diff --git a/src/OneButton.cpp b/src/OneButton.cpp index 2fe770a..ad337e0 100644 --- a/src/OneButton.cpp +++ b/src/OneButton.cpp @@ -62,28 +62,28 @@ OneButton::OneButton(const int pin, const boolean activeLow, const bool pullupAc void OneButton::setDebounceTicks(const unsigned int ms) { _debounce_ms = ms; -} +} // setDebounceTicks // explicitly set the number of millisec that have to pass by before a click is detected. void OneButton::setClickTicks(const unsigned int ms) { _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 unsigned int ms) { _press_ms = ms; -} +} // setPressTicks // save function for click event void OneButton::attachClick(callbackFunction newFunction) { _clickFunc = newFunction; -} +} // attachClick // save function for parameterized click event @@ -91,7 +91,7 @@ void OneButton::attachClick(parameterizedCallbackFunction newFunction, void *par { _paramClickFunc = newFunction; _clickFuncParam = parameter; -} +} // attachClick // save function for doubleClick event @@ -99,7 +99,7 @@ void OneButton::attachDoubleClick(callbackFunction newFunction) { _doubleClickFunc = newFunction; _maxClicks = max(_maxClicks, 2); -} +} // attachDoubleClick // save function for parameterized doubleClick event @@ -108,7 +108,7 @@ void OneButton::attachDoubleClick(parameterizedCallbackFunction newFunction, voi _paramDoubleClickFunc = newFunction; _doubleClickFuncParam = parameter; _maxClicks = max(_maxClicks, 2); -} +} // attachDoubleClick // save function for multiClick event @@ -116,7 +116,7 @@ void OneButton::attachMultiClick(callbackFunction newFunction) { _multiClickFunc = newFunction; _maxClicks = max(_maxClicks, 100); -} +} // attachMultiClick // save function for parameterized MultiClick event @@ -125,14 +125,14 @@ void OneButton::attachMultiClick(parameterizedCallbackFunction newFunction, void _paramMultiClickFunc = newFunction; _multiClickFuncParam = parameter; _maxClicks = max(_maxClicks, 100); -} +} // attachMultiClick // save function for longPressStart event void OneButton::attachLongPressStart(callbackFunction newFunction) { _longPressStartFunc = newFunction; -} +} // attachLongPressStart // save function for parameterized longPressStart event @@ -140,14 +140,14 @@ void OneButton::attachLongPressStart(parameterizedCallbackFunction newFunction, { _paramLongPressStartFunc = newFunction; _longPressStartFuncParam = parameter; -} +} // attachLongPressStart // save function for longPressStop event void OneButton::attachLongPressStop(callbackFunction newFunction) { _longPressStopFunc = newFunction; -} +} // attachLongPressStop // save function for parameterized longPressStop event @@ -155,14 +155,14 @@ void OneButton::attachLongPressStop(parameterizedCallbackFunction newFunction, v { _paramLongPressStopFunc = newFunction; _longPressStopFuncParam = parameter; -} +} // attachLongPressStop // save function for during longPress event void OneButton::attachDuringLongPress(callbackFunction newFunction) { _duringLongPressFunc = newFunction; -} +} // attachDuringLongPress // save function for parameterized during longPress event @@ -170,7 +170,7 @@ void OneButton::attachDuringLongPress(parameterizedCallbackFunction newFunction, { _paramDuringLongPressFunc = newFunction; _duringLongPressFuncParam = parameter; -} +} // attachDuringLongPress void OneButton::reset(void) @@ -189,8 +189,9 @@ int OneButton::getNumberClicks(void) /** - * @brief Check input of the configured pin, debounce input pin level 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) { @@ -206,7 +207,7 @@ void OneButton::tick(void) _lastDebounceTime = now; } } -} +} // tick() /** @@ -215,7 +216,7 @@ void OneButton::tick(void) void OneButton::_newState(stateMachine_t nextState) { _state = nextState; -} +} // _newState() /** @@ -233,7 +234,7 @@ void OneButton::tick(bool activeLevel) _newState(OneButton::OCS_DOWN); _startTime = now; // remember starting time _nClicks = 0; - } + } // if break; case OneButton::OCS_DOWN: @@ -247,7 +248,7 @@ void OneButton::tick(bool activeLevel) if (_longPressStartFunc) _longPressStartFunc(); if (_paramLongPressStartFunc) _paramLongPressStartFunc(_longPressStartFuncParam); _newState(OneButton::OCS_PRESS); - } + } // if break; case OneButton::OCS_UP: @@ -283,10 +284,10 @@ void OneButton::tick(bool activeLevel) // this was a multi click sequence. if (_multiClickFunc) _multiClickFunc(); if (_paramMultiClickFunc) _paramMultiClickFunc(_multiClickFuncParam); - } + } // if reset(); - } + } // if break; case OneButton::OCS_PRESS: @@ -300,7 +301,7 @@ void OneButton::tick(bool activeLevel) // still the button is pressed if (_duringLongPressFunc) _duringLongPressFunc(); if (_paramDuringLongPressFunc) _paramDuringLongPressFunc(_duringLongPressFuncParam); - } + } // if break; case OneButton::OCS_PRESSEND: @@ -315,6 +316,9 @@ void OneButton::tick(bool activeLevel) // unknown state detected -> reset state machine _newState(OneButton::OCS_INIT); break; - } + } // if } // OneButton.tick() + + +// end. diff --git a/src/OneButton.h b/src/OneButton.h index cb334dc..f56fb83 100644 --- a/src/OneButton.h +++ b/src/OneButton.h @@ -202,7 +202,7 @@ class OneButton OCS_DOWN = 1, // button is down OCS_UP = 2, // button is up OCS_COUNT = 3, - OCS_PRESS = 6, // button is holded down + OCS_PRESS = 6, // button is hold down OCS_PRESSEND = 7, }; From b5a883cd49839365e7809825e8f6b6372e7def39 Mon Sep 17 00:00:00 2001 From: Matthias Hertel Date: Sun, 7 May 2023 12:26:44 +0200 Subject: [PATCH 5/5] re-enable examples/SpecialInput with external input source. --- src/OneButton.h | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/OneButton.h b/src/OneButton.h index f56fb83..d32b8e1 100644 --- a/src/OneButton.h +++ b/src/OneButton.h @@ -121,8 +121,6 @@ class OneButton */ void tick(void); - -private: /** * @brief Call this function every time the input level has changed. * Using this function no digital input pin is checked because the current