├── .gitignore ├── LICENSE ├── README.md ├── examples ├── SmartButtonAnalog │ └── SmartButtonAnalog.ino ├── SmartButtonAnalogFlag │ └── SmartButtonAnalogFlag.ino ├── SmartButtonClick │ └── SmartButtonClick.ino ├── SmartButtonClickHold │ └── SmartButtonClickHold.ino ├── SmartButtonClickInterface │ └── SmartButtonClickInterface.ino ├── SmartButtonClickLambda │ └── SmartButtonClickLambda.ino ├── SmartButtonMulti │ └── SmartButtonMulti.ino └── SmartButtonRepeat │ └── SmartButtonRepeat.ino ├── library.properties └── src ├── SmartButton.cpp ├── SmartButton.h └── SmartButtonDefs.h /.gitignore: -------------------------------------------------------------------------------- 1 | .vscode 2 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 Marcin Borowicz 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # SmartButton Library 2 | SmartButton Library is used for handling various button events. 3 | It supports button press, release, multiple click and button hold events. 4 | Library is implemented in C++ with build-in Arduino port, but it is easy to 5 | port to different architecture. 6 | 7 | ## Features 8 | * asynchronous non-blocking API based on events callbacks 9 | * hardware independent high-level architecture 10 | * works well with digital, analogs and other signal input backends 11 | * easy to use with Arduino boards 12 | * unlimited number of buttons 13 | * support multi-click events 14 | * support press and release events 15 | * support hold and long-hold events 16 | * support auto-repeat events during hold and long-hold 17 | * build-in debounce algorithm 18 | * configurable timeouts 19 | 20 | ## Changelog 21 | 22 | ### 2020.08.12 - 0.3.0 23 | - [x] hold auto-repeat periodic events added 24 | 25 | ### 2020.08.10 - 0.2.0 26 | - [x] optional initialization by object-interface added 27 | - [x] optional initialization by bool-pointer added 28 | - [x] optional context passing added 29 | 30 | ## Example 31 | 32 | Single and double click example: 33 | 34 | ```cpp 35 | #include 36 | #include 37 | 38 | constexpr int BUTTON_PIN = 2; 39 | constexpr int LED_PIN = 13; 40 | 41 | using namespace smartbutton; 42 | 43 | SmartButton button(BUTTON_PIN, SmartButton::InputType::NORMAL_HIGH); 44 | 45 | void setup() 46 | { 47 | pinMode(LED_PIN, OUTPUT); // Digital output for led 48 | pinMode(BUTTON_PIN, INPUT_PULLUP); // Digital input with pull-up resistors (normal high) 49 | 50 | // Initialize and register smart button 51 | button.begin([] (SmartButton *button, SmartButton::Event event, int clickCounter) 52 | { 53 | if (event == SmartButton::Event::CLICK) { // Click event handler 54 | if (clickCounter == 1) { 55 | digitalWrite(LED_PIN, LOW); // Single click will turn led off 56 | } else if (clickCounter == 2) { 57 | digitalWrite(LED_PIN, HIGH); // Double click will turn led on 58 | } 59 | } 60 | }); 61 | } 62 | 63 | void loop() 64 | { 65 | SmartButton::service(); // Asynchronous service routine, should be called periodically 66 | } 67 | ``` 68 | -------------------------------------------------------------------------------- /examples/SmartButtonAnalog/SmartButtonAnalog.ino: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include 4 | 5 | constexpr int BUTTON_PIN = A0; 6 | constexpr int LED_PIN = 13; 7 | 8 | using namespace smartbutton; 9 | 10 | void eventCallback(SmartButton *button, SmartButton::Event event, int clickCounter) 11 | { 12 | if (event == SmartButton::Event::CLICK) { // Click event handler 13 | switch (clickCounter) { 14 | case 1: 15 | digitalWrite(LED_PIN, LOW); // Single click will turn led off 16 | break; 17 | case 2: 18 | digitalWrite(LED_PIN, HIGH); // Double click will turn led on 19 | break; 20 | default: 21 | break; 22 | } 23 | } 24 | } 25 | 26 | bool isPressedHandler(SmartButton *button) 27 | { 28 | int value = analogRead(BUTTON_PIN); 29 | return (value >= 512) ? true : false; // If ADC value >= 512 then button pressed 30 | } 31 | 32 | SmartButton button(isPressedHandler); 33 | 34 | void setup() 35 | { 36 | pinMode(LED_PIN, OUTPUT); // Digital output for led 37 | 38 | button.begin(eventCallback); // Initialize and register smart button 39 | } 40 | 41 | void loop() 42 | { 43 | SmartButton::service(); // Asynchronous service routine, should be called periodically 44 | } 45 | 46 | -------------------------------------------------------------------------------- /examples/SmartButtonAnalogFlag/SmartButtonAnalogFlag.ino: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include 4 | 5 | constexpr int BUTTON_PIN = A0; 6 | constexpr int LED_PIN = 13; 7 | 8 | using namespace smartbutton; 9 | 10 | void eventCallback(SmartButton *button, SmartButton::Event event, int clickCounter) 11 | { 12 | if (event == SmartButton::Event::CLICK) { // Click event handler 13 | switch (clickCounter) { 14 | case 1: 15 | digitalWrite(LED_PIN, LOW); // Single click will turn led off 16 | break; 17 | case 2: 18 | digitalWrite(LED_PIN, HIGH); // Double click will turn led on 19 | break; 20 | default: 21 | break; 22 | } 23 | } 24 | } 25 | 26 | static bool isPressedFlag; // Static variable with button pressed flag 27 | 28 | static void checkPressedFlag() 29 | { 30 | int value = analogRead(BUTTON_PIN); 31 | isPressedFlag = (value >= 512) ? true : false; // If ADC value >= 512 then button pressed 32 | } 33 | 34 | SmartButton button(&isPressedFlag); 35 | 36 | void setup() 37 | { 38 | pinMode(BUTTON_PIN, INPUT_PULLUP); 39 | pinMode(LED_PIN, OUTPUT); // Digital output for led 40 | 41 | button.begin(eventCallback); // Initialize and register smart button 42 | } 43 | 44 | void loop() 45 | { 46 | checkPressedFlag(); 47 | SmartButton::service(); // Asynchronous service routine, should be called periodically 48 | } 49 | 50 | -------------------------------------------------------------------------------- /examples/SmartButtonClick/SmartButtonClick.ino: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include 4 | 5 | constexpr int BUTTON_PIN = 2; 6 | constexpr int LED_PIN = 13; 7 | 8 | using namespace smartbutton; 9 | 10 | void eventCallback(SmartButton *button, SmartButton::Event event, int clickCounter) 11 | { 12 | if (event == SmartButton::Event::CLICK) { // Click event handler 13 | switch (clickCounter) { 14 | case 1: 15 | digitalWrite(LED_PIN, LOW); // Single click will turn led off 16 | break; 17 | case 2: 18 | digitalWrite(LED_PIN, HIGH); // Double click will turn led on 19 | break; 20 | default: 21 | break; 22 | } 23 | } 24 | } 25 | 26 | SmartButton button(BUTTON_PIN, SmartButton::InputType::NORMAL_HIGH); 27 | 28 | void setup() 29 | { 30 | pinMode(LED_PIN, OUTPUT); // Digital output for led 31 | pinMode(BUTTON_PIN, INPUT_PULLUP); // Digital input with pull-up resistors (normal high) 32 | 33 | button.begin(eventCallback); // Initialize and register smart button 34 | } 35 | 36 | void loop() 37 | { 38 | SmartButton::service(); // Asynchronous service routine, should be called periodically 39 | } 40 | 41 | -------------------------------------------------------------------------------- /examples/SmartButtonClickHold/SmartButtonClickHold.ino: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include 4 | 5 | constexpr int BUTTON_PIN = 2; 6 | constexpr int LED_PIN = 13; 7 | 8 | constexpr unsigned long BLINKING_FAST = 100UL; 9 | constexpr unsigned long BLINKING_SLOW = 500UL; 10 | 11 | using namespace smartbutton; 12 | 13 | static bool blinking = true; 14 | static unsigned long blinkingPeriod = BLINKING_SLOW; 15 | 16 | void eventCallback(SmartButton *button, SmartButton::Event event, int clickCounter) 17 | { 18 | if (event == SmartButton::Event::CLICK) { // Click event handler 19 | switch (clickCounter) { 20 | case 1: 21 | blinkingPeriod = BLINKING_FAST; // Single click will turn fast led blinking 22 | break; 23 | case 2: 24 | blinkingPeriod = BLINKING_SLOW; // Double click will turn slow led blinking 25 | break; 26 | default: 27 | break; 28 | } 29 | } else if (event == SmartButton::Event::HOLD) { // Hold press event handler 30 | blinking = !blinking; // Turning on/off blinking service 31 | } 32 | } 33 | 34 | SmartButton button(BUTTON_PIN, SmartButton::InputType::NORMAL_HIGH); 35 | 36 | void setup() 37 | { 38 | pinMode(LED_PIN, OUTPUT); // Digital output for led 39 | pinMode(BUTTON_PIN, INPUT_PULLUP); // Digital input with pull-up resistors (normal high) 40 | 41 | button.begin(eventCallback); // Initialize and register smart button 42 | } 43 | 44 | static void blinkService() 45 | { 46 | static unsigned long lastMillis = 0; 47 | static bool blinkingState = false; 48 | 49 | if (!blinking) 50 | return; 51 | 52 | if (millis() - lastMillis < blinkingPeriod) 53 | return; 54 | 55 | lastMillis = millis(); 56 | digitalWrite(LED_PIN, blinkingState); 57 | blinkingState = !blinkingState; 58 | } 59 | 60 | void loop() 61 | { 62 | SmartButton::service(); // Asynchronous service routine, should be called periodically 63 | blinkService(); // Asynchronous led blinking service 64 | } 65 | 66 | -------------------------------------------------------------------------------- /examples/SmartButtonClickInterface/SmartButtonClickInterface.ino: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include 4 | 5 | constexpr int BUTTON_PIN = 2; 6 | constexpr int LED_PIN = 13; 7 | 8 | using namespace smartbutton; 9 | 10 | class ButtonInterface: public SmartButtonInterface { 11 | 12 | public: 13 | virtual void event(SmartButton *button, SmartButton::Event event, int clickCounter) 14 | { 15 | if (event == SmartButton::Event::CLICK) { // Click event handler 16 | switch (clickCounter) { 17 | case 1: 18 | digitalWrite(LED_PIN, LOW); // Single click will turn led off 19 | break; 20 | case 2: 21 | digitalWrite(LED_PIN, HIGH); // Double click will turn led on 22 | break; 23 | default: 24 | break; 25 | } 26 | } 27 | } 28 | 29 | virtual bool isPressed(SmartButton *button) 30 | { 31 | return (digitalRead(BUTTON_PIN) == LOW) ? true : false; 32 | } 33 | }; 34 | 35 | ButtonInterface buttonInterface; 36 | 37 | SmartButton button(&buttonInterface); 38 | 39 | void setup() 40 | { 41 | pinMode(LED_PIN, OUTPUT); // Digital output for led 42 | pinMode(BUTTON_PIN, INPUT_PULLUP); // Digital input with pull-up resistors (normal high) 43 | 44 | button.begin(); // Initialize and register smart button 45 | } 46 | 47 | void loop() 48 | { 49 | SmartButton::service(); // Asynchronous service routine, should be called periodically 50 | } 51 | 52 | -------------------------------------------------------------------------------- /examples/SmartButtonClickLambda/SmartButtonClickLambda.ino: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | constexpr int BUTTON_PIN = 2; 5 | constexpr int LED_PIN = 13; 6 | 7 | using namespace smartbutton; 8 | 9 | SmartButton button(BUTTON_PIN, SmartButton::InputType::NORMAL_HIGH); 10 | 11 | void setup() 12 | { 13 | pinMode(LED_PIN, OUTPUT); // Digital output for led 14 | pinMode(BUTTON_PIN, INPUT_PULLUP); // Digital input with pull-up resistors (normal high) 15 | 16 | // Initialize and register smart button 17 | button.begin([] (SmartButton *button, SmartButton::Event event, int clickCounter) 18 | { 19 | if (event == SmartButton::Event::CLICK) { // Click event handler 20 | if (clickCounter == 1) { 21 | digitalWrite(LED_PIN, LOW); // Single click will turn led off 22 | } else if (clickCounter == 2) { 23 | digitalWrite(LED_PIN, HIGH); // Double click will turn led on 24 | } 25 | } 26 | }); 27 | } 28 | 29 | void loop() 30 | { 31 | SmartButton::service(); // Asynchronous service routine, should be called periodically 32 | } 33 | -------------------------------------------------------------------------------- /examples/SmartButtonMulti/SmartButtonMulti.ino: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include 4 | 5 | constexpr int BUTTON1_PIN = 2; 6 | constexpr int BUTTON2_PIN = A0; 7 | constexpr int LED_PIN = 13; 8 | 9 | constexpr unsigned long BLINKING_FAST = 100UL; 10 | constexpr unsigned long BLINKING_SLOW = 500UL; 11 | 12 | using namespace smartbutton; 13 | 14 | static bool blinking = true; 15 | static unsigned long blinkingPeriod = BLINKING_SLOW; 16 | 17 | void event1Callback(SmartButton *button, SmartButton::Event event, int clickCounter) 18 | { 19 | if (event == SmartButton::Event::CLICK) { // Click event handler 20 | switch (clickCounter) { 21 | case 1: 22 | blinkingPeriod = BLINKING_FAST; // Single click will turn fast led blinking 23 | break; 24 | case 2: 25 | blinkingPeriod = BLINKING_SLOW; // Double click will turn slow led blinking 26 | break; 27 | default: 28 | break; 29 | } 30 | } 31 | } 32 | 33 | void event2Callback(SmartButton *button, SmartButton::Event event, int clickCounter) 34 | { 35 | if (event == SmartButton::Event::HOLD) { // Hold press event handler 36 | blinking = !blinking; // Turning on/off blinking service 37 | } 38 | } 39 | 40 | bool isPressedHandler(SmartButton *button) 41 | { 42 | int value = analogRead(BUTTON2_PIN); 43 | return (value >= 512) ? true : false; // If ADC value >= 512 then button pressed 44 | } 45 | 46 | SmartButton button1(BUTTON1_PIN, SmartButton::InputType::NORMAL_HIGH); 47 | SmartButton button2(isPressedHandler); 48 | 49 | void setup() 50 | { 51 | pinMode(LED_PIN, OUTPUT); // Digital output for led 52 | pinMode(BUTTON1_PIN, INPUT_PULLUP); // Digital input with pull-up resistors (normal high) 53 | 54 | button1.begin(event1Callback); // Initialize and register smart button 55 | button2.begin(event2Callback); // Initialize and register smart button 56 | } 57 | 58 | static void blinkService() 59 | { 60 | static unsigned long lastMillis = 0; 61 | static bool blinkingState = false; 62 | 63 | if (!blinking) 64 | return; 65 | 66 | if (millis() - lastMillis < blinkingPeriod) 67 | return; 68 | 69 | lastMillis = millis(); 70 | digitalWrite(LED_PIN, blinkingState); 71 | blinkingState = !blinkingState; 72 | } 73 | 74 | void loop() 75 | { 76 | SmartButton::service(); // Asynchronous service routine, should be called periodically 77 | blinkService(); // Asynchronous led blinking service 78 | } 79 | 80 | -------------------------------------------------------------------------------- /examples/SmartButtonRepeat/SmartButtonRepeat.ino: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include 4 | 5 | constexpr int BUTTON_PIN = 2; 6 | constexpr int LED_PIN = 13; 7 | 8 | using namespace smartbutton; 9 | 10 | class ButtonInterface: public SmartButtonInterface { 11 | 12 | public: 13 | virtual void event(SmartButton *button, SmartButton::Event event, int clickCounter) 14 | { 15 | if (event == SmartButton::Event::HOLD || 16 | event == SmartButton::Event::HOLD_REPEAT || 17 | event == SmartButton::Event::LONG_HOLD || 18 | event == SmartButton::Event::LONG_HOLD_REPEAT) { 19 | toggle(); 20 | } 21 | } 22 | 23 | void toggle() { 24 | digitalWrite(LED_PIN, !digitalRead(LED_PIN)); 25 | } 26 | 27 | virtual bool isPressed(SmartButton *button) 28 | { 29 | return (digitalRead(BUTTON_PIN) == LOW) ? true : false; 30 | } 31 | }; 32 | 33 | ButtonInterface buttonInterface; 34 | 35 | SmartButton button(&buttonInterface); 36 | 37 | void setup() 38 | { 39 | pinMode(LED_PIN, OUTPUT); // Digital output for led 40 | pinMode(BUTTON_PIN, INPUT_PULLUP); // Digital input with pull-up resistors (normal high) 41 | 42 | button.begin(); // Initialize and register smart button 43 | } 44 | 45 | void loop() 46 | { 47 | SmartButton::service(); // Asynchronous service routine, should be called periodically 48 | } 49 | 50 | -------------------------------------------------------------------------------- /library.properties: -------------------------------------------------------------------------------- 1 | name=SmartButton Library 2 | version=0.3.0 3 | author=Marcin Borowicz 4 | maintainer=Marcin Borowicz 5 | sentence=Asynchronous SmartButton library for handling various button events. 6 | paragraph=Supports button press, release, multiple click, hold, long-hold, and auto-repeat events. 7 | category=Signal Input/Output 8 | url=https://github.com/marcinbor85/SmartButton 9 | architectures=* 10 | -------------------------------------------------------------------------------- /src/SmartButton.cpp: -------------------------------------------------------------------------------- 1 | #include "SmartButton.h" 2 | 3 | namespace smartbutton { 4 | 5 | SmartButton *_smartButtons = NULL; 6 | 7 | SmartButton::SmartButton( 8 | int pin, 9 | SmartButton::InputType inputType, 10 | SmartButton::IsPressedHandler isPressedHandler, 11 | bool *isPressedFlag, 12 | SmartButtonInterface *interface, 13 | unsigned long debounceTimeout, 14 | unsigned long clickTimeout, 15 | unsigned long holdTimeout, 16 | unsigned long longHoldTimeout, 17 | unsigned long holdRepeatPeriod, 18 | unsigned long longHoldRepeatPeriod): 19 | pin(pin), 20 | inputType(inputType), 21 | isPressedHandler(isPressedHandler), 22 | isPressedFlag(isPressedFlag), 23 | interface(interface), 24 | debounceTimeout(debounceTimeout), 25 | clickTimeout(clickTimeout), 26 | holdTimeout(holdTimeout), 27 | longHoldTimeout(longHoldTimeout), 28 | holdRepeatPeriod(holdRepeatPeriod), 29 | longHoldRepeatPeriod(longHoldRepeatPeriod), 30 | eventCallback(NULL) 31 | { 32 | 33 | } 34 | 35 | SmartButton::SmartButton(SmartButtonInterface *interface): SmartButton(-1, SmartButton::InputType::NORMAL_HIGH, NULL, NULL, interface) 36 | { 37 | 38 | } 39 | 40 | SmartButton::SmartButton(bool *isPressedFlag): SmartButton(-1, SmartButton::InputType::NORMAL_HIGH, NULL, isPressedFlag, NULL) 41 | { 42 | 43 | } 44 | 45 | SmartButton::SmartButton(SmartButton::IsPressedHandler isPressed): SmartButton(-1, SmartButton::InputType::NORMAL_HIGH, isPressed, NULL, NULL) 46 | { 47 | 48 | } 49 | 50 | SmartButton::SmartButton(int pin, SmartButton::InputType inputType): SmartButton(pin, inputType, NULL, NULL, NULL) 51 | { 52 | 53 | } 54 | 55 | void SmartButton::begin(void *context) 56 | { 57 | this->context = context; 58 | 59 | this->debounceTick = 0; 60 | this->pressTick = 0; 61 | this->clickCounter = 0; 62 | 63 | this->pressedFlag = false; 64 | this->state = SmartButton::State::RELEASED; 65 | 66 | this->next = _smartButtons; 67 | _smartButtons = this; 68 | } 69 | 70 | void SmartButton::begin(SmartButton::EventCallback eventCallback, void *context) 71 | { 72 | this->eventCallback = eventCallback; 73 | this->begin(context); 74 | } 75 | 76 | void SmartButton::end() 77 | { 78 | SmartButton *button = _smartButtons; 79 | SmartButton *prev = NULL; 80 | 81 | while (button != NULL) { 82 | if (button == this) { 83 | if (prev == NULL) { 84 | _smartButtons = button->next; 85 | } else { 86 | prev->next = button->next; 87 | } 88 | break; 89 | } 90 | prev = button; 91 | button = button->next; 92 | } 93 | } 94 | 95 | SmartButton::State SmartButton::getState() 96 | { 97 | return this->state; 98 | } 99 | 100 | bool SmartButton::isPressedDebounced() 101 | { 102 | return this->pressedFlag; 103 | } 104 | 105 | void* SmartButton::getContext() 106 | { 107 | return this->context; 108 | } 109 | 110 | bool SmartButton::getInputState() 111 | { 112 | bool s; 113 | if (this->pin >= 0) { 114 | s = getGpioState(this->pin); 115 | if (this->inputType == SmartButton::InputType::NORMAL_HIGH) { 116 | return !s; 117 | } else { 118 | return s; 119 | } 120 | } else if (this->isPressedHandler != NULL) { 121 | return this->isPressedHandler(this); 122 | } else if (this->isPressedFlag != NULL) { 123 | return *this->isPressedFlag; 124 | } else if (this->interface != NULL) { 125 | return this->interface->isPressed(this); 126 | } else { 127 | return false; 128 | } 129 | } 130 | 131 | void SmartButton::callEvent(SmartButton::Event event) 132 | { 133 | if (this->eventCallback != NULL) { 134 | this->eventCallback(this, event, this->clickCounter); 135 | } else if (this->interface != NULL) { 136 | this->interface->event(this, event, this->clickCounter); 137 | } 138 | } 139 | 140 | void SmartButton::debounce() 141 | { 142 | if (this->getInputState() == this->pressedFlag) { 143 | this->debounceTick = getTickValue(); 144 | } else if (getTickValue() - this->debounceTick >= this->debounceTimeout) { 145 | this->debounceTick = getTickValue(); 146 | this->pressedFlag = !this->pressedFlag; 147 | this->callEvent((this->pressedFlag != false) ? SmartButton::Event::PRESSED : SmartButton::Event::RELEASED); 148 | } 149 | } 150 | 151 | void SmartButton::process() 152 | { 153 | this->debounce(); 154 | 155 | switch (this->state) { 156 | case SmartButton::State::RELEASED: 157 | if (this->pressedFlag == false) { 158 | if ((this->clickCounter != 0) && (getTickValue() - this->pressTick > this->clickTimeout)) { 159 | this->callEvent(SmartButton::Event::CLICK); 160 | this->clickCounter = 0; 161 | } 162 | break; 163 | } 164 | this->state = SmartButton::State::PRESSED; 165 | this->pressTick = getTickValue(); 166 | break; 167 | case SmartButton::State::PRESSED: 168 | if (this->pressedFlag == false) { 169 | this->clickCounter++; 170 | this->state = SmartButton::State::RELEASED; 171 | break; 172 | } 173 | if (getTickValue() - this->pressTick < this->holdTimeout) 174 | break; 175 | this->callEvent(SmartButton::Event::HOLD); 176 | this->pressTick = getTickValue(); 177 | this->repeatTick = getTickValue(); 178 | this->state = SmartButton::State::HOLD; 179 | break; 180 | case SmartButton::State::HOLD: 181 | if (this->pressedFlag == false) { 182 | this->state = SmartButton::State::RELEASED; 183 | this->clickCounter = 0; 184 | break; 185 | } 186 | if (getTickValue() - this->pressTick < this->longHoldTimeout) { 187 | if (getTickValue() - this->repeatTick < this->holdRepeatPeriod) 188 | break; 189 | this->callEvent(SmartButton::Event::HOLD_REPEAT); 190 | this->repeatTick = getTickValue(); 191 | break; 192 | } 193 | this->callEvent(SmartButton::Event::LONG_HOLD); 194 | this->pressTick = getTickValue(); 195 | this->repeatTick = getTickValue(); 196 | this->state = SmartButton::State::LONG_HOLD; 197 | break; 198 | case SmartButton::State::LONG_HOLD: 199 | if (this->pressedFlag == false) { 200 | this->state = SmartButton::State::RELEASED; 201 | this->clickCounter = 0; 202 | } 203 | if (getTickValue() - this->repeatTick < this->longHoldRepeatPeriod) 204 | break; 205 | this->callEvent(SmartButton::Event::LONG_HOLD_REPEAT); 206 | this->repeatTick = getTickValue(); 207 | break; 208 | } 209 | } 210 | 211 | void SmartButton::service() 212 | { 213 | SmartButton *button = _smartButtons; 214 | 215 | while (button != NULL) { 216 | button->process(); 217 | button = button->next; 218 | } 219 | } 220 | 221 | }; 222 | -------------------------------------------------------------------------------- /src/SmartButton.h: -------------------------------------------------------------------------------- 1 | #ifndef SMART_BUTTON_H 2 | #define SMART_BUTTON_H 3 | 4 | #include "SmartButtonDefs.h" 5 | 6 | namespace smartbutton { 7 | 8 | class SmartButtonInterface; 9 | 10 | class SmartButton { 11 | 12 | public: 13 | enum class State { 14 | RELEASED, 15 | PRESSED, 16 | HOLD, 17 | LONG_HOLD 18 | }; 19 | 20 | enum class Event { 21 | RELEASED, 22 | PRESSED, 23 | CLICK, 24 | HOLD, 25 | HOLD_REPEAT, 26 | LONG_HOLD, 27 | LONG_HOLD_REPEAT 28 | }; 29 | 30 | enum class InputType { 31 | NORMAL_HIGH, 32 | NORMAL_LOW 33 | }; 34 | 35 | using IsPressedHandler = bool (*)(SmartButton *button); 36 | using EventCallback = void (*)(SmartButton *button, SmartButton::Event event, int clickCounter); 37 | 38 | explicit SmartButton(int pin, SmartButton::InputType inputType = SmartButton::InputType::NORMAL_HIGH); 39 | explicit SmartButton(SmartButton::IsPressedHandler isPressedHandler); 40 | explicit SmartButton(bool *isPressedFlag); 41 | explicit SmartButton(SmartButtonInterface *interface); 42 | 43 | SmartButton::State getState(void); 44 | bool isPressedDebounced(void); 45 | 46 | void begin(SmartButton::EventCallback eventCallback, void *context = NULL); 47 | void begin(void *context = NULL); 48 | void end(); 49 | 50 | void* getContext(); 51 | 52 | void process(); 53 | static void service(); 54 | 55 | SmartButton( 56 | int pin, 57 | SmartButton::InputType inputType, 58 | SmartButton::IsPressedHandler isPressedHandler, 59 | bool *isPressedFlag, 60 | SmartButtonInterface *interface, 61 | unsigned long debounceTimeout = DEFAULT_DEBOUNCE_TIMEOUT, 62 | unsigned long clickTimeout = DEFAULT_CLICK_TIMEOUT, 63 | unsigned long holdTimeout = DEFAULT_HOLD_TIMEOUT, 64 | unsigned long longHoldTimeout = DEFAULT_LONG_HOLD_TIMEOUT, 65 | unsigned long holdRepeatPeriod = DEFAULT_HOLD_REPEAT_PERIOD, 66 | unsigned long longHoldRepeatPeriod = DEFAULT_LONG_HOLD_REPEAT_PERIOD 67 | ); 68 | 69 | private: 70 | SmartButton() = delete; 71 | SmartButton(const SmartButton&) = delete; 72 | SmartButton& operator=(const SmartButton&) = delete; 73 | 74 | void debounce(); 75 | void callEvent(SmartButton::Event event); 76 | 77 | bool getInputState(); 78 | 79 | bool *isPressedFlag; 80 | SmartButtonInterface *interface; 81 | SmartButton::IsPressedHandler isPressedHandler; 82 | SmartButton::EventCallback eventCallback; 83 | void *context; 84 | 85 | const unsigned long debounceTimeout; 86 | const unsigned long clickTimeout; 87 | const unsigned long holdTimeout; 88 | const unsigned long longHoldTimeout; 89 | const unsigned long holdRepeatPeriod; 90 | const unsigned long longHoldRepeatPeriod; 91 | 92 | const int pin; 93 | const SmartButton::InputType inputType; 94 | 95 | unsigned long pressTick; 96 | unsigned long debounceTick; 97 | unsigned long repeatTick; 98 | 99 | bool pressedFlag; 100 | 101 | int clickCounter; 102 | SmartButton::State state; 103 | 104 | SmartButton *next; 105 | }; 106 | 107 | class SmartButtonInterface { 108 | 109 | public: 110 | virtual bool isPressed(SmartButton *button) = 0; 111 | virtual void event(SmartButton *button, SmartButton::Event event, int clickCounter) = 0; 112 | 113 | }; 114 | 115 | extern SmartButton *_smartButtons; 116 | 117 | }; 118 | 119 | #endif /* SMART_BUTTON_H */ 120 | -------------------------------------------------------------------------------- /src/SmartButtonDefs.h: -------------------------------------------------------------------------------- 1 | #ifndef SMART_BUTTON_DEFS_H 2 | #define SMART_BUTTON_DEFS_H 3 | 4 | #include 5 | 6 | namespace smartbutton { 7 | 8 | constexpr unsigned long DEFAULT_DEBOUNCE_TIMEOUT = 20UL; 9 | constexpr unsigned long DEFAULT_CLICK_TIMEOUT = 500UL; 10 | constexpr unsigned long DEFAULT_HOLD_TIMEOUT = 1000UL; 11 | constexpr unsigned long DEFAULT_LONG_HOLD_TIMEOUT = 2000UL; 12 | constexpr unsigned long DEFAULT_HOLD_REPEAT_PERIOD = 200UL; 13 | constexpr unsigned long DEFAULT_LONG_HOLD_REPEAT_PERIOD = 50UL; 14 | 15 | constexpr unsigned long (*getTickValue)() = millis; 16 | constexpr bool (*getGpioState)(int) = digitalRead; 17 | 18 | }; 19 | 20 | #endif /* SMART_BUTTON_DEFS_H */ 21 | --------------------------------------------------------------------------------