From 566cc737354d2d8f1173aa9af35d5a668a5d9da0 Mon Sep 17 00:00:00 2001 From: IhorNehrutsa Date: Sun, 7 May 2023 17:58:36 +0300 Subject: [PATCH 01/10] Add Debounce method for use in SpesialInput. --- examples/SpecialInput/SpecialInput.ino | 25 ++++++++++++++++++++----- src/OneButton.cpp | 14 ++++++++++++++ src/OneButton.h | 3 ++- 3 files changed, 36 insertions(+), 6 deletions(-) diff --git a/examples/SpecialInput/SpecialInput.ino b/examples/SpecialInput/SpecialInput.ino index 335df36..43969ba 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"); }); @@ -63,7 +78,7 @@ void setup() void loop() { // read your own source of input: - bool isPressed = (digitalRead(PIN_INPUT) == LOW); + bool isPressed = (button->debounce(digitalRead(PIN_INPUT)) == LOW); // call tick frequently with current push-state of the input button->tick(isPressed); diff --git a/src/OneButton.cpp b/src/OneButton.cpp index ad337e0..e3f08b7 100644 --- a/src/OneButton.cpp +++ b/src/OneButton.cpp @@ -187,6 +187,20 @@ int OneButton::getNumberClicks(void) return _nClicks; } +/** + * @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, diff --git a/src/OneButton.h b/src/OneButton.h index d32b8e1..76c80b4 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. */ @@ -211,6 +210,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 +222,7 @@ class OneButton public: int pin() const { return _pin; }; stateMachine_t state() const { return _state; }; + int debounce(const int value); }; #endif From 96555eb27d8549ec1852962a5ba51005ad836808 Mon Sep 17 00:00:00 2001 From: IhorNehrutsa Date: Sun, 7 May 2023 18:56:27 +0300 Subject: [PATCH 02/10] Use ...Ms functions instead of ...Ticks() --- README.md | 6 +++--- examples/BlinkMachine/BlinkMachine.ino | 2 +- examples/InterruptOneButton/InterruptOneButton.ino | 4 ++-- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index e8134d1..03807e1 100644 --- a/README.md +++ b/README.md @@ -126,9 +126,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)` | `500 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. 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); From 38c687e4267f7ba741b9247a7129a38f3d09cfd7 Mon Sep 17 00:00:00 2001 From: IhorNehrutsa Date: Sun, 7 May 2023 19:21:02 +0300 Subject: [PATCH 03/10] Update README.md --- README.md | 22 ++++++++++++++++++---- 1 file changed, 18 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 03807e1..e8861d6 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 @@ -100,6 +100,21 @@ void loop() { } ``` +Note that tick() is debouncing by itself but tick(activeLevel) is not. +See [SpecialInput.ino](https://github.com/mathertel/OneButton/blob/master/examples/SpecialInput/SpecialInput.ino) + +```CPP +void loop() +{ + // read your own source of input: + bool isPressed = (button->debounce(digitalRead(PIN_INPUT)) == LOW); + + // call tick frequently with current push-state of the input + button->tick(isPressed); + + // Do other things... +} +``` ## State Events @@ -127,7 +142,7 @@ otherwise it must wait for the double click timeout to pass. | Function | Default | Description | | ----------------------- | ---------- | ------------------------------------------------------------- | | `setDebounceMs(int)` | `50 msec` | Period of time in which to ignore additional level changes. | -| `setClickMs(int)` | `500 msec` | Timeout used to distinguish single clicks from double clicks. | +| `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 @@ -157,4 +172,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? - \ No newline at end of file From 45fbf7a2864392e6d94e02caed29af3710d3d4fe Mon Sep 17 00:00:00 2001 From: IhorNehrutsa Date: Sun, 7 May 2023 23:07:06 +0300 Subject: [PATCH 04/10] Add _fsm() --- src/OneButton.cpp | 22 ++++++++++------------ src/OneButton.h | 5 +++++ 2 files changed, 15 insertions(+), 12 deletions(-) diff --git a/src/OneButton.cpp b/src/OneButton.cpp index e3f08b7..6841c8b 100644 --- a/src/OneButton.cpp +++ b/src/OneButton.cpp @@ -187,6 +187,7 @@ int OneButton::getNumberClicks(void) return _nClicks; } + /** * @brief Debounce input pin level for use in SpesialInput. */ @@ -202,6 +203,7 @@ int OneButton::debounce(const int value) { return debouncedPinLevel; }; + /** * @brief Check input of the configured pin, * debounce input pin level and then @@ -210,20 +212,16 @@ int OneButton::debounce(const int value) { 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. */ @@ -236,7 +234,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); diff --git a/src/OneButton.h b/src/OneButton.h index 76c80b4..804f5ac 100644 --- a/src/OneButton.h +++ b/src/OneButton.h @@ -203,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. */ From b5ca3f626c7fc0981e58d393e365ed453133eae1 Mon Sep 17 00:00:00 2001 From: IhorNehrutsa Date: Sun, 7 May 2023 23:11:04 +0300 Subject: [PATCH 05/10] Revert README.md --- README.md | 16 ---------------- examples/SpecialInput/SpecialInput.ino | 2 +- 2 files changed, 1 insertion(+), 17 deletions(-) diff --git a/README.md b/README.md index e8861d6..02f655c 100644 --- a/README.md +++ b/README.md @@ -100,22 +100,6 @@ void loop() { } ``` -Note that tick() is debouncing by itself but tick(activeLevel) is not. -See [SpecialInput.ino](https://github.com/mathertel/OneButton/blob/master/examples/SpecialInput/SpecialInput.ino) - -```CPP -void loop() -{ - // read your own source of input: - bool isPressed = (button->debounce(digitalRead(PIN_INPUT)) == LOW); - - // call tick frequently with current push-state of the input - button->tick(isPressed); - - // Do other things... -} -``` - ## State Events Here's a full list of events handled by this library: diff --git a/examples/SpecialInput/SpecialInput.ino b/examples/SpecialInput/SpecialInput.ino index 43969ba..6c428cf 100644 --- a/examples/SpecialInput/SpecialInput.ino +++ b/examples/SpecialInput/SpecialInput.ino @@ -78,7 +78,7 @@ void setup() void loop() { // read your own source of input: - bool isPressed = (button->debounce(digitalRead(PIN_INPUT)) == LOW); + bool isPressed = (digitalRead(PIN_INPUT) == LOW); // call tick frequently with current push-state of the input button->tick(isPressed); From 4448a0eb9e449fa4a4d7ab2ac260053f0a50009f Mon Sep 17 00:00:00 2001 From: IhorNehrutsa Date: Sun, 7 May 2023 23:13:07 +0300 Subject: [PATCH 06/10] Update README.md --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 02f655c..2de93f5 100644 --- a/README.md +++ b/README.md @@ -100,6 +100,7 @@ void loop() { } ``` + ## State Events Here's a full list of events handled by this library: From c2bed04d7215f4b1457817baa4f2f15abf048367 Mon Sep 17 00:00:00 2001 From: IhorNehrutsa Date: Sun, 7 May 2023 23:21:07 +0300 Subject: [PATCH 07/10] Update README.md --- README.md | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/README.md b/README.md index 34221db..54c4064 100644 --- a/README.md +++ b/README.md @@ -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. @@ -170,7 +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? -<<<<<<< HEAD -======= - ->>>>>>> upstream/master From e56c597f97040617b931b1ff0dbd75ca980fe9cc Mon Sep 17 00:00:00 2001 From: IhorNehrutsa Date: Sun, 7 May 2023 23:25:14 +0300 Subject: [PATCH 08/10] Update OneButton.cpp --- src/OneButton.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/OneButton.cpp b/src/OneButton.cpp index 6841c8b..f9aae28 100644 --- a/src/OneButton.cpp +++ b/src/OneButton.cpp @@ -212,13 +212,14 @@ int OneButton::debounce(const int value) { void OneButton::tick(void) { if (_pin >= 0) { - fsm(debounce(digitalRead(_pin)) == _buttonPressed); + _fsm(debounce(digitalRead(_pin)) == _buttonPressed); + } } // tick() void OneButton::tick(bool activeLevel) { - fsm(debounce(activeLevel)); + _fsm(debounce(activeLevel)); } From 7b731948e36cc1fd5df36546eccd184590a4ef38 Mon Sep 17 00:00:00 2001 From: Ihor Nehrutsa Date: Mon, 8 May 2023 08:40:43 +0300 Subject: [PATCH 09/10] Update README.md --- README.md | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/README.md b/README.md index 54c4064..7f60d12 100644 --- a/README.md +++ b/README.md @@ -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 From 12538ad3346c19615b64bd0d54e686fc808c5507 Mon Sep 17 00:00:00 2001 From: Ihor Nehrutsa Date: Mon, 8 May 2023 08:56:51 +0300 Subject: [PATCH 10/10] Update OneButton.cpp --- src/OneButton.cpp | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/OneButton.cpp b/src/OneButton.cpp index f9aae28..cf472a7 100644 --- a/src/OneButton.cpp +++ b/src/OneButton.cpp @@ -267,9 +267,9 @@ void OneButton::_fsm(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: @@ -320,9 +320,9 @@ void OneButton::_fsm(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: