Skip to content

Commit

Permalink
Merge pull request #119 from IhorNehrutsa/debounce
Browse files Browse the repository at this point in the history
Thanks for cleanup.
  • Loading branch information
mathertel committed May 8, 2023
2 parents eab6e9d + 12538ad commit 1280bd0
Show file tree
Hide file tree
Showing 6 changed files with 73 additions and 40 deletions.
29 changes: 14 additions & 15 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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
Expand Down Expand Up @@ -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.

Expand All @@ -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
Expand All @@ -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.
Expand Down Expand Up @@ -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?
2 changes: 1 addition & 1 deletion examples/BlinkMachine/BlinkMachine.ino
Original file line number Diff line number Diff line change
Expand Up @@ -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


Expand Down
4 changes: 2 additions & 2 deletions examples/InterruptOneButton/InterruptOneButton.ino
Original file line number Diff line number Diff line change
Expand Up @@ -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()


Expand Down Expand Up @@ -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);

Expand Down
23 changes: 19 additions & 4 deletions examples/SpecialInput/SpecialInput.ino
Original file line number Diff line number Diff line change
Expand Up @@ -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.
*
Expand All @@ -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


Expand All @@ -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);
Expand All @@ -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"); });
Expand Down
47 changes: 30 additions & 17 deletions src/OneButton.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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.
*/
Expand All @@ -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);

Expand Down Expand Up @@ -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:
Expand Down Expand Up @@ -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:
Expand Down
8 changes: 7 additions & 1 deletion src/OneButton.h
Original file line number Diff line number Diff line change
Expand Up @@ -130,7 +130,6 @@ class OneButton
void tick(bool level);


public:
/**
* Reset the button state machine.
*/
Expand Down Expand Up @@ -204,13 +203,19 @@ class OneButton
OCS_PRESSEND = 7,
};

/**
* Run the finite state machine (FSM) using the given level.
*/
void _fsm(bool activeLevel);

/**
* Advance to a new state.
*/
void _newState(stateMachine_t nextState);

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()
Expand All @@ -222,6 +227,7 @@ class OneButton
public:
int pin() const { return _pin; };
stateMachine_t state() const { return _state; };
int debounce(const int value);
};

#endif

0 comments on commit 1280bd0

Please sign in to comment.