diff --git a/README.md b/README.md index 0cd8616..7f60d12 100644 --- a/README.md +++ b/README.md @@ -5,7 +5,7 @@ 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 by using the simple pattern above. You can find more details on this library at http://www.mathertel.de/Arduino/OneButtonLibrary.aspx @@ -16,7 +16,7 @@ The change log of this library can be found in [CHANGELOG](CHANGELOG.md). ## 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 +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). ```CPP @@ -103,7 +103,7 @@ void loop() { ### 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, +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. @@ -118,14 +118,14 @@ See also discussion in [Issue #112 ](https://github.com/mathertel/OneButton/issu Here's a full list of events handled by this library: -| Attach Function | Description | -| ----------------------- | ------------------------------------------------------ | -| `attachClick` | Fires as soon as a single click 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 1 second. | -| `attachDuringLongPress` | Fires periodically as long as the button is held down. | -| `attachLongPressStop` | Fires when the button is released after a long hold. | +| Attach Function | Description | +| ----------------------- | ------------------------------------------------------------- | +| `attachClick` | Fires as soon as a single click 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 @@ -139,9 +139,9 @@ otherwise it must wait for the double click timeout to pass. | Function | Default | Description | | ----------------------- | ---------- | ------------------------------------------------------------- | -| `setDebounceTicks(int)` | `50 msec` | Period of time in which to ignore additional level changes. | -| `setClickTicks(int)` | `500 msec` | Timeout used to distinguish single clicks from double clicks. | -| `setPressTicks(int)` | `800 msec` | Duration to hold a button to trigger a long press. | +| `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. | 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 press instead of a click. @@ -170,4 +170,3 @@ If your buttons aren't acting they way they should, check these items: 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? - diff --git a/examples/BlinkMachine/BlinkMachine.ino b/examples/BlinkMachine/BlinkMachine.ino index 156893e..05a54a7 100644 --- a/examples/BlinkMachine/BlinkMachine.ino +++ b/examples/BlinkMachine/BlinkMachine.ino @@ -96,7 +96,7 @@ void setup() { button.attachDoubleClick(myDoubleClickFunction); // set 80 msec. debouncing time. Default is 50 msec. - button.setDebounceTicks(80); + button.setDebounceMs(80); } // setup diff --git a/examples/InterruptOneButton/InterruptOneButton.ino b/examples/InterruptOneButton/InterruptOneButton.ino index 16e529e..92f8674 100644 --- a/examples/InterruptOneButton/InterruptOneButton.ino +++ b/examples/InterruptOneButton/InterruptOneButton.ino @@ -123,7 +123,7 @@ void multiClick() { // this function will be called when the button was held down for 1 second or more. void pressStart() { Serial.println("pressStart()"); - pressStartTime = millis() - 1000; // as set in setPressTicks() + pressStartTime = millis() - 1000; // as set in setPressMs() } // pressStart() @@ -153,7 +153,7 @@ void setup() { button.attachDoubleClick(doubleClick); button.attachMultiClick(multiClick); - button.setPressTicks(1000); // that is the time when LongPressStart is called + button.setPressMs(1000); // that is the time when LongPressStart is called button.attachLongPressStart(pressStart); button.attachLongPressStop(pressStop); diff --git a/examples/SpecialInput/SpecialInput.ino b/examples/SpecialInput/SpecialInput.ino index 335df36..6c428cf 100644 --- a/examples/SpecialInput/SpecialInput.ino +++ b/examples/SpecialInput/SpecialInput.ino @@ -7,7 +7,7 @@ * * Setup a test circuit: * * Connect a pushbutton to pin 2 (ButtonPin) and ground. - * + * * The sketch shows how to setup the library and bind the functions (singleClick, doubleClick) to the events. * In the loop function the button.tick function must be called as often as you like. * @@ -26,9 +26,14 @@ #if defined(ARDUINO_AVR_UNO) || defined(ARDUINO_AVR_NANO_EVERY) #define PIN_INPUT 2 -#else if defined(ESP8266) +#elif defined(ESP8266) #define PIN_INPUT D3 +#elif defined(ESP32) +// Example pin assignments for a ESP32 board +// Some boards have a BOOT switch using GPIO 0. +#define PIN_INPUT 0 + #endif @@ -41,6 +46,15 @@ void fClicked(void *s) Serial.println((char *)s); } +static void fDoubleClicked(void *oneButton) +{ + OneButton *button = (OneButton *)oneButton; + Serial.print("pin="); + Serial.print(button->pin()); + Serial.print(" state="); + Serial.println(button->state()); +} + void setup() { Serial.begin(115200); @@ -49,8 +63,9 @@ void setup() // create the OneButton instance without a pin. button = new OneButton(); - // Here is an example on how to use a parameter to the registered function: - button->attachClick(fClicked, "me"); + // Here is an example on how to use a parameter to the registered functions: + button->attachClick(fClicked, (void *)"me"); + button->attachDoubleClick(fDoubleClicked, &button); // Here is an example on how to use an inline function: button->attachDoubleClick([]() { Serial.println("DoubleClick"); }); diff --git a/src/OneButton.cpp b/src/OneButton.cpp index ad337e0..cf472a7 100644 --- a/src/OneButton.cpp +++ b/src/OneButton.cpp @@ -188,6 +188,22 @@ int OneButton::getNumberClicks(void) } +/** + * @brief Debounce input pin level for use in SpesialInput. + */ +int OneButton::debounce(const int value) { + now = millis(); // current (relative) time in msecs. + if (_lastDebouncePinLevel == value) { + if (now - _lastDebounceTime >= _debounce_ms) + debouncedPinLevel = value; + } else { + _lastDebounceTime = now; + _lastDebouncePinLevel = value; + } + return debouncedPinLevel; +}; + + /** * @brief Check input of the configured pin, * debounce input pin level and then @@ -196,20 +212,17 @@ int OneButton::getNumberClicks(void) void OneButton::tick(void) { if (_pin >= 0) { - int pinLevel = digitalRead(_pin); - now = millis(); // current (relative) time in msecs. - if (_lastDebouncePinLevel == pinLevel) { - if ((now - _lastDebounceTime) >= _debounce_ms) { - tick(pinLevel == _buttonPressed); // pinLevel is debounced here - } - } else { - _lastDebouncePinLevel = pinLevel; - _lastDebounceTime = now; - } + _fsm(debounce(digitalRead(_pin)) == _buttonPressed); } } // tick() +void OneButton::tick(bool activeLevel) +{ + _fsm(debounce(activeLevel)); +} + + /** * @brief Advance to a new state and save the last one to come back in cas of bouncing detection. */ @@ -222,7 +235,7 @@ void OneButton::_newState(stateMachine_t nextState) /** * @brief Run the finite state machine (FSM) using the given level. */ -void OneButton::tick(bool activeLevel) +void OneButton::_fsm(bool activeLevel) { unsigned long waitTime = (now - _startTime); @@ -254,9 +267,9 @@ void OneButton::tick(bool activeLevel) case OneButton::OCS_UP: // level is inactive - // count as a short button down - _nClicks++; - _newState(OneButton::OCS_COUNT); + // count as a short button down + _nClicks++; + _newState(OneButton::OCS_COUNT); break; case OneButton::OCS_COUNT: @@ -307,9 +320,9 @@ void OneButton::tick(bool activeLevel) case OneButton::OCS_PRESSEND: // button was released. - if (_longPressStopFunc) _longPressStopFunc(); - if (_paramLongPressStopFunc) _paramLongPressStopFunc(_longPressStopFuncParam); - reset(); + if (_longPressStopFunc) _longPressStopFunc(); + if (_paramLongPressStopFunc) _paramLongPressStopFunc(_longPressStopFuncParam); + reset(); break; default: diff --git a/src/OneButton.h b/src/OneButton.h index d32b8e1..804f5ac 100644 --- a/src/OneButton.h +++ b/src/OneButton.h @@ -130,7 +130,6 @@ class OneButton void tick(bool level); -public: /** * Reset the button state machine. */ @@ -204,6 +203,11 @@ class OneButton OCS_PRESSEND = 7, }; + /** + * Run the finite state machine (FSM) using the given level. + */ + void _fsm(bool activeLevel); + /** * Advance to a new state. */ @@ -211,6 +215,7 @@ class OneButton stateMachine_t _state = OCS_INIT; + int debouncedPinLevel = -1; int _lastDebouncePinLevel = -1; // used for pin debouncing unsigned long _lastDebounceTime = 0; // millis() unsigned long now = 0; // millis() @@ -222,6 +227,7 @@ class OneButton public: int pin() const { return _pin; }; stateMachine_t state() const { return _state; }; + int debounce(const int value); }; #endif