diff --git a/examples/SimpleOneButton/SimpleOneButton.ino b/examples/SimpleOneButton/SimpleOneButton.ino index bb7a088..c32fbd4 100644 --- a/examples/SimpleOneButton/SimpleOneButton.ino +++ b/examples/SimpleOneButton/SimpleOneButton.ino @@ -46,7 +46,7 @@ // Setup a new OneButton on pin PIN_INPUT // The 2. parameter activeLOW is true, because external wiring sets the button to LOW when pressed. -OneButton *button; +OneButton button; // In case the momentary button puts the input to HIGH when pressed: // The 2. parameter activeLOW is false when the external wiring sets the button to HIGH when pressed. @@ -67,10 +67,10 @@ void setup() digitalWrite(PIN_LED, ledState); // setup OneButton - button = new OneButton(PIN_INPUT, true); + button.setup(PIN_INPUT, INPUT_PULLUP, true); // link the doubleclick function to be called on a doubleclick event. - button->attachDoubleClick(doubleClick); + button.attachDoubleClick(doubleClick); } // setup @@ -78,7 +78,7 @@ void setup() void loop() { // keep watching the push button: - button->tick(); + button.tick(); // You can implement other code in here or just wait a while delay(10); diff --git a/src/OneButton.cpp b/src/OneButton.cpp index 0a83bb3..d58aeea 100644 --- a/src/OneButton.cpp +++ b/src/OneButton.cpp @@ -23,20 +23,19 @@ /** * @brief Construct a new OneButton object but not (yet) initialize the IO pin. */ -OneButton::OneButton() -{ +OneButton::OneButton() { _pin = -1; // further initialization has moved to OneButton.h } -/** - * Initialize the OneButton library. - * @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. - */ -OneButton::OneButton(const int pin, const bool activeLow, const bool pullupActive) -{ +// Initialize the OneButton library. +OneButton::OneButton(const int pin, const bool activeLow, const bool pullupActive) { + init(pin, pullupActive ? INPUT_PULLUP : INPUT, activeLow); +} // OneButton + + +// initialize or re-initialize the input pin +void OneButton::setup(const uint8_t pin, const uint8_t mode, const bool activeLow) { _pin = pin; if (activeLow) { @@ -48,159 +47,133 @@ OneButton::OneButton(const int pin, const bool activeLow, const bool pullupActiv _buttonPressed = HIGH; } - if (pullupActive) { - // use the given pin as input and activate internal PULLUP resistor. - pinMode(pin, INPUT_PULLUP); - } else { - // use the given pin as input - pinMode(pin, INPUT); - } -} // OneButton + pinMode(pin, mode); +} // 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(const int ms) { _debounce_ms = ms; -} // setDebounceMs +} // 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(const unsigned int ms) { _click_ms = ms; -} // setClickMs +} // 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(const unsigned int ms) { _press_ms = ms; -} // setPressMs +} // 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(const unsigned int ms) { _idle_ms = ms; -} // setIdleMs +} // setIdleMs // save function for click event -void OneButton::attachPress(callbackFunction newFunction) -{ +void OneButton::attachPress(callbackFunction newFunction) { _pressFunc = newFunction; -} // attachPress +} // attachPress // save function for parameterized click event -void OneButton::attachPress(parameterizedCallbackFunction newFunction, void *parameter) -{ +void OneButton::attachPress(parameterizedCallbackFunction newFunction, void *parameter) { _paramPressFunc = newFunction; _pressFuncParam = parameter; -} // attachPress +} // attachPress // save function for click event -void OneButton::attachClick(callbackFunction newFunction) -{ +void OneButton::attachClick(callbackFunction newFunction) { _clickFunc = newFunction; -} // attachClick +} // attachClick // save function for parameterized click event -void OneButton::attachClick(parameterizedCallbackFunction newFunction, void *parameter) -{ +void OneButton::attachClick(parameterizedCallbackFunction newFunction, void *parameter) { _paramClickFunc = newFunction; _clickFuncParam = parameter; -} // attachClick +} // attachClick // save function for doubleClick event -void OneButton::attachDoubleClick(callbackFunction newFunction) -{ +void OneButton::attachDoubleClick(callbackFunction newFunction) { _doubleClickFunc = newFunction; _maxClicks = max(_maxClicks, 2); -} // attachDoubleClick +} // attachDoubleClick // save function for parameterized doubleClick event -void OneButton::attachDoubleClick(parameterizedCallbackFunction newFunction, void *parameter) -{ +void OneButton::attachDoubleClick(parameterizedCallbackFunction newFunction, void *parameter) { _paramDoubleClickFunc = newFunction; _doubleClickFuncParam = parameter; _maxClicks = max(_maxClicks, 2); -} // attachDoubleClick +} // attachDoubleClick // save function for multiClick event -void OneButton::attachMultiClick(callbackFunction newFunction) -{ +void OneButton::attachMultiClick(callbackFunction newFunction) { _multiClickFunc = newFunction; _maxClicks = max(_maxClicks, 100); -} // attachMultiClick +} // attachMultiClick // save function for parameterized MultiClick event -void OneButton::attachMultiClick(parameterizedCallbackFunction newFunction, void *parameter) -{ +void OneButton::attachMultiClick(parameterizedCallbackFunction newFunction, void *parameter) { _paramMultiClickFunc = newFunction; _multiClickFuncParam = parameter; _maxClicks = max(_maxClicks, 100); -} // attachMultiClick +} // attachMultiClick // save function for longPressStart event -void OneButton::attachLongPressStart(callbackFunction newFunction) -{ +void OneButton::attachLongPressStart(callbackFunction newFunction) { _longPressStartFunc = newFunction; -} // attachLongPressStart +} // attachLongPressStart // save function for parameterized longPressStart event -void OneButton::attachLongPressStart(parameterizedCallbackFunction newFunction, void *parameter) -{ +void OneButton::attachLongPressStart(parameterizedCallbackFunction newFunction, void *parameter) { _paramLongPressStartFunc = newFunction; _longPressStartFuncParam = parameter; -} // attachLongPressStart +} // attachLongPressStart // save function for longPressStop event -void OneButton::attachLongPressStop(callbackFunction newFunction) -{ +void OneButton::attachLongPressStop(callbackFunction newFunction) { _longPressStopFunc = newFunction; -} // attachLongPressStop +} // attachLongPressStop // save function for parameterized longPressStop event -void OneButton::attachLongPressStop(parameterizedCallbackFunction newFunction, void *parameter) -{ +void OneButton::attachLongPressStop(parameterizedCallbackFunction newFunction, void *parameter) { _paramLongPressStopFunc = newFunction; _longPressStopFuncParam = parameter; -} // attachLongPressStop +} // attachLongPressStop // save function for during longPress event -void OneButton::attachDuringLongPress(callbackFunction newFunction) -{ +void OneButton::attachDuringLongPress(callbackFunction newFunction) { _duringLongPressFunc = newFunction; -} // attachDuringLongPress +} // attachDuringLongPress // save function for parameterized during longPress event -void OneButton::attachDuringLongPress(parameterizedCallbackFunction newFunction, void *parameter) -{ +void OneButton::attachDuringLongPress(parameterizedCallbackFunction newFunction, void *parameter) { _paramDuringLongPressFunc = newFunction; _duringLongPressFuncParam = parameter; -} // attachDuringLongPress +} // attachDuringLongPress // save function for idle button event -void OneButton::attachIdle(callbackFunction newFunction) -{ +void OneButton::attachIdle(callbackFunction newFunction) { _idleFunc = newFunction; -} // attachIdle +} // attachIdle -void OneButton::reset(void) -{ +void OneButton::reset(void) { _state = OneButton::OCS_INIT; _nClicks = 0; _startTime = millis(); @@ -209,8 +182,7 @@ void OneButton::reset(void) // ShaggyDog ---- return number of clicks in any case: single or multiple clicks -int OneButton::getNumberClicks(void) -{ +int OneButton::getNumberClicks(void) { return _nClicks; } @@ -219,20 +191,20 @@ int OneButton::getNumberClicks(void) * @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) + 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; + + if (_lastDebounceLevel == value) { + if (now - _lastDebounceTime >= abs(_debounce_ms)) debouncedLevel = value; - - if (_lastDebounceLevel == value) { - if (now - _lastDebounceTime >= abs(_debounce_ms)) - debouncedLevel = value; - } else { - _lastDebounceTime = now; - _lastDebounceLevel = value; - } - return debouncedLevel; + } else { + _lastDebounceTime = now; + _lastDebounceLevel = value; + } + return debouncedLevel; }; @@ -241,16 +213,14 @@ bool OneButton::debounce(const bool value) { * debounce button state and then * advance the finite state machine (FSM). */ -void OneButton::tick(void) -{ +void OneButton::tick(void) { if (_pin >= 0) { _fsm(debounce(digitalRead(_pin) == _buttonPressed)); } -} // tick() +} // tick() -void OneButton::tick(bool activeLevel) -{ +void OneButton::tick(bool activeLevel) { _fsm(debounce(activeLevel)); } @@ -258,124 +228,122 @@ void OneButton::tick(bool 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) -{ +void OneButton::_newState(stateMachine_t nextState) { _state = nextState; -} // _newState() +} // _newState() /** * @brief Run the finite state machine (FSM) using the given level. */ -void OneButton::_fsm(bool activeLevel) -{ +void OneButton::_fsm(bool activeLevel) { 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)) - if (_idleFunc) { - _idleState = true; - _idleFunc(); - } - - // waiting for level to become active. - if (activeLevel) { - _newState(OneButton::OCS_DOWN); - _startTime = now; // remember starting time - _nClicks = 0; - - if (_pressFunc) _pressFunc(); - if (_paramPressFunc) _paramPressFunc(_pressFuncParam); - } // if - break; - - case OneButton::OCS_DOWN: - // waiting for level to become inactive. - - if (!activeLevel) { - _newState(OneButton::OCS_UP); - _startTime = now; // remember starting time - - } else if (waitTime > _press_ms) { - if (_longPressStartFunc) _longPressStartFunc(); - if (_paramLongPressStartFunc) _paramLongPressStartFunc(_longPressStartFuncParam); - _newState(OneButton::OCS_PRESS); - } // if - break; - - case OneButton::OCS_UP: - // level is inactive - - // count as a short button down - _nClicks++; - _newState(OneButton::OCS_COUNT); - break; - - case OneButton::OCS_COUNT: - // dobounce time is over, count clicks - - if (activeLevel) { - // button is down again - _newState(OneButton::OCS_DOWN); - _startTime = now; // remember starting time - - } else if ((waitTime >= _click_ms) || (_nClicks == _maxClicks)) { - // now we know how many clicks have been made. - - if (_nClicks == 1) { - // this was 1 click only. - if (_clickFunc) _clickFunc(); - if (_paramClickFunc) _paramClickFunc(_clickFuncParam); - - } else if (_nClicks == 2) { - // this was a 2 click sequence. - if (_doubleClickFunc) _doubleClickFunc(); - if (_paramDoubleClickFunc) _paramDoubleClickFunc(_doubleClickFuncParam); + case OneButton::OCS_INIT: + // on idle for idle_ms call idle function + if (!_idleState and (waitTime > _idle_ms)) + if (_idleFunc) { + _idleState = true; + _idleFunc(); + } + + // waiting for level to become active. + if (activeLevel) { + _newState(OneButton::OCS_DOWN); + _startTime = now; // remember starting time + _nClicks = 0; + + if (_pressFunc) _pressFunc(); + if (_paramPressFunc) _paramPressFunc(_pressFuncParam); + } // if + break; + + case OneButton::OCS_DOWN: + // waiting for level to become inactive. + + if (!activeLevel) { + _newState(OneButton::OCS_UP); + _startTime = now; // remember starting time + + } else if (waitTime > _press_ms) { + if (_longPressStartFunc) _longPressStartFunc(); + if (_paramLongPressStartFunc) _paramLongPressStartFunc(_longPressStartFuncParam); + _newState(OneButton::OCS_PRESS); + } // if + break; + + case OneButton::OCS_UP: + // level is inactive + + // count as a short button down + _nClicks++; + _newState(OneButton::OCS_COUNT); + break; + + case OneButton::OCS_COUNT: + // dobounce time is over, count clicks + + if (activeLevel) { + // button is down again + _newState(OneButton::OCS_DOWN); + _startTime = now; // remember starting time + + } else if ((waitTime >= _click_ms) || (_nClicks == _maxClicks)) { + // now we know how many clicks have been made. + + if (_nClicks == 1) { + // this was 1 click only. + if (_clickFunc) _clickFunc(); + if (_paramClickFunc) _paramClickFunc(_clickFuncParam); + + } else if (_nClicks == 2) { + // this was a 2 click sequence. + if (_doubleClickFunc) _doubleClickFunc(); + if (_paramDoubleClickFunc) _paramDoubleClickFunc(_doubleClickFuncParam); + + } else { + // this was a multi click sequence. + if (_multiClickFunc) _multiClickFunc(); + if (_paramMultiClickFunc) _paramMultiClickFunc(_multiClickFuncParam); + } // if + + reset(); + } // if + break; + + case OneButton::OCS_PRESS: + // waiting for pin being release after long press. + + if (!activeLevel) { + _newState(OneButton::OCS_PRESSEND); } else { - // this was a multi click sequence. - if (_multiClickFunc) _multiClickFunc(); - if (_paramMultiClickFunc) _paramMultiClickFunc(_multiClickFuncParam); - } // if - + // still the button is pressed + 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. + + if (_longPressStopFunc) _longPressStopFunc(); + if (_paramLongPressStopFunc) _paramLongPressStopFunc(_longPressStopFuncParam); reset(); - } // if - break; - - case OneButton::OCS_PRESS: - // waiting for pin being release after long press. - - if (!activeLevel) { - _newState(OneButton::OCS_PRESSEND); - - } else { - // still the button is pressed - 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. - - if (_longPressStopFunc) _longPressStopFunc(); - if (_paramLongPressStopFunc) _paramLongPressStopFunc(_longPressStopFuncParam); - reset(); - break; - - default: - // unknown state detected -> reset state machine - _newState(OneButton::OCS_INIT); - break; - } // if - -} // OneButton.tick() + 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 a706c10..b8ea690 100644 --- a/src/OneButton.h +++ b/src/OneButton.h @@ -30,53 +30,75 @@ // ----- Callback function types ----- extern "C" { -typedef void (*callbackFunction)(void); -typedef void (*parameterizedCallbackFunction)(void *); + typedef void (*callbackFunction)(void); + typedef void (*parameterizedCallbackFunction)(void *); } -class OneButton -{ +class OneButton { public: // ----- Constructor ----- + + /* + * Create a OneButton instance. + * use setup(...) to specify the hardware configuration. + */ OneButton(); /** - * Initialize the OneButton library. + * 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); + 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. + */ + 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 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 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 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) { _long_press_interval_ms = ms; }; + void setLongPressIntervalMs(const unsigned int ms) { + _long_press_interval_ms = ms; + }; /** * set # millisec after idle is assumed. @@ -174,30 +196,34 @@ class OneButton * @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) */ - bool isIdle() const { return _state == OCS_INIT; } + bool isIdle() const { + return _state == OCS_INIT; + } /** * @return true when a long press is detected */ - bool isLongPressed() const { return _state == OCS_PRESS; }; + bool isLongPressed() const { + return _state == OCS_PRESS; + }; 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 _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. + 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; @@ -231,10 +257,10 @@ class OneButton // define FiniteStateMachine enum stateMachine_t : int { OCS_INIT = 0, - OCS_DOWN = 1, // button is down - OCS_UP = 2, // button is up - OCS_COUNT = 3, - OCS_PRESS = 6, // button is hold down + 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, }; @@ -253,28 +279,36 @@ class OneButton bool _idleState = false; bool debouncedLevel = false; - bool _lastDebounceLevel = false; // used for pin debouncing - unsigned long _lastDebounceTime = 0; // millis() - unsigned long now = 0; // millis() + 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 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 + 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; }; + int pin() const { + return _pin; + }; + stateMachine_t state() const { + return _state; + }; bool debounce(const bool value); - int debouncedValue() const { return debouncedLevel; }; + 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); }; + unsigned long getPressedMs() { + return (millis() - _startTime); + }; }; #endif