diff --git a/src/OneButton.cpp b/src/OneButton.cpp index 80fb5fd..b8d8819 100644 --- a/src/OneButton.cpp +++ b/src/OneButton.cpp @@ -1,349 +1,251 @@ -/** - * @file OneButton.cpp - * - * @brief Library for detecting button clicks, doubleclicks and long press - * pattern on a single button. - * - * @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 - * - * More information on: https://www.mathertel.de/Arduino/OneButtonLibrary.aspx - * - * Changelog: see OneButton.h - */ - #include "OneButton.h" -// ----- Initialization and Default Values ----- - -/** - * @brief Construct a new OneButton object but not (yet) initialize the IO pin. - */ OneButton::OneButton() { - _pin = -1; - // further initialization has moved to OneButton.h + // Default constructor for boolean mode } -// Initialize the OneButton library. OneButton::OneButton(const int pin, const bool activeLow, const bool pullupActive) { + // Constructor for hardware mode setup(pin, pullupActive ? INPUT_PULLUP : INPUT, activeLow); -} // OneButton - + _useHardware = true; +} -// initialize or re-initialize the input pin void OneButton::setup(const uint8_t pin, const uint8_t mode, const bool activeLow) { + // Configure hardware pin _pin = pin; - - if (activeLow) { - // the button connects the input pin to GND when pressed. - _buttonPressed = LOW; - - } else { - // the button connects the input pin to VCC when pressed. - _buttonPressed = HIGH; - } - + _buttonPressed = activeLow ? LOW : HIGH; pinMode(pin, mode); + _useHardware = true; } +void OneButton::setupBool(bool activeLow) { + // Configure for boolean mode + _buttonPressed = activeLow; + _useHardware = false; +} -// explicitly set the number of millisec that have to pass by before a click is assumed stable. -void OneButton::setDebounceMs(const int ms) { +void OneButton::setDebounceMs(int ms) { + // Set debounce time in milliseconds _debounce_ms = ms; -} // setDebounceMs - +} -// explicitly set the number of millisec that have to pass by before a click is detected. -void OneButton::setClickMs(const unsigned int ms) { +void OneButton::setClickMs(unsigned int ms) { + // Set click detection time _click_ms = ms; -} // setClickMs - +} -// explicitly set the number of millisec that have to pass by before a long button press is detected. -void OneButton::setPressMs(const unsigned int ms) { +void OneButton::setPressMs(unsigned int ms) { + // Set long press detection time _press_ms = ms; -} // setPressMs +} -// explicitly set the number of millisec that have to pass by before button idle is detected. -void OneButton::setIdleMs(const unsigned int ms) { +void OneButton::setIdleMs(unsigned int ms) { + // Set idle detection time _idle_ms = ms; -} // setIdleMs +} -// save function for click event +// Event attachment implementations void OneButton::attachPress(callbackFunction newFunction) { _pressFunc = newFunction; -} // attachPress - - -// save function for parameterized click event +} void OneButton::attachPress(parameterizedCallbackFunction newFunction, void *parameter) { _paramPressFunc = newFunction; _pressFuncParam = parameter; -} // attachPress - -// save function for click event +} void OneButton::attachClick(callbackFunction newFunction) { _clickFunc = newFunction; -} // attachClick - - -// save function for parameterized click event +} void OneButton::attachClick(parameterizedCallbackFunction newFunction, void *parameter) { _paramClickFunc = newFunction; _clickFuncParam = parameter; -} // attachClick - - -// save function for doubleClick event +} void OneButton::attachDoubleClick(callbackFunction newFunction) { _doubleClickFunc = newFunction; _maxClicks = max(_maxClicks, 2); -} // attachDoubleClick - - -// save function for parameterized doubleClick event +} void OneButton::attachDoubleClick(parameterizedCallbackFunction newFunction, void *parameter) { _paramDoubleClickFunc = newFunction; _doubleClickFuncParam = parameter; _maxClicks = max(_maxClicks, 2); -} // attachDoubleClick - - -// save function for multiClick event +} void OneButton::attachMultiClick(callbackFunction newFunction) { _multiClickFunc = newFunction; _maxClicks = max(_maxClicks, 100); -} // attachMultiClick - - -// save function for parameterized MultiClick event +} void OneButton::attachMultiClick(parameterizedCallbackFunction newFunction, void *parameter) { _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 +} void OneButton::attachLongPressStart(parameterizedCallbackFunction newFunction, void *parameter) { _paramLongPressStartFunc = newFunction; _longPressStartFuncParam = parameter; -} // attachLongPressStart - - -// save function for longPressStop event +} void OneButton::attachLongPressStop(callbackFunction newFunction) { _longPressStopFunc = newFunction; -} // attachLongPressStop - - -// save function for parameterized longPressStop event +} void OneButton::attachLongPressStop(parameterizedCallbackFunction newFunction, void *parameter) { _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 +} void OneButton::attachDuringLongPress(parameterizedCallbackFunction newFunction, void *parameter) { _paramDuringLongPressFunc = newFunction; _duringLongPressFuncParam = parameter; -} // attachDuringLongPress - - -// save function for idle button event +} void OneButton::attachIdle(callbackFunction newFunction) { _idleFunc = newFunction; -} // attachIdle - +} -void OneButton::reset(void) { - _state = OneButton::OCS_INIT; +void OneButton::reset() { + // Reset state machine to initial state + _state = OCS_INIT; _nClicks = 0; _startTime = millis(); _idleState = false; } - -// ShaggyDog ---- return number of clicks in any case: single or multiple clicks -int OneButton::getNumberClicks(void) { +int OneButton::getNumberClicks() { + // Return current click count return _nClicks; } - -/** - * @brief Debounce input pin level for use in SpesialInput. - */ -bool OneButton::debounce(const bool value) { - now = millis(); // current (relative) time in msecs. - - // Don't debounce going into active state, if _debounce_ms is negative - if (value && _debounce_ms < 0) - debouncedLevel = value; +bool OneButton::debounce(bool value) { + // Debounce logic for input signal + unsigned long now = millis(); + if (value && _debounce_ms < 0) { + _debouncedLevel = value; + } if (_lastDebounceLevel == value) { - if (now - _lastDebounceTime >= abs(_debounce_ms)) - debouncedLevel = value; + if (now - _lastDebounceTime >= static_cast(abs(_debounce_ms))) { + _debouncedLevel = value; + } } else { _lastDebounceTime = now; _lastDebounceLevel = value; } - return debouncedLevel; -}; - + return _debouncedLevel; +} -/** - * @brief Check input of the configured pin, - * debounce button state and then - * advance the finite state machine (FSM). - */ -void OneButton::tick(void) { - if (_pin >= 0) { - _fsm(debounce(digitalRead(_pin) == _buttonPressed)); +void OneButton::tick() { + // Process hardware pin input + if (_useHardware && _pin >= 0) { + bool activeLevel = (digitalRead(_pin) == _buttonPressed); + tick(activeLevel); } -} // tick() - +} void OneButton::tick(bool activeLevel) { + // Process boolean input _fsm(debounce(activeLevel)); } - -/** - * @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) { + // Transition to new state _state = nextState; -} // _newState() - +} -/** - * @brief Run the finite state machine (FSM) using the given level. - */ void OneButton::_fsm(bool activeLevel) { - unsigned long waitTime = (now - _startTime); + // Finite State Machine implementation + unsigned long now = millis(); + unsigned long waitTime = now - _startTime; - // Implementation of the state machine switch (_state) { - case OneButton::OCS_INIT: - // on idle for idle_ms call idle function - if (!_idleState and (waitTime > _idle_ms)) + case OCS_INIT: + // Handle idle state + if (!_idleState && (waitTime > _idle_ms)) { if (_idleFunc) { _idleState = true; _idleFunc(); } + } - // waiting for level to become active. + // Detect button press if (activeLevel) { - _newState(OneButton::OCS_DOWN); - _startTime = now; // remember starting time + _newState(OCS_DOWN); + _startTime = now; _nClicks = 0; + // Trigger press callbacks if (_pressFunc) _pressFunc(); if (_paramPressFunc) _paramPressFunc(_pressFuncParam); - } // if + } break; - case OneButton::OCS_DOWN: - // waiting for level to become inactive. - + case OCS_DOWN: + // Handle button release or long press if (!activeLevel) { - _newState(OneButton::OCS_UP); - _startTime = now; // remember starting time - + _newState(OCS_UP); + _startTime = now; } else if (waitTime > _press_ms) { + // Long press detected if (_longPressStartFunc) _longPressStartFunc(); if (_paramLongPressStartFunc) _paramLongPressStartFunc(_longPressStartFuncParam); - _newState(OneButton::OCS_PRESS); - } // if + _newState(OCS_PRESS); + } break; - case OneButton::OCS_UP: - // level is inactive - - // count as a short button down + case OCS_UP: + // Button released, count as a click _nClicks++; - _newState(OneButton::OCS_COUNT); + _newState(OCS_COUNT); break; - case OneButton::OCS_COUNT: - // dobounce time is over, count clicks - + case OCS_COUNT: + // Count clicks and detect multi-click patterns if (activeLevel) { - // button is down again - _newState(OneButton::OCS_DOWN); - _startTime = now; // remember starting time - + // Another press detected + _newState(OCS_DOWN); + _startTime = now; } else if ((waitTime >= _click_ms) || (_nClicks == _maxClicks)) { - // now we know how many clicks have been made. - + // Click sequence complete if (_nClicks == 1) { - // this was 1 click only. + // Single click if (_clickFunc) _clickFunc(); if (_paramClickFunc) _paramClickFunc(_clickFuncParam); - } else if (_nClicks == 2) { - // this was a 2 click sequence. + // Double click if (_doubleClickFunc) _doubleClickFunc(); if (_paramDoubleClickFunc) _paramDoubleClickFunc(_doubleClickFuncParam); - } else { - // this was a multi click sequence. + // Multi-click if (_multiClickFunc) _multiClickFunc(); if (_paramMultiClickFunc) _paramMultiClickFunc(_multiClickFuncParam); - } // if - + } reset(); - } // if + } break; - case OneButton::OCS_PRESS: - // waiting for pin being release after long press. - + case OCS_PRESS: + // Handle long press if (!activeLevel) { - _newState(OneButton::OCS_PRESSEND); - + _newState(OCS_PRESSEND); } else { - // still the button is pressed + // Trigger during-long-press callbacks if ((now - _lastDuringLongPressTime) >= _long_press_interval_ms) { if (_duringLongPressFunc) _duringLongPressFunc(); if (_paramDuringLongPressFunc) _paramDuringLongPressFunc(_duringLongPressFuncParam); _lastDuringLongPressTime = now; } - } // if + } break; - case OneButton::OCS_PRESSEND: - // button was released. - + case OCS_PRESSEND: + // Long press ended if (_longPressStopFunc) _longPressStopFunc(); if (_paramLongPressStopFunc) _paramLongPressStopFunc(_longPressStopFuncParam); reset(); break; default: - // unknown state detected -> reset state machine - _newState(OneButton::OCS_INIT); + // Reset on unknown state + _newState(OCS_INIT); break; - } // if - -} // OneButton.tick() - - -// end. + } +} \ No newline at end of file diff --git a/src/OneButton.h b/src/OneButton.h index 57fb34a..45f6d79 100644 --- a/src/OneButton.h +++ b/src/OneButton.h @@ -1,314 +1,145 @@ -// ----- -// OneButton.h - Library for detecting button clicks, doubleclicks and long -// press pattern on a single button. This class is implemented for use with the -// Arduino environment. Copyright (c) by Matthias Hertel, -// http://www.mathertel.de This work is licensed under a BSD style license. See -// http://www.mathertel.de/License.aspx More information on: -// http://www.mathertel.de/Arduino -// ----- -// 02.10.2010 created by Matthias Hertel -// 21.04.2011 transformed into a library -// 01.12.2011 include file changed to work with the Arduino 1.0 environment -// 23.03.2014 Enhanced long press functionalities by adding longPressStart and -// longPressStop callbacks -// 21.09.2015 A simple way for debounce detection added. -// 14.05.2017 Debouncing improvements. -// 25.06.2018 Optional third parameter for deactivating pullup. -// 26.09.2018 Anatoli Arkhipenko: Included solution to use library with other -// sources of input. -// 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 #define OneButton_h #include "Arduino.h" -// ----- Callback function types ----- - extern "C" { typedef void (*callbackFunction)(void); typedef void (*parameterizedCallbackFunction)(void *); } - class OneButton { public: - // ----- Constructor ----- - - /* - * Create a OneButton instance. - * use setup(...) to specify the hardware configuration. - */ + // Constructors OneButton(); - - /** - * Create a OneButton instance and setup. - * @param pin The pin to be used for input from a momentary button. - * @param activeLow Set to true when the input level is LOW when the button is pressed, Default is true. - * @param pullupActive Activate the internal pullup when available. Default is true. - */ explicit OneButton(const int pin, const bool activeLow = true, const bool pullupActive = true); - - // ----- Set runtime parameters ----- - - - /** - * Initialize or re-initialize the input pin. - * @param pin The pin to be used for input from a momentary button. - * @param mode Any of the modes also used in pinMode like INPUT or INPUT_PULLUP (default). - * @param activeLow Set to true when the input level is LOW when the button is pressed, Default is true. - */ + + // Pin configuration void setup(const uint8_t pin, const uint8_t mode = INPUT_PULLUP, const bool activeLow = true); - - - /** - * set # millisec after safe click is assumed. - */ - [[deprecated("Use setDebounceMs() instead.")]] - void setDebounceTicks(const unsigned int ms) { - setDebounceMs(ms); - }; // deprecated - void setDebounceMs(const int ms); - - /** - * set # millisec after single click is assumed. - */ - [[deprecated("Use setClickMs() instead.")]] - void setClickTicks(const unsigned int ms) { - setClickMs(ms); - }; // deprecated - void setClickMs(const unsigned int ms); - - /** - * set # millisec after press is assumed. - */ - [[deprecated("Use setPressMs() instead.")]] - void setPressTicks(const unsigned int ms) { - setPressMs(ms); - }; // deprecated - void setPressMs(const unsigned int ms); - - /** - * set interval in msecs between calls of the DuringLongPress event. - * 0 ms is the fastest events calls. - */ - void setLongPressIntervalMs(const unsigned int ms) { + + // Boolean mode configuration + void setupBool(bool activeLow = true); + + // Timing parameter settings + void setDebounceMs(int ms); + void setClickMs(unsigned int ms); + void setPressMs(unsigned int ms); + void setIdleMs(unsigned int ms); + void setLongPressIntervalMs(unsigned int ms) { _long_press_interval_ms = ms; - }; - - /** - * set # millisec after idle is assumed. - */ - void setIdleMs(const unsigned int ms); - - // ----- Attach events functions ----- + } - /** - * Attach an event to be called immediately when a depress is detected. - * @param newFunction This function will be called when the event has been detected. - */ + // Event attachment functions void attachPress(callbackFunction newFunction); void attachPress(parameterizedCallbackFunction newFunction, void *parameter); - - /** - * Attach an event to be called when a single click is detected. - * @param newFunction This function will be called when the event has been detected. - */ void attachClick(callbackFunction newFunction); void attachClick(parameterizedCallbackFunction newFunction, void *parameter); - - /** - * Attach an event to be called after a double click is detected. - * @param newFunction This function will be called when the event has been detected. - */ void attachDoubleClick(callbackFunction newFunction); void attachDoubleClick(parameterizedCallbackFunction newFunction, void *parameter); - - /** - * Attach an event to be called after a multi click is detected. - * @param newFunction This function will be called when the event has been detected. - */ void attachMultiClick(callbackFunction newFunction); void attachMultiClick(parameterizedCallbackFunction newFunction, void *parameter); - - /** - * Attach an event to fire when the button is pressed and held down. - * @param newFunction - */ void attachLongPressStart(callbackFunction newFunction); void attachLongPressStart(parameterizedCallbackFunction newFunction, void *parameter); - - /** - * Attach an event to fire as soon as the button is released after a long press. - * @param newFunction - */ void attachLongPressStop(callbackFunction newFunction); void attachLongPressStop(parameterizedCallbackFunction newFunction, void *parameter); - - /** - * Attach an event to fire periodically while the button is held down. - * The period of calls is set by setLongPressIntervalMs(ms). - * @param newFunction - */ void attachDuringLongPress(callbackFunction newFunction); void attachDuringLongPress(parameterizedCallbackFunction newFunction, void *parameter); - - /** - * Attach an event when the button is in idle position. - * @param newFunction - */ void attachIdle(callbackFunction newFunction); - // ----- State machine functions ----- - - /** - * @brief Call this function every some milliseconds for checking the input - * level at the initialized digital pin. - */ - 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. - */ + // State machine processing + void tick(); void tick(bool activeLevel); - - - /** - * Reset the button state machine. - */ - void reset(void); - - - /* - * return number of clicks in any case: single or multiple clicks - */ - int getNumberClicks(void); - - - /** - * @return true if we are currently handling button press flow - * (This allows power sensitive applications to know when it is safe to power down the main CPU) - */ + + // State control + void reset(); + int getNumberClicks(); bool isIdle() const { return _state == OCS_INIT; } - - /** - * @return true when a long press is detected - */ bool isLongPressed() const { return _state == OCS_PRESS; - }; + } + unsigned long getPressedMs() { + return (millis() - _startTime); + } + // Mode switching + void useHardware(bool use) { + _useHardware = use; + } + bool isUsingHardware() const { + return _useHardware; + } private: - int _pin = -1; // hardware pin number. - 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 - unsigned int _idle_ms = 1000; // number of msecs before idle is detected - - 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 _pressFunc = NULL; - parameterizedCallbackFunction _paramPressFunc = NULL; - void *_pressFuncParam = NULL; - - callbackFunction _clickFunc = NULL; - parameterizedCallbackFunction _paramClickFunc = NULL; - void *_clickFuncParam = NULL; - - callbackFunction _doubleClickFunc = NULL; - parameterizedCallbackFunction _paramDoubleClickFunc = NULL; - void *_doubleClickFuncParam = NULL; - - callbackFunction _multiClickFunc = NULL; - parameterizedCallbackFunction _paramMultiClickFunc = NULL; - void *_multiClickFuncParam = NULL; - - callbackFunction _longPressStartFunc = NULL; - parameterizedCallbackFunction _paramLongPressStartFunc = NULL; - void *_longPressStartFuncParam = NULL; - - callbackFunction _longPressStopFunc = NULL; - parameterizedCallbackFunction _paramLongPressStopFunc = NULL; - void *_longPressStopFuncParam = NULL; - - callbackFunction _duringLongPressFunc = NULL; - parameterizedCallbackFunction _paramDuringLongPressFunc = NULL; - void *_duringLongPressFuncParam = NULL; - - callbackFunction _idleFunc = NULL; - - // These variables that hold information across the upcoming tick calls. - // They are initialized once on program start and are updated every time the - // tick function is called. - - // define FiniteStateMachine - enum stateMachine_t : int { - OCS_INIT = 0, - OCS_DOWN = 1, // button is down - OCS_UP = 2, // button is up - OCS_COUNT = 3, // in multi press-mode, counting - OCS_PRESS = 6, // button is hold down - OCS_PRESSEND = 7, + // Hardware pin and mode + int _pin = -1; + bool _useHardware = false; + + // Timing parameters + int _debounce_ms = 50; + unsigned int _click_ms = 400; + unsigned int _press_ms = 800; + unsigned int _idle_ms = 1000; + bool _buttonPressed = false; + + // Callback function pointers + callbackFunction _pressFunc = nullptr; + parameterizedCallbackFunction _paramPressFunc = nullptr; + void* _pressFuncParam = nullptr; + + callbackFunction _clickFunc = nullptr; + parameterizedCallbackFunction _paramClickFunc = nullptr; + void* _clickFuncParam = nullptr; + + callbackFunction _doubleClickFunc = nullptr; + parameterizedCallbackFunction _paramDoubleClickFunc = nullptr; + void* _doubleClickFuncParam = nullptr; + + callbackFunction _multiClickFunc = nullptr; + parameterizedCallbackFunction _paramMultiClickFunc = nullptr; + void* _multiClickFuncParam = nullptr; + + callbackFunction _longPressStartFunc = nullptr; + parameterizedCallbackFunction _paramLongPressStartFunc = nullptr; + void* _longPressStartFuncParam = nullptr; + + callbackFunction _longPressStopFunc = nullptr; + parameterizedCallbackFunction _paramLongPressStopFunc = nullptr; + void* _longPressStopFuncParam = nullptr; + + callbackFunction _duringLongPressFunc = nullptr; + parameterizedCallbackFunction _paramDuringLongPressFunc = nullptr; + void* _duringLongPressFuncParam = nullptr; + + callbackFunction _idleFunc = nullptr; + + // State machine states + enum stateMachine_t { + OCS_INIT = 0, // Initial state + OCS_DOWN = 1, // Button pressed down + OCS_UP = 2, // Button released + OCS_COUNT = 3, // Counting clicks + OCS_PRESS = 6, // Long press active + OCS_PRESSEND = 7, // Long press ended }; - /** - * Run the finite state machine (FSM) using the given level. - */ + // Internal state machine functions void _fsm(bool activeLevel); - - /** - * Advance to a new state. - */ void _newState(stateMachine_t nextState); + bool debounce(bool value); + // State machine variables stateMachine_t _state = OCS_INIT; - bool _idleState = false; - - bool debouncedLevel = false; - bool _lastDebounceLevel = false; // used for pin debouncing - unsigned long _lastDebounceTime = 0; // millis() - unsigned long now = 0; // millis() - - unsigned long _startTime = 0; // start time of current activeLevel change - 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. - - unsigned int _long_press_interval_ms = 0; // interval in msecs between calls of the DuringLongPress event - unsigned long _lastDuringLongPressTime = 0; // used to produce the DuringLongPress interval - -public: - int pin() const { - return _pin; - }; - stateMachine_t state() const { - return _state; - }; - bool debounce(const bool value); - int debouncedValue() const { - return debouncedLevel; - }; - - /** - * @brief Use this function in the DuringLongPress and LongPressStop events to get the time since the button was pressed. - * @return milliseconds from the start of the button press. - */ - unsigned long getPressedMs() { - return (millis() - _startTime); - }; + bool _debouncedLevel = false; + bool _lastDebounceLevel = false; + unsigned long _lastDebounceTime = 0; + unsigned long _startTime = 0; + int _nClicks = 0; + int _maxClicks = 1; + unsigned int _long_press_interval_ms = 0; + unsigned long _lastDuringLongPressTime = 0; }; -#endif +#endif \ No newline at end of file