├── .clang-format ├── .github └── workflows │ ├── compile-examples.yml │ └── cppcheck.yml ├── .gitignore ├── .markdownlint.json ├── CHANGELOG.md ├── LICENSE ├── README.md ├── examples ├── BlinkMachine │ └── BlinkMachine.ino ├── FunctionalButton │ └── FunctionalButton.ino ├── InterruptOneButton │ └── InterruptOneButton.ino ├── LongPressEvents │ └── LongPressEvents.ino ├── SimpleOneButton │ └── SimpleOneButton.ino ├── SpecialInput │ └── SpecialInput.ino └── TwoButtons │ └── TwoButtons.ino ├── keywords.txt ├── library.json ├── library.properties ├── run_cppcheck.sh ├── src ├── OneButton.cpp ├── OneButton.h ├── OneButtonTiny.cpp └── OneButtonTiny.h └── suppressions.txt /.clang-format: -------------------------------------------------------------------------------- 1 | # Taken from https://github.com/arduino/arduino-language-server/blob/e453c5fbd059bae673bb21d028f5ca8e690744be/handler/handler.go#L1769-L1952 2 | # Which will be used in the IDE 2.0+ when 'format sketch' option is selected 3 | 4 | Language: Cpp 5 | # LLVM is the default style setting, used when a configuration option is not set here 6 | BasedOnStyle: LLVM 7 | AccessModifierOffset: -2 8 | AlignAfterOpenBracket: Align 9 | AlignConsecutiveAssignments: false 10 | AlignConsecutiveBitFields: false 11 | AlignConsecutiveDeclarations: false 12 | AlignConsecutiveMacros: false 13 | AlignEscapedNewlines: DontAlign 14 | AlignOperands: Align 15 | AlignTrailingComments: true 16 | AllowAllArgumentsOnNextLine: true 17 | AllowAllConstructorInitializersOnNextLine: true 18 | AllowAllParametersOfDeclarationOnNextLine: true 19 | AllowShortBlocksOnASingleLine: Always 20 | AllowShortCaseLabelsOnASingleLine: true 21 | AllowShortEnumsOnASingleLine: true 22 | AllowShortFunctionsOnASingleLine: Empty 23 | AllowShortIfStatementsOnASingleLine: Always 24 | AllowShortLambdasOnASingleLine: Empty 25 | AllowShortLoopsOnASingleLine: true 26 | AlwaysBreakAfterDefinitionReturnType: None 27 | AlwaysBreakAfterReturnType: None 28 | AlwaysBreakBeforeMultilineStrings: false 29 | AlwaysBreakTemplateDeclarations: No 30 | BinPackArguments: true 31 | BinPackParameters: true 32 | # Only used when "BreakBeforeBraces" set to "Custom" 33 | BraceWrapping: 34 | AfterCaseLabel: false 35 | AfterClass: false 36 | AfterControlStatement: Never 37 | AfterEnum: false 38 | AfterFunction: false 39 | AfterNamespace: false 40 | #AfterObjCDeclaration: 41 | AfterStruct: false 42 | AfterUnion: false 43 | AfterExternBlock: false 44 | BeforeCatch: false 45 | BeforeElse: false 46 | BeforeLambdaBody: false 47 | BeforeWhile: false 48 | IndentBraces: false 49 | SplitEmptyFunction: false 50 | SplitEmptyRecord: false 51 | SplitEmptyNamespace: false 52 | # Java-specific 53 | #BreakAfterJavaFieldAnnotations: 54 | BreakBeforeBinaryOperators: NonAssignment 55 | BreakBeforeBraces: Attach 56 | BreakBeforeTernaryOperators: true 57 | BreakConstructorInitializers: BeforeColon 58 | BreakInheritanceList: BeforeColon 59 | BreakStringLiterals: false 60 | ColumnLimit: 0 61 | # "" matches none 62 | CommentPragmas: "" 63 | CompactNamespaces: false 64 | ConstructorInitializerAllOnOneLineOrOnePerLine: true 65 | ConstructorInitializerIndentWidth: 2 66 | ContinuationIndentWidth: 2 67 | Cpp11BracedListStyle: false 68 | DeriveLineEnding: true 69 | DerivePointerAlignment: true 70 | DisableFormat: false 71 | # Docs say "Do not use this in config files". The default (LLVM 11.0.1) is "false". 72 | #ExperimentalAutoDetectBinPacking: 73 | FixNamespaceComments: false 74 | ForEachMacros: [] 75 | IncludeBlocks: Preserve 76 | IncludeCategories: [] 77 | # "" matches none 78 | IncludeIsMainRegex: "" 79 | IncludeIsMainSourceRegex: "" 80 | IndentCaseBlocks: true 81 | IndentCaseLabels: true 82 | IndentExternBlock: Indent 83 | IndentGotoLabels: false 84 | IndentPPDirectives: None 85 | IndentWidth: 2 86 | IndentWrappedFunctionNames: false 87 | InsertTrailingCommas: None 88 | # Java-specific 89 | #JavaImportGroups: 90 | # JavaScript-specific 91 | #JavaScriptQuotes: 92 | #JavaScriptWrapImports 93 | KeepEmptyLinesAtTheStartOfBlocks: true 94 | MacroBlockBegin: "" 95 | MacroBlockEnd: "" 96 | # Set to a large number to effectively disable 97 | MaxEmptyLinesToKeep: 100000 98 | NamespaceIndentation: None 99 | NamespaceMacros: [] 100 | # Objective C-specific 101 | #ObjCBinPackProtocolList: 102 | #ObjCBlockIndentWidth: 103 | #ObjCBreakBeforeNestedBlockParam: 104 | #ObjCSpaceAfterProperty: 105 | #ObjCSpaceBeforeProtocolList 106 | PenaltyBreakAssignment: 1 107 | PenaltyBreakBeforeFirstCallParameter: 1 108 | PenaltyBreakComment: 1 109 | PenaltyBreakFirstLessLess: 1 110 | PenaltyBreakString: 1 111 | PenaltyBreakTemplateDeclaration: 1 112 | PenaltyExcessCharacter: 1 113 | PenaltyReturnTypeOnItsOwnLine: 1 114 | # Used as a fallback if alignment style can't be detected from code (DerivePointerAlignment: true) 115 | PointerAlignment: Right 116 | RawStringFormats: [] 117 | ReflowComments: false 118 | SortIncludes: false 119 | SortUsingDeclarations: false 120 | SpaceAfterCStyleCast: false 121 | SpaceAfterLogicalNot: false 122 | SpaceAfterTemplateKeyword: false 123 | SpaceBeforeAssignmentOperators: true 124 | SpaceBeforeCpp11BracedList: false 125 | SpaceBeforeCtorInitializerColon: true 126 | SpaceBeforeInheritanceColon: true 127 | SpaceBeforeParens: ControlStatements 128 | SpaceBeforeRangeBasedForLoopColon: true 129 | SpaceBeforeSquareBrackets: false 130 | SpaceInEmptyBlock: false 131 | SpaceInEmptyParentheses: false 132 | SpacesBeforeTrailingComments: 2 133 | SpacesInAngles: false 134 | SpacesInCStyleCastParentheses: false 135 | SpacesInConditionalStatement: false 136 | SpacesInContainerLiterals: false 137 | SpacesInParentheses: false 138 | SpacesInSquareBrackets: false 139 | Standard: Auto 140 | StatementMacros: [] 141 | TabWidth: 2 142 | TypenameMacros: [] 143 | # Default to LF if line endings can't be detected from the content (DeriveLineEnding). 144 | UseCRLF: false 145 | UseTab: Never 146 | WhitespaceSensitiveMacros: [] -------------------------------------------------------------------------------- /.github/workflows/compile-examples.yml: -------------------------------------------------------------------------------- 1 | # This is a basic workflow to help you get started with Actions for Arduino library projects 2 | 3 | name: Compile Examples 4 | 5 | # Controls when the action will run. 6 | on: 7 | # Triggers the workflow on push or pull request events but only for the develop branch 8 | push: 9 | branches: [develop,master] 10 | pull_request: 11 | branches: [develop,master] 12 | 13 | # Allows you to run this workflow manually from the Actions tab 14 | workflow_dispatch: 15 | 16 | jobs: 17 | 18 | # This defines a job for checking the Arduino library format specifications 19 | # see 20 | 21 | lint: 22 | name: check library format 23 | runs-on: ubuntu-latest 24 | # continue-on-error: true 25 | 26 | steps: 27 | - uses: actions/checkout@v4 28 | 29 | # Arduino - lint 30 | - name: Arduino-lint 31 | uses: arduino/arduino-lint-action@v1 32 | with: 33 | library-manager: update 34 | verbose: false 35 | 36 | # These jobs are used to compile the examples fot the specific processor/board. 37 | # see 38 | 39 | compile-uno: 40 | name: use avr:uno 41 | runs-on: ubuntu-latest 42 | 43 | steps: 44 | - uses: actions/checkout@v4 45 | 46 | # Compile Examples for UNO 47 | - name: Compile examples on Uno 48 | uses: arduino/compile-sketches@v1 49 | with: 50 | verbose: true 51 | fqbn: arduino:avr:uno 52 | sketch-paths: | 53 | - 'examples/SimpleOneButton' 54 | - 'examples/TwoButtons' 55 | - 'examples/BlinkMachine' 56 | - 'examples/InterruptOneButton' 57 | - 'examples/SpecialInput' 58 | 59 | compile-esp8266: 60 | name: use esp8266 61 | runs-on: ubuntu-latest 62 | 63 | steps: 64 | - uses: actions/checkout@v4 65 | - name: compile sketches 66 | uses: arduino/compile-sketches@v1 67 | with: 68 | verbose: true 69 | platforms: | 70 | # Install ESP8266 platform via Boards Manager 71 | - name: esp8266:esp8266 72 | source-url: https://arduino.esp8266.com/stable/package_esp8266com_index.json 73 | version: 3.1.2 74 | fqbn: esp8266:esp8266:nodemcuv2 75 | sketch-paths: | 76 | - 'examples/SimpleOneButton' 77 | - 'examples/TwoButtons' 78 | - 'examples/BlinkMachine' 79 | - 'examples/InterruptOneButton' 80 | - 'examples/SpecialInput' 81 | 82 | compile-esp32: 83 | name: use ESP32 2.x 84 | runs-on: ubuntu-latest 85 | 86 | steps: 87 | - uses: actions/checkout@v4 88 | - name: compile sketches 89 | uses: arduino/compile-sketches@v1 90 | with: 91 | verbose: true 92 | platforms: | 93 | # Install ESP32 platform via Boards Manager 94 | - name: "esp32:esp32" 95 | source-url: https://raw.githubusercontent.com/espressif/arduino-esp32/gh-pages/package_esp32_index.json 96 | version: 2.0.8 97 | fqbn: esp32:esp32:esp32 98 | sketch-paths: | 99 | - 'examples/SimpleOneButton' 100 | - 'examples/TwoButtons' 101 | - 'examples/BlinkMachine' 102 | - 'examples/InterruptOneButton' 103 | - 'examples/SpecialInput' 104 | 105 | compile-esp32-v3: 106 | name: use ESP32 3.x 107 | runs-on: ubuntu-latest 108 | 109 | steps: 110 | - uses: actions/checkout@v4 111 | - name: compile sketches 112 | uses: arduino/compile-sketches@v1 113 | with: 114 | verbose: true 115 | platforms: | 116 | # Install ESP32 platform via Boards Manager 117 | - name: "esp32:esp32" 118 | source-url: https://raw.githubusercontent.com/espressif/arduino-esp32/gh-pages/package_esp32_index.json 119 | version: 3.0.4 120 | fqbn: esp32:esp32:esp32 121 | sketch-paths: | 122 | - 'examples/SimpleOneButton' 123 | - 'examples/TwoButtons' 124 | - 'examples/BlinkMachine' 125 | - 'examples/InterruptOneButton' 126 | - 'examples/SpecialInput' 127 | 128 | compile-arduino-nano-esp32: 129 | name: use Arduino Nano ESP32 130 | runs-on: ubuntu-latest 131 | 132 | steps: 133 | - uses: actions/checkout@v4 134 | 135 | - name: compile sketches 136 | uses: arduino/compile-sketches@v1 137 | with: 138 | verbose: true 139 | fqbn: arduino:esp32:nano_nora 140 | sketch-paths: | 141 | - 'examples/SimpleOneButton' 142 | - 'examples/TwoButtons' 143 | - 'examples/BlinkMachine' 144 | - 'examples/InterruptOneButton' 145 | - 'examples/SpecialInput' 146 | -------------------------------------------------------------------------------- /.github/workflows/cppcheck.yml: -------------------------------------------------------------------------------- 1 | name: cppcheck-action-test 2 | on: [push] 3 | 4 | jobs: 5 | build: 6 | name: cppcheck-test 7 | runs-on: ubuntu-latest 8 | steps: 9 | 10 | - uses: actions/checkout@v2 11 | 12 | - name: Install cppcheck 13 | run: | 14 | sudo apt-get install cppcheck 15 | 16 | - name: Run cppcheck 17 | run: | 18 | ./run_cppcheck.sh 19 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | .idea/ 3 | .vscode 4 | _* 5 | build 6 | -------------------------------------------------------------------------------- /.markdownlint.json: -------------------------------------------------------------------------------- 1 | { 2 | "default": true, 3 | "no-hard-tabs": true, 4 | "no-trailing-spaces": true, 5 | "blank_lines": true, 6 | "no-multiple-blanks": { 7 | "maximum": 2 8 | }, 9 | "ul-indent": { 10 | "indent": 2 11 | }, 12 | "ul-style": { 13 | "style": "asterisk" 14 | }, 15 | "line-length": false, 16 | "no-inline-html": false, 17 | "code-block-style": { 18 | "style": "fenced" 19 | }, 20 | "no-duplicate-header": { 21 | "allow_different_nesting": true 22 | } 23 | } -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | All notable changes to this project will be documented in this file starting 2021. 4 | 5 | ## Version 2.6.1 - 2024-08-02 6 | 7 | fixing compiler error Issue #147 8 | 9 | ## Version 2.6.0 - 2024-08-01 10 | 11 | * The new `setup(...)` function allows deferred initialisation. 12 | * The SimpleOneButton.ino includes a configuration for the Arduino Nano ESP32 13 | * Supporting a new press event. 14 | * using `bool` instead of `boolean` that is a deprecated type by Arduino. 15 | * changes in debouncing. 16 | * standard Arduino style .clang formatting in changed files. 17 | 18 | ## Version 2.5.0 - 2023-12-02 19 | 20 | This release is a minor update including som smaller fixes. 21 | 22 | * Functions marked with deprecated will be removed in version 3.x 23 | * Formatting of source code conformint the standard Arduino IDE 2.0 formatting using .clang-format 24 | * Version for platform.io in sync with version for Arduino 25 | * Introducing the `OneButtonTiny` class for small environments with limited program space and memory. 26 | 27 | 28 | ## Version 2.1.0 - 2023-05-10 29 | 30 | This release is a minor update as there is new internal functionality and 31 | some functions have been renamed. 32 | 33 | The former functions `setDebounceTicks`, `setClickTicks` and `setPressTicks` are marked deprecated. 34 | The term `Ticks` in these functions where confusing. Replace them with the ...Ms function calls. 35 | There is no functional change on them. 36 | 37 | * CPP Checks added in Github actions. Thanks to @mkinney 38 | * Debouncing input levels implemented in a central place. Thanks to @IhorNehrutsa 39 | * Docu for using lamda functions as callbacks, Thanks to @gergovari 40 | * .clang-format file added to support code formatting in IDE 2.x (and others) 41 | * Fixing examples for ESP8266 and ESP32. 42 | * GitHub Action extended to compile for ESP8266 and ESP32 43 | 44 | Many thanks to the improvements included by (**@IhorNehrutsa**) 45 | 46 | ## Version 2.0.4 - 2022-01-22 47 | 48 | * checked for ESP32 (SimpleOneButton, InterruptOneButton, BlinkMachine) 49 | and included example PIN definitions for ESP32 50 | * Documentation changes 51 | 52 | ## Version 2.0.3 - 2021-10-26 53 | 54 | * fixing parameter missuse and potential crash 55 | 56 | ## Version 2.0.1 - 2021-01-31 57 | 58 | * Compiler warning removed 59 | * Documentation 60 | 61 | ## Version 2.0.0 - 2021-01-22 62 | 63 | * CHANGELOG created. 64 | * Many thanks to the improvements included from #27 (**@aslobodyanuk**), #59 (**@ShaggyDog18**) and #73 (**@geeksville**). 65 | 66 | This is a major update with breaking changes. 67 | 68 | The **states** are re-factored to support counting the clicks. 69 | 70 | By design only one of the events (click, doubleClick, MultiClick) are triggered within one interaction. 71 | As a consequence a single-click interaction is detected after waiting some milliseconds (see setClickTicks()) without another click happening; 72 | Only if you have not attached any double-click event function the waiting time can be skipped. 73 | 74 | Detecting a long 'down' not only works with the first but always as the last click. 75 | 76 | The number of actual clicks can be retrieved from the library any time. 77 | 78 | The function **getPressedTicks()** was removed. See example SimpleOneButton on how to get that time by using attachLongPressStart to save starting time. 79 | 80 | The function **attachPressStart()** is removed as **attachLongPressStart()** does the same but also supports parameters. 81 | 82 | One additional feature has been added not to call the event functions from the interrupt routine and detect 83 | the need for event functions to be called only when the tick() function is called from the main loop() method. 84 | This is because some boards and processors do not support timing or Serial functions (among others) from interrupt routines. 85 | 86 | The function **isIdle()** was added to allow detect a current interaction. 87 | 88 | The library now supports to detect multiple (>2) clicks in a row using **attachMultiClick()** . 89 | 90 | * The internal _state is using enum instead of plain numbers to make the library more readable. 91 | * functions that had been marked deprecated are now removed. (attachPress->attachLongPressXXX) 92 | * added const to constant parameters to enable meaningful compiler warnings. 93 | * added code for de-bouncing double clicks from pull 27. 94 | * added isIdle() function to find out that the internal state is `init`. 95 | 96 | ### Examples 97 | 98 | * Examples run on NodeMCU boards. (the library worked already). 99 | 100 | * The **SimpleOneButton.ino** example got some cleanup and definition to be used with ESP8266 boards as well. 101 | 102 | * The **InterruptOneButton.ino** example now is using attachInterrupt instead of UNO specific register modifications. 103 | 104 | * The **SpecialInput.ino** example was added to show how to use the OneButton algorithm and input pattern recognition with your own source of input. 105 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mathertel/OneButton/0cebe0c726cbb535941e49ccdc156443a8a7aaf1/LICENSE -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Arduino OneButton Library 2 | 3 | This Arduino library is improving the usage of a singe button for input. 4 | It shows how to use an digital input pin with a single pushbutton attached 5 | for detecting some of the typical button press events like single clicks, double clicks and long-time pressing. 6 | This enables you to reuse the same button for multiple functions and lowers the hardware investments. 7 | 8 | This is also a sample for implementing simple finite-state machines by using the simple pattern above. 9 | 10 | You can find more details on this library at 11 | 12 | 13 | The change log of this library can be found in [CHANGELOG](CHANGELOG.md). 14 | 15 | 16 | ## Getting Started 17 | 18 | Clone this repository into `Arduino/Libraries` or use the built-in Arduino IDE Library manager to install 19 | a copy of this library. You can find more detail about installing libraries 20 | [here, on Arduino's website](https://www.arduino.cc/en/guide/libraries). 21 | 22 | ```CPP 23 | #include 24 | #include 25 | ``` 26 | 27 | Each physical button requires its own `OneButton` instance. 28 | 29 | 30 | ### Initialize on Instance creation (old way) 31 | 32 | You can create a global instance and pass the hardware configurtion directly at compile time: 33 | 34 | ```cpp 35 | // Declare and initialize 36 | OneButton btn = OneButton( 37 | BUTTON_PIN, // Input pin for the button 38 | true, // Button is active LOW 39 | true // Enable internal pull-up resistor 40 | ); 41 | ``` 42 | 43 | This works for most boards. However, some boards will initialize the hardware AFTER the initialization of global 44 | defined instances and the Input pin will not work at all. 45 | 46 | 47 | ### Explicit setup and deferred initialization (new, more compatible option) 48 | 49 | By using an explicit initialization using the `setup(...)` function solves the problem of the initialization order. 50 | Also this is also a good option in case you do not know the Hardware configuration at compile time. 51 | 52 | Declare a global instance, un-initialized: 53 | 54 | ```cpp 55 | OneButton btn; 56 | ``` 57 | 58 | In the main `setup()` function the instance will be initialized by passing the hardware configuration. Pass the input 59 | mode as known from pinMode(): 60 | 61 | ```cpp 62 | btn.setup( 63 | BUTTON_PIN, // Input pin for the button 64 | INPUT_PULLUP, // INPUT and enable the internal pull-up resistor 65 | true // Button is active LOW 66 | ); 67 | ``` 68 | 69 | In the SimpleOneButton example shows how to use this sequence. In the new `setup(...)` function the pinMode can be 70 | given in the second parameter to allow all kind of hardware options. 71 | 72 | 73 | ## OneButton Tiny version 74 | 75 | The OneButton Library was extended over time with functionality that was requested for specific 76 | use cases. This makes the library growing over time too and therefore was limiting use cases using very small processors like attiny84. 77 | 78 | Staring with version 2.5 the OneButton Library starts supporting these processors with limited 79 | memory and low cpu frequencies by introducing the `OneButtonTiny` class that offers a subset of 80 | the features of the complete `OneButton` class by exposing the following events as callbacks: 81 | 82 | * Click event 83 | * DoubleClick event 84 | * LongPressStart event 85 | * Callbacks without parameters 86 | 87 | This saves up to 1k of binary program space that is a huge amount on these processors. 88 | 89 | With Version 2.5 the `OneButtonTiny` class is now in a beta state. 90 | 91 | * Any Issues or pull requests fixing problems are welcome. 92 | * Any new feature request for the `OneButtonTiny` class will be rejected to keep size small. 93 | * New, reasonable functionality will be added to the OneButton class only. 94 | 95 | 96 | ### Initialize a Button to GND 97 | 98 | ```CPP 99 | #define BUTTON_PIN 4 100 | 101 | /** 102 | * Initialize a new OneButton instance for a button 103 | * connected to digital pin 4 and GND, which is active low 104 | * and uses the internal pull-up resistor. 105 | */ 106 | 107 | OneButton btn = OneButton( 108 | BUTTON_PIN, // Input pin for the button 109 | true, // Button is active LOW 110 | true // Enable internal pull-up resistor 111 | ); 112 | ``` 113 | 114 | 115 | ### Initialize a Button to VCC 116 | 117 | ```CPP 118 | #define BUTTON_PIN 4 119 | 120 | /** 121 | * Initialize a new OneButton instance for a button 122 | * connected to digital pin 4, which is active high. 123 | * As this does not use any internal resistor 124 | * an external resistor (4.7k) may be required to create a LOW signal when the button is not pressed. 125 | */ 126 | 127 | OneButton btn = OneButton( 128 | BUTTON_PIN, // Input pin for the button 129 | false, // Button is active high 130 | false // Disable internal pull-up resistor 131 | ); 132 | ``` 133 | 134 | 135 | ### Attach State Events 136 | 137 | Once you have your button initialized, you can handle events by attaching them to the button 138 | instance. Events can either be static functions or lambdas (without captured variables). 139 | 140 | ```CPP 141 | // Handler function for a single click: 142 | static void handleClick() { 143 | Serial.println("Clicked!"); 144 | } 145 | 146 | // Single Click event attachment 147 | btn.attachClick(handleClick); 148 | 149 | // Double Click event attachment with lambda 150 | btn.attachDoubleClick([]() { 151 | Serial.println("Double Pressed!"); 152 | }); 153 | 154 | // Handler function for MultiClick the button with self pointer as a parameter 155 | static void handleMultiClick(OneButton *oneButton) { 156 | Serial.println("MultiClick pin=%d debouncedValue=%d!", oneButton->pin(), oneButton->debouncedValue()); 157 | } 158 | 159 | // MultiClick button event attachment with self pointer as a parameter 160 | btn.attachMultiClick(handleMultiClick, &btn); 161 | ``` 162 | 163 | ### Don't forget to `tick()` 164 | 165 | In order for `OneButton` to work correctly, you must call `tick()` on __each button instance__ 166 | within your main `loop()`. If you're not getting any button events, this is probably why. 167 | 168 | ```CPP 169 | void loop() { 170 | btn.tick(); 171 | 172 | // Do other things... 173 | } 174 | ``` 175 | 176 | 177 | ### Usage with lambdas that capture context 178 | 179 | You __can't pass__ a lambda-__with-context__ to an argument which expects a __function pointer__. To work that around, 180 | use `paramtererizedCallbackFunction`. We pass the context (so the pointer to the object we want to access) to the library 181 | and it will give it back to the lambda. 182 | 183 | ```CPP 184 | okBtn.attachClick([](void *ctx){Serial.println(*((BtnHandler*)ctx) -> state);}, this); 185 | ``` 186 | 187 | See also discussion in [Issue #112](https://github.com/mathertel/OneButton/issues/112). 188 | 189 | 190 | ## State Events 191 | 192 | Here's a full list of events handled by this library: 193 | 194 | | Attach Function | Description | 195 | | ----------------------- | ------------------------------------------------------------- | 196 | | `attachPress` | Fires as soon as a press is detected. | 197 | | `attachClick` | Fires as soon as a single click press and release is detected.| 198 | | `attachDoubleClick` | Fires as soon as a double click is detected. | 199 | | `attachMultiClick` | Fires as soon as multiple clicks have been detected. | 200 | | `attachLongPressStart` | Fires as soon as the button is held down for 800 milliseconds.| 201 | | `attachDuringLongPress` | Fires periodically as long as the button is held down. | 202 | | `attachLongPressStop` | Fires when the button is released after a long hold. | 203 | 204 | 205 | ### Event Timing 206 | 207 | Valid events occur when `tick()` is called after a specified number of milliseconds. You can use 208 | the following functions to change the timing. 209 | 210 | __Note:__ Attaching a double click will increase the delay for detecting a single click. If a double 211 | click event is not attached, the library will assume a valid single click after one click duration, 212 | otherwise it must wait for the double click timeout to pass. 213 | This is because a single click callback must not to be triggered in case of a double click event. 214 | 215 | | Function | Default | Description | 216 | | ----------------------- | ---------- | ------------------------------------------------------------- | 217 | | `setDebounceMs(int)` | `50 msec` | Period of time in which to ignore additional level changes. | 218 | | `setClickMs(int)` | `400 msec` | Timeout used to distinguish single clicks from double clicks. | 219 | | `setPressMs(int)` | `800 msec` | Duration to hold a button to trigger a long press. | 220 | 221 | You may change these default values but be aware that when you specify too short times 222 | it is hard to click twice or you will create a long press instead of a click. 223 | 224 | The former functions `setDebounceTicks`, `setClickTicks` and `setPressTicks` are marked deprecated. 225 | The term `Ticks` in these functions where confusing. Replace them with the ...Ms function calls. 226 | There is no functional change on them. 227 | 228 | Set debounce ms to a negative value to only debounce on release. `setDebounceMs(-25);` will immediately 229 | update to a pressed state, and will debounce for 25ms going into the released state. This will expidite 230 | the `attachPress` callback function to run instantly. 231 | 232 | 233 | ### Additional Functions 234 | 235 | `OneButton` also provides a couple additional functions to use for querying button status: 236 | 237 | | Function | Description | 238 | | ----------------------- | ------------------------------------------------------------------------------ | 239 | | `bool isLongPressed()` | Detect whether or not the button is currently inside a long press. | 240 | | `int getPressedMs()` | Get the current number of milliseconds that the button has been held down for. | 241 | | `int pin()` | Get the OneButton pin | 242 | | `int state()` | Get the OneButton state | 243 | | `int debouncedValue()` | Get the OneButton debounced value | 244 | 245 | 246 | ### `tick()` and `reset()` 247 | 248 | You can specify a logic level when calling `tick(bool)`, which will skip reading the pin and use 249 | that level instead. If you wish to reset the internal state of your buttons, call `reset()`. 250 | 251 | 252 | ## Troubleshooting 253 | 254 | If your buttons aren't acting they way they should, check these items: 255 | 256 | 1. Check your wiring and pin numbers. 257 | 2. Did you call `tick()` on each button instance in your loop? 258 | 3. Did you alter your clock timers in any way without adjusting ticks? 259 | -------------------------------------------------------------------------------- /examples/BlinkMachine/BlinkMachine.ino: -------------------------------------------------------------------------------- 1 | /* 2 | BlinkMachine.ino 3 | 4 | This is a sample sketch to show how to use the OneButtonLibrary to detect double-click events on a button. 5 | 6 | Copyright (c) by Matthias Hertel, http://www.mathertel.de 7 | This work is licensed under a BSD style license. See http://www.mathertel.de/License.aspx 8 | More information on: http://www.mathertel.de/Arduino 9 | 10 | The library internals are explained at 11 | http://www.mathertel.de/Arduino/OneButtonLibrary.aspx 12 | 13 | Setup a test circuit: 14 | * Connect a pushbutton to pin A1 (Uno) or D3 (ESP8266) and ground. 15 | * The pin 13 (UNO) or D4 (ESP8266) is used for output attach a led and resistor to ground 16 | or see the built-in led on the standard arduino board. 17 | 18 | The Sketch shows how to setup the library and bind a "machine" that can blink the LED slow or fast. 19 | A click on the button turns the led on. 20 | A doubleclick on the button changes the blink rate from ON to SLOW to FAST and back. 21 | In the loop function the button.tick function has to be called as often as you like. 22 | 23 | State-Diagram 24 | 25 | start 26 | | +-------\ 27 | V V | 28 | -------- ------ | 29 | | OFF |<--click-+->| ON | | 30 | -------- | ------ | 31 | ^ | | | 32 | | | d-click | 33 | longpress | | | 34 | | V | 35 | | ------ | 36 | +- | SLOW | | 37 | | ------ | 38 | | | | 39 | | d-click | 40 | | | | 41 | | V d-click 42 | | ------ | 43 | +--| FAST |---/ 44 | ------ 45 | */ 46 | 47 | // 06.10.2012 created by Matthias Hertel 48 | // 26.03.2017 state diagram added, minor changes 49 | 50 | // #include "OneButton.h" 51 | #include "OneButtonTiny.h" // This example also works with reduced OneButtonTiny class saving. 52 | 53 | // The actions I ca do... 54 | typedef enum { 55 | ACTION_OFF, // set LED "OFF". 56 | ACTION_ON, // set LED "ON" 57 | ACTION_SLOW, // blink LED "SLOW" 58 | ACTION_FAST // blink LED "FAST" 59 | } MyActions; 60 | 61 | #if defined(ARDUINO_AVR_UNO) || defined(ARDUINO_AVR_NANO_EVERY) ||defined(ARDUINO_UNOR4_WIFI) 62 | // Example for Arduino UNO with input button on pin 2 and builtin LED on pin 13 63 | #define PIN_INPUT A1 64 | #define PIN_LED 13 65 | 66 | #elif defined(ARDUINO_attiny) 67 | // Example for Arduino ATTiny85 68 | #define PIN_INPUT PB0 69 | #define PIN_LED PB1 70 | 71 | #elif defined(ESP8266) 72 | // Example for NodeMCU with input button using FLASH button on D3 and using the led on -12 module (D4). 73 | // This LED is lighting on output level LOW. 74 | #define PIN_INPUT D3 75 | #define PIN_LED D4 76 | 77 | #elif defined(ESP32) 78 | // Example pin assignments for a ESP32 board 79 | // Some boards have a BOOT switch using GPIO 0. 80 | #define PIN_INPUT 0 81 | // Attach a LED using GPIO 25 and VCC. The LED is on when output level is LOW. 82 | #define PIN_LED 25 83 | 84 | #endif 85 | 86 | // Setup a new OneButton on pin PIN_INPUT. 87 | // OneButton button(PIN_INPUT, true); 88 | OneButtonTiny button(PIN_INPUT, true); // This example also works with reduced OneButtonTiny class saving. 89 | 90 | MyActions nextAction = ACTION_OFF; // no action when starting 91 | 92 | 93 | // setup code here, to run once. 94 | void setup() { 95 | // enable the standard led on pin 13. 96 | pinMode(PIN_LED, OUTPUT); // sets the digital pin as output 97 | 98 | // link the myClickFunction function to be called on a click event. 99 | button.attachClick(myClickFunction); 100 | 101 | // link the doubleclick function to be called on a doubleclick event. 102 | button.attachDoubleClick(myDoubleClickFunction); 103 | 104 | // link the doubleclick function to be called on a doubleclick event. 105 | button.attachLongPressStart(myDoubleClickFunction); 106 | 107 | // set 80 msec. debouncing time. Default is 50 msec. 108 | button.setDebounceMs(80); 109 | } // setup 110 | 111 | 112 | // main code here, to run repeatedly: 113 | void loop() { 114 | unsigned long now = millis(); 115 | 116 | // keep watching the push button: 117 | button.tick(); 118 | 119 | // You can implement other code in here or just wait a while 120 | 121 | if (nextAction == ACTION_OFF) { 122 | // do nothing. 123 | digitalWrite(PIN_LED, LOW); 124 | 125 | } else if (nextAction == ACTION_ON) { 126 | // turn LED on 127 | digitalWrite(PIN_LED, HIGH); 128 | 129 | } else if (nextAction == ACTION_SLOW) { 130 | // do a slow blinking 131 | if (now % 1000 < 500) { 132 | digitalWrite(PIN_LED, LOW); 133 | } else { 134 | digitalWrite(PIN_LED, HIGH); 135 | } // if 136 | 137 | } else if (nextAction == ACTION_FAST) { 138 | // do a fast blinking 139 | if (now % 200 < 100) { 140 | digitalWrite(PIN_LED, LOW); 141 | } else { 142 | digitalWrite(PIN_LED, HIGH); 143 | } // if 144 | } // if 145 | } // loop 146 | 147 | 148 | // this function will be called when the button was pressed 1 time and them some time has passed. 149 | void myClickFunction() { 150 | if (nextAction == ACTION_OFF) 151 | nextAction = ACTION_ON; 152 | else 153 | nextAction = ACTION_OFF; 154 | } // myClickFunction 155 | 156 | 157 | // this function will be called when the button was pressed 2 times in a short timeframe. 158 | void myDoubleClickFunction() { 159 | if (nextAction == ACTION_ON) { 160 | nextAction = ACTION_SLOW; 161 | 162 | } else if (nextAction == ACTION_SLOW) { 163 | nextAction = ACTION_FAST; 164 | 165 | } else if (nextAction == ACTION_FAST) { 166 | nextAction = ACTION_ON; 167 | } // if 168 | } // myDoubleClickFunction 169 | 170 | 171 | // this function will be called when a long press was detected. 172 | void myLongPressFunction() { 173 | nextAction = ACTION_OFF; 174 | } // myLongPressFunction 175 | 176 | // End 177 | -------------------------------------------------------------------------------- /examples/FunctionalButton/FunctionalButton.ino: -------------------------------------------------------------------------------- 1 | /** 2 | * FunctionalButton.ino - Example for the OneButtonLibrary library. 3 | * This is a sample sketch to show how to use OneClick library functionally on ESP32,ESP8266... 4 | * 5 | */ 6 | #include 7 | #include 8 | 9 | class Button{ 10 | private: 11 | OneButton button; 12 | int value; 13 | public: 14 | explicit Button(uint8_t pin):button(pin) { 15 | button.attachClick([](void *scope) { ((Button *) scope)->Clicked();}, this); 16 | button.attachDoubleClick([](void *scope) { ((Button *) scope)->DoubleClicked();}, this); 17 | button.attachLongPressStart([](void *scope) { ((Button *) scope)->LongPressed();}, this); 18 | } 19 | 20 | void Clicked(){ 21 | Serial.println("Click then value++"); 22 | value++; 23 | } 24 | 25 | void DoubleClicked(){ 26 | 27 | Serial.println("DoubleClick"); 28 | } 29 | 30 | void LongPressed(){ 31 | Serial.println("LongPress and the value is"); 32 | Serial.println(value); 33 | } 34 | 35 | void handle(){ 36 | button.tick(); 37 | } 38 | }; 39 | 40 | Button button(0); 41 | 42 | void setup() { 43 | Serial.begin(115200); 44 | } 45 | 46 | void loop() { 47 | button.handle(); 48 | } 49 | -------------------------------------------------------------------------------- /examples/InterruptOneButton/InterruptOneButton.ino: -------------------------------------------------------------------------------- 1 | /* 2 | InterruptOneButton.ino - Example for the OneButtonLibrary library. 3 | This is a sample sketch to show how to use the OneButtonLibrary 4 | to detect double-click events on a button by using interrupts. 5 | The library internals are explained at 6 | http://www.mathertel.de/Arduino/OneButtonLibrary.aspx 7 | 8 | Good article on Arduino UNO interrupts: 9 | https://arduino.stackexchange.com/questions/30968/how-do-interrupts-work-on-the-arduino-uno-and-similar-boards 10 | ... and the processor datasheet. 11 | 12 | Setup a test circuit: 13 | * Connect a pushbutton to the PIN_INPUT (see defines for processor specific examples) and ground. 14 | * The PIN_LED (see defines for processor specific examples) is used for output attach a led and resistor to VCC. 15 | or maybe use a built-in led on the standard arduino board. 16 | 17 | The sketch shows how to setup the library and bind the functions (singleClick, doubleClick) to the events. 18 | In the loop function the button.tick function must be called as often as you like. 19 | By using interrupts the internal state advances even when for longer time the button.tick is not called. 20 | */ 21 | 22 | // 03.03.2011 created by Matthias Hertel 23 | // 01.12.2011 extension changed to work with the Arduino 1.0 environment 24 | // 04.11.2017 Interrupt version created. 25 | // 04.11.2017 Interrupt version using attachInterrupt. 26 | 27 | #include "OneButton.h" 28 | 29 | #if defined(ARDUINO_AVR_UNO) || defined(ARDUINO_AVR_NANO_EVERY) 30 | // Example for Arduino UNO with input button on pin 2 and builtin LED on pin 13 31 | // attachInterrupt only supports pin 2 and 3 on UNO. 32 | // See https://www.arduino.cc/reference/en/language/functions/external-interrupts/attachinterrupt/ 33 | #define PIN_INPUT 2 34 | #define PIN_LED 13 35 | 36 | #elif defined(ESP8266) 37 | // Example for NodeMCU with input button using FLASH button on D3 and using the led on -12 module (D4). 38 | // This LED is lighting on output level LOW. 39 | #define PIN_INPUT D3 40 | #define PIN_LED D4 41 | 42 | #elif defined(ESP32) 43 | // Example pin assignments for a ESP32 board 44 | // Some boards have a BOOT switch using GPIO 0. 45 | #define PIN_INPUT 0 46 | // Attach a LED using GPIO 25 and VCC. The LED is on when output level is LOW. 47 | #define PIN_LED 25 48 | 49 | #endif 50 | 51 | // Setup a new OneButton on pin PIN_INPUT 52 | // The 2. parameter activeLOW is true, because external wiring sets the button to LOW when pressed. 53 | OneButton button(PIN_INPUT, true); 54 | 55 | // current LED state, staring with LOW (0) 56 | int ledState = LOW; 57 | 58 | // save the millis when a press has started. 59 | unsigned long pressStartTime; 60 | 61 | // In case the momentary button puts the input to HIGH when pressed: 62 | // The 2. parameter activeLOW is false when the external wiring sets the button to HIGH when pressed. 63 | // The 3. parameter can be used to disable the PullUp . 64 | // OneButton button(PIN_INPUT, false, false); 65 | 66 | 67 | // This function is called from the interrupt when the signal on the PIN_INPUT has changed. 68 | // do not use Serial in here. 69 | #if defined(ARDUINO_AVR_UNO) || defined(ARDUINO_AVR_NANO_EVERY) 70 | void checkTicks() { 71 | // include all buttons here to be checked 72 | button.tick(); // just call tick() to check the state. 73 | } 74 | 75 | #elif defined(ESP8266) 76 | ICACHE_RAM_ATTR void checkTicks() { 77 | // include all buttons here to be checked 78 | button.tick(); // just call tick() to check the state. 79 | } 80 | 81 | #elif defined(ESP32) 82 | void IRAM_ATTR checkTicks() { 83 | // include all buttons here to be checked 84 | button.tick(); // just call tick() to check the state. 85 | } 86 | 87 | #endif 88 | 89 | 90 | // this function will be called when the button was pressed 1 time only. 91 | void singleClick() { 92 | Serial.println("singleClick() detected."); 93 | } // singleClick 94 | 95 | 96 | // this function will be called when the button was pressed 2 times in a short timeframe. 97 | void doubleClick() { 98 | Serial.println("doubleClick() detected."); 99 | 100 | ledState = !ledState; // reverse the LED 101 | digitalWrite(PIN_LED, ledState); 102 | } // doubleClick 103 | 104 | 105 | // this function will be called when the button was pressed multiple times in a short timeframe. 106 | void multiClick() { 107 | int n = button.getNumberClicks(); 108 | if (n == 3) { 109 | Serial.println("tripleClick detected."); 110 | } else if (n == 4) { 111 | Serial.println("quadrupleClick detected."); 112 | } else { 113 | Serial.print("multiClick("); 114 | Serial.print(n); 115 | Serial.println(") detected."); 116 | } 117 | 118 | ledState = !ledState; // reverse the LED 119 | digitalWrite(PIN_LED, ledState); 120 | } // multiClick 121 | 122 | 123 | // this function will be called when the button was held down for 1 second or more. 124 | void pressStart() { 125 | Serial.println("pressStart()"); 126 | pressStartTime = millis() - 1000; // as set in setPressMs() 127 | } // pressStart() 128 | 129 | 130 | // this function will be called when the button was released after a long hold. 131 | void pressStop() { 132 | Serial.print("pressStop("); 133 | Serial.print(millis() - pressStartTime); 134 | Serial.println(") detected."); 135 | } // pressStop() 136 | 137 | 138 | // setup code here, to run once: 139 | void setup() { 140 | Serial.begin(115200); 141 | Serial.println("One Button Example with interrupts."); 142 | 143 | // enable the led output. 144 | pinMode(PIN_LED, OUTPUT); // sets the digital pin as output 145 | digitalWrite(PIN_LED, ledState); 146 | 147 | // setup interrupt routine 148 | // when not registering to the interrupt the sketch also works when the tick is called frequently. 149 | attachInterrupt(digitalPinToInterrupt(PIN_INPUT), checkTicks, CHANGE); 150 | 151 | // link the xxxclick functions to be called on xxxclick event. 152 | button.attachClick(singleClick); 153 | button.attachDoubleClick(doubleClick); 154 | button.attachMultiClick(multiClick); 155 | 156 | button.setPressMs(1000); // that is the time when LongPressStart is called 157 | button.attachLongPressStart(pressStart); 158 | button.attachLongPressStop(pressStop); 159 | 160 | // A1-Option for UNO: 161 | // it is possible to use e.g. A1 but then some direct register modifications and an ISR has to be used: 162 | // You may have to modify the next 2 lines if using another pin than A1 163 | // PCICR |= (1 << PCIE1); // This enables Pin Change Interrupt 1 that covers the Analog input pins or Port C. 164 | // PCMSK1 |= (1 << PCINT9); // This enables the interrupt for pin 1 of Port C: This is A1. 165 | 166 | } // setup 167 | 168 | // A1-Option for UNO: 169 | // The Interrupt Service Routine for Pin Change Interrupt 1 170 | // This routine will only be called on any signal change on A1: exactly where we need to check. 171 | // ISR(PCINT1_vect) 172 | // { 173 | // // keep watching the push button: 174 | // button.tick(); // just call tick() to check the state. 175 | // } 176 | 177 | 178 | // main code here, to run repeatedly: 179 | void loop() { 180 | // keep watching the push button, even when no interrupt happens: 181 | button.tick(); 182 | 183 | // You can implement other code in here or just wait a while 184 | delay(10); 185 | } // loop 186 | 187 | 188 | // End 189 | -------------------------------------------------------------------------------- /examples/LongPressEvents/LongPressEvents.ino: -------------------------------------------------------------------------------- 1 | /* 2 | This is a sample sketch to show how to use the OneButtonLibrary 3 | to detect long press events on a button. 4 | The library internals are explained at 5 | http://www.mathertel.de/Arduino/OneButtonLibrary.aspx 6 | 7 | Setup a test circuit: 8 | * Connect a pushbutton to PIN_INPUT (ButtonPin) and ground. 9 | 10 | The sketch shows how to setup the library and bind the functions (LongPressStart, LongPressStop, DuringLongPress) to the events. 11 | In the loop function the button.tick function must be called as often as you like. 12 | The output of the program is: 13 | 14 | OneButton Example. 15 | Please press and hold the button for a few seconds. 16 | 810 - LongPressStart() 17 | 820 - DuringLongPress() 18 | 1820 - DuringLongPress() 19 | 2820 - DuringLongPress() 20 | 3820 - DuringLongPress() 21 | 4820 - DuringLongPress() 22 | 5820 - DuringLongPress() 23 | 6550 - LongPressStop() 24 | */ 25 | 26 | // 05.05.2023 created by Ihor Nehrutsa 27 | 28 | #include "OneButton.h" 29 | 30 | #if defined(ARDUINO_AVR_UNO) || defined(ARDUINO_AVR_NANO_EVERY) 31 | // Example for Arduino UNO with input button on pin 2 32 | #define PIN_INPUT 2 33 | 34 | #elif defined(ESP8266) 35 | // Example for NodeMCU with input button using FLASH button on D3 36 | #define PIN_INPUT D3 37 | 38 | #elif defined(ESP32) 39 | // Example pin assignments for a ESP32 board 40 | // Some boards have a BOOT switch using GPIO 0. 41 | #define PIN_INPUT 0 42 | 43 | #endif 44 | 45 | // Setup a new OneButton on pin PIN_INPUT 46 | // The 2. parameter activeLOW is true, because external wiring sets the button to LOW when pressed. 47 | OneButton button(PIN_INPUT, true); 48 | 49 | // In case the momentary button puts the input to HIGH when pressed: 50 | // The 2. parameter activeLOW is false when the external wiring sets the button to HIGH when pressed. 51 | // The 3. parameter can be used to disable the PullUp . 52 | // OneButton button(PIN_INPUT, false, false); 53 | 54 | // setup code here, to run once: 55 | void setup() 56 | { 57 | Serial.begin(115200); 58 | Serial.println("\nOneButton Example."); 59 | Serial.println("Please press and hold the button for a few seconds."); 60 | 61 | // link functions to be called on events. 62 | button.attachLongPressStart(LongPressStart, &button); 63 | button.attachDuringLongPress(DuringLongPress, &button); 64 | button.attachLongPressStop(LongPressStop, &button); 65 | 66 | button.setLongPressIntervalMs(1000); 67 | } // setup 68 | 69 | 70 | // main code here, to run repeatedly: 71 | void loop() 72 | { 73 | // keep watching the push button: 74 | button.tick(); 75 | 76 | // You can implement other code in here or just wait a while 77 | delay(10); 78 | } // loop 79 | 80 | 81 | // this function will be called when the button started long pressed. 82 | void LongPressStart(void *oneButton) 83 | { 84 | Serial.print(((OneButton *)oneButton)->getPressedMs()); 85 | Serial.println("\t - LongPressStart()"); 86 | } 87 | 88 | // this function will be called when the button is released. 89 | void LongPressStop(void *oneButton) 90 | { 91 | Serial.print(((OneButton *)oneButton)->getPressedMs()); 92 | Serial.println("\t - LongPressStop()\n"); 93 | } 94 | 95 | // this function will be called when the button is held down. 96 | void DuringLongPress(void *oneButton) 97 | { 98 | Serial.print(((OneButton *)oneButton)->getPressedMs()); 99 | Serial.println("\t - DuringLongPress()"); 100 | } 101 | 102 | // End 103 | -------------------------------------------------------------------------------- /examples/SimpleOneButton/SimpleOneButton.ino: -------------------------------------------------------------------------------- 1 | /* 2 | This is a sample sketch to show how to use the OneButtonLibrary 3 | to detect double-click events on a button. 4 | The library internals are explained at 5 | http://www.mathertel.de/Arduino/OneButtonLibrary.aspx 6 | 7 | Setup a test circuit: 8 | * Connect a pushbutton to pin A1 (ButtonPin) and ground. 9 | * The pin 13 (StatusPin) is used for output attach a led and resistor to ground 10 | or see the built-in led on the standard arduino board. 11 | 12 | The sketch shows how to setup the library and bind the functions (singleClick, doubleClick) to the events. 13 | In the loop function the button.tick function must be called as often as you like. 14 | */ 15 | 16 | // 03.03.2011 created by Matthias Hertel 17 | // 01.12.2011 extension changed to work with the Arduino 1.0 environment 18 | 19 | #include "OneButton.h" 20 | 21 | #if defined(ARDUINO_AVR_UNO) || defined(ARDUINO_AVR_NANO_EVERY) 22 | // Example for Arduino UNO with input button on pin 2 and builtin LED on pin 13 23 | #define PIN_INPUT 2 24 | #define PIN_LED 13 25 | 26 | #elif defined(ESP8266) 27 | // Example for NodeMCU with input button using FLASH button on D3 and using the led on -12 module (D4). 28 | // This LED is lighting on output level LOW. 29 | #define PIN_INPUT D3 30 | #define PIN_LED D4 31 | 32 | #elif defined(ESP32) && defined(ARDUINO_NANO_ESP32) 33 | // For Arduino Nano ESP32 use D3 for button 34 | #define PIN_INPUT D3 35 | // For Arduino Nano ESP32 use e.g. LED_RED 36 | #define PIN_LED LED_RED 37 | 38 | #elif defined(ESP32) 39 | // Example pin assignments for a ESP32 board 40 | // Some boards have a BOOT switch using GPIO 0. 41 | #define PIN_INPUT 0 42 | // Attach a LED using GPIO 25 and VCC. The LED is on when output level is LOW. 43 | #define PIN_LED 25 44 | 45 | #endif 46 | 47 | // Setup a new OneButton on pin PIN_INPUT 48 | // The 2. parameter activeLOW is true, because external wiring sets the button to LOW when pressed. 49 | OneButton button; 50 | 51 | // In case the momentary button puts the input to HIGH when pressed: 52 | // The 2. parameter activeLOW is false when the external wiring sets the button to HIGH when pressed. 53 | // The 3. parameter can be used to disable the PullUp . 54 | // OneButton button(PIN_INPUT, false, false); 55 | 56 | // current LED state, staring with LOW (0) 57 | int ledState = LOW; 58 | 59 | // setup code here, to run once: 60 | void setup() 61 | { 62 | Serial.begin(115200); 63 | Serial.println("One Button Example with polling."); 64 | 65 | // enable the standard led on pin PIN_LED. 66 | pinMode(PIN_LED, OUTPUT); // sets the digital pin as output 67 | digitalWrite(PIN_LED, ledState); 68 | 69 | // setup OneButton 70 | button.setup(PIN_INPUT, INPUT_PULLUP, true); 71 | 72 | // link the doubleclick function to be called on a doubleclick event. 73 | button.attachDoubleClick(doubleClick); 74 | } // setup 75 | 76 | 77 | // main code here, to run repeatedly: 78 | void loop() 79 | { 80 | // keep watching the push button: 81 | button.tick(); 82 | 83 | // You can implement other code in here or just wait a while 84 | delay(10); 85 | } // loop 86 | 87 | 88 | // this function will be called when the button was pressed 2 times in a short timeframe. 89 | void doubleClick() 90 | { 91 | Serial.println("x2"); 92 | 93 | ledState = !ledState; // reverse the LED 94 | digitalWrite(PIN_LED, ledState); 95 | } // doubleClick 96 | 97 | // End -------------------------------------------------------------------------------- /examples/SpecialInput/SpecialInput.ino: -------------------------------------------------------------------------------- 1 | /* 2 | * SpecialInput.ino - Example for the OneButtonLibrary library. 3 | * This is a sample sketch to show how to use the OneClick library on other input sources than standard digital pins. 4 | * 5 | * The library internals are explained at 6 | * http://www.mathertel.de/Arduino/OneButtonLibrary.aspx 7 | * 8 | * Setup a test circuit: 9 | * * Connect a pushbutton to pin 2 (ButtonPin) and ground. 10 | * 11 | * The sketch shows how to setup the library and bind the functions (singleClick, doubleClick) to the events. 12 | * In the loop function the button.tick function must be called as often as you like. 13 | * 14 | * * 22.01.2021 created by Matthias Hertel 15 | * * 07.02.2023 ESP32 Support added. 16 | */ 17 | 18 | #include "Arduino.h" 19 | #include "OneButton.h" 20 | 21 | // This is an example on how to use the OneClick library on other input sources than standard digital pins. 22 | // 1. do not use a pin in the initialization of the OneClick library. 23 | // 2. pass the input state to the tick function. 24 | 25 | // You can also find how to create an instance in setup and not by declaration. 26 | // You can also find how to use inline callback functions. 27 | 28 | #if defined(ARDUINO_AVR_UNO) || defined(ARDUINO_AVR_NANO_EVERY) 29 | #define PIN_INPUT 2 30 | 31 | #elif defined(ESP8266) 32 | #define PIN_INPUT D3 33 | 34 | #elif defined(ESP32) 35 | // Example pin assignments for a ESP32 board 36 | // Some boards have a BOOT switch using GPIO 0. 37 | #define PIN_INPUT 0 38 | 39 | #endif 40 | 41 | 42 | // OneButton instance will be created in setup. 43 | OneButton *button; 44 | 45 | void fClicked(void *s) { 46 | Serial.print("Click:"); 47 | Serial.println((char *)s); 48 | } 49 | 50 | static void fDoubleClicked(void *oneButton) { 51 | OneButton *button = (OneButton *)oneButton; 52 | Serial.print("pin="); 53 | Serial.print(button->pin()); 54 | Serial.print(" state="); 55 | Serial.println(button->state()); 56 | } 57 | 58 | void setup() { 59 | Serial.begin(115200); 60 | Serial.println("One Button Example with custom input."); 61 | 62 | // create the OneButton instance without a pin. 63 | button = new OneButton(); 64 | 65 | // Here is an example on how to use a parameter to the registered functions: 66 | button->attachClick(fClicked, (void *)"me"); 67 | button->attachDoubleClick(fDoubleClicked, &button); 68 | 69 | // Here is an example on how to use an inline function: 70 | button->attachDoubleClick([]() { 71 | Serial.println("DoubleClick"); 72 | }); 73 | 74 | // setup your own source of input: 75 | pinMode(PIN_INPUT, INPUT_PULLUP); 76 | 77 | } // setup() 78 | 79 | void loop() { 80 | // read your own source of input: 81 | bool isPressed = (digitalRead(PIN_INPUT) == LOW); 82 | 83 | // call tick frequently with current push-state of the input 84 | button->tick(isPressed); 85 | } // loop() 86 | -------------------------------------------------------------------------------- /examples/TwoButtons/TwoButtons.ino: -------------------------------------------------------------------------------- 1 | /* 2 | This is a sample sketch to show how to use the OneButtonLibrary 3 | to detect click events on 2 buttons in parallel. 4 | The library internals are explained at 5 | http://www.mathertel.de/Arduino/OneButtonLibrary.aspx 6 | 7 | Setup a test circuit: 8 | * Connect a pushbutton to pin A1 (ButtonPin) and ground. 9 | * Connect a pushbutton to pin A2 (ButtonPin) and ground. 10 | * The Serial interface is used for output the detected button events. 11 | 12 | The Sketch shows how to setup the library and bind 2 buttons to their functions. 13 | In the loop function the button1.tick and button2.tick functions have to be called as often as you like. 14 | */ 15 | 16 | // 01.03.2014 created by Matthias Hertel 17 | // ... and working. 18 | 19 | /* Sample output: 20 | 21 | Starting TwoButtons... 22 | Button 1 click. 23 | Button 2 click. 24 | Button 1 doubleclick. 25 | Button 2 doubleclick. 26 | Button 1 longPress start 27 | Button 1 longPress... 28 | Button 1 longPress... 29 | Button 1 longPress... 30 | Button 1 longPress stop 31 | Button 2 longPress start 32 | Button 2 longPress... 33 | Button 2 longPress... 34 | Button 2 longPress stop 35 | 36 | */ 37 | 38 | #include "OneButton.h" 39 | 40 | #if defined(ARDUINO_AVR_UNO) || defined(ARDUINO_AVR_NANO_EVERY) 41 | #define PIN_INPUT1 A1 42 | #define PIN_INPUT2 A2 43 | 44 | #elif defined(ESP8266) 45 | #define PIN_INPUT1 D3 46 | #define PIN_INPUT2 D4 47 | 48 | #elif defined(ESP32) 49 | #define PIN_INPUT1 32 50 | #define PIN_INPUT2 33 51 | 52 | #endif 53 | 54 | // Setup a new OneButton on pin PIN_INPUT2. 55 | OneButton button1(PIN_INPUT1, true); 56 | // Setup a new OneButton on pin PIN_INPUT2. 57 | OneButton button2(PIN_INPUT2, true); 58 | 59 | 60 | // setup code here, to run once: 61 | void setup() { 62 | // Setup the Serial port. see http://arduino.cc/en/Serial/IfSerial 63 | Serial.begin(115200); 64 | while (!Serial) { 65 | ; // wait for serial port to connect. Needed for Leonardo only 66 | } 67 | Serial.println("Starting TwoButtons..."); 68 | 69 | // link the button 1 functions. 70 | button1.attachClick(click1); 71 | button1.attachDoubleClick(doubleclick1); 72 | button1.attachLongPressStart(longPressStart1); 73 | button1.attachLongPressStop(longPressStop1); 74 | button1.attachDuringLongPress(longPress1); 75 | 76 | // link the button 2 functions. 77 | button2.attachClick(click2); 78 | button2.attachDoubleClick(doubleclick2); 79 | button2.attachLongPressStart(longPressStart2); 80 | button2.attachLongPressStop(longPressStop2); 81 | button2.attachDuringLongPress(longPress2); 82 | 83 | } // setup 84 | 85 | 86 | // main code here, to run repeatedly: 87 | void loop() { 88 | // keep watching the push buttons: 89 | button1.tick(); 90 | button2.tick(); 91 | 92 | // You can implement other code in here or just wait a while 93 | delay(10); 94 | } // loop 95 | 96 | 97 | // ----- button 1 callback functions 98 | 99 | // This function will be called when the button1 was pressed 1 time (and no 2. button press followed). 100 | void click1() { 101 | Serial.println("Button 1 click."); 102 | } // click1 103 | 104 | 105 | // This function will be called when the button1 was pressed 2 times in a short timeframe. 106 | void doubleclick1() { 107 | Serial.println("Button 1 doubleclick."); 108 | } // doubleclick1 109 | 110 | 111 | // This function will be called once, when the button1 is pressed for a long time. 112 | void longPressStart1() { 113 | Serial.println("Button 1 longPress start"); 114 | } // longPressStart1 115 | 116 | 117 | // This function will be called often, while the button1 is pressed for a long time. 118 | void longPress1() { 119 | Serial.println("Button 1 longPress..."); 120 | } // longPress1 121 | 122 | 123 | // This function will be called once, when the button1 is released after beeing pressed for a long time. 124 | void longPressStop1() { 125 | Serial.println("Button 1 longPress stop"); 126 | } // longPressStop1 127 | 128 | 129 | // ... and the same for button 2: 130 | 131 | void click2() { 132 | Serial.println("Button 2 click."); 133 | } // click2 134 | 135 | 136 | void doubleclick2() { 137 | Serial.println("Button 2 doubleclick."); 138 | } // doubleclick2 139 | 140 | 141 | void longPressStart2() { 142 | Serial.println("Button 2 longPress start"); 143 | } // longPressStart2 144 | 145 | 146 | void longPress2() { 147 | Serial.println("Button 2 longPress..."); 148 | } // longPress2 149 | 150 | void longPressStop2() { 151 | Serial.println("Button 2 longPress stop"); 152 | } // longPressStop2 153 | 154 | 155 | // End 156 | -------------------------------------------------------------------------------- /keywords.txt: -------------------------------------------------------------------------------- 1 | ####################################### 2 | # Syntax Coloring Map for OneButton 3 | ####################################### 4 | 5 | ####################################### 6 | # Datatypes (KEYWORD1) 7 | ####################################### 8 | 9 | callbackFunction KEYWORD1 10 | parameterizedCallbackFunction KEYWORD1 11 | 12 | ####################################### 13 | # Methods and Functions (KEYWORD2) 14 | ####################################### 15 | 16 | setClickTicks KEYWORD2 17 | setPressTicks KEYWORD2 18 | setDebounceTicks KEYWORD2 19 | attachClick KEYWORD2 20 | attachDoubleClick KEYWORD2 21 | attachMultiClick KEYWORD2 22 | attachLongPressStart KEYWORD2 23 | attachLongPressStop KEYWORD2 24 | attachDuringLongPress KEYWORD2 25 | tick KEYWORD2 26 | reset KEYWORD2 27 | getNumberClicks KEYWORD2 28 | isIdle KEYWORD2 29 | isLongPressed KEYWORD2 30 | 31 | ####################################### 32 | # Instances (KEYWORD2) 33 | ####################################### 34 | 35 | OneButton KEYWORD2 36 | 37 | ####################################### 38 | # Constants (LITERAL1) 39 | ####################################### 40 | 41 | 42 | -------------------------------------------------------------------------------- /library.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "OneButton", 3 | "version": "2.6.1", 4 | "keywords": "arduino, button, pushbutton", 5 | "description": "This Arduino library is improving the usage of a singe button for input. It shows how to use an digital input pin with a single pushbutton attached for detecting some of the typical button press events like single clicks, double clicks and long-time pressing. This enables you to reuse the same button for multiple functions and lowers the hardware invests.", 6 | "repository": { 7 | "type": "git", 8 | "url": "https://github.com/mathertel/OneButton" 9 | }, 10 | "authors": { 11 | "name": "Matthias Hertel", 12 | "email": "mathertel@hotmail.com", 13 | "url": "https://www.mathertel.de/impressum.aspx" 14 | }, 15 | "license": "BSD-3-Clause", 16 | "homepage": "http://www.mathertel.de/Arduino/OneButtonLibrary.aspx", 17 | "frameworks": "arduino", 18 | "platforms": "*" 19 | } 20 | -------------------------------------------------------------------------------- /library.properties: -------------------------------------------------------------------------------- 1 | name=OneButton 2 | version=2.6.1 3 | author=Matthias Hertel 4 | maintainer=Matthias Hertel, https://www.mathertel.de 5 | sentence=Arduino library for improving the usage of a singe input button. 6 | paragraph=It supports detecting events like single, double, multiple clicks and long-time pressing. This enables you to reuse the same button for multiple functions and lowers the hardware invests. 7 | category=Signal Input/Output 8 | url=https://github.com/mathertel/OneButton 9 | architectures=* 10 | includes=OneButton.h 11 | license=BSD-3-Clause 12 | -------------------------------------------------------------------------------- /run_cppcheck.sh: -------------------------------------------------------------------------------- 1 | cppcheck --enable=all --suppressions-list=suppressions.txt --language=c++ -I./src --error-exitcode=2 ./src/* 2 | -------------------------------------------------------------------------------- /src/OneButton.cpp: -------------------------------------------------------------------------------- 1 | /** 2 | * @file OneButton.cpp 3 | * 4 | * @brief Library for detecting button clicks, doubleclicks and long press 5 | * pattern on a single button. 6 | * 7 | * @author Matthias Hertel, https://www.mathertel.de 8 | * @Copyright Copyright (c) by Matthias Hertel, https://www.mathertel.de. 9 | * Ihor Nehrutsa, Ihor.Nehrutsa@gmail.com 10 | * 11 | * This work is licensed under a BSD style license. See 12 | * http://www.mathertel.de/License.aspx 13 | * 14 | * More information on: https://www.mathertel.de/Arduino/OneButtonLibrary.aspx 15 | * 16 | * Changelog: see OneButton.h 17 | */ 18 | 19 | #include "OneButton.h" 20 | 21 | // ----- Initialization and Default Values ----- 22 | 23 | /** 24 | * @brief Construct a new OneButton object but not (yet) initialize the IO pin. 25 | */ 26 | OneButton::OneButton() { 27 | _pin = -1; 28 | // further initialization has moved to OneButton.h 29 | } 30 | 31 | // Initialize the OneButton library. 32 | OneButton::OneButton(const int pin, const bool activeLow, const bool pullupActive) { 33 | setup(pin, pullupActive ? INPUT_PULLUP : INPUT, activeLow); 34 | } // OneButton 35 | 36 | 37 | // initialize or re-initialize the input pin 38 | void OneButton::setup(const uint8_t pin, const uint8_t mode, const bool activeLow) { 39 | _pin = pin; 40 | 41 | if (activeLow) { 42 | // the button connects the input pin to GND when pressed. 43 | _buttonPressed = LOW; 44 | 45 | } else { 46 | // the button connects the input pin to VCC when pressed. 47 | _buttonPressed = HIGH; 48 | } 49 | 50 | pinMode(pin, mode); 51 | } 52 | 53 | 54 | // explicitly set the number of millisec that have to pass by before a click is assumed stable. 55 | void OneButton::setDebounceMs(const int ms) { 56 | _debounce_ms = ms; 57 | } // setDebounceMs 58 | 59 | 60 | // explicitly set the number of millisec that have to pass by before a click is detected. 61 | void OneButton::setClickMs(const unsigned int ms) { 62 | _click_ms = ms; 63 | } // setClickMs 64 | 65 | 66 | // explicitly set the number of millisec that have to pass by before a long button press is detected. 67 | void OneButton::setPressMs(const unsigned int ms) { 68 | _press_ms = ms; 69 | } // setPressMs 70 | 71 | // explicitly set the number of millisec that have to pass by before button idle is detected. 72 | void OneButton::setIdleMs(const unsigned int ms) { 73 | _idle_ms = ms; 74 | } // setIdleMs 75 | 76 | // save function for click event 77 | void OneButton::attachPress(callbackFunction newFunction) { 78 | _pressFunc = newFunction; 79 | } // attachPress 80 | 81 | 82 | // save function for parameterized click event 83 | void OneButton::attachPress(parameterizedCallbackFunction newFunction, void *parameter) { 84 | _paramPressFunc = newFunction; 85 | _pressFuncParam = parameter; 86 | } // attachPress 87 | 88 | // save function for click event 89 | void OneButton::attachClick(callbackFunction newFunction) { 90 | _clickFunc = newFunction; 91 | } // attachClick 92 | 93 | 94 | // save function for parameterized click event 95 | void OneButton::attachClick(parameterizedCallbackFunction newFunction, void *parameter) { 96 | _paramClickFunc = newFunction; 97 | _clickFuncParam = parameter; 98 | } // attachClick 99 | 100 | 101 | // save function for doubleClick event 102 | void OneButton::attachDoubleClick(callbackFunction newFunction) { 103 | _doubleClickFunc = newFunction; 104 | _maxClicks = max(_maxClicks, 2); 105 | } // attachDoubleClick 106 | 107 | 108 | // save function for parameterized doubleClick event 109 | void OneButton::attachDoubleClick(parameterizedCallbackFunction newFunction, void *parameter) { 110 | _paramDoubleClickFunc = newFunction; 111 | _doubleClickFuncParam = parameter; 112 | _maxClicks = max(_maxClicks, 2); 113 | } // attachDoubleClick 114 | 115 | 116 | // save function for multiClick event 117 | void OneButton::attachMultiClick(callbackFunction newFunction) { 118 | _multiClickFunc = newFunction; 119 | _maxClicks = max(_maxClicks, 100); 120 | } // attachMultiClick 121 | 122 | 123 | // save function for parameterized MultiClick event 124 | void OneButton::attachMultiClick(parameterizedCallbackFunction newFunction, void *parameter) { 125 | _paramMultiClickFunc = newFunction; 126 | _multiClickFuncParam = parameter; 127 | _maxClicks = max(_maxClicks, 100); 128 | } // attachMultiClick 129 | 130 | 131 | // save function for longPressStart event 132 | void OneButton::attachLongPressStart(callbackFunction newFunction) { 133 | _longPressStartFunc = newFunction; 134 | } // attachLongPressStart 135 | 136 | 137 | // save function for parameterized longPressStart event 138 | void OneButton::attachLongPressStart(parameterizedCallbackFunction newFunction, void *parameter) { 139 | _paramLongPressStartFunc = newFunction; 140 | _longPressStartFuncParam = parameter; 141 | } // attachLongPressStart 142 | 143 | 144 | // save function for longPressStop event 145 | void OneButton::attachLongPressStop(callbackFunction newFunction) { 146 | _longPressStopFunc = newFunction; 147 | } // attachLongPressStop 148 | 149 | 150 | // save function for parameterized longPressStop event 151 | void OneButton::attachLongPressStop(parameterizedCallbackFunction newFunction, void *parameter) { 152 | _paramLongPressStopFunc = newFunction; 153 | _longPressStopFuncParam = parameter; 154 | } // attachLongPressStop 155 | 156 | 157 | // save function for during longPress event 158 | void OneButton::attachDuringLongPress(callbackFunction newFunction) { 159 | _duringLongPressFunc = newFunction; 160 | } // attachDuringLongPress 161 | 162 | 163 | // save function for parameterized during longPress event 164 | void OneButton::attachDuringLongPress(parameterizedCallbackFunction newFunction, void *parameter) { 165 | _paramDuringLongPressFunc = newFunction; 166 | _duringLongPressFuncParam = parameter; 167 | } // attachDuringLongPress 168 | 169 | 170 | // save function for idle button event 171 | void OneButton::attachIdle(callbackFunction newFunction) { 172 | _idleFunc = newFunction; 173 | } // attachIdle 174 | 175 | 176 | void OneButton::reset(void) { 177 | _state = OneButton::OCS_INIT; 178 | _nClicks = 0; 179 | _startTime = millis(); 180 | _idleState = false; 181 | } 182 | 183 | 184 | // ShaggyDog ---- return number of clicks in any case: single or multiple clicks 185 | int OneButton::getNumberClicks(void) { 186 | return _nClicks; 187 | } 188 | 189 | 190 | /** 191 | * @brief Debounce input pin level for use in SpesialInput. 192 | */ 193 | bool OneButton::debounce(const bool value) { 194 | now = millis(); // current (relative) time in msecs. 195 | 196 | // Don't debounce going into active state, if _debounce_ms is negative 197 | if (value && _debounce_ms < 0) 198 | debouncedLevel = value; 199 | 200 | if (_lastDebounceLevel == value) { 201 | if (now - _lastDebounceTime >= abs(_debounce_ms)) 202 | debouncedLevel = value; 203 | } else { 204 | _lastDebounceTime = now; 205 | _lastDebounceLevel = value; 206 | } 207 | return debouncedLevel; 208 | }; 209 | 210 | 211 | /** 212 | * @brief Check input of the configured pin, 213 | * debounce button state and then 214 | * advance the finite state machine (FSM). 215 | */ 216 | void OneButton::tick(void) { 217 | if (_pin >= 0) { 218 | _fsm(debounce(digitalRead(_pin) == _buttonPressed)); 219 | } 220 | } // tick() 221 | 222 | 223 | void OneButton::tick(bool activeLevel) { 224 | _fsm(debounce(activeLevel)); 225 | } 226 | 227 | 228 | /** 229 | * @brief Advance to a new state and save the last one to come back in cas of bouncing detection. 230 | */ 231 | void OneButton::_newState(stateMachine_t nextState) { 232 | _state = nextState; 233 | } // _newState() 234 | 235 | 236 | /** 237 | * @brief Run the finite state machine (FSM) using the given level. 238 | */ 239 | void OneButton::_fsm(bool activeLevel) { 240 | unsigned long waitTime = (now - _startTime); 241 | 242 | // Implementation of the state machine 243 | switch (_state) { 244 | case OneButton::OCS_INIT: 245 | // on idle for idle_ms call idle function 246 | if (!_idleState and (waitTime > _idle_ms)) 247 | if (_idleFunc) { 248 | _idleState = true; 249 | _idleFunc(); 250 | } 251 | 252 | // waiting for level to become active. 253 | if (activeLevel) { 254 | _newState(OneButton::OCS_DOWN); 255 | _startTime = now; // remember starting time 256 | _nClicks = 0; 257 | 258 | if (_pressFunc) _pressFunc(); 259 | if (_paramPressFunc) _paramPressFunc(_pressFuncParam); 260 | } // if 261 | break; 262 | 263 | case OneButton::OCS_DOWN: 264 | // waiting for level to become inactive. 265 | 266 | if (!activeLevel) { 267 | _newState(OneButton::OCS_UP); 268 | _startTime = now; // remember starting time 269 | 270 | } else if (waitTime > _press_ms) { 271 | if (_longPressStartFunc) _longPressStartFunc(); 272 | if (_paramLongPressStartFunc) _paramLongPressStartFunc(_longPressStartFuncParam); 273 | _newState(OneButton::OCS_PRESS); 274 | } // if 275 | break; 276 | 277 | case OneButton::OCS_UP: 278 | // level is inactive 279 | 280 | // count as a short button down 281 | _nClicks++; 282 | _newState(OneButton::OCS_COUNT); 283 | break; 284 | 285 | case OneButton::OCS_COUNT: 286 | // dobounce time is over, count clicks 287 | 288 | if (activeLevel) { 289 | // button is down again 290 | _newState(OneButton::OCS_DOWN); 291 | _startTime = now; // remember starting time 292 | 293 | } else if ((waitTime >= _click_ms) || (_nClicks == _maxClicks)) { 294 | // now we know how many clicks have been made. 295 | 296 | if (_nClicks == 1) { 297 | // this was 1 click only. 298 | if (_clickFunc) _clickFunc(); 299 | if (_paramClickFunc) _paramClickFunc(_clickFuncParam); 300 | 301 | } else if (_nClicks == 2) { 302 | // this was a 2 click sequence. 303 | if (_doubleClickFunc) _doubleClickFunc(); 304 | if (_paramDoubleClickFunc) _paramDoubleClickFunc(_doubleClickFuncParam); 305 | 306 | } else { 307 | // this was a multi click sequence. 308 | if (_multiClickFunc) _multiClickFunc(); 309 | if (_paramMultiClickFunc) _paramMultiClickFunc(_multiClickFuncParam); 310 | } // if 311 | 312 | reset(); 313 | } // if 314 | break; 315 | 316 | case OneButton::OCS_PRESS: 317 | // waiting for pin being release after long press. 318 | 319 | if (!activeLevel) { 320 | _newState(OneButton::OCS_PRESSEND); 321 | 322 | } else { 323 | // still the button is pressed 324 | if ((now - _lastDuringLongPressTime) >= _long_press_interval_ms) { 325 | if (_duringLongPressFunc) _duringLongPressFunc(); 326 | if (_paramDuringLongPressFunc) _paramDuringLongPressFunc(_duringLongPressFuncParam); 327 | _lastDuringLongPressTime = now; 328 | } 329 | } // if 330 | break; 331 | 332 | case OneButton::OCS_PRESSEND: 333 | // button was released. 334 | 335 | if (_longPressStopFunc) _longPressStopFunc(); 336 | if (_paramLongPressStopFunc) _paramLongPressStopFunc(_longPressStopFuncParam); 337 | reset(); 338 | break; 339 | 340 | default: 341 | // unknown state detected -> reset state machine 342 | _newState(OneButton::OCS_INIT); 343 | break; 344 | } // if 345 | 346 | } // OneButton.tick() 347 | 348 | 349 | // end. 350 | -------------------------------------------------------------------------------- /src/OneButton.h: -------------------------------------------------------------------------------- 1 | // ----- 2 | // OneButton.h - Library for detecting button clicks, doubleclicks and long 3 | // press pattern on a single button. This class is implemented for use with the 4 | // Arduino environment. Copyright (c) by Matthias Hertel, 5 | // http://www.mathertel.de This work is licensed under a BSD style license. See 6 | // http://www.mathertel.de/License.aspx More information on: 7 | // http://www.mathertel.de/Arduino 8 | // ----- 9 | // 02.10.2010 created by Matthias Hertel 10 | // 21.04.2011 transformed into a library 11 | // 01.12.2011 include file changed to work with the Arduino 1.0 environment 12 | // 23.03.2014 Enhanced long press functionalities by adding longPressStart and 13 | // longPressStop callbacks 14 | // 21.09.2015 A simple way for debounce detection added. 15 | // 14.05.2017 Debouncing improvements. 16 | // 25.06.2018 Optional third parameter for deactivating pullup. 17 | // 26.09.2018 Anatoli Arkhipenko: Included solution to use library with other 18 | // sources of input. 19 | // 26.09.2018 Initialization moved into class declaration. 20 | // 26.09.2018 Jay M Ericsson: compiler warnings removed. 21 | // 29.01.2020 improvements from ShaggyDog18 22 | // 07.05.2023 Debouncing in one point. #118 23 | // ----- 24 | 25 | #ifndef OneButton_h 26 | #define OneButton_h 27 | 28 | #include "Arduino.h" 29 | 30 | // ----- Callback function types ----- 31 | 32 | extern "C" { 33 | typedef void (*callbackFunction)(void); 34 | typedef void (*parameterizedCallbackFunction)(void *); 35 | } 36 | 37 | 38 | class OneButton { 39 | public: 40 | // ----- Constructor ----- 41 | 42 | /* 43 | * Create a OneButton instance. 44 | * use setup(...) to specify the hardware configuration. 45 | */ 46 | OneButton(); 47 | 48 | /** 49 | * Create a OneButton instance and setup. 50 | * @param pin The pin to be used for input from a momentary button. 51 | * @param activeLow Set to true when the input level is LOW when the button is pressed, Default is true. 52 | * @param pullupActive Activate the internal pullup when available. Default is true. 53 | */ 54 | explicit OneButton(const int pin, const bool activeLow = true, const bool pullupActive = true); 55 | 56 | // ----- Set runtime parameters ----- 57 | 58 | 59 | /** 60 | * Initialize or re-initialize the input pin. 61 | * @param pin The pin to be used for input from a momentary button. 62 | * @param mode Any of the modes also used in pinMode like INPUT or INPUT_PULLUP (default). 63 | * @param activeLow Set to true when the input level is LOW when the button is pressed, Default is true. 64 | */ 65 | void setup(const uint8_t pin, const uint8_t mode = INPUT_PULLUP, const bool activeLow = true); 66 | 67 | 68 | /** 69 | * set # millisec after safe click is assumed. 70 | */ 71 | [[deprecated("Use setDebounceMs() instead.")]] 72 | void setDebounceTicks(const unsigned int ms) { 73 | setDebounceMs(ms); 74 | }; // deprecated 75 | void setDebounceMs(const int ms); 76 | 77 | /** 78 | * set # millisec after single click is assumed. 79 | */ 80 | [[deprecated("Use setClickMs() instead.")]] 81 | void setClickTicks(const unsigned int ms) { 82 | setClickMs(ms); 83 | }; // deprecated 84 | void setClickMs(const unsigned int ms); 85 | 86 | /** 87 | * set # millisec after press is assumed. 88 | */ 89 | [[deprecated("Use setPressMs() instead.")]] 90 | void setPressTicks(const unsigned int ms) { 91 | setPressMs(ms); 92 | }; // deprecated 93 | void setPressMs(const unsigned int ms); 94 | 95 | /** 96 | * set interval in msecs between calls of the DuringLongPress event. 97 | * 0 ms is the fastest events calls. 98 | */ 99 | void setLongPressIntervalMs(const unsigned int ms) { 100 | _long_press_interval_ms = ms; 101 | }; 102 | 103 | /** 104 | * set # millisec after idle is assumed. 105 | */ 106 | void setIdleMs(const unsigned int ms); 107 | 108 | // ----- Attach events functions ----- 109 | 110 | /** 111 | * Attach an event to be called immediately when a depress is detected. 112 | * @param newFunction This function will be called when the event has been detected. 113 | */ 114 | void attachPress(callbackFunction newFunction); 115 | void attachPress(parameterizedCallbackFunction newFunction, void *parameter); 116 | 117 | /** 118 | * Attach an event to be called when a single click is detected. 119 | * @param newFunction This function will be called when the event has been detected. 120 | */ 121 | void attachClick(callbackFunction newFunction); 122 | void attachClick(parameterizedCallbackFunction newFunction, void *parameter); 123 | 124 | /** 125 | * Attach an event to be called after a double click is detected. 126 | * @param newFunction This function will be called when the event has been detected. 127 | */ 128 | void attachDoubleClick(callbackFunction newFunction); 129 | void attachDoubleClick(parameterizedCallbackFunction newFunction, void *parameter); 130 | 131 | /** 132 | * Attach an event to be called after a multi click is detected. 133 | * @param newFunction This function will be called when the event has been detected. 134 | */ 135 | void attachMultiClick(callbackFunction newFunction); 136 | void attachMultiClick(parameterizedCallbackFunction newFunction, void *parameter); 137 | 138 | /** 139 | * Attach an event to fire when the button is pressed and held down. 140 | * @param newFunction 141 | */ 142 | void attachLongPressStart(callbackFunction newFunction); 143 | void attachLongPressStart(parameterizedCallbackFunction newFunction, void *parameter); 144 | 145 | /** 146 | * Attach an event to fire as soon as the button is released after a long press. 147 | * @param newFunction 148 | */ 149 | void attachLongPressStop(callbackFunction newFunction); 150 | void attachLongPressStop(parameterizedCallbackFunction newFunction, void *parameter); 151 | 152 | /** 153 | * Attach an event to fire periodically while the button is held down. 154 | * The period of calls is set by setLongPressIntervalMs(ms). 155 | * @param newFunction 156 | */ 157 | void attachDuringLongPress(callbackFunction newFunction); 158 | void attachDuringLongPress(parameterizedCallbackFunction newFunction, void *parameter); 159 | 160 | /** 161 | * Attach an event when the button is in idle position. 162 | * @param newFunction 163 | */ 164 | void attachIdle(callbackFunction newFunction); 165 | 166 | // ----- State machine functions ----- 167 | 168 | /** 169 | * @brief Call this function every some milliseconds for checking the input 170 | * level at the initialized digital pin. 171 | */ 172 | void tick(void); 173 | 174 | /** 175 | * @brief Call this function every time the input level has changed. 176 | * Using this function no digital input pin is checked because the current 177 | * level is given by the parameter. 178 | * Run the finite state machine (FSM) using the given level. 179 | */ 180 | void tick(bool activeLevel); 181 | 182 | 183 | /** 184 | * Reset the button state machine. 185 | */ 186 | void reset(void); 187 | 188 | 189 | /* 190 | * return number of clicks in any case: single or multiple clicks 191 | */ 192 | int getNumberClicks(void); 193 | 194 | 195 | /** 196 | * @return true if we are currently handling button press flow 197 | * (This allows power sensitive applications to know when it is safe to power down the main CPU) 198 | */ 199 | bool isIdle() const { 200 | return _state == OCS_INIT; 201 | } 202 | 203 | /** 204 | * @return true when a long press is detected 205 | */ 206 | bool isLongPressed() const { 207 | return _state == OCS_PRESS; 208 | }; 209 | 210 | 211 | private: 212 | int _pin = -1; // hardware pin number. 213 | int _debounce_ms = 50; // number of msecs for debounce times. 214 | unsigned int _click_ms = 400; // number of msecs before a click is detected. 215 | unsigned int _press_ms = 800; // number of msecs before a long button press is detected 216 | unsigned int _idle_ms = 1000; // number of msecs before idle is detected 217 | 218 | int _buttonPressed = 0; // this is the level of the input pin when the button is pressed. 219 | // LOW if the button connects the input pin to GND when pressed. 220 | // HIGH if the button connects the input pin to VCC when pressed. 221 | 222 | // These variables will hold functions acting as event source. 223 | callbackFunction _pressFunc = NULL; 224 | parameterizedCallbackFunction _paramPressFunc = NULL; 225 | void *_pressFuncParam = NULL; 226 | 227 | callbackFunction _clickFunc = NULL; 228 | parameterizedCallbackFunction _paramClickFunc = NULL; 229 | void *_clickFuncParam = NULL; 230 | 231 | callbackFunction _doubleClickFunc = NULL; 232 | parameterizedCallbackFunction _paramDoubleClickFunc = NULL; 233 | void *_doubleClickFuncParam = NULL; 234 | 235 | callbackFunction _multiClickFunc = NULL; 236 | parameterizedCallbackFunction _paramMultiClickFunc = NULL; 237 | void *_multiClickFuncParam = NULL; 238 | 239 | callbackFunction _longPressStartFunc = NULL; 240 | parameterizedCallbackFunction _paramLongPressStartFunc = NULL; 241 | void *_longPressStartFuncParam = NULL; 242 | 243 | callbackFunction _longPressStopFunc = NULL; 244 | parameterizedCallbackFunction _paramLongPressStopFunc = NULL; 245 | void *_longPressStopFuncParam = NULL; 246 | 247 | callbackFunction _duringLongPressFunc = NULL; 248 | parameterizedCallbackFunction _paramDuringLongPressFunc = NULL; 249 | void *_duringLongPressFuncParam = NULL; 250 | 251 | callbackFunction _idleFunc = NULL; 252 | 253 | // These variables that hold information across the upcoming tick calls. 254 | // They are initialized once on program start and are updated every time the 255 | // tick function is called. 256 | 257 | // define FiniteStateMachine 258 | enum stateMachine_t : int { 259 | OCS_INIT = 0, 260 | OCS_DOWN = 1, // button is down 261 | OCS_UP = 2, // button is up 262 | OCS_COUNT = 3, // in multi press-mode, counting 263 | OCS_PRESS = 6, // button is hold down 264 | OCS_PRESSEND = 7, 265 | }; 266 | 267 | /** 268 | * Run the finite state machine (FSM) using the given level. 269 | */ 270 | void _fsm(bool activeLevel); 271 | 272 | /** 273 | * Advance to a new state. 274 | */ 275 | void _newState(stateMachine_t nextState); 276 | 277 | stateMachine_t _state = OCS_INIT; 278 | 279 | bool _idleState = false; 280 | 281 | bool debouncedLevel = false; 282 | bool _lastDebounceLevel = false; // used for pin debouncing 283 | unsigned long _lastDebounceTime = 0; // millis() 284 | unsigned long now = 0; // millis() 285 | 286 | unsigned long _startTime = 0; // start time of current activeLevel change 287 | int _nClicks = 0; // count the number of clicks with this variable 288 | int _maxClicks = 1; // max number (1, 2, multi=3) of clicks of interest by registration of event functions. 289 | 290 | unsigned int _long_press_interval_ms = 0; // interval in msecs between calls of the DuringLongPress event 291 | unsigned long _lastDuringLongPressTime = 0; // used to produce the DuringLongPress interval 292 | 293 | public: 294 | int pin() const { 295 | return _pin; 296 | }; 297 | stateMachine_t state() const { 298 | return _state; 299 | }; 300 | bool debounce(const bool value); 301 | int debouncedValue() const { 302 | return debouncedLevel; 303 | }; 304 | 305 | /** 306 | * @brief Use this function in the DuringLongPress and LongPressStop events to get the time since the button was pressed. 307 | * @return milliseconds from the start of the button press. 308 | */ 309 | unsigned long getPressedMs() { 310 | return (millis() - _startTime); 311 | }; 312 | }; 313 | 314 | #endif 315 | -------------------------------------------------------------------------------- /src/OneButtonTiny.cpp: -------------------------------------------------------------------------------- 1 | /** 2 | * @file OneButton.cpp 3 | * 4 | * @brief Library for detecting button clicks, doubleclicks and long press 5 | * pattern on a single button. 6 | * 7 | * @author Matthias Hertel, https://www.mathertel.de 8 | * @Copyright Copyright (c) by Matthias Hertel, https://www.mathertel.de. 9 | * Ihor Nehrutsa, Ihor.Nehrutsa@gmail.com 10 | * 11 | * This work is licensed under a BSD style license. See 12 | * http://www.mathertel.de/License.aspx 13 | * 14 | * More information on: https://www.mathertel.de/Arduino/OneButtonLibrary.aspx 15 | * 16 | * Changelog: see OneButtonTiny.h 17 | */ 18 | 19 | #include "OneButtonTiny.h" 20 | 21 | // ----- Initialization and Default Values ----- 22 | 23 | /** 24 | * Initialize the OneButton library. 25 | * @param pin The pin to be used for input from a momentary button. 26 | * @param activeLow Set to true when the input level is LOW when the button is pressed, Default is true. 27 | * @param pullupActive Activate the internal pullup when available. Default is true. 28 | */ 29 | OneButtonTiny::OneButtonTiny(const int pin, const bool activeLow, const bool pullupActive) { 30 | _pin = pin; 31 | 32 | if (activeLow) { 33 | // the button connects the input pin to GND when pressed. 34 | _buttonPressed = LOW; 35 | 36 | } else { 37 | // the button connects the input pin to VCC when pressed. 38 | _buttonPressed = HIGH; 39 | } 40 | 41 | if (pullupActive) { 42 | // use the given pin as input and activate internal PULLUP resistor. 43 | pinMode(pin, INPUT_PULLUP); 44 | } else { 45 | // use the given pin as input 46 | pinMode(pin, INPUT); 47 | } 48 | } // OneButton 49 | 50 | 51 | // explicitly set the number of millisec that have to pass by before a click is assumed stable. 52 | void OneButtonTiny::setDebounceMs(const unsigned int ms) { 53 | _debounce_ms = ms; 54 | } // setDebounceMs 55 | 56 | 57 | // explicitly set the number of millisec that have to pass by before a click is detected. 58 | void OneButtonTiny::setClickMs(const unsigned int ms) { 59 | _click_ms = ms; 60 | } // setClickMs 61 | 62 | 63 | // explicitly set the number of millisec that have to pass by before a long button press is detected. 64 | void OneButtonTiny::setPressMs(const unsigned int ms) { 65 | _press_ms = ms; 66 | } // setPressMs 67 | 68 | 69 | // save function for click event 70 | void OneButtonTiny::attachClick(callbackFunction newFunction) { 71 | _clickFunc = newFunction; 72 | } // attachClick 73 | 74 | 75 | // save function for doubleClick event 76 | void OneButtonTiny::attachDoubleClick(callbackFunction newFunction) { 77 | _doubleClickFunc = newFunction; 78 | } // attachDoubleClick 79 | 80 | 81 | // save function for longPressStart event 82 | void OneButtonTiny::attachLongPressStart(callbackFunction newFunction) { 83 | _longPressStartFunc = newFunction; 84 | } // attachLongPressStart 85 | 86 | 87 | void OneButtonTiny::reset(void) { 88 | _state = OneButtonTiny::OCS_INIT; 89 | _nClicks = 0; 90 | _startTime = 0; 91 | } 92 | 93 | 94 | /** 95 | * @brief Debounce input pin level for use in SpecialInput. 96 | */ 97 | int OneButtonTiny::debounce(const int value) { 98 | now = millis(); // current (relative) time in msecs. 99 | if (_lastDebouncePinLevel == value) { 100 | if (now - _lastDebounceTime >= _debounce_ms) 101 | debouncedPinLevel = value; 102 | } else { 103 | _lastDebounceTime = now; 104 | _lastDebouncePinLevel = value; 105 | } 106 | return debouncedPinLevel; 107 | }; 108 | 109 | 110 | /** 111 | * @brief Check input of the configured pin, 112 | * debounce input pin level and then 113 | * advance the finite state machine (FSM). 114 | */ 115 | void OneButtonTiny::tick(void) { 116 | if (_pin >= 0) { 117 | _fsm(debounce(digitalRead(_pin)) == _buttonPressed); 118 | } 119 | } // tick() 120 | 121 | 122 | void OneButtonTiny::tick(bool activeLevel) { 123 | _fsm(debounce(activeLevel)); 124 | } 125 | 126 | 127 | /** 128 | * @brief Advance to a new state and save the last one to come back in cas of bouncing detection. 129 | */ 130 | void OneButtonTiny::_newState(stateMachine_t nextState) { 131 | _state = nextState; 132 | } // _newState() 133 | 134 | 135 | /** 136 | * @brief Run the finite state machine (FSM) using the given level. 137 | */ 138 | void OneButtonTiny::_fsm(bool activeLevel) { 139 | unsigned long waitTime = (now - _startTime); 140 | 141 | // Implementation of the state machine 142 | switch (_state) { 143 | case OneButtonTiny::OCS_INIT: 144 | // waiting for level to become active. 145 | if (activeLevel) { 146 | _newState(OneButtonTiny::OCS_DOWN); 147 | _startTime = now; // remember starting time 148 | _nClicks = 0; 149 | } // if 150 | break; 151 | 152 | case OneButtonTiny::OCS_DOWN: 153 | // waiting for level to become inactive. 154 | 155 | if (!activeLevel) { 156 | _newState(OneButtonTiny::OCS_UP); 157 | _startTime = now; // remember starting time 158 | 159 | } else if ((activeLevel) && (waitTime > _press_ms)) { 160 | if (_longPressStartFunc) _longPressStartFunc(); 161 | _newState(OneButtonTiny::OCS_PRESS); 162 | } // if 163 | break; 164 | 165 | case OneButtonTiny::OCS_UP: 166 | // level is inactive 167 | 168 | // count as a short button down 169 | _nClicks++; 170 | _newState(OneButtonTiny::OCS_COUNT); 171 | break; 172 | 173 | case OneButtonTiny::OCS_COUNT: 174 | // dobounce time is over, count clicks 175 | 176 | if (activeLevel) { 177 | // button is down again 178 | _newState(OneButtonTiny::OCS_DOWN); 179 | _startTime = now; // remember starting time 180 | 181 | } else if ((waitTime >= _click_ms) || (_nClicks == 2)) { 182 | // now we know how many clicks have been made. 183 | 184 | if (_nClicks == 1) { 185 | // this was 1 click only. 186 | if (_clickFunc) _clickFunc(); 187 | 188 | } else if (_nClicks == 2) { 189 | // this was a 2 click sequence. 190 | if (_doubleClickFunc) _doubleClickFunc(); 191 | 192 | } // if 193 | 194 | reset(); 195 | } // if 196 | break; 197 | 198 | case OneButtonTiny::OCS_PRESS: 199 | // waiting for pin being release after long press. 200 | 201 | if (!activeLevel) { 202 | _newState(OneButtonTiny::OCS_PRESSEND); 203 | _startTime = now; 204 | } // if 205 | break; 206 | 207 | case OneButtonTiny::OCS_PRESSEND: 208 | // button was released. 209 | reset(); 210 | break; 211 | 212 | default: 213 | // unknown state detected -> reset state machine 214 | _newState(OneButtonTiny::OCS_INIT); 215 | break; 216 | } // if 217 | 218 | } // OneButton.tick() 219 | 220 | 221 | // end. 222 | -------------------------------------------------------------------------------- /src/OneButtonTiny.h: -------------------------------------------------------------------------------- 1 | // ----- 2 | // OneButtonTiny.h - Library for detecting button clicks, doubleclicks and long 3 | // press pattern on a single button. This class is implemented for use with the 4 | // Arduino environment. Copyright (c) by Matthias Hertel, 5 | // http://www.mathertel.de This work is licensed under a BSD style license. See 6 | // http://www.mathertel.de/License.aspx More information on: 7 | // http://www.mathertel.de/Arduino 8 | // ----- 9 | // 01.12.2023 created from OneButtonTiny to support tiny environments. 10 | // ----- 11 | 12 | #ifndef OneButtonTiny_h 13 | #define OneButtonTiny_h 14 | 15 | #include "Arduino.h" 16 | 17 | // ----- Callback function types ----- 18 | 19 | extern "C" { 20 | typedef void (*callbackFunction)(void); 21 | } 22 | 23 | 24 | class OneButtonTiny { 25 | public: 26 | // ----- Constructor ----- 27 | 28 | /** 29 | * Initialize the OneButtonTiny library. 30 | * @param pin The pin to be used for input from a momentary button. 31 | * @param activeLow Set to true when the input level is LOW when the button is pressed, Default is true. 32 | * @param pullupActive Activate the internal pullup when available. Default is true. 33 | */ 34 | explicit OneButtonTiny(const int pin, const bool activeLow = true, const bool pullupActive = true); 35 | 36 | /** 37 | * set # millisec after safe click is assumed. 38 | */ 39 | void setDebounceMs(const unsigned int ms); 40 | 41 | /** 42 | * set # millisec after single click is assumed. 43 | */ 44 | void setClickMs(const unsigned int ms); 45 | 46 | /** 47 | * set # millisec after press is assumed. 48 | */ 49 | void setPressMs(const unsigned int ms); 50 | 51 | // ----- Attach events functions ----- 52 | 53 | /** 54 | * Attach an event to be called when a single click is detected. 55 | * @param newFunction This function will be called when the event has been detected. 56 | */ 57 | void attachClick(callbackFunction newFunction); 58 | 59 | /** 60 | * Attach an event to be called after a double click is detected. 61 | * @param newFunction This function will be called when the event has been detected. 62 | */ 63 | void attachDoubleClick(callbackFunction newFunction); 64 | 65 | /** 66 | * Attach an event to be called after a multi click is detected. 67 | * @param newFunction This function will be called when the event has been detected. 68 | */ 69 | void attachMultiClick(callbackFunction newFunction); 70 | 71 | /** 72 | * Attach an event to fire when the button is pressed and held down. 73 | * @param newFunction 74 | */ 75 | void attachLongPressStart(callbackFunction newFunction); 76 | 77 | 78 | // ----- State machine functions ----- 79 | 80 | /** 81 | * @brief Call this function every some milliseconds for checking the input 82 | * level at the initialized digital pin. 83 | */ 84 | void tick(void); 85 | 86 | /** 87 | * @brief Call this function every time the input level has changed. 88 | * Using this function no digital input pin is checked because the current 89 | * level is given by the parameter. 90 | * Run the finite state machine (FSM) using the given level. 91 | */ 92 | void tick(bool level); 93 | 94 | 95 | /** 96 | * Reset the button state machine. 97 | */ 98 | void reset(void); 99 | 100 | 101 | /** 102 | * @return true if we are currently handling button press flow 103 | * (This allows power sensitive applications to know when it is safe to power down the main CPU) 104 | */ 105 | bool isIdle() const { 106 | return _state == OCS_INIT; 107 | } 108 | 109 | 110 | private: 111 | int _pin = -1; // hardware pin number. 112 | unsigned int _debounce_ms = 50; // number of msecs for debounce times. 113 | unsigned int _click_ms = 400; // number of msecs before a click is detected. 114 | unsigned int _press_ms = 800; // number of msecs before a long button press is detected 115 | 116 | int _buttonPressed = 0; // this is the level of the input pin when the button is pressed. 117 | // LOW if the button connects the input pin to GND when pressed. 118 | // HIGH if the button connects the input pin to VCC when pressed. 119 | 120 | // These variables will hold functions acting as event source. 121 | callbackFunction _clickFunc = NULL; 122 | callbackFunction _doubleClickFunc = NULL; 123 | callbackFunction _longPressStartFunc = NULL; 124 | 125 | // These variables that hold information across the upcoming tick calls. 126 | // They are initialized once on program start and are updated every time the 127 | // tick function is called. 128 | 129 | // define FiniteStateMachine 130 | enum stateMachine_t : int { 131 | OCS_INIT = 0, 132 | OCS_DOWN = 1, // button is down 133 | OCS_UP = 2, // button is up 134 | OCS_COUNT = 3, 135 | OCS_PRESS = 6, // button is hold down 136 | OCS_PRESSEND = 7, 137 | }; 138 | 139 | /** 140 | * Run the finite state machine (FSM) using the given level. 141 | */ 142 | void _fsm(bool activeLevel); 143 | 144 | /** 145 | * Advance to a new state. 146 | */ 147 | void _newState(stateMachine_t nextState); 148 | 149 | stateMachine_t _state = OCS_INIT; 150 | 151 | int debouncedPinLevel = -1; 152 | int _lastDebouncePinLevel = -1; // used for pin debouncing 153 | unsigned long _lastDebounceTime = 0; // millis() 154 | unsigned long now = 0; // millis() 155 | 156 | unsigned long _startTime = 0; // start of current input change to checking debouncing 157 | int _nClicks = 0; // count the number of clicks with this variable 158 | 159 | public: 160 | int pin() const { 161 | return _pin; 162 | }; 163 | stateMachine_t state() const { 164 | return _state; 165 | }; 166 | int debounce(const int value); 167 | int debouncedValue() const { 168 | return debouncedPinLevel; 169 | }; 170 | }; 171 | 172 | #endif 173 | -------------------------------------------------------------------------------- /suppressions.txt: -------------------------------------------------------------------------------- 1 | // cppcheck suppressions 2 | unusedFunction 3 | missingInclude 4 | 5 | // this one might be something to look into 6 | knownConditionTrueFalse 7 | --------------------------------------------------------------------------------