From 94415c87fd95a5aa684f09a51b00e6d5e3a06455 Mon Sep 17 00:00:00 2001 From: zhong lixin Date: Wed, 6 Aug 2025 10:10:01 +0800 Subject: [PATCH 1/7] Added boolean mode support and dual-mode operation Added boolean mode support and dual-mode operation --- src/OneButton.cpp | 316 ++++++++++++++-------------------------- src/OneButton.h | 357 ++++++++++++---------------------------------- 2 files changed, 203 insertions(+), 470 deletions(-) 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 From eca19130e7489e97faa7de5389cd38ea2db6608d Mon Sep 17 00:00:00 2001 From: zhong lixin Date: Fri, 26 Dec 2025 14:47:29 +0800 Subject: [PATCH 2/7] Add files via upload --- src/OneButton.cpp | 152 ++++++++++++++++++++++++++++++++++++++- src/OneButton.h | 178 +++++++++++++++++++++++++++++++++++++++++++--- 2 files changed, 320 insertions(+), 10 deletions(-) diff --git a/src/OneButton.cpp b/src/OneButton.cpp index b8d8819..88af46b 100644 --- a/src/OneButton.cpp +++ b/src/OneButton.cpp @@ -1,15 +1,53 @@ +/** + * @file OneButtonbool.cpp + * + * @brief Library for detecting button clicks, doubleclicks and long press + * pattern on a single button. Extended version with boolean mode support. + * + * @author Matthias Hertel, https://www.mathertel.de + * @Copyright Copyright (c) by Matthias Hertel, https://www.mathertel.de. + * Ihor Nehrutsa, Ihor.Nehrutsa@gmail.com + * Boolean mode extension by [Your Name] + * + * 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 for boolean mode. + * Use setupBool() to configure active level. + */ OneButton::OneButton() { // Default constructor for boolean mode + // No hardware pin initialization required } +/** + * @brief Construct a new OneButton object with hardware pin configuration. + * @param pin The pin to be used for input. + * @param activeLow Set to true when input is LOW when pressed. + * @param pullupActive Activate internal pullup resistor. + */ OneButton::OneButton(const int pin, const bool activeLow, const bool pullupActive) { // Constructor for hardware mode setup(pin, pullupActive ? INPUT_PULLUP : INPUT, activeLow); _useHardware = true; } +/** + * @brief Initialize or re-initialize the input pin for hardware mode. + * @param pin The pin to be used for input. + * @param mode Pin mode (INPUT, INPUT_PULLUP, etc.) + * @param activeLow Set to true when input is LOW when pressed. + */ void OneButton::setup(const uint8_t pin, const uint8_t mode, const bool activeLow) { // Configure hardware pin _pin = pin; @@ -18,90 +56,174 @@ void OneButton::setup(const uint8_t pin, const uint8_t mode, const bool activeLo _useHardware = true; } +/** + * @brief Configure the button for boolean mode. + * Use this when button state is provided via tick(bool) method. + * @param activeLow Set to true when active level is false/0. + */ void OneButton::setupBool(bool activeLow) { // Configure for boolean mode _buttonPressed = activeLow; _useHardware = false; } +/** + * @brief Set debounce time in milliseconds. + * @param ms Debounce time in milliseconds. + */ void OneButton::setDebounceMs(int ms) { // Set debounce time in milliseconds _debounce_ms = ms; } +/** + * @brief Set click detection time in milliseconds. + * @param ms Click detection time in milliseconds. + */ void OneButton::setClickMs(unsigned int ms) { // Set click detection time _click_ms = ms; } +/** + * @brief Set long press detection time in milliseconds. + * @param ms Long press detection time in milliseconds. + */ void OneButton::setPressMs(unsigned int ms) { // Set long press detection time _press_ms = ms; } +/** + * @brief Set idle detection time in milliseconds. + * @param ms Idle detection time in milliseconds. + */ void OneButton::setIdleMs(unsigned int ms) { // Set idle detection time _idle_ms = ms; } -// Event attachment implementations +// ----- Event attachment implementations ----- + +/** + * @brief Attach press event callback. + */ void OneButton::attachPress(callbackFunction newFunction) { _pressFunc = newFunction; } + +/** + * @brief Attach parameterized press event callback. + */ void OneButton::attachPress(parameterizedCallbackFunction newFunction, void *parameter) { _paramPressFunc = newFunction; _pressFuncParam = parameter; } + +/** + * @brief Attach click event callback. + */ void OneButton::attachClick(callbackFunction newFunction) { _clickFunc = newFunction; } + +/** + * @brief Attach parameterized click event callback. + */ void OneButton::attachClick(parameterizedCallbackFunction newFunction, void *parameter) { _paramClickFunc = newFunction; _clickFuncParam = parameter; } + +/** + * @brief Attach double click event callback. + */ void OneButton::attachDoubleClick(callbackFunction newFunction) { _doubleClickFunc = newFunction; _maxClicks = max(_maxClicks, 2); } + +/** + * @brief Attach parameterized double click event callback. + */ void OneButton::attachDoubleClick(parameterizedCallbackFunction newFunction, void *parameter) { _paramDoubleClickFunc = newFunction; _doubleClickFuncParam = parameter; _maxClicks = max(_maxClicks, 2); } + +/** + * @brief Attach multi-click event callback. + */ void OneButton::attachMultiClick(callbackFunction newFunction) { _multiClickFunc = newFunction; _maxClicks = max(_maxClicks, 100); } + +/** + * @brief Attach parameterized multi-click event callback. + */ void OneButton::attachMultiClick(parameterizedCallbackFunction newFunction, void *parameter) { _paramMultiClickFunc = newFunction; _multiClickFuncParam = parameter; _maxClicks = max(_maxClicks, 100); } + +/** + * @brief Attach long press start event callback. + */ void OneButton::attachLongPressStart(callbackFunction newFunction) { _longPressStartFunc = newFunction; } + +/** + * @brief Attach parameterized long press start event callback. + */ void OneButton::attachLongPressStart(parameterizedCallbackFunction newFunction, void *parameter) { _paramLongPressStartFunc = newFunction; _longPressStartFuncParam = parameter; } + +/** + * @brief Attach long press stop event callback. + */ void OneButton::attachLongPressStop(callbackFunction newFunction) { _longPressStopFunc = newFunction; } + +/** + * @brief Attach parameterized long press stop event callback. + */ void OneButton::attachLongPressStop(parameterizedCallbackFunction newFunction, void *parameter) { _paramLongPressStopFunc = newFunction; _longPressStopFuncParam = parameter; } + +/** + * @brief Attach during long press event callback. + */ void OneButton::attachDuringLongPress(callbackFunction newFunction) { _duringLongPressFunc = newFunction; } + +/** + * @brief Attach parameterized during long press event callback. + */ void OneButton::attachDuringLongPress(parameterizedCallbackFunction newFunction, void *parameter) { _paramDuringLongPressFunc = newFunction; _duringLongPressFuncParam = parameter; } + +/** + * @brief Attach idle event callback. + */ void OneButton::attachIdle(callbackFunction newFunction) { _idleFunc = newFunction; } +/** + * @brief Reset button state machine to initial state. + */ void OneButton::reset() { // Reset state machine to initial state _state = OCS_INIT; @@ -110,11 +232,20 @@ void OneButton::reset() { _idleState = false; } +/** + * @brief Get current click count. + * @return Number of clicks in current sequence. + */ int OneButton::getNumberClicks() { // Return current click count return _nClicks; } +/** + * @brief Debounce input signal. + * @param value Raw input value. + * @return Debounced value. + */ bool OneButton::debounce(bool value) { // Debounce logic for input signal unsigned long now = millis(); @@ -133,6 +264,9 @@ bool OneButton::debounce(bool value) { return _debouncedLevel; } +/** + * @brief Check hardware pin state (hardware mode only). + */ void OneButton::tick() { // Process hardware pin input if (_useHardware && _pin >= 0) { @@ -141,16 +275,28 @@ void OneButton::tick() { } } +/** + * @brief Process boolean input (boolean mode). + * @param activeLevel Current button state (true = pressed, false = released). + */ void OneButton::tick(bool activeLevel) { // Process boolean input _fsm(debounce(activeLevel)); } +/** + * @brief Transition to new state. + * @param nextState Next state to transition to. + */ void OneButton::_newState(stateMachine_t nextState) { // Transition to new state _state = nextState; } +/** + * @brief Finite State Machine implementation. + * @param activeLevel Debounced button state. + */ void OneButton::_fsm(bool activeLevel) { // Finite State Machine implementation unsigned long now = millis(); @@ -248,4 +394,6 @@ void OneButton::_fsm(bool activeLevel) { _newState(OCS_INIT); break; } -} \ No newline at end of file +} + +// end. \ No newline at end of file diff --git a/src/OneButton.h b/src/OneButton.h index 45f6d79..46448cd 100644 --- a/src/OneButton.h +++ b/src/OneButton.h @@ -1,8 +1,37 @@ +// ----- +// OneButtonbool.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 +// 26.12.2025 Boolean mode support added: Allows button state to be provided +// programmatically via tick(bool) method instead of reading from hardware pin. +// Useful for software buttons, remote controls, or any external button state source. +// ----- + #ifndef OneButton_h #define OneButton_h #include "Arduino.h" +// ----- Callback function types ----- + extern "C" { typedef void (*callbackFunction)(void); typedef void (*parameterizedCallbackFunction)(void *); @@ -10,63 +39,196 @@ extern "C" { class OneButton { public: - // Constructors + // ----- Constructors ----- + + /** + * @brief Create a OneButton instance for boolean mode. + * Use setupBool(...) to configure boolean mode without hardware pin. + */ OneButton(); + + /** + * @brief Create a OneButton instance with hardware pin configuration. + * @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); - // Pin configuration + // ----- Pin configuration ----- + + /** + * @brief Initialize or re-initialize the input pin for hardware mode. + * @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. + */ void setup(const uint8_t pin, const uint8_t mode = INPUT_PULLUP, const bool activeLow = true); - // Boolean mode configuration + // ----- Boolean mode configuration ----- + + /** + * @brief Configure the button for boolean mode (no hardware pin). + * Use this mode when button state is provided programmatically via tick(bool). + * @param activeLow Set to true when active level is false/0, Default is true. + */ void setupBool(bool activeLow = true); - // Timing parameter settings + // ----- Timing parameter settings ----- + + /** + * @brief Set debounce time in milliseconds. + * @param ms Debounce time in milliseconds. + */ void setDebounceMs(int ms); + + /** + * @brief Set click detection time in milliseconds. + * @param ms Click detection time in milliseconds. + */ void setClickMs(unsigned int ms); + + /** + * @brief Set long press detection time in milliseconds. + * @param ms Long press detection time in milliseconds. + */ void setPressMs(unsigned int ms); + + /** + * @brief Set idle detection time in milliseconds. + * @param ms Idle detection time in milliseconds. + */ void setIdleMs(unsigned int ms); + + /** + * @brief Set interval between during-long-press callbacks. + * @param ms Interval in milliseconds (0 = fastest possible). + */ void setLongPressIntervalMs(unsigned int ms) { _long_press_interval_ms = ms; } - // Event attachment functions + // ----- Event attachment functions ----- + + /** + * @brief Attach an event to be called when button is initially pressed. + * @param newFunction Callback function. + */ void attachPress(callbackFunction newFunction); void attachPress(parameterizedCallbackFunction newFunction, void *parameter); + + /** + * @brief Attach an event to be called when a single click is detected. + * @param newFunction Callback function. + */ void attachClick(callbackFunction newFunction); void attachClick(parameterizedCallbackFunction newFunction, void *parameter); + + /** + * @brief Attach an event to be called when a double click is detected. + * @param newFunction Callback function. + */ void attachDoubleClick(callbackFunction newFunction); void attachDoubleClick(parameterizedCallbackFunction newFunction, void *parameter); + + /** + * @brief Attach an event to be called when multiple clicks are detected. + * @param newFunction Callback function. + */ void attachMultiClick(callbackFunction newFunction); void attachMultiClick(parameterizedCallbackFunction newFunction, void *parameter); + + /** + * @brief Attach an event to fire when long press starts. + * @param newFunction Callback function. + */ void attachLongPressStart(callbackFunction newFunction); void attachLongPressStart(parameterizedCallbackFunction newFunction, void *parameter); + + /** + * @brief Attach an event to fire when long press ends. + * @param newFunction Callback function. + */ void attachLongPressStop(callbackFunction newFunction); void attachLongPressStop(parameterizedCallbackFunction newFunction, void *parameter); + + /** + * @brief Attach an event to fire periodically during long press. + * @param newFunction Callback function. + */ void attachDuringLongPress(callbackFunction newFunction); void attachDuringLongPress(parameterizedCallbackFunction newFunction, void *parameter); + + /** + * @brief Attach an event to fire when button is idle. + * @param newFunction Callback function. + */ void attachIdle(callbackFunction newFunction); - // State machine processing + // ----- State machine processing ----- + + /** + * @brief Check hardware pin state (hardware mode only). + */ void tick(); + + /** + * @brief Process button state provided programmatically (boolean mode). + * @param activeLevel Current button state (true = pressed, false = released). + */ void tick(bool activeLevel); - // State control + // ----- State control ----- + + /** + * @brief Reset button state machine to initial state. + */ void reset(); + + /** + * @brief Get current click count. + * @return Number of clicks detected in current sequence. + */ int getNumberClicks(); + + /** + * @brief Check if button is in idle state. + * @return true if button is idle. + */ bool isIdle() const { return _state == OCS_INIT; } + + /** + * @brief Check if button is in long press state. + * @return true if button is in long press state. + */ bool isLongPressed() const { return _state == OCS_PRESS; } + + /** + * @brief Get duration of current button press. + * @return Milliseconds since button was pressed. + */ unsigned long getPressedMs() { return (millis() - _startTime); } - // Mode switching + // ----- Mode switching ----- + + /** + * @brief Enable or disable hardware mode. + * @param use true for hardware mode, false for boolean mode. + */ void useHardware(bool use) { _useHardware = use; } + + /** + * @brief Check current mode. + * @return true if using hardware mode, false if using boolean mode. + */ bool isUsingHardware() const { return _useHardware; } From 6ae65f93b196162de8d60fcf188c13a92c2fa4e0 Mon Sep 17 00:00:00 2001 From: zhong lixin Date: Fri, 26 Dec 2025 14:48:10 +0800 Subject: [PATCH 3/7] Create virtualButton --- examples/virtualButton | 1 + 1 file changed, 1 insertion(+) create mode 100644 examples/virtualButton diff --git a/examples/virtualButton b/examples/virtualButton new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/examples/virtualButton @@ -0,0 +1 @@ + From 1ea7ce44513ef3bc7f3dc7978f14622bb40d96fb Mon Sep 17 00:00:00 2001 From: zhong lixin Date: Fri, 26 Dec 2025 14:48:34 +0800 Subject: [PATCH 4/7] Delete examples/virtualButton --- examples/virtualButton | 1 - 1 file changed, 1 deletion(-) delete mode 100644 examples/virtualButton diff --git a/examples/virtualButton b/examples/virtualButton deleted file mode 100644 index 8b13789..0000000 --- a/examples/virtualButton +++ /dev/null @@ -1 +0,0 @@ - From 81fb3884b8841db7ccb3848bb2279c88f2709db4 Mon Sep 17 00:00:00 2001 From: zhong lixin Date: Fri, 26 Dec 2025 14:50:52 +0800 Subject: [PATCH 5/7] Create dd --- examples/dd | 1 + 1 file changed, 1 insertion(+) create mode 100644 examples/dd diff --git a/examples/dd b/examples/dd new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/examples/dd @@ -0,0 +1 @@ + From 1d96ee7d89b02c2652fbb9aab3df08812ff83537 Mon Sep 17 00:00:00 2001 From: zhong lixin Date: Fri, 26 Dec 2025 14:51:07 +0800 Subject: [PATCH 6/7] Delete examples/dd --- examples/dd | 1 - 1 file changed, 1 deletion(-) delete mode 100644 examples/dd diff --git a/examples/dd b/examples/dd deleted file mode 100644 index 8b13789..0000000 --- a/examples/dd +++ /dev/null @@ -1 +0,0 @@ - From 87fc9185b87e979ad81f634112ad2bc4dca3807b Mon Sep 17 00:00:00 2001 From: zhong lixin Date: Fri, 26 Dec 2025 14:53:35 +0800 Subject: [PATCH 7/7] Update README.md --- README.md | 379 ++++++++++++++++++++++++++++++++++-------------------- 1 file changed, 242 insertions(+), 137 deletions(-) diff --git a/README.md b/README.md index f15348c..1d89ddb 100644 --- a/README.md +++ b/README.md @@ -1,101 +1,203 @@ -# Arduino OneButton Library +Arduino OneButton Library +This Arduino library improves the usage of a single button for input. +It shows how to use a digital input pin with a single pushbutton attached +for detecting typical button press events like single clicks, double clicks, and long-time pressing. +This enables you to reuse the same button for multiple functions and lowers hardware investments. -This Arduino library is improving the usage of a singe button for input. -It shows how to use an digital input pin with a single pushbutton attached -for detecting some of the typical button press events like single clicks, double clicks and long-time pressing. -This enables you to reuse the same button for multiple functions and lowers the hardware investments. - -This is also a sample for implementing simple finite-state machines by using the simple pattern above. +This is also a sample for implementing simple finite-state machines using the simple pattern above. You can find more details on this library at - - -The change log of this library can be found in [CHANGELOG](CHANGELOG.md). +http://www.mathertel.de/Arduino/OneButtonLibrary.aspx +The change log of this library can be found in CHANGELOG. -## Getting Started - -Clone this repository into `Arduino/Libraries` or use the built-in Arduino IDE Library manager to install +Getting Started +Clone this repository into Arduino/Libraries or use the built-in Arduino IDE Library manager to install a copy of this library. You can find more detail about installing libraries -[here, on Arduino's website](https://www.arduino.cc/en/guide/libraries). +here, on Arduino's website. -```CPP +CPP #include #include -``` +Each physical button requires its own OneButton instance. + +Features +Hardware Mode: Connect physical buttons to GPIO pins + +Boolean Mode: Use software-generated button states without hardware pins + +Multiple Click Detection: Single, double, triple, and multi-click support + +Long Press Detection: With start, during, and stop events + +Debouncing: Configurable debounce timing -Each physical button requires its own `OneButton` instance. +Non-blocking: Uses millis() for timing, no delay() calls +Callback Support: Both simple and parameterized callback functions -### Initialize on Instance creation (old way) +Boolean Mode (Software Button) +Version 2.6 introduces Boolean Mode, allowing you to use OneButton without hardware pins. +This is useful for: -You can create a global instance and pass the hardware configurtion directly at compile time: +Software buttons in UI interfaces + +Remote control signals + +Testing button logic without hardware + +Simulating button presses from other sources + +Virtual buttons in applications + +Boolean Mode Initialization +CPP +#include + +// Create a button instance in boolean mode +OneButton virtualButton; + +void setup() { + // Configure for boolean mode (active low) + virtualButton.setupBool(true); + + // Set timing parameters + virtualButton.setDebounceMs(20); + virtualButton.setClickMs(500); + virtualButton.setPressMs(800); + + // Attach event handlers + virtualButton.attachClick(handleClick); + virtualButton.attachDoubleClick(handleDoubleClick); + virtualButton.attachLongPressStart(handleLongPressStart); + + Serial.begin(115200); +} -```cpp +void loop() { + // Get button state from any source (e.g., network, sensor, UI) + bool buttonState = getButtonStateFromSource(); + + // Process the button state + virtualButton.tick(buttonState); + + // Other code... +} +Providing Button State Programmatically +CPP +// Example: Simulating button presses in a test +void simulateButtonPress() { + // Press the button + virtualButton.tick(true); + delay(50); + + // Release the button + virtualButton.tick(false); +} + +// Example: Button state from a web interface +bool webButtonState = false; + +void loop() { + // Update button state from web + webButtonState = checkWebButton(); + + // Process button + virtualButton.tick(webButtonState); +} +Boolean Mode Event Handling +Boolean mode supports all the same events as hardware mode: + +CPP +// Single click +virtualButton.attachClick([]() { + Serial.println("Virtual button clicked!"); +}); + +// Double click +virtualButton.attachDoubleClick([]() { + Serial.println("Virtual button double clicked!"); +}); + +// Long press +virtualButton.attachLongPressStart([]() { + Serial.println("Virtual button long press started!"); +}); + +virtualButton.attachLongPressStop([]() { + Serial.println("Virtual button long press stopped!"); +}); + +// During long press (called periodically) +virtualButton.attachDuringLongPress([]() { + Serial.println("Virtual button still pressed..."); +}); + +// Press event (immediate on press) +virtualButton.attachPress([]() { + Serial.println("Virtual button pressed!"); +}); + +// Idle event (when no activity for idle time) +virtualButton.attachIdle([]() { + Serial.println("Virtual button idle!"); +}); +Hardware Mode (Physical Button) +Initialize on Instance creation (old way) +You can create a global instance and pass the hardware configuration directly at compile time: + +cpp // Declare and initialize OneButton btn = OneButton( BUTTON_PIN, // Input pin for the button true, // Button is active LOW true // Enable internal pull-up resistor ); -``` - -This works for most boards. However, some boards will initialize the hardware AFTER the initialization of global -defined instances and the Input pin will not work at all. +This works for most boards. However, some boards will initialize the hardware AFTER the initialization of global defined instances and the Input pin will not work at all. - -### Explicit setup and deferred initialization (new, more compatible option) - -By using an explicit initialization using the `setup(...)` function solves the problem of the initialization order. -Also this is also a good option in case you do not know the Hardware configuration at compile time. +Explicit setup and deferred initialization (new, more compatible option) +By using an explicit initialization using the setup(...) function solves the problem of the initialization order. +Also this is a good option in case you do not know the Hardware configuration at compile time. Declare a global instance, un-initialized: -```cpp +cpp OneButton btn; -``` +In the main setup() function the instance will be initialized by passing the hardware configuration. Pass the input mode as known from pinMode(): -In the main `setup()` function the instance will be initialized by passing the hardware configuration. Pass the input -mode as known from pinMode(): - -```cpp +cpp btn.setup( BUTTON_PIN, // Input pin for the button INPUT_PULLUP, // INPUT and enable the internal pull-up resistor true // Button is active LOW ); -``` +The SimpleOneButton example shows how to use this sequence. In the new setup(...) function the pinMode can be given in the second parameter to allow all kinds of hardware options. -In the SimpleOneButton example shows how to use this sequence. In the new `setup(...)` function the pinMode can be -given in the second parameter to allow all kind of hardware options. +OneButton Tiny version +The OneButton Library was extended over time with functionality that was requested for specific use cases. This makes the library grow over time too and therefore was limiting use cases using very small processors like attiny84. +Starting with version 2.5, the OneButton Library started supporting these processors with limited memory and low CPU frequencies by introducing the OneButtonTiny class that offers a subset of the features of the complete OneButton class by exposing the following events as callbacks: -## OneButton Tiny version +Click event -The OneButton Library was extended over time with functionality that was requested for specific -use cases. This makes the library growing over time too and therefore was limiting use cases using very small processors like attiny84. +DoubleClick event -Staring with version 2.5 the OneButton Library starts supporting these processors with limited -memory and low cpu frequencies by introducing the `OneButtonTiny` class that offers a subset of -the features of the complete `OneButton` class by exposing the following events as callbacks: +LongPressStart event -* Click event -* DoubleClick event -* LongPressStart event -* Callbacks without parameters +Callbacks without parameters -This saves up to 1k of binary program space that is a huge amount on these processors. +This saves up to 1k of binary program space which is a huge amount on these processors. -With Version 2.5 the `OneButtonTiny` class is now in a beta state. +With Version 2.5, the OneButtonTiny class is now in a beta state. -* Any Issues or pull requests fixing problems are welcome. -* Any new feature request for the `OneButtonTiny` class will be rejected to keep size small. -* New, reasonable functionality will be added to the OneButton class only. +Any Issues or pull requests fixing problems are welcome. +Any new feature request for the OneButtonTiny class will be rejected to keep size small. -### Initialize a Button to GND +New, reasonable functionality will be added to the OneButton class only. -```CPP +Initialize a Button to GND +CPP #define BUTTON_PIN 4 /** @@ -109,12 +211,8 @@ OneButton btn = OneButton( true, // Button is active LOW true // Enable internal pull-up resistor ); -``` - - -### Initialize a Button to VCC - -```CPP +Initialize a Button to VCC +CPP #define BUTTON_PIN 4 /** @@ -129,15 +227,10 @@ OneButton btn = OneButton( false, // Button is active high false // Disable internal pull-up resistor ); -``` - +Attach State Events +Once you have your button initialized, you can handle events by attaching them to the button instance. Events can either be static functions or lambdas (without captured variables). -### Attach State Events - -Once you have your button initialized, you can handle events by attaching them to the button -instance. Events can either be static functions or lambdas (without captured variables). - -```CPP +CPP // Handler function for a single click: static void handleClick() { Serial.println("Clicked!"); @@ -158,101 +251,113 @@ static void handleMultiClick(OneButton *oneButton) { // MultiClick button event attachment with self pointer as a parameter btn.attachMultiClick(handleMultiClick, &btn); -``` +Don't forget to tick() +In order for OneButton to work correctly, you must call tick() on each button instance within your main loop(). If you're not getting any button events, this is probably why. -### Don't forget to `tick()` +For hardware mode: -In order for `OneButton` to work correctly, you must call `tick()` on __each button instance__ -within your main `loop()`. If you're not getting any button events, this is probably why. - -```CPP +CPP void loop() { btn.tick(); // Do other things... } -``` - +For boolean mode: -### Usage with lambdas that capture context - -You __can't pass__ a lambda-__with-context__ to an argument which expects a __function pointer__. To work that around, -use `paramtererizedCallbackFunction`. We pass the context (so the pointer to the object we want to access) to the library -and it will give it back to the lambda. - -```CPP -okBtn.attachClick([](void *ctx){Serial.println(*((BtnHandler*)ctx) -> state);}, this); -``` +CPP +void loop() { + bool buttonState = getButtonState(); // Get state from your source + btn.tick(buttonState); -See also discussion in [Issue #112](https://github.com/mathertel/OneButton/issues/112). + // Do other things... +} +Mode Switching +You can switch between hardware and boolean modes at runtime: +CPP +// Switch to boolean mode +btn.useHardware(false); -## State Events +// Switch to hardware mode (requires pin to be configured) +btn.useHardware(true); +// Check current mode +if (btn.isUsingHardware()) { + Serial.println("Using hardware mode"); +} else { + Serial.println("Using boolean mode"); +} +State Events Here's a full list of events handled by this library: -| Attach Function | Description | -| ----------------------- | ------------------------------------------------------------- | -| `attachPress` | Fires as soon as a press is detected. | -| `attachClick` | Fires as soon as a single click press and release is detected.| -| `attachDoubleClick` | Fires as soon as a double click is detected. | -| `attachMultiClick` | Fires as soon as multiple clicks have been detected. | -| `attachLongPressStart` | Fires as soon as the button is held down for 800 milliseconds.| -| `attachDuringLongPress` | Fires periodically as long as the button is held down. | -| `attachLongPressStop` | Fires when the button is released after a long hold. | - - -### Event Timing - -Valid events occur when `tick()` is called after a specified number of milliseconds. You can use -the following functions to change the timing. +Attach Function Description +attachPress Fires as soon as a press is detected. +attachClick Fires as soon as a single click press and release is detected. +attachDoubleClick Fires as soon as a double click is detected. +attachMultiClick Fires as soon as multiple clicks have been detected. +attachLongPressStart Fires as soon as the button is held down for 800 milliseconds. +attachDuringLongPress Fires periodically as long as the button is held down. +attachLongPressStop Fires when the button is released after a long hold. +attachIdle Fires when button is idle for the specified time. +Event Timing +Valid events occur when tick() is called after a specified number of milliseconds. You can use the following functions to change the timing. + +Note: Attaching a double click will increase the delay for detecting a single click. If a double click event is not attached, the library will assume a valid single click after one click duration, otherwise it must wait for the double click timeout to pass. This is because a single click callback must not be triggered in case of a double click event. + +Function Default Description +setDebounceMs(int) 50 msec Period of time in which to ignore additional level changes. +setClickMs(int) 400 msec Timeout used to distinguish single clicks from double clicks. +setPressMs(int) 800 msec Duration to hold a button to trigger a long press. +setIdleMs(int) 1000 msec Duration of inactivity to trigger idle event. +setLongPressIntervalMs(int) 0 msec Interval between during-long-press callbacks. 0 = as fast as possible. +You may change these default values but be aware that when you specify too short times it is hard to click twice or you will create a long press instead of a click. + +The former functions setDebounceTicks, setClickTicks and setPressTicks are marked deprecated. +The term Ticks in these functions was confusing. Replace them with the ...Ms function calls. +There is no functional change on them. -__Note:__ Attaching a double click will increase the delay for detecting a single click. If a double -click event is not attached, the library will assume a valid single click after one click duration, -otherwise it must wait for the double click timeout to pass. -This is because a single click callback must not to be triggered in case of a double click event. +Set debounce ms to a negative value to only debounce on release. setDebounceMs(-25); will immediately update to a pressed state, and will debounce for 25ms going into the released state. This will expedite the attachPress callback function to run instantly. -| Function | Default | Description | -| ----------------------- | ---------- | ------------------------------------------------------------- | -| `setDebounceMs(int)` | `50 msec` | Period of time in which to ignore additional level changes. | -| `setClickMs(int)` | `400 msec` | Timeout used to distinguish single clicks from double clicks. | -| `setPressMs(int)` | `800 msec` | Duration to hold a button to trigger a long press. | +Additional Functions +OneButton also provides a couple additional functions to use for querying button status: -You may change these default values but be aware that when you specify too short times -it is hard to click twice or you will create a long press instead of a click. +Function Description +bool isLongPressed() Detect whether or not the button is currently inside a long press. +bool isIdle() Detect whether the button is in idle state. +int getPressedMs() Get the current number of milliseconds that the button has been held down for. +int getNumberClicks() Get the number of clicks in the current sequence. +int pin() Get the OneButton pin (returns -1 for boolean mode). +int state() Get the OneButton state +int debouncedValue() Get the OneButton debounced value +void reset() Reset the button state machine to initial state. +Troubleshooting +If your buttons aren't acting the way they should, check these items: -The former functions `setDebounceTicks`, `setClickTicks` and `setPressTicks` are marked deprecated. -The term `Ticks` in these functions where confusing. Replace them with the ...Ms function calls. -There is no functional change on them. +Check your wiring and pin numbers (for hardware mode). -Set debounce ms to a negative value to only debounce on release. `setDebounceMs(-25);` will immediately -update to a pressed state, and will debounce for 25ms going into the released state. This will expidite -the `attachPress` callback function to run instantly. +Did you call tick() on each button instance in your loop? +For boolean mode, are you providing the correct button state (true for pressed, false for released)? -### Additional Functions +Did you set appropriate timing parameters for your use case? -`OneButton` also provides a couple additional functions to use for querying button status: +Are you using the correct active level (active low vs active high)? -| Function | Description | -| ----------------------- | ------------------------------------------------------------------------------ | -| `bool isLongPressed()` | Detect whether or not the button is currently inside a long press. | -| `int getPressedMs()` | Get the current number of milliseconds that the button has been held down for. | -| `int pin()` | Get the OneButton pin | -| `int state()` | Get the OneButton state | -| `int debouncedValue()` | Get the OneButton debounced value | +Did you alter your clock timers in any way without adjusting ticks? +Examples +The library includes several examples: -### `tick()` and `reset()` +SimpleOneButton: Basic hardware button example -You can specify a logic level when calling `tick(bool)`, which will skip reading the pin and use -that level instead. If you wish to reset the internal state of your buttons, call `reset()`. +OneButtonBool_Example: Boolean mode example with simulated button presses +TimerOneButton: Example using Timer interrupts -## Troubleshooting +TwoButtons: Example with multiple buttons -If your buttons aren't acting they way they should, check these items: +License +This library is licensed under BSD 3-Clause License. -1. Check your wiring and pin numbers. -2. Did you call `tick()` on each button instance in your loop? -3. Did you alter your clock timers in any way without adjusting ticks? +Contributing +Contributions are welcome! Please feel free to submit a Pull Request.