first commit

This commit is contained in:
jaksatomovic 2024-03-06 12:40:28 +01:00
commit 868b14f8c8
286 changed files with 59925 additions and 0 deletions

1
lib/Button2/.github/FUNDING.yml vendored Normal file
View file

@ -0,0 +1 @@
ko_fi: lennart0815

11
lib/Button2/.gitignore vendored Normal file
View file

@ -0,0 +1,11 @@
# MAC .dot folder
.DS_Store
# vs code files
.vscode
*.code-workspace
# platformio ini file
platformio.ini
.pio
# arduino_ci unit test binaries and artifacts
*.bin
*.bin.dSYM

186
lib/Button2/CHANGELOG.md Executable file
View file

@ -0,0 +1,186 @@
# Changelog
**Note:** Unreleased changes are checked in but not part of an official release (available through the Arduino IDE or PlatfomIO) yet. This allows you to test WiP features and give feedback to them.
## Unreleased
- nothing, so far
## [2.3.1] - 2024-01-02
- bugfix for RP2040, as pointed out by [kilrah](https://github.com/kilrah) in issue [#60](https://github.com/LennartHennigs/Button2/issues/60)
## [2.3.0] - 2024-01-02
- renamed Button2 constants, they now start with `BTN_` (BREAKING CHANGE)
- added `Hardware.h` it implements hardware pin abstraction. Needed for unit testing
- added Unit Tests
- added `resetPressedState()` function
- added `ESP32S2S3CapacitiveTouch.ino` suggested by [ryancasler](https://github.com/ryancasler) in PR #57
## [2.2.4] - 2023-06-22
- `getNumberOfClicks()` now works inside a callback and after the `wait()`statement(s).
- Refactored code in `Button2.cpp`
## [2.2.3] - 2023-06-21
- Included PR for issue [#54](https://github.com/LennartHennigs/Button2/issues/54)
## [2.2.2] - 2022-12-16
- Another stab at the bug [#46](https://github.com/LennartHennigs/Button2/issues/46)
## [2.2.1] - 2022-12-16
- Fixed bug [#46](https://github.com/LennartHennigs/Button2/issues/46) that in some instances long clicks are wrongly triggered
## [2.2.0] - 2022-12-13
- Refactored the main `loop()`
- Rewrote click detection
- Cleaned up the long press handling
- Removed compiler switches they made the code unreadable and they only saved a few bytes (could be a BREAKING CHANGE)
- Added `byte getLongClickCount()` function
- Updated the [LongpressHandler](https://github.com/LennartHennigs/Button2/blob/master/examples/LongpressHandler/LongpressHandler.ino) example
- Defaults (x>3)-clicks to triple
- Fixed bug with button ID
## [2.1.0] - 2022-11-03
- Removed the capacitive touch functionality out of main library. (BREAKING CHANGE). The constructor and `begin()` lost a parameter. Instead I provide a custom handler example for cap. touch [ESP32CapacitiveTouch.ino](https://github.com/LennartHennigs/Button2/blob/master/examples/ESP32CapacitiveTouch/ESP32CapacitiveTouch.ino). For reasons, see [#45](https://github.com/LennartHennigs/Button2/issues/45).
- Added an ESP32 timer interrupt example [ESP32TimerInterrupt.ino](https://github.com/LennartHennigs/Button2/blob/master/examples/ESP32TimerInterrupt/ESP32TimerInterrupt.ino) based on [#43](https://github.com/LennartHennigs/Button2/issues/43).
- Added compiler switches in `Button.h` to remove click detection code, as mentioned in [#44](https://github.com/LennartHennigs/Button2/issues/44).
- Clarified the difference between the `setLongClickHandler` and the `setLongClickDetectedHandler` in the README and the [MultiHandler](https://github.com/LennartHennigs/Button2/blob/master/examples/MultiHandler/MultiHandler.ino) example as mentioned in [#41](https://github.com/LennartHennigs/Button2/issues/41). (The handler set via `setLongClickHandler` waits until you release the button, the second one is called as soon as the defined long-click time has passed.)
- Made `byte _getState()` into a `const` function.
## [2.0.3] - 2022-05-26
- Fixed bug with the button ID as pointed out by [Jon](https://github.com/mscreations) in [#39](https://github.com/LennartHennigs/Button2/pull/39).
## [2.0.2] - 2022-05-21
- Added example for the [M5Stack Core2](https://github.com/LennartHennigs/Button2/blob/master/examples/M5StackCore2CustomHandler/M5StackCore2CustomHandler.ino) - showing how to add a custom handler for the touch buttons.
## [2.0.1] - 2022-04-22
- Fixed bug `longclick_detected_counter` is not properly initalized as mentioned in [#37](https://github.com/LennartHennigs/Button2/pull/37).
## [2.0.0] - 2022-04-04
- House keeping
- Refactored `loop()` - cleaned up conditions, should be easier to understand now.
- Renamed `getAttachedPin()`to `getPin()` (BREAKING CHANGE).
- Fixed a bug that the first click type was falsly returned by `getClickType()`.
- Possibility define your own "_getState" function for non standard buttons as suggested in [#32](https://github.com/LennartHennigs/Button2/issues/32).
- Refactored `isPressedRaw()` to also use `_getState()`.
- Introduced a `VIRTUAL_PIN` constant using it in the constructor or `begin()` will skip pin initalization.
- Added `setButtonStateFunction(StateCallbackFunction f)` to assign your own "_getState" function.
- Added [CustomButtonStateHandler.ino](https://github.com/LennartHennigs/Button2/blob/master/examples/CustomButtonStateHandler/CustomButtonStateHandler.ino) example.
- Improved click type handling.
- Added `clickType` and removed constants for determining the click type (BREAKING CHANGE)
- Renamed `getClickType()` to `getType()` (BREAKING CHANGE)
- Added `clickToString` function to print the `clickType` enum value
- Added IDs button instances
- Added `getID()`, it returns an auto incremented `int` ID for the button, as suggest in [#34](https://github.com/LennartHennigs/Button2/pull/34)
- Added `setID()`, it allows you to set your own IDs but then you need to ensure its uniqeness yourself
- Added possibility to use the button class inside your main `loop()` function (instead of using callback handlers)
- Added `bool wasPressed()` function
- Added `read(bool keepState = false)`, it returns the button press type (as a `clickType` enum)
- Added `wait(bool keepState = false)`, it combines `wasPressed()` and `read()` methods and halts execution until a button press took place
- Added `waitForClick()`, `waitForDouble()`, `waitForTriple()` and `waitForLong()` as well
- Added [ButtonLoop.ino](https://github.com/LennartHennigs/Button2/blob/master/examples/ButtonLoop/ButtonLoop.ino) example to showcase the "loop" functions
## [1.6.5] - 2021-09-12
- Fixed problem with `std::function` as found by [ItsFlo](https://github.com/ItsFlo) in pull request [#29](https://github.com/LennartHennigs/Button2/pull/29)
## [1.6.4] - 2021-09-12
- Use `std::function` to allow C++ 11 lambda functions as suggested by [jacobdekeizer](https://github.com/jacobdekeizer) in pull request [#29](https://github.com/LennartHennigs/Button2/pull/29)
## [1.6.3] - 2021-09-12
- added two new examples: [MultiHandlerTwoButtons.ino](https://github.com/LennartHennigs/Button2/blob/master/examples/MultiHandlerTwoButtons/MultiHandlerTwoButtons.ino) and [TrackDualButtonClick.ino](https://github.com/LennartHennigs/Button2/blob/master/examples/TrackDualButtonClick/TrackDualButtonClick.ino)
- added examples to the [README.md](https://github.com/LennartHennigs/Button2/blob/master/README.md)
- initialized `pin` in `_getState()`
- a bit of variable clean up
- updated setting of inital state of `state` and `prev_state`
## [1.6.2] - 2021-06-22
- initialized `pin` property to 255 instead of -1, as pointed out by [rin67630](https://github.com/rin67630) in issue [#26](https://github.com/LennartHennigs/Button2/issues/26)
## [1.6.1] - 2021-03-22
- updated [README.md](https://github.com/LennartHennigs/Button2/blob/master/README.md)
- added `const` to getter functions
## [1.6.0] - 2021-02-10
- added getter/setter functions for debounce, longclick and doubleclick timeouts
- removed debounce timeout parameter from `contructor` and `begin()`
## [1.5.4] - 2021-02-08
- Added `getAttachPin()` function, as suggested by [madivad](https://github.com/madivad) in issue [#23](https://github.com/LennartHennigs/Button2/issues/23)
## [1.5.3] - 2021-01-26
- Fixed a bug in the constructor, as suggested by [alex-s-v](https://github.com/alex-s-v) in pull request [#22](https://github.com/LennartHennigs/Button2/pull/22)
## [1.5.2] - 2021-01-26
- Fixed a bug in the `isPressed()` function, as suggested by [zenturacp](https://github.com/zenturacp) in [#21](https://github.com/LennartHennigs/Button2/issues/21)
## [1.5.1] - 2021-01-04
- Fixed a bug in the `loop()` function
## [1.5.0] - 2021-01-03
- Added default constructor and `begin()` function
- Added pull request by [skelstar](https://github.com/skelstar) to add the `setLongClickDetectedHandler()` function which is triggered as soon as the longclick timeout has passed
## [1.4.1] - 2020-12-19
- Moved activeLow outside of isCapacitive condition (as suggested by [Wai Lin](https://github.com/w4ilun) in [#18](https://github.com/LennartHennigs/Button2/pull/18)
## [1.4.0] - 2020-11-06
- Updated LongpressHandler example - changed variable name to from `button` to `button`
- toggled `pressed` and `released` (as suggesed by [TommyC81](https://github.com/TommyC81) in [#16](https://github.com/LennartHennigs/Button2/issues/16))
- added debug function `isPressedRaw()` (as suggesed by [TommyC81](https://github.com/TommyC81) in [#16](https://github.com/LennartHennigs/Button2/issues/16))
- fixed bug with `click_count` (as suggesed by [TommyC81](https://github.com/TommyC81) in [#16](https://github.com/LennartHennigs/Button2/issues/16))
- changed return types of `getNumberOfClicks()` and `getClickType()` to `byte`
## [1.3.0] - 2020-11-06
- Added capacitive touch sensor capabilties (for ESP32) (as suggested by [qubolino](https://github.com/qubolino) in [#11](https://github.com/LennartHennigs/Button2/issues/11))
- Removed deprecated entry in the library.properties file (as suggested by [SangLe](https://github.com/SNL5943)) in [#15](https://github.com/LennartHennigs/Button2/issues/15)
- Added `const` modifier to functions (as suggested by [Anton-V-K](https://github.com/Anton-V-K) in [#13](https://github.com/LennartHennigs/Button2/issues/13))
## [1.2.0] - 2020-04-16
- Added possibility to define your own timeouts for clicks (as suggested by [cmeldas](https://github.com/cmeldas) in [#10](https://github.com/LennartHennigs/Button2/issues/10))
- Removed `yield()` in main `loop()` since it caused some problems
- Created and added CHANGELOG.md
## [1.1.0] - 2020-03-27
- Changed the private functions to protected (as suggested by [Nagymadar](https://github.com/Nagymadar) in [#9](https://github.com/LennartHennigs/Button2/issues/9))
- Added support for active high buttons (as suggested by [Nagymadar](https://github.com/Nagymadar) in [#8](https://github.com/LennartHennigs/Button2/issues/8))
- Added `reset()` function to unset all functions (as suggested by [Nagymadar](https://github.com/Nagymadar) in [#7](https://github.com/LennartHennigs/Button2/issues/7))
- Added a `yield()` command to the main `loop()`
- Changed the times for double and triple click
- Fixed error in `SingleButton.ino` (as suggested by [alexthe-red](https://github.com/alexthe-red) in [#3](https://github.com/LennartHennigs/Button2/issues/3))
- Added the library to the Arduino IDE
## [1.0.0] - 2017-11-09
- initial release
## Note
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

21
lib/Button2/LICENSE Executable file
View file

@ -0,0 +1,21 @@
MIT License
Copyright (c) 2017-2022 Lennart Hennigs
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

295
lib/Button2/README.md Executable file
View file

@ -0,0 +1,295 @@
# Button2
Arduino/ESP library to simplify working with buttons.
- Author: Lennart Hennigs (<https://www.lennarthennigs.de>)
- Copyright (C) 2017-2023 Lennart Hennigs.
- Released under the MIT license.
## Description
This library allows you to use callback functions to track single, double, triple and long clicks. Alternatively, it provides function to use in your main `loop()`.
The library also takes care of debouncing. Using this lib will reduce and simplify your source code significantly.
It has been tested with Arduino, ESP8266 and ESP32 devices.
To see the latest changes to the library please take a look at the [Changelog](https://github.com/LennartHennigs/Button2/blob/master/CHANGELOG.md).
If you find this library helpful please consider giving it a ⭐️ at [GitHub](https://github.com/LennartHennigs/Button2) and/or [buy me a ☕️](https://ko-fi.com/lennart0815).
Thank you!
## How To Use
This library allows you to define a button and uses callback functions to detect different types of button interactions.
If you don't want to use callback there are also functions available for using it in your code's main `loop()`.
### Definition
- Include the library on top
```c++
#include "Button2.h"
```
- Define the button either using the `constructor` or the `begin()` function.
```c++
void begin(byte attachTo, byte buttonMode = INPUT_PULLUP, boolean activeLow = true);
```
### Button Types
- You can use the class for "real" buttons (*pullup*, *pulldown*, and *active low*).
- Per default the button pins are defined as `INPUT_PULLUP`. You can override this upon creation.
```c++
#include "Button2.h"
#define BUTTON_PIN D3
Button2 button;
void setup() {
button.begin(BUTTON_PIN);
}
```
- You can also the library with other types of buttons, e.g. capacitive touch or ones handled via I2C. See the section on defining custom handlers below.
### Callback Handler
- Instead of frequently checking the button state in your main `loop()` this class allows you to assign callback functions.
- You can define callback functions to track various types of clicks:
- `setTapHandler()` will be be called when any click occurs. This is the most basic handler. It ignores all timings built-in the library for double or triple click detection.
- `setClickHandler()` will be triggered after a single click occurred.
- `setChangedHandler()`, `setPressedHandler()` and `setReleasedHandler()` allow to detect basic interactions.
- `setLongClickDetectedHandler()` will be triggered as soon as the long click timeout has passed.
- `setLongClickHandler()` will be triggered after the button has released.
- `setDoubleClickHandler()` and `setTripleClickHandler()` detect complex interactions.
- **Note:** You will experience a short delay with `setClickHandler()` and `setLongClickHandler()` as need to check whether a long or multi-click is in progress. For immediate feedback use `setTapHandler()`or `setLongClickDetectedHandler()`
- You can assign callback functions for single or for multiple buttons.
- You can track individual or multiple events with a single handler.
- Please take a look at the included examples (see below) to get an overview over the different callback handlers and their usage.
- All callback functions need a `Button2` reference parameter. There the reference to the triggered button is stored. This can used to call status functions, e.g. `wasPressedFor()`.
### Longpress Handling
- There are two possible callback functions: `setLongClickDetectedHandler()` and `setLongClickHandler()`.
- `setLongClickDetectedHandler()` will be called as soon as the defined timeout has passed.
- `setLongClickHandler()` will only be called after the button has been released.
- `setLongClickDetectedRetriggerable(bool retriggerable)` allows you to define whether want to get multiple notifications for a **single** long click depending on the timeout.
- `getLongClickCount()` gets you the number of long clicks this is useful when `retriggerable` is set.
### The Loop
- For the class to work, you need to call the button's `loop()` member function in your sketch's `loop()` function.
```c++
#include "Button2.h"
#define BUTTON_PIN D3
Button2 button;
void handleTap(Button2& b) {
// check for really long clicks
if (b.wasPressedFor() > 1000) {
// do something
}
}
void setup() {
button.begin(BUTTON_PIN);
button.setTapHandler(handleTap);
}
void loop() {
button.loop();
}
```
- As the `loop()`function needs to be called continuously, `delay()` and other blocking functions will interfere with the detection of clicks. Consider cleaning up your loop or call the `loop()` function via an interrupt.
- Please see the *examples* below for more details.
### Using an timer interrupt instead
- Alternatively, you can call the button's `loop()` function via a timer interrupt.
- I haven't tried this extensively, USE THIS AT YOUR OWN RISK!
- You need make sure that the interval is quick enough that it can detect your timeouts (see below).
- There is an example for the ESP32 [ESP32TimerInterrupt.ino](https://github.com/LennartHennigs/Button2/blob/master/examples/ESP32TimerInterrupt/ESP32TimerInterrupt.ino) that I tested.
### Timeouts
- The default timeouts for events are (in ms):
```c++
#define BTN_DEBOUNCE_MS 50
#define BTN_LONGCLICK_MS 200
#define BTN_DOUBLECLICK_MS 300
```
- You can define your own timeouts by using these setter functions:
- `void setDebounceTime(unsigned int ms)`
- `void setLongClickTime(unsigned int ms)`
- `void setDoubleClickTime(unsigned int ms)`
- There are also getter functions available, if needed.
### Using Button2 in the main `loop()`
- Even though I suggest to use handlers for tracking events, you can also use Button2 to check button's state in the main loop
- `bool wasPressed()` allows you to check whether the button was pressed
- `clickType read(bool keepState = false)` gives you the type of click that took place
- `clickType wait(bool keepState = false)` combines `read()` and `wasPressed()` and halts execution until a button click was detected. Thus, it is blocking code.
- The `clickType` is an enum defined as...
```c++
enum clickType {
single_click,
double_click,
triple_click,
long_click,
empty
};
```
- There are also dedicated waits (`waitForClick()`, `waitForDouble()`, `waitForTriple()` and `waitForLong()`) to detect a specific type
- The `read()` and the *wait* functions will reset the state of `wasPressed()` unless specified otherwise (via a `bool` parameter)
- `resetPressedState()` allows you to clear value returned by `wasPressed()` it is similar to passing `keepState = false` for `read()` or `wait()`.
- Check out the [ButtonLoop.ino](https://github.com/LennartHennigs/Button2/blob/master/examples/ButtonLoop/ButtonLoop.ino) example to see it in action
### Status Functions
- There are several status functions available to check the status of a button:
``` c++
unsigned int wasPressedFor() const;
byte getNumberOfClicks() const;
byte getType() const;
boolean isPressed() const;
boolean isPressedRaw() const;
bool wasPressed() const;
```
### IDs for Button Instances
- Each button instance gets a unique (auto incremented) ID upon creation.
- You can get a buttons' ID via `getID()`.
- Alternatively, you can use `setID(int newID)` to set a new one. But then you need to make sure that they are unique.
### Creating A Custom Button State Handler
- Out of the box *Button2* supports regular hardware buttons.
- If you want to add other button types you need to define your own function that tracks the state of the button.
- Use `setButtonStateFunction()` to assign it to your *Button2* instance
- Make the button pin 'VIRTUAL', i.e. by calling `button.begin(VIRTUAL_PIN);`
- And don't forget to initialize the button as this cannot be handled by *Button2*
- See [ESP32CapacitiveTouch.ino](https://github.com/LennartHennigs/Button2/blob/master/examples/ESP32CapacitiveTouch/ESP32CapacitiveTouch.ino), [M5StackCore2CustomHandler.ino](https://github.com/LennartHennigs/Button2/blob/master/examples/M5StackCore2CustomHandler/M5StackCore2CustomHandler.ino), and [CustomButtonStateHandler.ino](https://github.com/LennartHennigs/Button2/blob/master/examples/CustomButtonStateHandler/CustomButtonStateHandler.ino) as examples.
## Examples
- [SingleButtonSimple.ino](https://github.com/LennartHennigs/Button2/blob/master/examples/SingleButtonSimple/SingleButtonSimple.ino) the most basic example, shows how to assign event handlers
- [LongpressHandler.ino](https://github.com/LennartHennigs/Button2/blob/master/examples/LongpressHandler/LongpressHandler.ino) shows how determine the time of a button press
- [SingleButton.ino](https://github.com/LennartHennigs/Button2/blob/master/examples/SingleButton/SingleButton.ino) shows the different event handlers
- [MultipleButtons.ino](https://github.com/LennartHennigs/Button2/blob/master/examples/MultipleButtons/MultipleButtons.ino) how to use two buttons
- [MultiHandler.ino](https://github.com/LennartHennigs/Button2/blob/master/examples/MultiHandler/MultiHandler.ino) how to use a single handler for multiple events
- [MultiHandlerTwoButtons.ino](https://github.com/LennartHennigs/Button2/blob/master/examples/MultiHandlerTwoButtons/MultiHandlerTwoButtons.ino) a single handler for multiple buttons
- [TrackDualButtonClick.ino](https://github.com/LennartHennigs/Button2/blob/master/examples/TrackDualButtonClick/TrackDualButtonClick.ino) how to detect when two buttons are clicked at the same time
- [CustomButtonStateHandler.ino](https://github.com/LennartHennigs/Button2/blob/master/examples/CustomButtonStateHandler/CustomButtonStateHandler.ino) - how to assign your own button handler
- [ESP32CapacitiveTouch.ino](https://github.com/LennartHennigs/Button2/blob/master/examples/ESP32CapacitiveTouch/ESP32CapacitiveTouch.ino) how to access the ESP32s capacitive touch handlers
- [M5StackCore2CustomHandler.ino](https://github.com/LennartHennigs/Button2/blob/master/examples/M5StackCore2CustomHandler/M5StackCore2CustomHandler.ino) - example for the M5Stack Core2 touch buttons
- [ESP32TimerInterrupt.ino](https://github.com/LennartHennigs/Button2/blob/master/examples/ESP32TimerInterrupt/ESP32TimerInterrupt.ino) - how to use a timer interrupt with the library.
- [ButtonLoop.ino](https://github.com/LennartHennigs/Button2/blob/master/examples/ButtonLoop/ButtonLoop.ino) how to use the button class in the main loop (I recommend using handlers, but well...)
## Class Definition
The button class offers a few additional functions, please take a look at the *Class Definition* below.
See below the constructors and member functions the library provides:
```c++
Button2();
Button2(byte attachTo, byte buttonMode = INPUT_PULLUP, boolean activeLow = true);
void begin(byte attachTo, byte buttonMode = INPUT_PULLUP, boolean activeLow = true);
void setDebounceTime(unsigned int ms);
void setLongClickTime(unsigned int ms);
void setDoubleClickTime(unsigned int ms);
unsigned int getDebounceTime();
unsigned int getLongClickTime();
unsigned int getDoubleClickTime();
byte getPin();
void reset();
void setButtonStateFunction(StateCallbackFunction f);
void setChangedHandler(CallbackFunction f);
void setPressedHandler(CallbackFunction f);
void setReleasedHandler(CallbackFunction f);
void setTapHandler(CallbackFunction f);
void setClickHandler(CallbackFunction f);
void setDoubleClickHandler(CallbackFunction f);
void setTripleClickHandler(CallbackFunction f);
void setLongClickHandler(CallbackFunction f);
void setLongClickDetectedHandler(CallbackFunction f);
void setLongClickDetectedRetriggerable(bool retriggerable);
void byte getLongClickCount() const;
unsigned int wasPressedFor() const;
void resetPressedState();
boolean isPressed() const;
boolean isPressedRaw() const;
bool wasPressed() const;
clickType read(bool keepState = false);
clickType wait(bool keepState = false);
void waitForClick(bool keepState = false);
void waitForDouble(bool keepState = false);
void waitForTriple(bool keepState = false);
void waitForLong(bool keepState = false);
byte getNumberOfClicks() const;
byte getType() const;
String clickToString(clickType type) const;
int getID() const;
void setID(int newID);
bool operator == (Button2 &rhs);
void loop();
```
## Installation
Open the Arduino IDE choose "Sketch > Include Library" and search for "Button2".
Or download the ZIP archive (<https://github.com/lennarthennigs/Button2/zipball/master>), and choose "Sketch > Include Library > Add .ZIP Library..." and select the downloaded file.
## License
MIT License
Copyright (c) 2017-2023 Lennart Hennigs
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

View file

@ -0,0 +1,35 @@
/////////////////////////////////////////////////////////////////
#include "Button2.h"
/////////////////////////////////////////////////////////////////
#define BUTTON_PIN 2
/////////////////////////////////////////////////////////////////
Button2 button;
/////////////////////////////////////////////////////////////////
void setup() {
Serial.begin(9600);
delay(50);
Serial.println("\n\nButton Loop Demo");
button.begin(BUTTON_PIN);
}
/////////////////////////////////////////////////////////////////
void loop() {
button.loop();
Serial.println(button.clickToString(button.wait()));
Serial.println(button.getNumberOfClicks());
}
/////////////////////////////////////////////////////////////////

View file

@ -0,0 +1,39 @@
/////////////////////////////////////////////////////////////////
#include "Button2.h"
/////////////////////////////////////////////////////////////////
Button2 button;
/////////////////////////////////////////////////////////////////
byte myButtonStateHandler() {
return digitalRead(39);
}
/////////////////////////////////////////////////////////////////
void myTapHandler(Button2 &b) {
Serial.println("tap");
}
/////////////////////////////////////////////////////////////////
void setup() {
Serial.begin(9600);
delay(100);
Serial.println("\n\nCustom Buttom Test");
button.begin(VIRTUAL_PIN);
pinMode(39, INPUT_PULLUP);
button.setButtonStateFunction(myButtonStateHandler);
button.setTapHandler(myTapHandler);
}
/////////////////////////////////////////////////////////////////
void loop() {
button.loop();
}
/////////////////////////////////////////////////////////////////

View file

@ -0,0 +1,50 @@
/////////////////////////////////////////////////////////////////
#if !defined(ESP32)
#error This sketch needs an ESP32
#else
/////////////////////////////////////////////////////////////////
#include "Button2.h"
/////////////////////////////////////////////////////////////////
Button2 button;
byte pin = 4;
/////////////////////////////////////////////////////////////////
byte capStateHandler() {
int capa = touchRead(pin);
return capa < button.getDebounceTime() ? LOW : HIGH;
}
/////////////////////////////////////////////////////////////////
void setup() {
Serial.begin(9600);
delay(50);
Serial.println("\n\nCapacitive Touch Demo");
button.setDebounceTime(35);
button.setButtonStateFunction(capStateHandler);
button.setClickHandler(click);
button.begin(VIRTUAL_PIN);
}
/////////////////////////////////////////////////////////////////
void loop() {
button.loop();
}
/////////////////////////////////////////////////////////////////
void click(Button2& btn) {
Serial.println("click\n");
}
/////////////////////////////////////////////////////////////////
#endif
/////////////////////////////////////////////////////////////////

View file

@ -0,0 +1,66 @@
/////////////////////////////////////////////////////////////////
// see https://github.com/LennartHennigs/Button2/pull/57
/////////////////////////////////////////////////////////////////
#if !defined(ESP32)
#error This sketch needs an ESP32 S2 or S3
#else
/////////////////////////////////////////////////////////////////
#include "Button2.h"
#define TOUCH_PIN T5 // Must declare the touch assignment, not the pin.
int threshold = 1500; // ESP32S2
bool touchdetected = false;
byte buttonState = HIGH;// HIGH is for unpressed, pressed = LOW
/////////////////////////////////////////////////////////////////
Button2 button;
/////////////////////////////////////////////////////////////////
void gotTouch() {
touchdetected = true;
}
byte capStateHandler() {
return buttonState;
}
/////////////////////////////////////////////////////////////////
void setup() {
Serial.begin(115200);
delay(50);
Serial.println("\n\nCapacitive Touch Demo");
touchAttachInterrupt(TOUCH_PIN, gotTouch, threshold);
button.setDebounceTime(35);
button.setButtonStateFunction(capStateHandler);
button.setClickHandler(click);
button.begin(BTN_VIRTUAL_PIN);
}
/////////////////////////////////////////////////////////////////
void loop() {
button.loop();
if (touchdetected) {
touchdetected = false;
if (touchInterruptGetLastStatus(TOUCH_PIN)) {
buttonState = LOW;
} else {
buttonState = HIGH;
}
}
}
/////////////////////////////////////////////////////////////////
void click(Button2& btn) {
Serial.println("click\n");
}
/////////////////////////////////////////////////////////////////
#endif
/////////////////////////////////////////////////////////////////

View file

@ -0,0 +1,51 @@
/////////////////////////////////////////////////////////////////
#if !defined(ESP32)
#error This sketch needs an ESP32
#else
/////////////////////////////////////////////////////////////////
#include "Button2.h"
/////////////////////////////////////////////////////////////////
#define BUTTON_PIN 39
/////////////////////////////////////////////////////////////////
Button2 btn;
hw_timer_t *timer = NULL;
/////////////////////////////////////////////////////////////////
void IRAM_ATTR onTimer() {
btn.loop();
}
/////////////////////////////////////////////////////////////////
void click(Button2 &b) {
Serial.println("click");
}
/////////////////////////////////////////////////////////////////
void setup() {
Serial.begin(9600);
btn.begin(BUTTON_PIN);
btn.setTapHandler(click);
timer = timerBegin(0, 80, true);
timerAttachInterrupt(timer, &onTimer, true);
timerAlarmWrite(timer, 10000, true); // every 0.1 seconds
timerAlarmEnable(timer);
}
/////////////////////////////////////////////////////////////////
void loop() {
}
/////////////////////////////////////////////////////////////////
#endif
/////////////////////////////////////////////////////////////////

View file

@ -0,0 +1,76 @@
/////////////////////////////////////////////////////////////////
#if !defined(ESP32)
#error This sketch needs an ESP32 Classic
#else
/////////////////////////////////////////////////////////////////
#include "Button2.h"
#define TOUCH_PIN T0 // Must declare the touch assignment, not the pin. For example, T0 is GPIO4 and T3 is GPIO 15.
// You can look up the touch assignment in the datasheet: https://www.espressif.com/sites/default/files/documentation/esp32_datasheet_en.pdf
int threshold = 40; // ESP32
bool touchActive = false;
bool lastTouchActive = false;
bool testingLower = true;
byte buttonState = HIGH;// HIGH is for unpressed, pressed = LOW
/////////////////////////////////////////////////////////////////
Button2 button;
/////////////////////////////////////////////////////////////////
void gotTouchEvent(){
if (lastTouchActive != testingLower) {
touchActive = !touchActive;
testingLower = !testingLower;
// Touch ISR will be inverted: Lower <--> Higher than the Threshold after ISR event is noticed
touchInterruptSetThresholdDirection(testingLower);
}
}
byte capStateHandler() {
return buttonState;
}
/////////////////////////////////////////////////////////////////
void setup() {
Serial.begin(115200);
delay(50);
Serial.println("\n\nCapacitive Touch Demo");
touchAttachInterrupt(TOUCH_PIN, gotTouchEvent, threshold);
// Touch ISR will be activated when touchRead is lower than the Threshold
touchInterruptSetThresholdDirection(testingLower);
button.setDebounceTime(35);
button.setButtonStateFunction(capStateHandler);
button.setClickHandler(click);
button.begin(VIRTUAL_PIN);
}
/////////////////////////////////////////////////////////////////
void loop() {
button.loop();
if(lastTouchActive != touchActive){
lastTouchActive = touchActive;
if (touchActive) {
buttonState = LOW;
} else {
buttonState = HIGH;
}
}
}
/////////////////////////////////////////////////////////////////
void click(Button2& btn) {
Serial.println("click\n");
}
/////////////////////////////////////////////////////////////////
#endif
/////////////////////////////////////////////////////////////////

View file

@ -0,0 +1,62 @@
/////////////////////////////////////////////////////////////////
#include "Button2.h"
/////////////////////////////////////////////////////////////////
#define BUTTON_PIN 2
/////////////////////////////////////////////////////////////////
Button2 button;
/////////////////////////////////////////////////////////////////
void setup() {
Serial.begin(9600);
while (!Serial) {
delay(20);
}
Serial.println("\n\nLongpress Handler Demo");
button.begin(BUTTON_PIN);
// button.setLongClickDetectedRetriggerable(true);
button.setLongClickHandler(longClick);
button.setLongClickDetectedHandler(longClickDetected);
}
/////////////////////////////////////////////////////////////////
void loop() {
button.loop();
}
/////////////////////////////////////////////////////////////////
void longClick(Button2& btn) {
unsigned int time = btn.wasPressedFor();
Serial.print("You clicked ");
if (time > 1500) {
Serial.print("a really really long time.");
} else if (time > 1000) {
Serial.print("a really long time.");
} else if (time > 500) {
Serial.print("a long time.");
} else {
Serial.print("long.");
}
Serial.print(" (");
Serial.print(time);
Serial.println(" ms)");
}
/////////////////////////////////////////////////////////////////
void longClickDetected(Button2& btn) {
Serial.print("long click #");
Serial.print(btn.getLongClickCount());
Serial.println(" detected");
}
/////////////////////////////////////////////////////////////////

View file

@ -0,0 +1,98 @@
#include "M5Core2.h"
#include "Button2.h"
/////////////////////////////////////////////////////////////////
Button2 button;
bool cleared = false;
/////////////////////////////////////////////////////////////////
byte myButtonStateHandler() {
return !(M5.BtnA.isPressed());
}
/////////////////////////////////////////////////////////////////
void setup() {
M5.begin();
button.setDoubleClickTime(300);
// button.setChangedHandler(changed);
// button.setPressedHandler(pressed);
button.setReleasedHandler(released);
// button.setTapHandler(tap);
button.setClickHandler(click);
button.setLongClickDetectedHandler(longClickDetected);
// button.setLongClickHandler(longClick);
button.setDoubleClickHandler(doubleClick);
button.setTripleClickHandler(tripleClick);
button.setButtonStateFunction(myButtonStateHandler);
button.begin(VIRTUAL_PIN);
M5.Lcd.clear();
M5.Lcd.setTextSize(2);
M5.Lcd.setTextWrap(true, true);
M5.Lcd.print("Button A Test");
M5.Lcd.print("\n");
M5.Lcd.print("\n");
}
void loop() {
button.loop();
// clear screen if wrap happened
if (M5.Lcd.getCursorY() <= 16) {
if (!cleared) {
M5.Lcd.clear();
Serial.println("now");
cleared = true;
}
} else {
cleared = false;
}
M5.update();
}
/////////////////////////////////////////////////////////////////
void pressed(Button2& btn) {
M5.Lcd.print("pressed");
M5.Lcd.print("\n");
}
void released(Button2& btn) {
M5.Lcd.print("released: ");
M5.Lcd.print(btn.wasPressedFor());
M5.Lcd.print("\n");
}
void changed(Button2& btn) {
M5.Lcd.print("changed");
M5.Lcd.print("\n");
}
void click(Button2& btn) {
M5.Lcd.print("click");
M5.Lcd.print("\n");
}
void longClickDetected(Button2& btn) {
M5.Lcd.print("long click detected");
M5.Lcd.print("\n");
}
void longClick(Button2& btn) {
M5.Lcd.print("long click");
M5.Lcd.print("\n");
}
void doubleClick(Button2& btn) {
M5.Lcd.print("double click");
M5.Lcd.print("\n");
}
void tripleClick(Button2& btn) {
M5.Lcd.print("triple click\n");
M5.Lcd.print("\n");
}
void tap(Button2& btn) {
M5.Lcd.print("tap");
M5.Lcd.print("\n");
}

View file

@ -0,0 +1,55 @@
/////////////////////////////////////////////////////////////////
#include "Button2.h"
/////////////////////////////////////////////////////////////////
#define BUTTON_PIN 2
/////////////////////////////////////////////////////////////////
Button2 button;
/////////////////////////////////////////////////////////////////
void setup() {
Serial.begin(9600);
delay(50);
Serial.println("\n\nMulti Handler Demo");
button.begin(BUTTON_PIN);
button.setClickHandler(handler);
// button.setLongClickHandler(handler); // this will only be called upon release
button.setLongClickDetectedHandler(handler); // this will only be called upon detection
button.setDoubleClickHandler(handler);
button.setTripleClickHandler(handler);
}
/////////////////////////////////////////////////////////////////
void loop() {
button.loop();
}
/////////////////////////////////////////////////////////////////
void handler(Button2& btn) {
switch (btn.getType()) {
case single_click:
break;
case double_click:
Serial.print("double ");
break;
case triple_click:
Serial.print("triple ");
break;
case long_click:
Serial.print("long");
break;
}
Serial.print("click");
Serial.print(" (");
Serial.print(btn.getNumberOfClicks());
Serial.println(")");
}
/////////////////////////////////////////////////////////////////

View file

@ -0,0 +1,53 @@
/////////////////////////////////////////////////////////////////
#include "Button2.h"
/////////////////////////////////////////////////////////////////
#define BUTTON_PIN_1 3
#define BUTTON_PIN_2 4
/////////////////////////////////////////////////////////////////
Button2 button_1, button_2;
/////////////////////////////////////////////////////////////////
void setup() {
Serial.begin(9600);
delay(50);
Serial.println("\n\nMulti Handler Demo w/ 2 buttons");
button_1.begin(BUTTON_PIN_1);
button_1.setClickHandler(handler);
button_1.setDoubleClickHandler(handler);
button_2.begin(BUTTON_PIN_2);
button_2.setClickHandler(handler);
button_2.setDoubleClickHandler(handler);
}
/////////////////////////////////////////////////////////////////
void loop() {
button_1.loop();
button_2.loop();
}
/////////////////////////////////////////////////////////////////
void handler(Button2& btn) {
switch (btn.getType()) {
case single_click:
break;
case double_click:
Serial.print("double ");
break;
}
Serial.print("click ");
Serial.print("on button #");
Serial.print((btn == button_1) ? "1" : "2");
Serial.println();
}
/////////////////////////////////////////////////////////////////

View file

@ -0,0 +1,45 @@
/////////////////////////////////////////////////////////////////
#include "Button2.h"
/////////////////////////////////////////////////////////////////
#define BUTTON_A_PIN 2
#define BUTTON_B_PIN 0
/////////////////////////////////////////////////////////////////
Button2 buttonA, buttonB;
/////////////////////////////////////////////////////////////////
void setup() {
Serial.begin(9600);
delay(50);
Serial.println("\n\nMultiple Buttons Demo");
buttonA.begin(BUTTON_A_PIN);
buttonA.setClickHandler(click);
buttonB.begin(BUTTON_B_PIN);
buttonB.setClickHandler(click);
}
/////////////////////////////////////////////////////////////////
void loop() {
buttonA.loop();
buttonB.loop();
}
/////////////////////////////////////////////////////////////////
void click(Button2& btn) {
if (btn == buttonA) {
Serial.println("A clicked");
} else if (btn == buttonB) {
Serial.println("B clicked");
}
}
/////////////////////////////////////////////////////////////////

View file

@ -0,0 +1,80 @@
/////////////////////////////////////////////////////////////////
#include "Button2.h"
/////////////////////////////////////////////////////////////////
#define BUTTON_PIN 37
/////////////////////////////////////////////////////////////////
Button2 button;
/////////////////////////////////////////////////////////////////
void setup() {
Serial.begin(9600);
delay(50);
Serial.println("\n\nButton Demo");
button.begin(BUTTON_PIN);
// button.setLongClickTime(1000);
// button.setDoubleClickTime(400);
Serial.println(" Longpress Time:\t" + String(button.getLongClickTime()) + "ms");
Serial.println(" DoubleClick Time:\t" + String(button.getDoubleClickTime()) + "ms");
Serial.println();
// button.setChangedHandler(changed);
// button.setPressedHandler(pressed);
button.setReleasedHandler(released);
// button.setTapHandler(tap);
button.setClickHandler(click);
button.setLongClickDetectedHandler(longClickDetected);
button.setLongClickHandler(longClick);
button.setLongClickDetectedRetriggerable(false);
button.setDoubleClickHandler(doubleClick);
button.setTripleClickHandler(tripleClick);
}
/////////////////////////////////////////////////////////////////
void loop() {
button.loop();
}
/////////////////////////////////////////////////////////////////
void pressed(Button2& btn) {
Serial.println("pressed");
}
void released(Button2& btn) {
Serial.print("released: ");
Serial.println(btn.wasPressedFor());
}
void changed(Button2& btn) {
Serial.println("changed");
}
void click(Button2& btn) {
Serial.println("click\n");
}
void longClickDetected(Button2& btn) {
Serial.println("long click detected");
}
void longClick(Button2& btn) {
Serial.println("long click\n");
}
void doubleClick(Button2& btn) {
Serial.println("double click\n");
}
void tripleClick(Button2& btn) {
Serial.println("triple click\n");
Serial.println(btn.getNumberOfClicks());
}
void tap(Button2& btn) {
Serial.println("tap");
}
/////////////////////////////////////////////////////////////////

View file

@ -0,0 +1,51 @@
/////////////////////////////////////////////////////////////////
#include "Button2.h"
/////////////////////////////////////////////////////////////////
#define BUTTON_PIN 2
/////////////////////////////////////////////////////////////////
Button2 button;
/////////////////////////////////////////////////////////////////
void setup() {
Serial.begin(9600);
delay(50);
Serial.println("\n\nButton Demo");
button.begin(BUTTON_PIN);
button.setChangedHandler(changed);
//button.setPressedHandler(pressed);
//button.setReleasedHandler(released);
// setTapHandler() is called by any type of click, longpress or shortpress
button.setTapHandler(tap);
}
/////////////////////////////////////////////////////////////////
void loop() {
button.loop();
}
/////////////////////////////////////////////////////////////////
void pressed(Button2& btn) {
Serial.println("pressed");
}
void released(Button2& btn) {
Serial.print("released: ");
Serial.println(btn.wasPressedFor());
}
void changed(Button2& btn) {
Serial.println("changed");
}
void tap(Button2& btn) {
Serial.println("tap");
}
/////////////////////////////////////////////////////////////////

View file

@ -0,0 +1,64 @@
/////////////////////////////////////////////////////////////////
#include "Button2.h"
/////////////////////////////////////////////////////////////////
#define BUTTON_PIN_1 3
#define BUTTON_PIN_2 4
/////////////////////////////////////////////////////////////////
Button2 button_1, button_2;
unsigned long now = 0;
byte counter = 0;
/////////////////////////////////////////////////////////////////
void setup() {
Serial.begin(9600);
delay(50);
Serial.println("\n\nTrack dual button press & release");
button_1.begin(BUTTON_PIN_1);
button_1.setPressedHandler(pressed);
button_1.setReleasedHandler(released);
button_2.begin(BUTTON_PIN_2);
button_2.setPressedHandler(pressed);
button_2.setReleasedHandler(released);
}
/////////////////////////////////////////////////////////////////
void loop() {
button_1.loop();
button_2.loop();
}
/////////////////////////////////////////////////////////////////
void pressed(Button2& btn) {
counter++;
if (counter == 2) {
now = millis();
Serial.println("now!");
}
}
/////////////////////////////////////////////////////////////////
void released(Button2& btn) {
counter--;
if (counter == 0) {
if (now != 0) {
Serial.print("Pressed for: ");
Serial.print(millis() - now);
Serial.println("ms");
}
now = 0;
}
}
/////////////////////////////////////////////////////////////////

44
lib/Button2/keywords.txt Executable file
View file

@ -0,0 +1,44 @@
Button2 KEYWORD1
begin KEYWORD2
setDebounceTime KEYWORD2
setLongClickTime KEYWORD2
setDoubleClickTime KEYWORD2
getDebounceTime KEYWORD2
getLongClickTime KEYWORD2
getDoubleClickTime KEYWORD2
getPin KEYWORD2
setLongClickDetectedRetriggerable KEYWORD2
reset KEYWORD2
setChangedHandler KEYWORD2
setPressedHandler KEYWORD2
setReleasedHandler KEYWORD2
setTapHandler KEYWORD2
setClickHandler KEYWORD2
setDoubleClickHandler KEYWORD2
setTripleClickHandler KEYWORD2
setLongClickHandler KEYWORD2
setLongClickDetectedHandler KEYWORD2
wasPressedFor KEYWORD2
isPressed KEYWORD2
isPressedRaw KEYWORD2
getNumberOfClicks KEYWORD2
getType KEYWORD2
clickToString KEYWORD2
getID KEYWORD2
setID KEYWORD2
wasPressed KEYWORD2
resetPressedState KEYWORD2
read KEYWORD2
getLongClickCount KEYWORD2
wait KEYWORD2
waitForClick KEYWORD2
waitForDouble KEYWORD2
waitForTriple KEYWORD2
waitForLong KEYWORD2
loop KEYWORD2
BTN_DEBOUNCE_MS LITERAL1
BTN_LONGCLICK_MS LITERAL1
BTN_DOUBLECLICK_MS LITERAL1
BTN_UNDEFINED_PIN LITERAL1
BTN_VIRTUAL_PIN LITERAL1
clickType LITERAL1

17
lib/Button2/library.json Normal file
View file

@ -0,0 +1,17 @@
{
"name": "Button2",
"version": "2.3.1",
"keywords": "button",
"description": "Arduino/ESP Library to simplify working with buttons. It allows you to use callback functions to track single, double, triple and long clicks. Alternatively, it provides function to use in your main loop(). The library also takes care of debouncing. Using this lib will reduce and simplify your source code significantly.",
"homepage": "https://github.com/LennartHennigs/Button2",
"repository": {
"type": "git",
"url": "https://github.com/LennartHennigs/Button2"
},
"authors": {
"name": "Lennart Hennigs",
"url": "https://lennarthennigs.de"
},
"frameworks": "arduino",
"platforms": "*"
}

View file

@ -0,0 +1,9 @@
name=Button2
version=2.3.1
author=Lennart Hennigs
maintainer=Lennart Hennigs <mail@lennarthennigs.de>
sentence=Arduino/ESP library to simplify working with buttons.
paragraph=It allows you to use callback functions to track single, double, triple and long clicks. Alternatively, it provides function to use in your main loop(). The library also takes care of debouncing. Using this lib will reduce and simplify your source code significantly.
category=Communication
url=https://github.com/LennartHennigs/Button2
architectures=*

477
lib/Button2/src/Button2.cpp Executable file
View file

@ -0,0 +1,477 @@
/////////////////////////////////////////////////////////////////
/*
Button2.cpp - Arduino Library to simplify working with buttons.
Copyright (C) 2017-2023 Lennart Hennigs.
Released under the MIT license.
*/
/////////////////////////////////////////////////////////////////
#include "Button2.h"
/////////////////////////////////////////////////////////////////
// initialize static counter for the IDs
int Button2::_nextID = 0;
/////////////////////////////////////////////////////////////////
// default constructor
Button2::Button2() {
pin = BTN_UNDEFINED_PIN;
_setID();
}
/////////////////////////////////////////////////////////////////
// constructor
Button2::Button2(byte attachTo, byte buttonMode /* = INPUT_PULLUP */, boolean activeLow /* = true */, Hardware *hardware /* = ArduinoHardware() */) {
begin(attachTo, buttonMode, activeLow, hardware);
_setID();
}
/////////////////////////////////////////////////////////////////
void Button2::begin(byte attachTo, byte buttonMode /* = INPUT_PULLUP */, boolean activeLow /* = true */, Hardware *hardware /* = ArduinoHardware() */) {
hw = hardware;
pin = attachTo;
longclick_counter = 0;
longclick_retriggerable = false;
_pressedState = activeLow ? LOW : HIGH;
if (attachTo != BTN_VIRTUAL_PIN) {
hw->pinMode(attachTo, buttonMode);
}
// state = activeLow ? HIGH : LOW;
state = _getState();
prev_state = state;
}
/////////////////////////////////////////////////////////////////
void Button2::setDebounceTime(unsigned int ms) {
debounce_time_ms = ms;
}
/////////////////////////////////////////////////////////////////
void Button2::setLongClickTime(unsigned int ms) {
longclick_time_ms = ms;
}
/////////////////////////////////////////////////////////////////
void Button2::setDoubleClickTime(unsigned int ms) {
doubleclick_time_ms = ms;
}
/////////////////////////////////////////////////////////////////
unsigned int Button2::getDebounceTime() const {
return debounce_time_ms;
}
/////////////////////////////////////////////////////////////////
unsigned int Button2::getLongClickTime() const {
return longclick_time_ms;
}
/////////////////////////////////////////////////////////////////
unsigned int Button2::getDoubleClickTime() const {
return doubleclick_time_ms;
}
/////////////////////////////////////////////////////////////////
byte Button2::getPin() const {
return pin;
}
/////////////////////////////////////////////////////////////////
void Button2::setButtonStateFunction(StateCallbackFunction f) {
get_state_cb = f;
}
/////////////////////////////////////////////////////////////////
bool Button2::operator==(Button2 &rhs) {
return (this == &rhs);
}
/////////////////////////////////////////////////////////////////
void Button2::setChangedHandler(CallbackFunction f) {
change_cb = f;
}
/////////////////////////////////////////////////////////////////
void Button2::setPressedHandler(CallbackFunction f) {
pressed_cb = f;
}
/////////////////////////////////////////////////////////////////
void Button2::setReleasedHandler(CallbackFunction f) {
released_cb = f;
}
/////////////////////////////////////////////////////////////////
void Button2::setClickHandler(CallbackFunction f) {
click_cb = f;
}
/////////////////////////////////////////////////////////////////
void Button2::setTapHandler(CallbackFunction f) {
tap_cb = f;
}
/////////////////////////////////////////////////////////////////
void Button2::setLongClickHandler(CallbackFunction f) {
long_cb = f;
}
/////////////////////////////////////////////////////////////////
void Button2::setLongClickDetectedRetriggerable(bool retriggerable) {
longclick_retriggerable = retriggerable;
}
/////////////////////////////////////////////////////////////////
void Button2::setLongClickDetectedHandler(CallbackFunction f) {
longclick_detected_cb = f;
}
/////////////////////////////////////////////////////////////////
void Button2::setDoubleClickHandler(CallbackFunction f) {
double_cb = f;
}
/////////////////////////////////////////////////////////////////
void Button2::setTripleClickHandler(CallbackFunction f) {
triple_cb = f;
}
/////////////////////////////////////////////////////////////////
unsigned int Button2::wasPressedFor() const {
return down_time_ms;
}
/////////////////////////////////////////////////////////////////
boolean Button2::isPressed() const {
return (state == _pressedState);
}
/////////////////////////////////////////////////////////////////
boolean Button2::isPressedRaw() const {
return (_getState() == _pressedState);
}
/////////////////////////////////////////////////////////////////
byte Button2::getNumberOfClicks() const {
return last_click_count;
}
/////////////////////////////////////////////////////////////////
clickType Button2::getType() const {
return last_click_type;
}
/////////////////////////////////////////////////////////////////
int Button2::getID() const {
return id;
}
/////////////////////////////////////////////////////////////////
void Button2::setID(int newID) {
id = newID;
}
/////////////////////////////////////////////////////////////////
String Button2::clickToString(clickType type) const {
if (type == single_click) return "single click";
if (type == double_click) return "double click";
if (type == triple_click) return "triple click";
if (type == long_click) return "long click";
return "none";
}
/////////////////////////////////////////////////////////////////
bool Button2::wasPressed() const {
return was_pressed;
}
/////////////////////////////////////////////////////////////////
void Button2::resetPressedState() {
last_click_count = 0;
was_pressed = false;
last_click_type = empty;
click_count = 0;
down_time_ms = 0;
pressed_triggered = false;
longclick_detected = false;
longclick_reported = false;
longclick_counter = 0;
}
/////////////////////////////////////////////////////////////////
clickType Button2::read(bool keepState /* = false */) {
if (keepState) return last_click_type;
clickType res = last_click_type;
resetPressedState();
return res;
}
/////////////////////////////////////////////////////////////////
clickType Button2::wait(bool keepState /* = false */) {
while (!wasPressed()) {
loop();
}
return read(keepState);
}
/////////////////////////////////////////////////////////////////
void Button2::waitForClick(bool keepState /* = false */) {
do {
while (!wasPressed()) {
loop();
}
} while (read() != single_click);
}
/////////////////////////////////////////////////////////////////
void Button2::waitForDouble(bool keepState /* = false */) {
do {
while (!wasPressed()) {
loop();
}
} while (read() != double_click);
}
/////////////////////////////////////////////////////////////////
void Button2::waitForTriple(bool keepState /* = false */) {
do {
while (!wasPressed()) {
loop();
}
} while (read() != triple_click);
}
/////////////////////////////////////////////////////////////////
void Button2::waitForLong(bool keepState /* = false */) {
do {
while (!wasPressed()) {
loop();
}
} while (read() != long_click);
}
/////////////////////////////////////////////////////////////////
void Button2::reset() {
pin = BTN_UNDEFINED_PIN;
resetPressedState();
pressed_cb = NULL;
released_cb = NULL;
change_cb = NULL;
tap_cb = NULL;
click_cb = NULL;
long_cb = NULL;
longclick_detected_cb = NULL;
double_cb = NULL;
triple_cb = NULL;
}
/////////////////////////////////////////////////////////////////
void Button2::loop() {
if (pin == BTN_UNDEFINED_PIN) return;
prev_state = state;
state = _getState();
if (state == _pressedState) {
_handlePress(millis());
} else {
_handleRelease(millis());
}
}
/////////////////////////////////////////////////////////////////
void Button2::_handlePress(long now) {
// is it pressed now?
if (prev_state != _pressedState) {
_pressedNow(now);
return;
}
// is it pressed for a while?
if (!pressed_triggered) {
if (now - down_ms >= debounce_time_ms) {
pressed_triggered = true;
_validKeypress();
}
}
// only check for long press on the first click
if (click_count == 1) {
_checkForLongClick(now);
}
}
/////////////////////////////////////////////////////////////////
void Button2::_setID() {
id = _nextID;
_nextID++;
}
/////////////////////////////////////////////////////////////////
void Button2::_handleRelease(long now) {
// is it released right now?
if (prev_state == _pressedState) {
_releasedNow(now);
return;
}
// report click after double click time has passed
if (now - click_ms > doubleclick_time_ms) {
_reportClicks();
}
}
/////////////////////////////////////////////////////////////////
void Button2::_pressedNow(long now) {
down_ms = now;
pressed_triggered = false;
click_ms = down_ms;
}
/////////////////////////////////////////////////////////////////
void Button2::_validKeypress() {
click_count++;
if (change_cb != NULL) change_cb(*this);
if (pressed_cb != NULL) pressed_cb(*this);
}
/////////////////////////////////////////////////////////////////
void Button2::_checkForLongClick(long now) {
if (longclick_detected_cb == NULL) return;
if (longclick_reported) return;
// has the longclick_ms period has been exceeded?
if (now - down_ms < (longclick_time_ms * (longclick_counter + 1))) return;
// report multiple?
if (!longclick_retriggerable) {
longclick_reported = true;
}
longclick_counter++;
longclick_detected_cb(*this);
longclick_detected = true;
}
/////////////////////////////////////////////////////////////////
byte Button2::getLongClickCount() const {
return longclick_counter;
}
/////////////////////////////////////////////////////////////////
void Button2::_reportClicks() {
// no click
if (click_count == 0) return;
last_click_count = click_count;
// single or long press
if (click_count == 1) {
// long press
if (longclick_detected) {
last_click_type = long_click;
if (long_cb != NULL) long_cb(*this);
longclick_counter = 0;
// single click
} else {
last_click_type = single_click;
if (click_cb != NULL) click_cb (*this);
}
// double click
} else if (click_count == 2) {
last_click_type = double_click;
if (double_cb != NULL) double_cb(*this);
// triple or x-clicks
} else {
last_click_type = triple_click;
if (triple_cb != NULL) triple_cb(*this);
}
was_pressed = true;
click_count = 0;
click_ms = 0;
longclick_detected = false;
longclick_reported = false;
}
/////////////////////////////////////////////////////////////////
void Button2::_releasedNow(long now) {
down_time_ms = now - down_ms;
// is it beyond debounce time?
if (down_time_ms < debounce_time_ms) return;
// trigger release
if (change_cb != NULL) change_cb(*this);
if (released_cb != NULL) released_cb(*this);
// trigger tap
if (tap_cb != NULL) tap_cb(*this);
// was it a longclick? (precedes single / double / triple clicks)
if (down_time_ms >= longclick_time_ms) {
longclick_detected = true;
}
}
/////////////////////////////////////////////////////////////////
byte Button2::_getState() const {
if (get_state_cb != NULL) {
return get_state_cb();
} else {
return hw->digitalRead(pin);
}
}
/////////////////////////////////////////////////////////////////

165
lib/Button2/src/Button2.h Executable file
View file

@ -0,0 +1,165 @@
/////////////////////////////////////////////////////////////////
/*
Button2.h - Arduino Library to simplify working with buttons.
Copyright (C) 2017-2023 Lennart Hennigs.
Released under the MIT license.
*/
/////////////////////////////////////////////////////////////////
#pragma once
#ifndef Button2_h
#define Button2_h
/////////////////////////////////////////////////////////////////
#if defined(ARDUINO_ARCH_ESP32) || defined(ESP8266)
#include <functional>
#endif
#include <Arduino.h>
#include "Hardware.h"
/////////////////////////////////////////////////////////////////
const unsigned int BTN_DEBOUNCE_MS = 50;
const unsigned int BTN_LONGCLICK_MS = 200;
const unsigned int BTN_DOUBLECLICK_MS = 300;
const unsigned int BTN_UNDEFINED_PIN = 255;
const unsigned int BTN_VIRTUAL_PIN = 254;
/////////////////////////////////////////////////////////////////
enum clickType {
single_click,
double_click,
triple_click,
long_click,
empty
};
class Button2 {
protected:
int id;
byte pin;
byte state;
byte prev_state;
byte click_count = 0;
byte last_click_count = 0;
clickType last_click_type = empty;
bool was_pressed = false;
unsigned long click_ms;
unsigned long down_ms;
bool longclick_retriggerable;
uint16_t longclick_counter = 0;
bool longclick_detected = false;
bool longclick_reported = false;
unsigned int debounce_time_ms = BTN_DEBOUNCE_MS;
unsigned int longclick_time_ms = BTN_LONGCLICK_MS;
unsigned int doubleclick_time_ms = BTN_DOUBLECLICK_MS;
unsigned int down_time_ms = 0;
bool pressed_triggered = false;
#if defined(ARDUINO_ARCH_ESP32) || defined(ESP8266)
typedef std::function<void(Button2 &btn)> CallbackFunction;
typedef std::function<byte()> StateCallbackFunction;
#else
typedef void (*CallbackFunction)(Button2 &);
typedef byte (*StateCallbackFunction)();
#endif
StateCallbackFunction get_state_cb = NULL;
CallbackFunction pressed_cb = NULL;
CallbackFunction released_cb = NULL;
CallbackFunction change_cb = NULL;
CallbackFunction tap_cb = NULL;
CallbackFunction click_cb = NULL;
CallbackFunction long_cb = NULL;
CallbackFunction longclick_detected_cb = NULL;
CallbackFunction double_cb = NULL;
CallbackFunction triple_cb = NULL;
void _handlePress(long now);
void _handleRelease(long now);
void _releasedNow(long now);
void _pressedNow(long now);
void _validKeypress();
void _checkForLongClick(long now);
void _reportClicks();
void _setID();
public:
Button2();
Button2(byte attachTo, byte buttonMode = INPUT_PULLUP, boolean activeLow = true, Hardware* hardware = new ArduinoHardware());
void begin(byte attachTo, byte buttonMode = INPUT_PULLUP, boolean activeLow = true, Hardware* hardware = new ArduinoHardware());
void setDebounceTime(unsigned int ms);
void setLongClickTime(unsigned int ms);
void setDoubleClickTime(unsigned int ms);
unsigned int getDebounceTime() const;
unsigned int getLongClickTime() const;
unsigned int getDoubleClickTime() const;
byte getPin() const;
void reset();
void setButtonStateFunction(StateCallbackFunction f);
void setChangedHandler(CallbackFunction f);
void setPressedHandler(CallbackFunction f);
void setReleasedHandler(CallbackFunction f);
void setTapHandler(CallbackFunction f);
void setClickHandler(CallbackFunction f);
void setDoubleClickHandler(CallbackFunction f);
void setTripleClickHandler(CallbackFunction f);
void setLongClickHandler(CallbackFunction f);
void setLongClickDetectedHandler(CallbackFunction f);
void setLongClickDetectedRetriggerable(bool retriggerable);
unsigned int wasPressedFor() const;
boolean isPressed() const;
boolean isPressedRaw() const;
void resetPressedState();
bool wasPressed() const;
clickType read(bool keepState = false);
clickType wait(bool keepState = false);
void waitForClick(bool keepState = false);
void waitForDouble(bool keepState = false);
void waitForTriple(bool keepState = false);
void waitForLong(bool keepState = false);
byte getNumberOfClicks() const;
byte getLongClickCount() const;
clickType getType() const;
String clickToString(clickType type) const;
int getID() const;
void setID(int newID);
bool operator==(Button2 &rhs);
void loop();
private:
static int _nextID;
byte _pressedState;
byte _getState() const;
Hardware* hw;
};
/////////////////////////////////////////////////////////////////
#endif
/////////////////////////////////////////////////////////////////

View file

@ -0,0 +1,99 @@
/////////////////////////////////////////////////////////////////
/*
Hardware.h - Implements Arduino Hardware Abstraction Layer.
Part or Button2 library for Arduino.
Copyright (C) 2017-2023 Lennart Hennigs.
Released under the MIT license.
*/
/////////////////////////////////////////////////////////////////
#include <Arduino.h>
/////////////////////////////////////////////////////////////////
#pragma once
#ifndef AbstractHardware_h
#define AbstractHardware_h
/////////////////////////////////////////////////////////////////
// disable warnings for unused parameters
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wunused-parameter"
/////////////////////////////////////////////////////////////////
// virtual class for hardware abstraction
class Hardware {
public:
virtual int digitalRead(int pin) = 0;
virtual void pinMode(int pin, int mode) = 0;
virtual void digitalWrite(int pin, int value) = 0;
};
/////////////////////////////////////////////////////////////////
// implementation for Arduino
#ifdef ARDUINO_ARCH_RP2040
class ArduinoHardware : public Hardware {
public:
int digitalRead(int pin) {
return ::digitalRead(pin);
}
void pinMode(int pin, int mode) {
::pinMode(pin, static_cast<PinMode>(mode));
}
void digitalWrite(int pin, int value) {
::digitalWrite(pin, static_cast<PinStatus>(value));
}
};
#else
class ArduinoHardware : public Hardware {
public:
int digitalRead(int pin) {
return ::digitalRead(pin);
}
void pinMode(int pin, int mode) {
::pinMode(pin, mode);
}
void digitalWrite(int pin, int value) {
::digitalWrite(pin, value);
}
};
#endif
/////////////////////////////////////////////////////////////////
// implementation for testing
class MockHardware : public Hardware {
private:
int pin_state[255];
int pin_mode[255];
public:
MockHardware() {
for(int i = 0; i < 255; i++) {
pin_state[i] = HIGH;
pin_mode[i] = OUTPUT;
}
}
int digitalRead(int pin) override {
return pin_state[pin];
}
void pinMode(int pin, int mode) override {
pin_mode[pin] = mode;
}
void digitalWrite(int pin, int value) override {
pin_state[pin] = value;
}
int getPinMode(int pin) {
return pin_mode[pin];
}
};
/////////////////////////////////////////////////////////////////
#pragma GCC diagnostic pop
#endif
/////////////////////////////////////////////////////////////////

View file

@ -0,0 +1,498 @@
#line 2 "Button2Test.ino"
/////////////////////////////////////////////////////////////////
/*
Unit tests for Button2 library.
Arduino Library to simplify working with buttons.
Created by Lennart Hennigs
Tested on Wemos D1 Mini, M5Stack ESP32, Arduino UNO
*/
/////////////////////////////////////////////////////////////////
/*
To add:
- check retriggerable long-clicks (setLongClickDetectedRetriggerable getLongClickCount())
- check loop functions (read(), wait(), waitForClick(), waitForDouble(), waitForTriple())
- check custom handler
*/
/////////////////////////////////////////////////////////////////
#include "AUnitVerbose.h"
// #include "AUnit.h"
#include "Button2.h"
using namespace aunit;
/////////////////////////////////////////////////////////////////
#define SERIAL_SPEED 115200
#define VERBOSE_CHANGED false
#define VERBOSE_PRESS_RELEASE false
#define VERBOSE_MAIN_EVENTS false // clicks, long presses
/////////////////////////////////////////////////////////////////
#define BUTTON_PIN 37
#define BUTTON_MODE INPUT_PULLUP
#define BUTTON_ACTIVE LOW
#define DEBOUNCE_MS BTN_DEBOUNCE_MS + 5
/////////////////////////////////////////////////////////////////
MockHardware hw;
Button2 button;
bool pressed = false;
bool released = false;
bool tap = false;
bool longclick = false;
bool long_detected = false;
int long_detected_count = 0;
bool changed = false;
/////////////////////////////////////////////////////////////////
void setup_test_runner() {
TestRunner::setVerbosity(Verbosity::kDefault);
// TestRunner::setVerbosity(Verbosity::kAssertionFailed);
// TestRunner::setVerbosityVerbosity::kAll);
TestRunner::list();
/*
TestRunner::exclude("*");
// TestRunner::include("basics_*");
// TestRunner::include("defaults_*");
// TestRunner::include("settings_*");
// TestRunner::include("clicks_*");
// TestRunner::include("other_*");
*/
}
/////////////////////////////////////////////////////////////////
// helper functions
/////////////////////////////////////////////////////////////////
// pressing the button
void press() {
hw.digitalWrite(BUTTON_PIN, BUTTON_ACTIVE);
button.loop();
}
// letting it go
void release() {
hw.digitalWrite(BUTTON_PIN, !BUTTON_ACTIVE);
button.loop();
}
// emulate a button click
void click(unsigned long duration) {
press();
delay(duration);
button.loop();
release();
}
// resets all handler vars
void resetHandlerVars() {
pressed = false;
released = false;
tap = false;
longclick = false;
long_detected = false;
changed = false;
long_detected_count = 0;
}
/////////////////////////////////////////////////////////////////
// BASICS
/////////////////////////////////////////////////////////////////
test(basics, loop) {
resetHandlerVars();
button.resetPressedState();
// press the button down
hw.digitalWrite(BUTTON_PIN, BUTTON_ACTIVE);
button.loop();
// wait a while
delay(DEBOUNCE_MS);
// is there an update without the loop?
assertFalse(pressed);
// there should be one now
button.loop();
assertTrue(pressed);
// release the button
hw.digitalWrite(BUTTON_PIN, !BUTTON_ACTIVE);
// wait a while
delay(DEBOUNCE_MS);
// is there an update without the loop?
assertFalse(released);
// there should be one now
button.loop();
assertTrue(released);
}
/////////////////////////////////////////////////////////////////
test(basics, equal_operator) {
Button2 b;
assertTrue(button == button);
assertTrue(b == b);
assertFalse(button == b);
}
/////////////////////////////////////////////////////////////////
// BASICS
/////////////////////////////////////////////////////////////////
test(clicks, changed_handler) {
button.resetPressedState();
changed = false;
press();
// wait a bit
delay(DEBOUNCE_MS);
button.loop();
// test
assertTrue(changed);
changed = false;
// let go
release();
// test
assertTrue(changed);
// clean up
changed = false;
}
/////////////////////////////////////////////////////////////////
test(clicks, is_not_pressed) {
release();
button.resetPressedState();
// run the tests
assertFalse(button.isPressed());
}
/////////////////////////////////////////////////////////////////
test(clicks, is_pressed) {
release();
button.resetPressedState();
press();
// wait a bit
delay(DEBOUNCE_MS);
button.loop();
// test
assertTrue(button.isPressed());
assertTrue(pressed);
release();
// clean up
pressed = false;
}
/////////////////////////////////////////////////////////////////
test(clicks, is_pressed_raw) {
button.resetPressedState();
press();
// wait a bit
delay(BTN_DEBOUNCE_MS);
button.loop();
// test
assertTrue(button.isPressedRaw());
release();
// clean up
assertFalse(button.isPressedRaw());
pressed = false;
}
/////////////////////////////////////////////////////////////////
test(clicks, not_a_click) {
resetHandlerVars();
button.resetPressedState();
// click, but too short
click(BTN_DEBOUNCE_MS - 10);
delay(BTN_DOUBLECLICK_MS);
button.loop();
// run the tests
assertFalse(button.wasPressed());
assertFalse(tap);
}
/////////////////////////////////////////////////////////////////
test(clicks, single_click) {
resetHandlerVars();
button.resetPressedState();
// click
int time = DEBOUNCE_MS;
click(time);
// wait out the double click time
delay(BTN_DOUBLECLICK_MS);
button.loop();
int pressedFor = button.wasPressedFor();
// run the tests
assertTrue(button.wasPressed());
assertNear(time, pressedFor, 10);
assertEqual(button.getType(), single_click);
assertEqual(button.getNumberOfClicks(), 1);
assertTrue(tap);
}
/////////////////////////////////////////////////////////////////
test(clicks, long_click) {
resetHandlerVars();
button.resetPressedState();
// long click
int time = BTN_LONGCLICK_MS + 10;
click(time);
// wait out the double click time
delay(BTN_DOUBLECLICK_MS);
button.loop();
int pressedFor = button.wasPressedFor();
// run the tests
assertTrue(button.wasPressed());
assertNear(time, pressedFor, 10);
assertEqual(button.getType(), long_click);
assertEqual(button.getNumberOfClicks(), 1);
assertTrue(long_detected);
assertTrue(longclick);
assertTrue(tap);
}
/////////////////////////////////////////////////////////////////
test(clicks, double_click) {
button.resetPressedState();
// 2x click
int time = DEBOUNCE_MS;
click(time);
click(time);
// wait out the double click time
delay(BTN_DOUBLECLICK_MS);
button.loop();
// run the tests
assertTrue(button.wasPressed());
assertEqual(button.getType(), double_click);
assertEqual(button.getNumberOfClicks(), 2);
}
/////////////////////////////////////////////////////////////////
test(clicks, triple_click) {
button.resetPressedState();
// 3x click
int time = DEBOUNCE_MS;
click(time);
click(time);
click(time);
// wait out the double click time
delay(BTN_DOUBLECLICK_MS);
button.loop();
// run the tests
assertTrue(button.wasPressed());
assertEqual(button.getType(), triple_click);
assertEqual(button.getNumberOfClicks(), 3);
}
/////////////////////////////////////////////////////////////////
test(clicks, more_than_3_click) {
button.resetPressedState();
// 4x click
int time = DEBOUNCE_MS;
click(time);
click(time);
click(time);
click(time);
delay(BTN_DOUBLECLICK_MS);
button.loop();
// run the tests
assertTrue(button.wasPressed());
assertEqual(button.getType(), triple_click);
assertEqual(button.getNumberOfClicks(), 4);
}
/////////////////////////////////////////////////////////////////
test (clicks, reset) {
button.resetPressedState();
// single click
int time = DEBOUNCE_MS;
click(time);
// wait out the double click time
delay(BTN_DOUBLECLICK_MS);
button.loop();
// run the tests
assertTrue(button.wasPressed());
assertEqual(button.getNumberOfClicks(), 1);
// now reset the "click memory"
button.resetPressedState();
assertFalse(button.wasPressed());
assertEqual(button.getNumberOfClicks(), 0);
}
/////////////////////////////////////////////////////////////////
// DEFAULTS
/////////////////////////////////////////////////////////////////
test(defaults, doubleclick_time) {
assertEqual(button.getDoubleClickTime(), BTN_DOUBLECLICK_MS);
}
/////////////////////////////////////////////////////////////////
test(defaults, debounce_time) {
assertEqual(button.getDebounceTime(), BTN_DEBOUNCE_MS);
}
/////////////////////////////////////////////////////////////////
test(defaults, longclick_time) {
assertEqual(button.getLongClickTime(), BTN_LONGCLICK_MS);
}
/////////////////////////////////////////////////////////////////
test(defaults, id) {
assertTrue(button.getID() == 0);
}
/////////////////////////////////////////////////////////////////
test(defaults, pin) {
assertTrue(button.getPin() == BUTTON_PIN);
}
/////////////////////////////////////////////////////////////////
// SETTING
/////////////////////////////////////////////////////////////////
test(settings, id) {
int id = 10;
button.setID(id);
// run the tests
assertTrue(button.getID() == id);
// clean up
button.setID(0);
}
/////////////////////////////////////////////////////////////////
test(settings, additional_ids) {
Button2 b;
assertNotEqual(b.getID(), button.getID());
}
/////////////////////////////////////////////////////////////////
test(settings, doubleclick_time) {
unsigned int dc_time = BTN_DOUBLECLICK_MS + 1;
button.setDoubleClickTime(dc_time);
// run the tests
assertEqual(button.getDoubleClickTime(), dc_time);
// clean up
button.setDoubleClickTime(BTN_DOUBLECLICK_MS);
}
/////////////////////////////////////////////////////////////////
test(settings, longclick_time) {
unsigned int lc_time = BTN_LONGCLICK_MS + 1;
button.setLongClickTime(lc_time);
// run the tests
assertEqual(button.getLongClickTime(), lc_time);
// clean up
button.setLongClickTime(BTN_LONGCLICK_MS);
}
/////////////////////////////////////////////////////////////////
test(settings, debounce_time) {
unsigned int debounce_time = BTN_DEBOUNCE_MS + 1;
button.setDebounceTime(debounce_time);
// run the tests
assertEqual(button.getDebounceTime(), debounce_time);
// clean up
button.setDebounceTime(BTN_DEBOUNCE_MS);
}
/////////////////////////////////////////////////////////////////
// OTHER
/////////////////////////////////////////////////////////////////
test(other, to_string) {
assertStringCaseEqual(button.clickToString(clickType::single_click), String("single click"));
assertStringCaseEqual(button.clickToString(clickType::double_click), String("double click"));
assertStringCaseEqual(button.clickToString(clickType::triple_click), String("triple click"));
assertStringCaseEqual(button.clickToString(clickType::long_click), String("long click"));
}
/////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////
// ignore unused params in the lamda functions
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wunused-parameter"
/////////////////////////////////////////////////////////////////
void setup() {
setup_test_runner();
// setup serial
delay(1000);
Serial.begin(SERIAL_SPEED);
while(!Serial) {}
Serial.println(F("\n\nButton2 Unit Tests"));
// set up button
button.begin(BUTTON_PIN, BUTTON_MODE, BUTTON_ACTIVE == LOW, &hw);
button.setPressedHandler([](Button2& b) {
if (VERBOSE_PRESS_RELEASE) Serial.println(F("-- PRESSED"));
released = false;
pressed = true;
});
button.setReleasedHandler([](Button2& b) {
if (VERBOSE_PRESS_RELEASE) Serial.println(F("-- RELEASED"));
released = true;
pressed = false;
tap = true;
});
button.setChangedHandler([](Button2& b) {
if (VERBOSE_CHANGED) Serial.println(F("-- CHANGED"));
changed = true;
});
button.setClickHandler([](Button2& b) {
if (VERBOSE_MAIN_EVENTS) Serial.println(F(" -- CLICK"));
});
button.setLongClickHandler([](Button2& b) {
if (VERBOSE_MAIN_EVENTS) Serial.println(F(" -- LONG"));
longclick = true;
});
button.setLongClickDetectedHandler([](Button2& b) {
if (VERBOSE_MAIN_EVENTS) Serial.println(" -- LONG DETECTED");
long_detected = true;
long_detected_count = long_detected_count + 1;
});
button.setDoubleClickHandler([](Button2& b) {
if (VERBOSE_MAIN_EVENTS) Serial.println(" -- DOUBLE");
});
button.setTripleClickHandler([](Button2& b) {
if (VERBOSE_MAIN_EVENTS) Serial.println(" -- TRIPLE ");
});
}
#pragma GCC diagnostic push
/////////////////////////////////////////////////////////////////
void loop() {
button.loop();
aunit::TestRunner::run();
}
/////////////////////////////////////////////////////////////////