{"id":16714624,"url":"https://github.com/bxparks/acebutton","last_synced_at":"2025-04-05T12:06:43.290Z","repository":{"id":41148683,"uuid":"120046920","full_name":"bxparks/AceButton","owner":"bxparks","description":"An adjustable, compact, event-driven button library for Arduino that debounces and dispatches events to a user-defined event handler.","archived":false,"fork":false,"pushed_at":"2024-07-25T23:08:23.000Z","size":3310,"stargazers_count":387,"open_issues_count":0,"forks_count":37,"subscribers_count":17,"default_branch":"develop","last_synced_at":"2024-10-13T21:05:51.678Z","etag":null,"topics":["arduino","arduino-library","button","debounce-button"],"latest_commit_sha":null,"homepage":"","language":"C++","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/bxparks.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":"CONTRIBUTING.md","funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2018-02-03T00:49:20.000Z","updated_at":"2024-10-13T18:33:07.000Z","dependencies_parsed_at":"2024-10-25T18:34:56.621Z","dependency_job_id":"306e6ec9-e24c-4a2d-883a-4f17b26a18c5","html_url":"https://github.com/bxparks/AceButton","commit_stats":null,"previous_names":[],"tags_count":34,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/bxparks%2FAceButton","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/bxparks%2FAceButton/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/bxparks%2FAceButton/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/bxparks%2FAceButton/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/bxparks","download_url":"https://codeload.github.com/bxparks/AceButton/tar.gz/refs/heads/develop","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":247332605,"owners_count":20921853,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["arduino","arduino-library","button","debounce-button"],"created_at":"2024-10-12T21:06:15.887Z","updated_at":"2025-04-05T12:06:43.267Z","avatar_url":"https://github.com/bxparks.png","language":"C++","funding_links":[],"categories":[],"sub_categories":[],"readme":"# AceButton\n\n[![AUnit Tests](https://github.com/bxparks/AceButton/actions/workflows/aunit_tests.yml/badge.svg)](https://github.com/bxparks/AceButton/actions/workflows/aunit_tests.yml)\n\nAn adjustable, compact, event-driven button library for Arduino platforms.\n\nThis library provides classes which accept inputs from a mechanical button\nconnected to a digital input pin on the Arduino. The library should be able to\nhandle momentary buttons, maintained buttons, and switches, but it was designed\nprimarily for momentary (aka push) buttons.\n\nThe library is named \"AceButton\" because:\n\n* many configurations of the button are **adjustable**, either at compile-time\n  or run-time\n* the library is optimized to create **compact** objects which take up\n  a minimal amount of static memory\n* the library detects changes in the button state and sends **events** to\n  a user-defined `EventHandler` callback function\n\nMost of the features of the library can be accessed through 2 classes,\nusing either a callback function or an interface:\n\n* `AceButton` (class)\n* `ButtonConfig` (class)\n* `EventHandler` (typedef for callback function)\n* `IEventHandler` (interface)\n\nThe `AceButton` class contains the logic for debouncing and determining if a\nparticular event has occurred.\n\nThe `ButtonConfig` class holds various timing parameters, the event handler,\ncode for reading the button, and code for getting the internal clock.\n\nThe `EventHandler` is a user-defined callback function with a specific signature\nwhich is registered with the `ButtonConfig` object. When the library detects\ninteresting events, the callback function is called by the library, allowing the\nclient code to handle the event.\n\nThe `IEventHandler` is an interface (pure abstract class) that provides an\nalternative to the `EventHandler`. Instead of using a callback function, an\nobject of type `IEventHandler` can be used to handle the button events.\n\nThe supported events are:\n\n* `AceButton::kEventPressed`\n* `AceButton::kEventReleased`\n* `AceButton::kEventClicked`\n* `AceButton::kEventDoubleClicked`\n* `AceButton::kEventLongPressed`\n* `AceButton::kEventRepeatPressed`\n* `AceButton::kEventLongReleased` (v1.8)\n* `AceButton::kEventHeartBeat` (v1.10)\n\nThe basic `ButtonConfig` class assumes that each button is connected to a single\ndigital input pin. In some situations, the number of buttons that we want is\ngreater than the number of input pins available. This library provides\n2 subclasses of `ButtonConfig` which may be useful:\n\n* `EncodedButtonConfig`\n    * Supports binary encoded buttons, to read `2^N - 1` buttons using `N`\n      pins (e.g. 7 buttons using 3 digital pins).\n* `LadderButtonConfig`\n    * Supports 1-8 buttons (maybe more) on a single analog pin through a\n      resistor ladder. The `analogRead()` method is used to read the different\n      voltage levels corresponding to each button.\n\nBoth `EncodedButtonConfig` and `LadderButtonConfig` support all events listed\nabove (e.g. `kEventClicked` and `kEventDoubleClicked`).\n\n**Version**: 1.10.1 (2023-05-25)\n\n**Changelog**: [CHANGELOG.md](CHANGELOG.md)\n\n## Table of Contents\n\n* [Features](#Features)\n* [HelloButton](#HelloButton)\n* [Installation](#Installation)\n    * [External Dependencies](#ExternalDependencies)\n    * [Source Code](#SourceCode)\n* [Documentation](#Documentation)\n    * [Examples](#Examples)\n* [Usage](#Usage)\n    * [Include Header and Use Namespace](#IncludeHeader)\n    * [Pin Wiring and Initialization](#PinWiring)\n    * [AceButton Class](#AceButtonClass)\n        * [Sampling Rate](#SamplingRate)\n        * [Compiler Error on Pin 0](#CompilerErrorOnPin0)\n    * [ButtonConfig Class](#ButtonConfigClass)\n        * [System ButtonConfig](#SystemButtonConfig)\n        * [Configuring the EventHandler](#ConfiguringEventHandler)\n        * [Timing Parameters](#TimingParameters)\n        * [Hardware Dependencies](#HardwareDependencies)\n        * [Multiple ButtonConfig Instances](#MultipleButtonConfigs)\n    * [EventHandler Typedef](#EventHandlerTypedef)\n        * [EventHandler Signature](#EventHandlerSignature)\n        * [EventHandler Parameters](#EventHandlerParameters)\n        * [One EventHandler Per ButtonConfig](#OneEventHandler)\n        * [EventHandler Tips](#EventHandlerTips)\n    * [Event Types](#EventTypes)\n    * [ButtonConfig Feature Flags](#ButtonConfigFeatureFlags)\n        * [Event Activation](#EventActivation)\n        * [Event Suppression](#EventSuppression)\n    * [Single Button Simplifications](#SingleButtonSimplifications)\n    * [Multiple Buttons](#MultipleButtons)\n* [Advanced Topics](#AdvancedTopics)\n    * [Object-based Event Handler](#ObjectBasedEventHandler)\n    * [Distinguishing Clicked and DoubleClicked](#ClickedAndDoubleClicked)\n    * [Distinguishing Pressed and LongPressed](#PressedAndLongPressed)\n    * [Events After Reboot](#EventsAfterReboot)\n    * [Orphaned Clicks](#OrphanedClicks)\n    * [Binary Encoded Buttons](#BinaryEncodedButtons)\n    * [Resistor Ladder Buttons](#ResistorLadderButtons)\n    * [Dynamic Allocation on the Heap](#HeapAllocation)\n    * [Digital Write Fast](#DigitalWriteFast)\n    * [Heart Beat Event](#HeartBeat)\n* [Resource Consumption](#ResourceConsumption)\n    * [SizeOf Classes](#SizeOfClasses)\n    * [Flash And Static Memory](#FlashAndStaticMemory)\n    * [CPU Cycles](#CpuCycles)\n* [System Requirements](#SystemRequirements)\n    * [Hardware](#Hardware)\n    * [Tool Chain](#ToolChain)\n    * [Operating System](#OperatingSystem)\n* [Background Motivation](#BackgroundMotivation)\n  * [Non-goals](#NonGoals)\n* [Bugs and Limitations](#BugsAndLimitations)\n* [License](#License)\n* [Feedback and Support](#FeedbackAndSupport)\n* [Author](#Author)\n\n\u003ca name=\"Features\"\u003e\u003c/a\u003e\n## Features\n\nHere are the high-level features of the AceButton library:\n\n* debounces the mechanical contact\n* supports both pull-up and pull-down wiring\n* event-driven through a user-defined `EventHandler` callback function\n* event-driven through an object-based `IEventHandler` (\u003e= v1.6)\n* supports the following event types:\n    * `kEventPressed`\n    * `kEventReleased`\n    * `kEventClicked`\n    * `kEventDoubleClicked`\n    * `kEventLongPressed`\n    * `kEventRepeatPressed`\n    * `kEventLongReleased`\n    * `kEventHeartBeat`\n* adjustable configurations at runtime or compile-time\n    * timing parameters\n    * `digitalRead()` button read function can be overridden\n    * `millis()` clock function can be overridden\n* small memory footprint\n    * each `AceButton` consumes 17 bytes (8-bit) or 20 bytes (32-bit)\n    * each `ButtonConfig` consumes 20 bytes (8-bit) or 24 bytes (32-bit)\n    * one System `ButtonConfig` instance created automatically by the library\n    * 970-2180 bytes of flash memory for the simple case of 1 AceButton and 1\n      ButtonConfig, depending on 8-bit or 32-bit processors\n* supports multiple buttons on shared pins using various circuits\n    * [Binary Encoded buttons](docs/binary_encoding/README.md)\n      (e.g. 7 buttons using 3 pins)\n    * [Resistor Ladder buttons](docs/resistor_ladder/README.md) (e.g. 5 buttons\n      on a single analog pin)\n* only 13-15 microseconds (on 16MHz ATmega328P) per polling call to\n  `AceButton::check()`\n* extensive testing\n    * thoroughly unit tested using [AUnit](https://github.com/bxparks/AUnit)\n    * Tier 1 support includes: Arduino AVR (UNO, Nano, Micro etc),\n      SAMD21 (Seeed XIAO M0), STM32 (Blue Pill), SAMD51 (Adafruit ItsyBitsy M4),\n      ESP8266, and ESP32\n\nCompared to other Arduino button libraries, I think the unique or exceptional\nfeatures of the AceButton library are:\n\n* many supported event types (e.g. LongPressed and RepeatPressed)\n* able to distinguish between Clicked and DoubleClicked\n* small memory usage\n* thorough unit testing\n* support for multiple buttons using Binary Encoding or a Resistor Ladder\n\n\u003ca name=\"HelloButton\"\u003e\u003c/a\u003e\n## HelloButton\n\nHere is a simple program (see [examples/HelloButton](examples/HelloButton))\nwhich controls the builtin LED on the Arduino board using a momentary button\nconnected to PIN 2.\n\n```C++\n#include \u003cAceButton.h\u003e\nusing namespace ace_button;\n\nconst int BUTTON_PIN = 2;\nconst int LED_ON = HIGH;\nconst int LED_OFF = LOW;\n\nAceButton button(BUTTON_PIN);\n\nvoid handleEvent(AceButton*, uint8_t, uint8_t);\n\nvoid setup() {\n  pinMode(LED_BUILTIN, OUTPUT);\n  pinMode(BUTTON_PIN, INPUT_PULLUP);\n  button.setEventHandler(handleEvent);\n}\n\nvoid loop() {\n  button.check();\n}\n\nvoid handleEvent(AceButton* /*button*/, uint8_t eventType,\n    uint8_t /*buttonState*/) {\n  switch (eventType) {\n    case AceButton::kEventPressed:\n      digitalWrite(LED_BUILTIN, LED_ON);\n      break;\n    case AceButton::kEventReleased:\n      digitalWrite(LED_BUILTIN, LED_OFF);\n      break;\n  }\n}\n```\n\n(The `button` and `buttonState` parameters are commented out to avoid an `unused\nparameter` warning from the compiler. We can't remove the parameters completely\nbecause the method signature is defined by the `EventHandler` typedef.)\n\n\u003ca name=\"Installation\"\u003e\u003c/a\u003e\n## Installation\n\nThe latest stable release is available in the Arduino IDE Library Manager.\nSearch for \"AceButton\". Click install.\n\nThe development version can be installed by cloning the\nGitHub repository (https://github.com/bxparks/AceButton), checking out the\n`develop` branch, then manually copying over the contents to the `./libraries`\ndirectory used by the Arduino IDE. (The result is a directory named\n`./libraries/AceButton`.)\n\nThe `master` branch contains the tagged stable releases.\n\n\u003ca name=\"ExternalDependencies\"\u003e\u003c/a\u003e\n### External Dependencies\n\nThe core of the library is self-contained and has no external dependencies.\n\nThe some programs in `examples/` may depend on:\n* AceCommon (https://github.com/bxparks/AceCommon)\n\nThe unit tests under `tests` depend on:\n* AUnit (https://github.com/bxparks/AUnit)\n\n\u003ca name=\"SourceCode\"\u003e\u003c/a\u003e\n### Source Code\n\nThe source files are organized as follows:\n* `src/AceButton.h` - main header file\n* `src/ace_button/` - all implementation files\n* `src/ace_button/testing/` - internal testing files\n* `tests/` - unit tests which require [AUnit](https://github.com/bxparks/AUnit)\n* `examples/` - example sketches\n\n\u003ca name=\"Documentation\"\u003e\u003c/a\u003e\n## Documentation\n\n* this [README.md](README.md)\n* [docs/binary_encoding/README.md](docs/binary_encoding/README.md)\n    * Explanation of reading buttons through binary encoding using the\n      `Encoded4To2ButtonConfig`, `Encoded8To3ButtonConfig`,\n      `EncodedButtonConfig` classes\n* [docs/resistor_ladder/README.md](docs/resistor_ladder/README.md)\n    * Explanation of reading buttons through a resistor ladder using the\n      `LadderButtonConfig` class\n* [Doxygen docs](https://bxparks.github.io/AceButton/html/) published on GitHub\n  Pages which can help navigate an unfamiliar code base.\n\n\u003ca name=\"Examples\"\u003e\u003c/a\u003e\n### Examples\n\nThe following example sketches are provided:\n\n* Basic Single Button\n    * [HelloButton](examples/HelloButton)\n        * minimal program that reads a switch and control the built-in LED\n    * [SingleButton](examples/SingleButton)\n        * single button wired with an internal pull-up resistor\n    * [SingleButtonPullDown](examples/SingleButtonPullDown)\n        * same as `SingleButton` but with an external pull-down resistor\n    * [SingleButtonUsingIEventHandler](examples/SingleButtonUsingIEventHandler)\n        * same as `SingleButton` using an object-based `IEventHandler`\n    * [Stopwatch](examples/Stopwatch)\n        * measures the speed of `AceButton:check()` with a start/stop/reset\n        * button uses `kFeatureLongPress`\n* Multiple Buttons\n    * [TwoButtonsUsingOneButtonConfig](examples/TwoButtonsUsingOneButtonConfig)\n        * two buttons using one ButtonConfig\n    * [TwoButtonsUsingTwoButtonConfigs](examples/TwoButtonsUsingTwoButtonConfigs/)\n        * two buttons using two ButtonConfigs\n    * [ThreeButtonsUsingOneButtonConfig](examples/ThreeButtonsUsingOneButtonConfig)\n        * three buttons using one ButtonConfig\n        * used as a reference for `ThreeButtonsUsingOneButtonConfigFast`\n          (below)\n    * [TunerButtons](examples/TunerButtons)\n        * implements 5 radio buttons (tune-up, tune-down, and 3 presets)\n        * shows multiple `ButtonConfig` and `EventHandler` instances\n        * shows an example of how to use `getId()`\n        * uses `kFeatureLongPress`, `kFeatureRepeatPress`,\n          `kFeatureSuppressAfterLongPress`, and\n          `kFeatureSuppressAfterRepeatPress`\n    * [ArrayButtons](examples/ArrayButtons)\n        * shows how to define an array of `AceButton` and initialize them using\n          the `init()` method in a loop\n    * [SimultaneousButtons](examples/SimultaneousButtons)\n        * detecting simultaneous Pressed and Released of 2 buttons using\n          a custom `IEventHandler`\n* Distinguishing Click versus Double-Click\n    * [ClickVersusDoubleClickUsingReleased](examples/ClickVersusDoubleClickUsingReleased)\n        * using a `kEventReleased` instead\n    * [ClickVersusDoubleClickUsingSuppression](examples/ClickVersusDoubleClickUsingSuppression)\n        * using the `kFeatureSuppressClickBeforeDoubleClick` flag at the cost of\n          increasing the response time\n    * [ClickVersusDoubleClickUsingBoth](examples/ClickVersusDoubleClickUsingBoth)\n        * combining both the \"UsingReleased\" and \"UsingSuppression\" techniques\n    * See [Distinguishing Clicked and DoubleClicked](#ClickedAndDoubleClicked)\n      subsection below for more info.\n* Distinguishing Pressed and LongPressed\n    * [examples/PressVersusLongPress](examples/PressVersusLongPress)\n    * See the [Distinguishing Pressed and LongPressed](#PressedAndLongPressed)\n      subsection below for more info.\n* [CapacitiveButton](examples/CapacitiveButton)\n    * reads a capacitive button using the\n      [CapacitiveSensor](https://github.com/PaulStoffregen/CapacitiveSensor)\n      library\n* [HeartBeat](examples/HeartBeat)\n    * demo of activating the new (v1.10) `kEventHeartBeat` feature, and using it\n      to generate 2 custom events: `kCustomEventLongPressed` (similar to\n      `kEventLongPressed`) and `kCustomEventLongReleased` (no built-in\n      equivalent)\n* Binary Encoded Buttons\n    * [Encoded4To2Buttons](examples/Encoded4To2Buttons)\n        * demo of `Encoded4To2ButtonConfig` class to decode `M=3` buttons with\n          `N=2` pins\n    * [Encoded8To3Buttons](examples/Encoded8To3Buttons)\n        * demo of `Encoded8To3ButtonConfig` class to decode `M=7` buttons with\n          `N=3` pins\n    * [Encoded16To4Buttons](examples/Encoded16To4Buttons)\n        * demo of general M-to-N `EncodedButtonConfig` class to handle `M=15`\n          buttons with `N=4` pins\n* Resistor Ladder Buttons\n    * [LadderButtonCalibrator](examples/LadderButtonCalibrator)\n        * print out the value returned by `analogRead()` for various buttons\n        * useful to compare the expected values of the resistor ladder versus\n          the actual values returned by the function\n    * [LadderButtons](examples/LadderButtons)\n        * demo of 4 buttons on a single analog pin using `analogRead()`\n    * [LadderButtonsTiny](examples/LadderButtonsTiny)\n        * 2 buttons on the `RESET/A0` pin of an ATtiny85 microcontroller\n        * avoids wasting the RESET pin, saving the other pins for other purposes\n* digitalWriteFast\n    * [SingleButtonFast](examples/SingleButtonFast)\n        * Same as `SingleButton` but using `ButtonConfigFast1\u003cPIN\u003e` which\n          uses the `digitalWriteFast` library\n    * [TwoButtonsUsingOneButtonConfigFast](examples/TwoButtonsUsingOneButtonConfigFast)\n        * Same as `TwoButtonsUsingOneButtonConfig` but using\n          `ButtonConfigFast2\u003cPIN0,PIN1\u003e` which uses the `digitalWriteFast`\n          library\n    * [ThreeButtonsUsingOneButtonConfigFast](examples/ThreeButtonsUsingOneButtonConfigFast)\n        * Same as `ThreeButtonsUsingOneButtonConfig` but using\n          `ButtonConfigFast3\u003cPIN0,PIN1,PIN2\u003e` which uses the `digitalWriteFast`\n          library\n* Benchmarks\n    * These are internal benchmark programs. They were not written as examples\n      of how to use the library.\n    * [AutoBenchmark](examples/AutoBenchmark)\n        * generates the timing stats (min/average/max) for the\n          `AceButton::check()` method for various types of events (idle,\n          press/release, click, double-click, and long-press)\n    * [MemoryBenchmark](examples/MemoryBenchmark/)\n        * determines the amount of flash memory consumed by various objects and\n          features of the library\n\n\u003ca name=\"Usage\"\u003e\u003c/a\u003e\n## Usage\n\nThere are 2 classes and one typedef that a user will normally interact with:\n\n* `AceButton` (class)\n* `ButtonConfig` (class)\n* `EventHandler` (typedef)\n\nAdvanced usage is supported by:\n\n* `EncodedButtonConfig` - binary encoded buttons supporting `2^N-1` buttons on\n  `N` digital pins\n* `LadderButtonConfig` - resistor ladder buttons using analog pins\n* `IEventHandler` - use a callback object instead of a callback function\n\nWe explain how to use these below.\n\n\u003ca name=\"IncludeHeader\"\u003e\u003c/a\u003e\n### Include Header and Use Namespace\n\nOnly a single header file `AceButton.h` is required to use this library.\nTo prevent name clashes with other libraries that the calling code may use, all\nclasses are defined in the `ace_button` namespace. To use the code without\nprepending the `ace_button::` prefix, use the `using` directive:\n\n```C++\n#include \u003cAceButton.h\u003e\nusing namespace ace_button;\n```\n\nIf you are dependent on just `AceButton`, the following might be sufficient:\n\n```C++\n#include \u003cAceButton.h\u003e\nusing ace_button::AceButton;\n```\n\n\u003ca name=\"PinWiring\"\u003e\u003c/a\u003e\n### Pin Wiring and Initialization\n\nThe `ButtonConfig` class supports the simplest wiring. Each button is connected\nto a single digital input pin, as shown below. In the example below, 3 buttons\nlabeled `S0`, `S1` and `S2` are connected to digital input pins `D2`, `D3`, and\n`D4`:\n\n![Direct Digital](docs/direct_digital/direct_digital.png)\n\nAn Arduino microcontroller pin can be in an `OUTPUT` mode, an `INPUT` mode, or\nan `INPUT_PULLUP` mode. This mode is controlled by the `pinMode()` method.\n\nBy default upon boot, the pin is set to the `INPUT` mode. However, this `INPUT`\nmode puts the pin into a high impedance state, which means that if there is no\nwire connected to the pin, the voltage on the pin is indeterminate. When the\ninput pin is read (using `digitalRead()`), the boolean value will be a random\nvalue. If you are using the pin in `INPUT` mode, you *must* connect an external\npull-up resistor (connected to Vcc) or pull-down resistor (connected to ground)\nso that the voltage level of the pin is defined when there is nothing connected\nto the pin (i.e. when the button is not pressed).\n\nThe `INPUT_PULLUP` mode is a special `INPUT` mode which tells the\nmicrocontroller to connect an internal pull-up resistor to the pin. It is\nactivated by calling `pinMode(pin, INPUT_PULLUP)` on the given `pin`. This mode\nis very convenient because it eliminates the external resistor, making the\nwiring simpler.\n\nThe 3 resistors `Rc1`, `Rc2` and `Rc3` are optional current limiting resistors.\nThey help protect the microcontroller in the case of misconfiguration. If the\npins are accidentally set to `OUTPUT` mode, then pressing one of the buttons\nwould connect the output pin directly to ground, causing a large amount of\ncurrent to flow that could permanently damage the microcontroller. The\nresistance value of 220 ohms (or maybe 330 ohms) is high enough to keep the\ncurrent within safety limits, but low enough compared to the internal pullup\nresistor that it is able to pull the digital pin to a logical 0 level. These\ncurrent limiting resistors are good safety measures, but I admit that I often\nget lazy and don't use them when doing quick experiments.\n\nThe AceButton library itself does *not* call the `pinMode()` function. The\ncalling application is responsible for calling `pinMode()`. Normally, this\nhappens in the global `setup()` method but the call can happen somewhere else if\nthe application requires it. The reason for decoupling the hardware\nconfiguration from the AceButton library is mostly because the library does not\nactually care about the specific hardware wiring of the button. It does not care\nwhether an external resistor is used, or the internal resistor is used. It only\ncares about whether the resistor is a pull-up or a pull-down.\n\nSee https://www.arduino.cc/en/Tutorial/DigitalPins for additional information\nabout the I/O pins on an Arduino.\n\n\u003ca name=\"AceButtonClass\"\u003e\u003c/a\u003e\n### AceButton Class\n\nThe `AceButton` class looks like this (not all public methods are shown):\n```C++\nnamespace ace_button {\n\nclass AceButton {\n  public:\n    static const uint8_t kEventPressed = 0;\n    static const uint8_t kEventReleased = 1;\n    static const uint8_t kEventClicked = 2;\n    static const uint8_t kEventDoubleClicked = 3;\n    static const uint8_t kEventLongPressed = 4;\n    static const uint8_t kEventRepeatPressed = 5;\n    static const uint8_t kEventLongReleased = 6;\n    static const uint8_t kEventHeartBeat = 7;\n\n    static const uint8_t kButtonStateUnknown = 127;\n\n    static __FlashStringHelper eventName(uint8_t e);\n\n    explicit AceButton(uint8_t pin = 0, uint8_t defaultReleasedState = HIGH,\n        uint8_t id = 0);\n    explicit AceButton(ButtonConfig* buttonConfig, uint8_t pin = 0,\n        uint8_t defaultReleasedState = HIGH, uint8_t id = 0);\n    void init(uint8_t pin = 0, uint8_t defaultReleasedState = HIGH,\n        uint8_t id = 0);\n    void init(ButtonConfig* buttonConfig, uint8_t pin = 0,\n        uint8_t defaultReleasedState = HIGH, uint8_t id = 0);\n\n    ButtonConfig* getButtonConfig();\n    void setButtonConfig(ButtonConfig* buttonConfig);\n    void setEventHandler(ButtonConfig::EventHandler eventHandler);\n\n    uint8_t getPin();\n    uint8_t getDefaultReleasedState();\n    uint8_t getId();\n\n    void check();\n};\n\n}\n```\n\nEach physical button will be handled by an instance of `AceButton`. At a\nminimum, the instance needs to be told the pin number of the button. This can\nbe done through the constructor:\n\n```C++\nconst uint8_t BUTTON_PIN = 2;\n\nAceButton button(BUTTON_PIN);\n\nvoid setup() {\n  pinMode(BUTTON_PIN, INPUT_PULLUP);\n  ...\n}\n```\n\nOr we can use the `init()` method in the `setup()`:\n\n```C++\nAceButton button;\n\nvoid setup() {\n  pinMode(BUTTON_PIN, INPUT_PULLUP);\n  button.init(BUTTON_PIN);\n  ...\n}\n```\n\nBoth the constructor and the `init()` function take 3 optional parameters as\nshown above:\n\n* `pin`: the I/O pin number assigned to the button\n* `defaultReleasedState`: the logical value of the button when it is in its\n  default \"released\" state (`HIGH` using a pull-up resistor,\n  `LOW` for a pull-down resistor)\n* `id`: an optional, user-defined identifier for the button,\n  for example, an index into an array with additional information\n\nThe `pin` must be defined either through the constructor or the `init()` method.\nBut the other two parameters may be optional in many cases.\n\n\u003ca name=\"SamplingRate\"\u003e\u003c/a\u003e\n### Sampling Rate\n\nTo read the state of the button, the `AceButton::check()` method should be\ncalled from the `loop()` method periodically. Roughly speaking, this should be\nabout 4 times faster than the value of `getDebounceDelay()` so that the various\nevent detection logic can work properly. For example, for the default debounce\ndelay is 20 ms, `AceButton::check()` should be called every 5 ms. I have\nsuccessfully experimented with using a sampling delay as large as 10 ms, but I\nrecommend about 5 ms in most cases.\n\nYou could call the `AceButton::check()` method directly in the global `loop()`\nfunction like this:\n\n```C++\nvoid loop() {\n  ...\n  button.check();\n  ...\n}\n```\n\nThis would sample the button as fast as possible on your particular\nmicroprocessor, perhaps as fast as 10,000 or 100,000 times a second, depending\non the other code that is in the `loop()` function.\n\nMost of the time, a high sampling rate is not a problem except for 2 things:\n\n* Calling the `AceButton::check()` has a small overhead and your processor could\n  be doing other things during that time.\n* If you use [Resistor Ladder Buttons](#ResistorLadderButtons) described below,\n  on an ESP8266, you will trigger a bug that causes the WiFi to disconnect if\n  you sample the `analogRead()` function more than a 1000 times/second.\n\nIf you want to limit the sampling rate, see the example code in [Rate\nLimit CheckButtons](docs/resistor_ladder/README.md#RateLimitCheckButtons). The\ncode relies on using a `static` variable to implement a non-blocking delay, like\nthis:\n\n```C++\nAceButton button;\n...\n\nvoid checkButtons() {\n  static uint16_t prev = millis();\n\n  // DO NOT USE delay(5) to do this.\n  // The (uint16_t) cast is required on 32-bit processors, harmless on 8-bit.\n  uint16_t now = millis();\n  if ((uint16_t) (now - prev) \u003e= 5) {\n    button.check();\n    prev = now;\n  }\n}\n\nvoid loop() {\n  checkButtons();\n  ...\n}\n```\n\n\u003ca name=\"CompilerErrorOnPin0\"\u003e\u003c/a\u003e\n### Compiler Error On Pin 0\n\nIf you attempt to use Pin 0 in the `AceButton()` constructor:\n```C++\nAceButton button(0);\n```\nyou may encounter a compile-time error such as this:\n```\nerror: call of overloaded 'AceButton(int)' is ambiguous\n```\n\nThe solution is to explicitly cast the `0` to a `uint8_t` type, or to\nassign it explicitly to a `uint8_t` const, like this:\n```C++\n// Explicit cast\nAceButton button((uint8_t) 0);\n\n// Or assign to a const first.\nstatic const uint8_t PIN = 0;\nAceButton button(PIN);\n\n```\nSee [Issue #40](https://github.com/bxparks/AceButton/issues/40) for details.\n\n\u003ca name=\"ButtonConfigClass\"\u003e\u003c/a\u003e\n### ButtonConfig Class\n\nThe core concept of the AceButton library is the separation of the\nbutton (`AceButton`) from its configuration (`ButtonConfig`).\n\n* The `AceButton` class has the logic for debouncing and detecting the various\n  events (Pressed, Released, etc), and the various bookkeeping variables\n  needed to implement the logic. These variables are associated with the\n  specific instance of that `AceButton`.\n* The `ButtonConfig` class has the various timing parameters which control\n  how much time is needed to detect certain events. This class also has the\n  ability to override the default methods for reading the pin (`readButton()`)\n  and the clock (`getClock()`). This ability allows unit tests to be written.\n\nThe class looks like this (not all public methods are shown):\n```C++\nnamespace ace_button {\n\nclass ButtonConfig {\n  public:\n    static const uint16_t kDebounceDelay = 20;\n    static const uint16_t kClickDelay = 200;\n    static const uint16_t kDoubleClickDelay = 400;\n    static const uint16_t kLongPressDelay = 1000;\n    static const uint16_t kRepeatPressDelay = 1000;\n    static const uint16_t kRepeatPressInterval = 200;\n    static const uint16_t kHeartBeatInterval = 5000;\n\n    typedef uint16_t FeatureFlagType;\n    static const FeatureFlagType kFeatureClick = 0x01;\n    static const FeatureFlagType kFeatureDoubleClick = 0x02;\n    static const FeatureFlagType kFeatureLongPress = 0x04;\n    static const FeatureFlagType kFeatureRepeatPress = 0x08;\n    static const FeatureFlagType kFeatureSuppressAfterClick = 0x10;\n    static const FeatureFlagType kFeatureSuppressAfterDoubleClick = 0x20;\n    static const FeatureFlagType kFeatureSuppressAfterLongPress = 0x40;\n    static const FeatureFlagType kFeatureSuppressAfterRepeatPress = 0x80;\n    static const FeatureFlagType kFeatureSuppressClickBeforeDoubleClick = 0x100;\n    static const FeatureFlagType kFeatureHeartBeat = 0x200;\n    static const FeatureFlagType kFeatureSuppressAll = (\n        kFeatureSuppressAfterClick\n        | kFeatureSuppressAfterDoubleClick\n        | kFeatureSuppressAfterLongPress\n        | kFeatureSuppressAfterRepeatPress\n        | kFeatureSuppressClickBeforeDoubleClick);\n\n    typedef void (*EventHandler)(AceButton* button, uint8_t eventType,\n        uint8_t buttonState);\n\n    ButtonConfig() = default;\n\n    uint16_t getDebounceDelay() const;\n    uint16_t getClickDelay() const;\n    uint16_t getDoubleClickDelay() const;\n    uint16_t getLongPressDelay() const;\n    uint16_t getRepeatPressDelay() const;\n    uint16_t getRepeatPressInterval() const;\n    uint16_t getHeartBeatInterval() const;\n\n    void setDebounceDelay(uint16_t debounceDelay);\n    void setClickDelay(uint16_t clickDelay);\n    void setDoubleClickDelay(uint16_t doubleClickDelay);\n    void setLongPressDelay(uint16_t longPressDelay);\n    void setRepeatPressDelay(uint16_t repeatPressDelay);\n    void setRepeatPressInterval(uint16_t repeatPressInterval);\n    void setHeartBeatInterval(uint16_t heartBeatInterval);\n\n    virtual unsigned long getClock();\n    virtual int readButton(uint8_t pin);\n\n    bool isFeature(FeatureFlagType features) const;\n    void setFeature(FeatureFlagType features);\n    void clearFeature(FeatureFlagType features);\n    void resetFeatures();\n\n    void setEventHandler(EventHandler eventHandler);\n    void setIEventHandler(IEventHandler* eventHandler);\n\n    static ButtonConfig* getSystemButtonConfig();\n};\n\n}\n```\n\nThe `ButtonConfig` (or a customized subclass) can be created and assigned to one\nor more `AceButton` instances using dependency injection through the\n`AceButton(ButtonConfig*)` constructor. This constructor also accepts the same\n`(pin, defaultReleasedState, id)` parameters as `init(pin, defaultReleasedState,\nid)` method. Sometimes it's easier to set all the parameters in one place using\nthe constructor. Other times, the parameters are not known until the\n`AceButton::init()` method can be called from the global `setup()` method.\n\n```C++\nconst uint8_t PIN1 = 2;\nconst uint8_t PIN2 = 4;\n\nButtonConfig buttonConfig;\nAceButton button1(\u0026buttonConfig, PIN1);\nAceButton button2(\u0026buttonConfig, PIN2);\n\nvoid setup() {\n  pinMode(PIN1, INPUT_PULLUP);\n  pinMode(PIN2, INPUT_PULLUP);\n  ...\n}\n```\n\nAnother way to inject the `ButtonConfig` dependency is to use the\n`AceButton::setButtonConfig()` method but it is recommended that you use the\nconstructor instead because the dependency is easier to follow.\n\n\u003ca name=\"SystemButtonConfig\"\u003e\u003c/a\u003e\n#### System ButtonConfig\n\nA single instance of `ButtonConfig` called the \"System ButtonConfig\" is\nautomatically created by the library at startup. By default, all instances of\n`AceButton` are automatically assigned to this singleton instance. We explain in\nthe _Single Button Simplifications_ section below how this simplifies the code\nneeded to handle a single button.\n\n\u003ca name=\"ConfiguringEventHandler\"\u003e\u003c/a\u003e\n#### Configuring the EventHandler\n\nThe `ButtonConfig` class provides a number of methods which are mostly\nused internally by the `AceButton` class. The one method which is expected\nto be used by the calling client code is `setEventHandler()` which\nassigns the user-defined `EventHandler` callback function to the `ButtonConfig`\ninstance. This is explained in more detail below in the\n**EventHandler** section below.\n\n\u003ca name=\"TimingParameters\"\u003e\u003c/a\u003e\n#### Timing Parameters\n\nHere are the methods to retrieve the timing parameters:\n\n* `uint16_t getDebounceDelay();` (default: 20 ms)\n* `uint16_t getClickDelay();` (default: 200 ms)\n* `uint16_t getDoubleClickDelay();` (default: 400 ms)\n* `uint16_t getLongPressDelay();` (default: 1000 ms)\n* `uint16_t getRepeatPressDelay();` (default: 1000 ms)\n* `uint16_t getRepeatPressInterval();` (default: 200 ms)\n\nThe default values of each timing parameter can be changed at run-time using\nthe following methods:\n\n* `void setDebounceDelay(uint16_t debounceDelay);`\n* `void setClickDelay(uint16_t clickDelay);`\n* `void setDoubleClickDelay(uint16_t doubleClickDelay);`\n* `void setLongPressDelay(uint16_t longPressDelay);`\n* `void setRepeatPressDelay(uint16_t repeatPressDelay);`\n* `void setRepeatPressInterval(uint16_t repeatPressInterval);`\n\n\u003ca name=\"HardwareDependencies\"\u003e\u003c/a\u003e\n#### Hardware Dependencies\n\nThe `ButtonConfig` class has 2 methods which provide hooks to its external\nhardware dependencies:\n\n* `virtual unsigned long getClock();`\n* `virtual int readButton(uint8_t pin);`\n\nBy default these are mapped to the underlying Arduino system functions\nrespectively:\n\n* `millis()`\n* `digitalRead()`\n\nUnit tests are possible because these methods are `virtual` and the hardware\ndependencies can be swapped out with fake ones.\n\n\u003ca name=\"MultipleButtonConfigs\"\u003e\u003c/a\u003e\n#### Multiple ButtonConfig Instances\n\nWe have assumed that there is a 1-to-many relationship between a `ButtonConfig`\nand the `AceButton`. In other words, multiple buttons will normally be\nassociated with a single configuration. Each `AceButton` has a pointer to an\ninstance of `ButtonConfig`. So the cost of separating the `ButtonConfig` from\n`AceButton` is 2 bytes in each instance of `AceButton`. Note that this is\nequivalent to adding virtual methods to `AceButton` (which would add 2 bytes),\nso in terms of static RAM size, this is a wash.\n\nThe library is designed to handle multiple buttons, and it assumes that the\nbuttons are normally grouped together into a handful of types. For example,\nconsider the buttons of a car radio. It has several types of buttons:\n\n* the tuner buttons (2, up and down)\n* the preset buttons (6)\n* the AM/FM band button (1)\n\nIn this example, there are 9 buttons, but only 3 instances of `ButtonConfig`\nwould be needed.\n\n\u003ca name=\"EventHandlerTypedef\"\u003e\u003c/a\u003e\n### EventHandler Typedef\n\nThe event handler is a callback function that gets called when the `AceButton`\nclass determines that an interesting event happened on the button. The\nadvantage of this mechanism is that all the complicated logic of determining\nthe various events happens inside the `AceButton` class, and the user will\nnormally not need to worry about the details.\n\n\u003ca name=\"EventHandlerSignature\"\u003e\u003c/a\u003e\n#### EventHandler Signature\n\nThe event handler is defined in the `ButtonConfig` class and has the following\nsignature:\n\n```C++\nclass ButtonConfig {\n  public:\n    typedef void (*EventHandler)(AceButton* button, uint8_t eventType,\n        uint8_t buttonState);\n    ...\n};\n```\n\nThe event handler is registered with the `ButtonConfig` object, not with the\n`AceButton` object, although the convenience method\n`AceButton::setEventHandler()` is provided as a pass-through to the underlying\n`ButtonConfig` (see the _Single Button Simplifications_ section below):\n\n```C++\nButtonConfig buttonConfig;\n\nvoid handleEvent(AceButton* button, uint8_t eventType, uint8_t buttonState) {\n  ...\n}\n\nvoid setup() {\n  ...\n  buttonConfig.setEventHandler(handleEvent);\n  ...\n}\n```\n\nThe motivation for this design is to save static memory. If multiple buttons\nare associated with a single `ButtonConfig`, then it is not necessary for every\nbutton of that type to hold the same pointer to the `EventHandler` function. It\nis only necessary to save that information once, in the `ButtonConfig` object.\n\n**Pro Tip 1**: Comment out the unused parameter(s) in the `handleEvent()` method\nto avoid the `unused parameter` compiler warning:\n```C++\nvoid handleEvent(AceButton* /*button*/, uint8_t eventType,\n    uint8_t /*buttonState*/) {\n  ...\n}\n```\nThe Arduino sketch compiler can get confused with the parameters commented out,\nso you may need to add a forward declaration for the `handleEvent()` method\nbefore the `setup()` method:\n```C++\nvoid handleEvent(AceButton*, uint8_t, uint8_t);\n```\n\n**Pro Tips 2**: The event handler can be an object instead of just a function\npointer. An object-based event handler can be useful in more complex\napplications with numerous buttons. See the section on *Object-based Event\nHandler* in the *Advanced Topics* below.\n\n\u003ca name=\"EventHandlerParameters\"\u003e\u003c/a\u003e\n#### EventHandler Parameters\n\nThe `EventHandler` function receives 3 parameters from the `AceButton`:\n\n* `button`\n    * pointer to the `AceButton` instance that generated this event\n    * can be used to retrieve the `getPin()` or the `getId()`\n* `eventType`\n    * the type of this event given by the various `AceButton::kEventXxx`\n      constants\n* `buttonState`\n    * the `HIGH` or `LOW` button state that generated this event\n\nThe `button` pointer should be used only to extract information about the\nbutton that triggered the event. It should **not** be used to modify the\nbutton's internal variables in any way within the eventHandler. The logic in\n`AceButton::check()` assumes that those internal variable are held constant,\nand if they are changed by the eventHandler, unpredictable results may occur.\n(I should have made the `button` be a `const AceButton*` but by the time I\nrealized this, there were too many users of the library already, and I did not\nwant to make a breaking change to the API.)\n\nIf you are using only a single button, then you should need to check\nonly the `eventType`.\n\nIt is not expected that `buttonState` will be needed very often. It should be\nsufficient to examine just the `eventType` to determine the action that needs to\nbe performed. Part of the difficulty with this parameter is that it has the\nvalue of `LOW` or `HIGH`, but the physical interpretation of those values\ndepends on whether the button was wired with a pull-up or pull-down resistor.\nUse the helper function `button-\u003eisReleased(buttonState)` to translate the raw\n`buttonState` into a more meaningful determination if you need it.\n\n\u003ca name=\"OneEventHandler\"\u003e\u003c/a\u003e\n#### One EventHandler Per ButtonConfig\n\nOnly a single `EventHandler` per `ButtonConfig` is supported. An alternative\nwould have been to register a separate event handler for each of the 8\n`kEventXxx` events. But each callback function requires 2 bytes of memory (on\n8-bit processors, or 4 bytes on 32-bit processors) and it was assumed that in\nmost cases, the calling client code would be interested in only a few of these\nevent types, so it seemed wasteful to allocate 16 or 32 bytes when most of these\nwould be unused. If the client code really wants separate event handlers, it can\nbe easily emulated by invoking them through the main event handler:\n\n```C++\nvoid handleEvent(AceButton* button, uint8_t eventType, uint8_t buttonState) {\n  switch (eventType) {\n    case AceButton::kEventPressed:\n      handleEventPressed(button, eventType, buttonState);\n      break;\n    case AceButton::kEventReleased:\n      handleEventReleased(button, eventType, buttonState);\n      break;\n    ...\n  }\n}\n```\n\n\u003ca name=\"EventHandlerTips\"\u003e\u003c/a\u003e\n#### EventHandler Tips\n\nThe Arduino runtime environment is single-threaded, so the `EventHandler` is\ncalled in the middle of the `AceButton::check()` method, in the same thread as\nthe `check()` method. It is therefore important to write the `EventHandler`\ncode to run somewhat quickly, so that the delay doesn't negatively impact the\nlogic of the `AceButton::check()` algorithm. Since `AceButton::check()` should\nrun approximately every 5 ms, the user-provided `EventHandler` should run\nsomewhat faster than 5 ms. Given a choice, it is probably better to use the\n`EventHandler` to set some flags or variables and return quickly, then do\nadditional processing from the `loop()` method.\n\nSometimes it is too convenient or unavoidable to perform a long-running\noperation inside the event handler (e.g. making an HTTP). This is fine, I have\ndone this occasionally. Just be aware that the button scanning operation will\nnot work during that long-running operation.\n\nSpeaking of threads, the API of the AceButton Library was designed to work in a\nmulti-threaded environment, if that situation were to occur in the Arduino\nworld.\n\n\u003ca name=\"EventTypes\"\u003e\u003c/a\u003e\n### Event Types\n\nThe supported events are defined by a list of integer (`uint8_t`) constants in\n`AceButton.h`:\n\n* `AceButton::kEventPressed` (always enabled, cannot be suppressed)\n* `AceButton::kEventReleased` (default: enabled)\n* `AceButton::kEventClicked` (default: disabled)\n* `AceButton::kEventDoubleClicked` (default: disabled)\n* `AceButton::kEventLongPressed` (default: disabled)\n* `AceButton::kEventRepeatPressed` (default: disabled)\n* `AceButton::kEventLongReleased` (default: disabled, autoenabled by\n  `kFeatureSuppressAfterLongPress`, new for v1.8)\n\nThese values are sent to the `EventHandler` in the `eventType` parameter.\n\nTwo of the events are enabled by default, four are disabled by default but can\nbe enabled by using a Feature flag described below.\n\nDuring development and debugging, it is useful to print a human-readable version\nof these integer constants. The `AceButton::eventName(e)` is a static function\nwhich returns a string for each event constant, and can be used like this:\n\n```C++\nvoid handleEvent(AceButton* button, uint8_t eventType, uint8_t buttonState) {\n  ...\n  Serial.print(AceButton::eventName(eventType));\n  ...\n}\n```\n\n\u003ca name=\"ButtonConfigFeatureFlags\"\u003e\u003c/a\u003e\n### ButtonConfig Feature Flags\n\nThere are 9 flags defined in `ButtonConfig` which can\ncontrol the behavior of `AceButton` event handling:\n\n* `ButtonConfig::kFeatureClick`\n* `ButtonConfig::kFeatureDoubleClick`\n* `ButtonConfig::kFeatureLongPress`\n* `ButtonConfig::kFeatureRepeatPress`\n* `ButtonConfig::kFeatureSuppressAfterClick`\n* `ButtonConfig::kFeatureSuppressAfterDoubleClick`\n* `ButtonConfig::kFeatureSuppressAfterLongPress`\n* `ButtonConfig::kFeatureSuppressAfterRepeatPress`\n* `ButtonConfig::kFeatureSuppressClickBeforeDoubleClick`\n* `ButtonConfig::kFeatureSuppressAll`\n\nThese constants are used to set or clear the given flag:\n\n```C++\n// Get the current config.\nButtonConfig* config = button.getButtonConfig();\n\n// Set a specific feature\nconfig-\u003esetFeature(ButtonConfig::kFeatureLongPress);\n\n// Clear a specific feature\nconfig-\u003eclearFeature(ButtonConfig::kFeatureLongPress);\n\n// Test for a specific feature\nif (config-\u003eisFeature(ButtonConfig::kFeatureLongPress)) {\n  ...\n}\n\n// Clear all features\nconfig-\u003eresetFeatures()\n```\n\nThe meaning of these flags are described below.\n\n\u003ca name=\"EventActivation\"\u003e\u003c/a\u003e\n#### Event Activation\n\nOf the various event types, the following are disabled by default:\n\n* `AceButton::kEventClicked`\n* `AceButton::kEventDoubleClicked`\n* `AceButton::kEventLongPressed`\n* `AceButton::kEventRepeatPressed`\n* `AceButton::kEventLongReleased`\n* `AceButton::kEventHeartBeat`\n\nTo receive these events, call `ButtonConfig::setFeature()` with the following\ncorresponding  flags:\n\n* `ButtonConfig::kFeatureClick`\n* `ButtonConfig::kFeatureDoubleClick`\n* `ButtonConfig::kFeatureLongPress`\n* `ButtonConfig::kFeatureRepeatPress`\n* `ButtonConfig::kFeatureSuppressAfterLongPress`\n    * suppresses `kEventReleased` after a LongPress, but turns on\n      `kEventLongReleased` as a side effect\n* `ButtonConfig::kFeatureHeartBeat`\n\nlike this:\n\n```C++\nButtonConfig *config = button.getButtonConfig();\nconfig-\u003esetFeature(ButtonConfig::kFeatureClick);\n\n```\nTo disable these events, call `ButtonConfig::clearFeature()` with one of these\nflags, like this:\n\n```C++\nButtonConfig *config = button.getButtonConfig();\nconfig-\u003eclearFeature(ButtonConfig::kFeatureLongPress);\n```\n\nEnabling `kFeatureDoubleClick` automatically enables `kFeatureClick`, because we\nneed to have a Clicked event before a DoubleClicked event can be detected.\n\nIt seems unlikely that both `LongPress` and `RepeatPress` events would be\nuseful at the same time, but both event types can be activated if you need it.\n\n\u003ca name=\"EventSuppression\"\u003e\u003c/a\u003e\n#### Event Suppression\n\nEvent types can be considered to be built up in layers, starting with the\nlowest level primitive events: Pressed and Released. Higher level events are\nbuilt on top of the lower level events through various timing delays. When a\nhigher level event is detected, it is sometimes useful to suppress the lower\nlevel event that was used to detect the higher level event.\n\nFor example, a Clicked event requires a Pressed event followed by a Released\nevent within a `ButtonConfig::getClickDelay()` milliseconds (200 ms by\ndefault). The Pressed event is always generated. If a Clicked event is\ndetected, we could choose to generate both a Released event and a Clicked\nevent, and this is the default behavior.\n\nHowever, many times, it is useful to suppress the Released event if the Clicked\nevent is detected. The `ButtonConfig` can be configured to suppress these lower\nlevel events. Call the `setFeature(feature)` method passing the various\n`kFeatureSuppressXxx` constants:\n\n* `ButtonConfig::kFeatureSuppressAfterClick`\n    * suppresses the `kEventReleased` event after a Clicked event is detected\n    * also suppresses the Released event from the *first* Clicked of a\n      DoubleClicked, since `kFeatureDoubleClick` automatically enables\n      `kFeatureClick`\n* `ButtonConfig::kFeatureSuppressAfterDoubleClick`\n    * suppresses the `kEventReleased` event and the *second* Clicked event if a\n      DoubleClicked event is detected\n* `ButtonConfig::kFeatureSuppressAfterLongPress`\n    * suppresses the `kEventReleased` event if a LongPressed event is detected\n    * (v1.8) automatically enables `kEventLongReleased` event as a substitute\n      for the suppressed `kEventReleased`, see [Distinguishing Pressed and Long\n      Pressed](#PressedAndLongPressed) subsection below for more details.\n* `ButtonConfig::kFeatureSuppressAfterRepeatPress`\n    * suppresses the `kEventReleased` event after the last RepeatPressed event\n* `ButtonConfig::kFeatureSuppressClickBeforeDoubleClick`\n    * The *first* `kEventClicked` event is postponed by `getDoubleClickDelay()`\n      millis until the code can determine if a DoubleClick has occurred. If so,\n      then the postponed `kEventClicked` message to the `EventHandler` is\n      suppressed.\n    * See [Distinguishing Clicked and DoubleClicked](#ClickedAndDoubleClicked)\n      subsection below for more info.\n* `ButtonConfig::kFeatureSuppressAll`\n    * a convenience parameter that is the equivalent of suppressing all of the\n      previous events\n\nBy default, no suppression is performed.\n\nAs an example, to suppress the `kEventReleased` after a `kEventLongPressed`\n(this is actually often the case), you would do this:\n\n```C++\nButtonConfig* config = button.getButtonConfig();\nconfig-\u003esetFeature(ButtonConfig::kFeatureSuppressAfterLongPress);\n```\n\nThe special convenient constant `kFeatureSuppressAll` is equivalent of using all\nsuppression constants:\n\n```C++\nButtonConfig* config = button.getButtonConfig();\nconfig-\u003esetFeature(ButtonConfig::kFeatureSuppressAll);\n```\n\nAll suppressions can be cleared by using:\n```C++\nButtonConfig* config = button.getButtonConfig();\nconfig-\u003eclearFeature(ButtonConfig::kFeatureSuppressAll);\n```\n\nNote, however, that the `isFeature(ButtonConfig::kFeatureSuppressAll)` currently\nmeans \"isAnyFeature() implemented?\" not \"areAllFeatures() implemented?\" I don't\nexpect `isFeature()` to be used often (or at all) for `kFeatureSuppressAll`.\n\nYou can clear all feature at once using:\n```C++\nButtonConfig* config = button.getButtonConfig();\nconfig-\u003eresetFeatures();\n```\nThis is useful if you want to reuse a `ButtonConfig` instance and you want to\nreset its feature flags to its initial state.\n\n\u003ca name=\"SingleButtonSimplifications\"\u003e\u003c/a\u003e\n### Single Button Simplifications\n\nAlthough the AceButton library is designed to shine for multiple buttons, you\nmay want to use it to handle just one button. The library provides some features\nto make this simple case easy.\n\n1. The library automatically creates one instance of `ButtonConfig`\n   called a \"System ButtonConfig\". This System ButtonConfig can be retrieved\n   using the class static method `ButtonConfig::getSystemButtonConfig()`.\n1. Every instance of `AceButton` is assigned an instance of the System\n   ButtonConfig by default (which can be overridden manually).\n1. A convenience method allows the `EventHandler` for the System\n   ButtonConfig to be set easily through `AceButton` itself, instead of having\n   to get the System ButtonConfig first, then set the event handler. In other\n   words, `button.setEventHandler(handleEvent)` is a synonym for\n   `button.getButtonConfig()-\u003esetEventHandler(handleEvent)`.\n\nThese simplifying features allow a single button to be configured and used like\nthis:\n\n```C++\nAceButton button(BUTTON_PIN);\n\nvoid setup() {\n  pinMode(BUTTON_PIN, INPUT_PULLUP);\n  button.setEventHandler(handleEvent);\n  ...\n}\n\nvoid loop() {\n  button.check();\n}\n\nvoid handleEvent(AceButton* button, uint8_t eventType, uint8_t buttonState) {\n  ...\n}\n```\n\nTo configure the System ButtonConfig, you may need to add something like\nthis to the `setup()` section:\n\n```C++\n  button.getButtonConfig()-\u003esetFeature(ButtonConfig::kFeatureLongPress);\n```\n\n\u003ca name=\"MultipleButtons\"\u003e\u003c/a\u003e\n### Multiple Buttons\n\nWhen transitioning from a single button to multiple buttons, it's important to\nremember what's happening underneath the convenience methods. The single\n`AceButton` button is assigned to the System ButtonConfig that was created\nautomatically. When an `EventHandler` is assigned to the button, it is actually\nassigned to the System ButtonConfig. All subsequent instances of `AceButton`\nwill also be associated with this event handler, unless another `ButtonConfig`\nis explicitly assigned.\n\nThere are at least 2 ways you can configure multiple buttons.\n\n**Option 1: Multiple ButtonConfigs**\n\n```C++\n#include \u003cAceButton.h\u003e\nusing namespace ace_button;\n\nButtonConfig config1;\nAceButton button1(\u0026config1);\nButtonConfig config2;\nAceButton button2(\u0026config2);\n\nvoid button1Handler(AceButton* button, uint8_t eventType, uint8_t buttonState) {\n  Serial.println(\"button1\");\n}\n\nvoid button2Handler(AceButton* button, uint8_t eventType, uint8_t buttonState) {\n  Serial.println(\"button2\");\n}\n\nvoid setup() {\n  Serial.begin(115200);\n  pinMode(6, INPUT_PULLUP);\n  pinMode(7, INPUT_PULLUP);\n  config1.setEventHandler(button1Handler);\n  config2.setEventHandler(button2Handler);\n  button1.init(6);\n  button2.init(7);\n}\n\nvoid loop() {\n  button1.check();\n  button2.check();\n}\n```\n\nSee the example sketch\n[TwoButtonsUsingTwoButtonConfigs](examples/TwoButtonsUsingTwoButtonConfigs)\nwhich uses 2 `ButtonConfig` instances to configure 2 `AceButton`\ninstances.\n\n**Option 2: Multiple Button Discriminators**\n\nAnother technique keeps the single system `ButtonConfig` and the single\n`EventHandler`, but use the `AceButton::getPin()` to discriminate between the\nmultiple buttons:\n\n```C++\n#include \u003cAceButton.h\u003e\nusing namespace ace_button;\n\nAceButton button1(6);\nAceButton button2(7);\n\nvoid button1Handler(AceButton* button, uint8_t eventType, uint8_t buttonState) {\n  Serial.println(\"button1\");\n}\n\nvoid button2Handler(AceButton* button, uint8_t eventType, uint8_t buttonState) {\n  Serial.println(\"button2\");\n}\n\nvoid buttonHandler(AceButton* button, uint8_t eventType, uint8_t buttonState) {\n  switch (button-\u003egetPin()) {\n    case 6:\n      button1Handler(button, eventType, buttonState);\n      break;\n    case 7:\n      button2Handler(button, eventType, buttonState);\n      break;\n  }\n}\n\nvoid setup() {\n  Serial.begin(115200);\n  pinMode(6, INPUT_PULLUP);\n  pinMode(7, INPUT_PULLUP);\n  ButtonConfig* config = ButtonConfig::getSystemButtonConfig();\n  config-\u003esetEventHandler(buttonHandler);\n}\n\nvoid loop() {\n  button1.check();\n  button2.check();\n}\n```\n\nSee the example code\n[TwoButtonsUsingOneButtonConfig](examples/TwoButtonsUsingOneButtonConfig).\nwhich uses a single `ButtonConfig` instance to handle 2 `AceButton`\ninstances.\n\nSometimes, it is more convenient to use the `AceButton::getId()` method\nto identify the button instead of the `AceButton::getPin()`.\nSee [ArrayButtons](examples/ArrayButtons) for an example.\n\n\u003ca name=\"AdvancedTopics\"\u003e\u003c/a\u003e\n## Advanced Topics\n\n\u003ca name=\"ObjectBasedEventHandler\"\u003e\u003c/a\u003e\n### Object-based Event Handler\n\nThe `EventHandler` is a typedef that is defined to be a function pointer. This\nis a simple, low-overhead design that produces the smallest memory footprint,\nand allows the event handler to be written with the smallest amount of\nboilerplate code. The user does not have to override a class.\n\nIn more complex applications involving larger number of `AceButton` and\n`ButtonConfig` objects, it is often useful for the `EventHandler` to be an\nobject instead of a simple function pointer. This is especially true if the\napplication uses Object Oriented Programming (OOP) techniques for modularity and\nencapsulation. Using an object as the event handler allows additional context\ninformation to be injected into the event handler.\n\nTo support OOP techniques, AceButton v1.6 adds:\n\n* `IEventHandler` interface class\n    * contains a single pure virtual function `handleEvent()`\n* `ButtonConfig::setIEventHandler()` method\n    * accepts a pointer to an instance of the `IEventHandler` interface.\n\nThe `IEventHandler` interface is simply this:\n```C++\nclass IEventHandler {\n  public:\n    virtual void handleEvent(AceButton* button, uint8_t eventType,\n        uint8_t buttonState) = 0;\n};\n```\n\nAt least one of `ButtonConfig::setEventHandler()` or\n`ButtonConfig::setIEventHandler()` must be called before events are actually\ndispatched. If both are called, the last one takes precedence.\n\nSee\n[examples/SingleButtonUsingIEventHandler](examples/SingleButtonUsingIEventHandler) for an example.\n\n\u003ca name=\"ClickedAndDoubleClicked\"\u003e\u003c/a\u003e\n### Distinguishing Clicked and DoubleClicked\n\nOn a project using only a small number of buttons (due to physical limits or the\nlimited availability of pins), it may be desirable to distinguish between a\nsingle Clicked event and a DoubleClicked event from a single button. This is a\nchallenging problem to solve because fundamentally, a DoubleClicked event *must\nalways* generate a Clicked event, because a Clicked event must happen before it\ncan become a DoubleClicked event.\n\nNotice that on a desktop computer (running Windows, MacOS or Linux), a\ndouble-click on a mouse always generates both a Clicked and a DoubleClicked. The\nfirst Click selects the given desktop object (e.g. an icon or a window), and\nthe DoubleClick performs some action on the selected object (e.g. open the\nicon, or resize the window).\n\nThe AceButton Library provides 3 solutions which may work for some projects:\n\n**Method 1:** The `kFeatureSuppressClickBeforeDoubleClick` flag causes the first\nClicked event to be detected, but the posting of the event message (i.e. the\ncall to the `EventHandler`) is postponed until the state of the DoubleClicked\ncan be determined. If the DoubleClicked happens, then the first Clicked event\nmessage is suppressed. If DoubleClicked does not occur, the long delayed\nClicked message is sent via the `EventHandler`.\n\nThere are two noticeable disadvantages of this method. First, the response time\nof all Clicked events is delayed by about 600 ms (`kClickDelay +\nkDoubleClickDelay`) whether or not the DoubleClicked event happens. Second, the\nuser may not be able to accurately produce a Clicked event (due to the physical\ncharacteristics of the button, or the user's dexterity).\n\nIt may also be worth noting that only the Clicked event is postponed.\nThe accompanying Released event of the Clicked event is not postponed. So a\nsingle click action (without a DoubleClick) produces the following sequence of\nevents to the EventHandler:\n\n1. `kEventPressed` - at time 0ms\n1. `kEventReleased` - at time 200ms\n1. `kEventClicked` - at time 600ms (200ms + 400ms)\n\nThe `ButtonConfig` configuration looks like this:\n```C++\nButtonConfig* buttonConfig = button.getButtonConfig();\nbuttonConfig-\u003esetFeature(ButtonConfig::kFeatureDoubleClick);\nbuttonConfig-\u003esetFeature(\n    ButtonConfig::kFeatureSuppressClickBeforeDoubleClick);\n```\n\nSee the example code at\n[ClickVersusDoubleClickUsingSuppression](examples/ClickVersusDoubleClickUsingSuppression/).\n\n**Method 2:** A viable alternative is to use the Released event instead of the\nClicked event to distinguish it from the DoubleClicked. For this method to work,\nwe need to suppress the Released event after both Clicked and DoubleClicked.\n\nThe advantage of using this method is that there is no response time lag in the\nhandling of the Released event. To the user, there is almost no difference\nbetween triggering on the Released event, versus triggering on the Clicked\nevent.\n\nThe disadvantage of this method is that the Clicked event must be be ignored\n(because of the spurious Clicked event generated by the DoubleClicked). If the\nuser accidentally presses and releases the button too quickly, it generates a\nClicked event, which will cause the program to do nothing.\n\nThe `ButtonConfig` configuration looks like this:\n```C++\nButtonConfig* buttonConfig = button.getButtonConfig();\nbuttonConfig-\u003esetEventHandler(handleEvent);\nbuttonConfig-\u003esetFeature(ButtonConfig::kFeatureDoubleClick);\nbuttonConfig-\u003esetFeature(ButtonConfig::kFeatureSuppressAfterClick);\nbuttonConfig-\u003esetFeature(ButtonConfig::kFeatureSuppressAfterDoubleClick);\n```\n\nSee the example code at\n[ClickVersusDoubleClickUsingReleased](examples/ClickVersusDoubleClickUsingReleased/).\n\n**Method 3:** We could actually combine both Methods 1 and 2 so that either\nReleased or a delayed Click is considered to be a \"Click\". This may be the best\nof both worlds.\n\nThe `ButtonConfig` configuration looks like this:\n```C++\nButtonConfig* buttonConfig = button.getButtonConfig();\nbuttonConfig-\u003esetEventHandler(handleEvent);\nbuttonConfig-\u003esetFeature(ButtonConfig::kFeatureDoubleClick);\nbuttonConfig-\u003esetFeature(\n    ButtonConfig::kFeatureSuppressClickBeforeDoubleClick);\nbuttonConfig-\u003esetFeature(ButtonConfig::kFeatureSuppressAfterClick);\nbuttonConfig-\u003esetFeature(ButtonConfig::kFeatureSuppressAfterDoubleClick);\n```\n\nSee the example code at\n`examples/ClickVersusDoubleClickUsingBoth/`.\n\n\u003ca name=\"PressedAndLongPressed\"\u003e\u003c/a\u003e\n### Distinguishing Pressed and LongPressed\n\nSometimes it is useful to capture both a Pressed event and a LongPressed event\nfrom a single button. Since every button press always triggers a `kEventPressed`\nevent, the only reasonable way to distinguish between Pressed and LongPressed is\nto use the `kEventReleased` as a substitute for the simple Pressed event. When\nwe activate `kFeatureLongPress`, we then must activate the\n`kFeatureSuppressAfterLongPress` feature to suppress the `kEventReleased` event\nafter the `kEventLongPressed` to avoid yet another overlap of events.\n\n```C++\nButtonConfig* config = button.getButtonConfig();\nconfig-\u003esetFeature(ButtonConfig::kFeatureLongPress);\nconfig-\u003esetFeature(ButtonConfig::kFeatureSuppressAfterLongPress);\n```\n\nThis works most of the time, but I encountered an edge case. Occasionally we\nwant to capture the Released event after the LongPressed event, even if\n`kEventReleased` must be suppressed as described above. To solve this edge case,\nin v1.8, I added a new event type `kEventLongReleased` which is triggered as a\nsubstitute for `kEventReleased`, only if `kFeatureSuppressAfterLongPress` is\nused to suppress `kEventReleased`.\n\nSee the example code at\n[examples/PressVersusLongPress](examples/PressVersusLongPress) to see how all\nthese come together.\n\n\u003ca name=\"EventsAfterReboot\"\u003e\u003c/a\u003e\n### Events After Reboot\n\nA number of edge cases occur when the microcontroller is rebooted:\n\n* if the button is held down, should the Pressed event be triggered?\n* if the button is in its natural Released state, should the Released event\n  happen?\n* if the button is Pressed down, and `ButtonConfig` is configured to\n  support RepeatPress events, should the `kEventRepeatPressed` events\n  be triggered initially?\n\nI think most users would expect that in all these cases, the answer is no, the\nmicrocontroller should not trigger an event until the button undergoes a\nhuman-initiated change in state. The AceButton library implements this logic.\n(It might be useful to make this configurable using a `ButtonConfig` feature\nflag but that is not implemented.)\n\nOn the other hand, it is sometimes useful to perform some special action if a\nbutton is pressed while the device is rebooted. To support this use-case, call\nthe `AceButton::isPressedRaw()` in the global `setup()` method (after the\nbutton is configured). It will directly call the `digitalRead()` method\nassociated with the button pin and return `true` if the button is in the\nPressed state.\n\n\u003ca name=\"OrphanedClicks\"\u003e\u003c/a\u003e\n### Orphaned Clicks\n\nWhen a Clicked event is generated, the `AceButton` class looks for a\nsecond Clicked event within a certain time delay (default 400 ms) to\ndetermine if the second Clicked event is actually a DoubleClicked event.\n\nAll internal timestamps in `AceButton` are stored as `uint16_t`\n(i.e. an unsigned integer of 16 bits) in millisecond units. A 16-bit\nunsigned counter rolls over after 65536 iterations. Therefore, if the second\nClicked event happens between (65.636 seconds, 66.036 seconds) after the first\nClicked event, a naive-logic would erroneously consider the (long-delayed)\nsecond click as a double-click.\n\nThe `AceButton` contains code that prevents this from happening.\n\nNote that even if the `AceButton` class uses an `unsigned long` type (a 32-bit\ninteger on the Arduino), the overflow problem would still occur after `2^32`\nmilliseconds (i.e. 49.7 days). To be strictly correct, the `AceButton` class\nwould still need logic to take care of orphaned Clicked events.\n\n\u003ca name=\"BinaryEncodedButtons\"\u003e\u003c/a\u003e\n### Binary Encoded Buttons\n\nInstead of allocating one pin for each button, we can use\n[Binary Encoding](http://www.learnabout-electronics.org/Digital/dig44.php) to\nsupport large number of buttons with only a few pins. The circuit can be\nimplemented using a [74LS148](https://www.ti.com/product/SN74LS148) chip, or\nsimple diodes like this:\n\n![8 To 3 Encoding](docs/binary_encoding/encoded_8to3_diodes.png)\n\nThree subclasses of `ButtonConfig` are provided to handle binary encoded\nbuttons:\n\n* `Encoded4To2ButtonConfig`: 3 buttons with 2 pins\n* `Encoded8To3ButtonConfig`: 7 buttons with 3 pins\n* `EncodedButtonConfig`: `M=2^N-1` buttons with `N` pins\n\nSee [docs/binary_encoding/README.md](docs/binary_encoding/README.md) for\ninformation on how to use these classes.\n\n\u003ca name=\"ResistorLadderButtons\"\u003e\u003c/a\u003e\n### Resistor Ladder Buttons\n\nIt is possible to attach 1-8 (maybe more) buttons on a single analog pin through\na resistor ladder, and use the `analogRead()` to read the different voltages\ngenerated by each button. An example circuit looks like this:\n\n![Parallel Resistor Ladder](docs/resistor_ladder/resistor_ladder_parallel.png)\n\nThe `LadderButtonConfig` class handles this configuration.\n\nSee [docs/resistor_ladder/README.md](docs/resistor_ladder/README.md) for\ninformation on how to use this class.\n\n\u003ca name=\"HeapAllocation\"\u003e\u003c/a\u003e\n### Dynamic Allocation on the Heap\n\nAll classes in this library were originally designed to be created statically at\nstartup time and never deleted during the lifetime of the application. Since\nthey were never meant to be deleted through the pointer, I did not include the\nvirtual destructor for polymorphic classes (i.e. `ButtonConfig` and its\nsubclasses). The `AceButton` class is not polymorphic and does not need a\nvirtual destructor.\n\nMost 8-bit processors have limited flash and static memory (for example,\n32 kB flash and 2 KB static for the Nano or UNO). Adding a virtual destructor\ncauses **600** additional bytes of flash memory to be consumed. I suspect this\nis due to the virtual destructor pulling the `malloc()` and `free()`\nfunctions which are needed to implement the `new` and `delete` operators. For a\nlibrary that consumes only about 1200 bytes on an 8-bit processor, this increase\nin flash memory size did not seem acceptable.\n\nFor 32-bit processors (e.g. ESP8266, ESP32) which have far more flash memory\n(e.g. 1 MB) and static memory (e.g. 80 kB), it seems reasonable to allow\n`AceButton` and `ButtonConfig` to be created and deleted from the heap.\n(See [Issue #46](https://github.com/bxparks/AceButton/issues/46) for the\nmotivation.) Testing shows that the virtual destructor adds only about 60-120\nbytes of flash memory for these microcontrollers, probably because the\n`malloc()` and `free()` functions are already pulled in by something else. The\n60-120 bytes of additional consumption seems trivial compared to the range of\n~256 kB to ~4 MB flash memory available on these 32-bit processors.\n\nTherefore, I added a virtual destructor for the `ButtonConfig` class (v1.5) and\nenabled it for all architectures *other* than `ARDUINO_ARCH_AVR` (v1.6.1). This\nprevents 8-bit processors with limited memory from suffering the overhead of an\nextra 600 bytes of flash memory usage.\n\nEven for 32-bit processors, I still recommend avoiding the creation and deletion\nof objects from the heap, to avoid the risk of heap fragmentation. If a variable\nnumber of buttons is needed, it might be possible to design the application so\nthat all buttons which will ever be needed are predefined in a global pool. Even\nif some of the `AceButton` and `ButtonConfig` instances are unused, the overhead\nis probably smaller than the overhead of wasted space due to heap fragmentation.\n\n\u003ca name=\"DigitalWriteFast\"\u003e\u003c/a\u003e\n### Digital Write Fast\n\nThe `digitalWriteFast` libraries provide smaller and faster alternative versions\nthe `digitalWrite()`, `digitalRead()`, and `pinMode()` functions. I have used 2\nof the following libraries, but there probably others:\n\n* https://github.com/watterott/Arduino-Libs/tree/master/digitalWriteFast\n    * not an independent library\n    * you must manually `git clone` the repo and copy or symlink the\n      `digitalWriteFast/` directory into your Arduino IDE `libraries` directory\n* https://github.com/NicksonYap/digitalWriteFast\n    * a simplified fork of the watterott library\n    * but seems to be unmaintained, e.g. does not support ATtiny85\n* https://github.com/ArminJo/digitalWriteFast\n    * another fork of the watterott library\n    * seems to be maintained\n\nThese libraries provide the following functions: `digitalWriteFast()`,\n`digitalReadFast()`, and `pinModeFast()` which are usually valid only AVR\nprocessors. These alternative functions depend on the pin number and value to be\ncompile-time constants, bypassing the pin number lookup tables used by the\nstandard versions. These fast versions can be 20-50X faster. More importantly in\nmany situations, they can save 100-500 bytes of flash memory by not pulling in\nthe pin number lookup tables.\n\nI created 3 alternative versions of `ButtonConfig` which use the\n`digitalWriteFast` libraries:\n\n* [src/ace_button/fast/ButtonConfigFast1.h](src/ace_button/fast/ButtonConfigFast1.h)\n* [src/ace_button/fast/ButtonConfigFast2.h](src/ace_button/fast/ButtonConfigFast2.h)\n* [src/ace_button/fast/ButtonConfigFast3.h](src/ace_button/fast/ButtonConfigFast3.h)\n\n(If `ButtonConfigFast4.h` is needed, it is easy to copy `ButtonConfigFast3.h`\nand create a 4-pin version.)\n\nThese classes use C++ templates on the pin numbers, so that they can be passed\nto the `digitalReadFast()` functions as compile-time constants. Because they\ndepend on an external `digitalWriteFast` library, they are **not** included in\nthe `\u003cAceButton.h\u003e` header file. They must be included explicitly, as shown\nbelow:\n\n```C++\n#include \u003cArduino.h\u003e\n#include \u003cAceButton.h\u003e\n#include \u003cdigitalWriteFast.h\u003e\n#include \u003cace_button/fast/ButtonConfigFast2.h\u003e\n\nusing namespace ace_button;\n\n// Physical pin numbers attached to the buttons.\nconst uint8_t BUTTON1_PHYSICAL_PIN = 2;\nconst uint8_t BUTTON2_PHYSICAL_PIN = 3;\n\n// Virtual pin numbers attached to the buttons.\nconst uint8_t BUTTON1_PIN = 0;\nconst uint8_t BUTTON2_PIN = 1;\n\nButtonConfigFast2\u003cBUTTON1_PHYSICAL_PIN, BUTTON2_PHYSICAL_PIN\u003e buttonConfig;\nAceButton button1(\u0026buttonConfig, BUTTON1_PIN);\nAceButton button2(\u0026buttonConfig, BUTTON2_PIN);\n\nvoid handleEvent(AceButton* button, uint8_t eventType, uint8_t buttonState) {\n  ...\n}\n\nvoid setup() {\n  ...\n}\n\nvoid loop() {\n  // Should be called every 4-5ms or faster, for the default debouncing time\n  // of ~20ms.\n  button1.check();\n  button2.check();\n}\n```\n\nEach physical pin number given as template arguments to the\n`ButtonConfigFast2\u003c\u003e` class corresponds to a virtual pin number (starting with\n0) assigned to the `AceButton` object.  Within the event handler, everything is\nreferenced by the virtual pin number, just like the `EncodedButtonConfig` and\n`LadderButtonConfig` classes.\n\nHere are the example programs for each `ButtonConfigFast{N}` class:\n\n* [examples/SingleButtonFast](examples/SingleButtonFast) (`ButtonConfigFast1`)\n* [examples/TwoButtonsUsingOneButtonConfigFast](examples/TwoButtonsUsingOneButtonConfigFast) (`ButtonConfigFast2`)\n* [examples/ThreeButtonsUsingOneButtonConfigFast](examples/ThreeButtonsUsingOneButtonConfigFast) (`ButtonConfigFast3`)\n\nThe `LadderButtonConfig` class uses `analogRead()` which does not seem to\ndirectly benefit from `digitalWriteFast` libraries. However, if you use\n`pinModeFast()` instead of `pinMode()` in your global `setup()` function, you\ncan save about 50 bytes of flash (I think).\n\nThe `Encoded4To2ButtonConfig` and `Encoded8To3ButtonConfig` classes would\nprobably benefit from `digitalWriteFast` libraries, but I have not created the\n\"fast\" versions of these (i.e. `Encoded4To2ButtonConfigFast` and\n`Encoded8To3ButtonConfigFast`) because I have not needed them personally.\n\nThe general `EncodedButtonConfig` class is more difficult to convert into a\n\"fast\" version, because its constructor takes a pointer argument to an array of\nphysical pins. These are not compile-time constants so we would not be able to\nuse the `digitalWriteFast` libraries directly. I think the best we could do is\ncreate special `Encoded16To4ButtonConfigFast` and `Encoded32To5ButtonConfigFast`\nclasses.\n\n\u003ca name=\"HeartBeat\"\u003e\u003c/a\u003e\n### Heart Beat Event\n\nVersion 1.10 added the `kEventHeartBeat` event. By default it is disabled. It\ncan be enabled using the `kFeatureHeartBeat` flag:\n\n```C++\nButtonConfig* config = button.getButtonConfig();\nconfig-\u003esetFeature(ButtonConfig::kFeatureHeartBeat);\n```\n\nWhen enabled, the `AceButton` object sends a `kEventHeartBeat` event at a\nperiodic interval, with the number of milliseconds managed by the following\nmethods on the `ButtonConfig` object:\n\n* `void setHeartBeatInterval(uint16_t interval)`\n* `uint16_t getHeartBeatInterval() const`\n\nThe default is 5000 milliseconds.\n\nThe primary purpose of the HeartBeat event is to allow the user-provided event\nhandler (`IEventHandler` will likely be easiest for this purpose) to generate\ncustom event types which are not provided by `AceButton` itself by default. When\nthe button does not undergo any change in state explicitly initiated by the user\n(e.g. Released for a long time), the `AceButton` object will not trigger any\nevents normally. By activating the `kFeatureHeartBeat`, the event handler can\ngenerate custom events such as \"Pressed for 5 minutes\", or \"Released for 5\nMinutes\". See [examples/HeartBeat](examples/HeartBeat) for an example of an\n`IEventHandler` that implements this.\n\nThe `kEventHeartBeat` is triggered only by the progression of time, and is not\naffected by any internal state of the `AceButton`, such as the debouncing state,\nor the various logic for detecting Clicked, DoubleClicked, and so on. The\n`HeartBeatInterval` is intended to be relatively large, with the default set to\n5000 milliseconds, to avoid the overhead of calling the event handler too often.\nUsing a smaller interval may affect the detection logic of various other button\nevents if the HeartBeat handler consumes too much CPU time.\n\nThe `buttonState` is passed to the event handler by the HeartBeat dispatcher,\nthrough the callback function or `IEventHandler` interface, for example:\n\n```C++\ntypedef void (*EventHandler)(AceButton* button, uint8_t eventType,\n    uint8_t buttonState);\n```\n\nThis button state will be the last known, debounced and validated state. It will\nnot be the current button state. This is because the HeartBeat detector operates\nindependently of the debouncing logic, and it did not seem appropriate for the\nunvalidated `buttonState` to be passed to the event handler just because the\ntimer for the HeartBeat triggered in the middle of the debouncing logic.\n\n\u003ca name=\"ResourceConsumption\"\u003e\u003c/a\u003e\n## Resource Consumption\n\n\u003ca name=\"SizeOfClasses\"\u003e\u003c/a\u003e\n### SizeOf Classes\n\nHere are the sizes of the various classes on the 8-bit AVR microcontrollers\n(Arduino Uno, Nano, etc):\n\n```\nsizeof(AceButton): 17\nsizeof(ButtonConfig): 20\nsizeof(ButtonConfigFast1\u003c\u003e): 20\nsizeof(ButtonConfigFast2\u003c\u003e): 20\nsizeof(ButtonConfigFast3\u003c\u003e): 20\nsizeof(Encoded4To2ButtonConfig): 23\nsizeof(Encoded8To3ButtonConfig): 24\nsizeof(EncodedButtonConfig): 27\nsizeof(LadderButtonConfig): 28\n```\n\nFor 32-bit microcontrollers:\n\n```\nsizeof(AceButton): 20\nsizeof(ButtonConfig): 24\nsizeof(Encoded4To2ButtonConfig): 28\nsizeof(Encoded8To3ButtonConfig): 28\nsizeof(EncodedButtonConfig): 36\nsizeof(LadderButtonConfig): 36\n```\n\n(An early version of `AceButton`, with only half of the functionality, consumed\n40 bytes. It got down to 11 bytes before additional functionality increased it\nto its current 17.)\n\n\u003ca name=\"FlashAndStaticMemory\"\u003e\u003c/a\u003e\n### Flash And Static Memory\n\n[MemoryBenchmark](examples/MemoryBenchmark/) was used to determine the\nsize of the library for various microcontrollers (Arduino Nano to ESP32). Here\nare 2 samples:\n\nArduino Nano\n\n```\n+--------------------------------------------------------------+\n| functionality                   |  flash/  ram |       delta |\n|---------------------------------+--------------+-------------|\n| Baseline                        |    462/   11 |     0/    0 |\n| Baseline+pinMode+digitalRead    |    766/   11 |   304/    0 |\n|---------------------------------+--------------+-------------|\n| ButtonConfig                    |   1970/   56 |  1508/   45 |\n| ButtonConfigFast1               |   1686/   56 |  1224/   45 |\n| ButtonConfigFast2               |   1586/   73 |  1124/   62 |\n| ButtonConfigFast3               |   1628/   90 |  1166/   79 |\n|---------------------------------+--------------+-------------|\n| Encoded4To2ButtonConfig         |   2098/   93 |  1636/   82 |\n| Encoded8To3ButtonConfig         |   2318/  162 |  1856/  151 |\n| EncodedButtonConfig             |   2362/  185 |  1900/  174 |\n|---------------------------------+--------------+-------------|\n| LadderButtonConfig              |   2360/  198 |  1898/  187 |\n+--------------------------------------------------------------+\n```\n\nESP8266:\n\n```\n+--------------------------------------------------------------+\n| functionality                   |  flash/  ram |       delta |\n|---------------------------------+--------------+-------------|\n| Baseline                        | 260105/27892 |     0/    0 |\n| Baseline+pinMode+digitalRead    | 260201/27892 |    96/    0 |\n|---------------------------------+--------------+-------------|\n| ButtonConfig                    | 261589/27944 |  1484/   52 |\n|---------------------------------+--------------+-------------|\n| Encoded4To2ButtonConfig         | 261741/27984 |  1636/   92 |\n| Encoded8To3ButtonConfig         | 261885/28064 |  1780/  172 |\n| EncodedButtonConfig             | 262013/28104 |  1908/  212 |\n|---------------------------------+--------------+-------------|\n| LadderButtonConfig              | 262057/28116 |  1952/  224 |\n+--------------------------------------------------------------+\n```\n\n\u003ca name=\"CpuCycles\"\u003e\u003c/a\u003e\n### CPU Cycles\n\nThe profiling numbers for `AceButton::check()`,\n`EncodedButtonConfig::checkButtons()`, and `LadderButtonConfig::checkButtons()`\ncan be found in [examples/AutoBenchmark](examples/AutoBenchmark). Here are 2\nsamples, in units of microseconds.\n\nArduino Nano:\n\n```\n+---------------------------+-------------+---------+\n| Button Event              | min/avg/max | samples |\n|---------------------------+-------------+---------|\n| idle                      |  12/ 16/ 24 |    1929 |\n| press/release             |  12/ 17/ 28 |    1924 |\n| click                     |  12/ 16/ 28 |    1925 |\n| double_click              |  12/ 16/ 32 |    1922 |\n| long_press/repeat_press   |  12/ 18/ 28 |    1923 |\n|---------------------------+-------------+---------|\n| ButtonConfigFast1         |  12/ 16/ 24 |    1932 |\n| ButtonConfigFast2         |  20/ 30/ 40 |    1905 |\n| ButtonConfigFast3         |  32/ 44/ 52 |    1880 |\n|---------------------------+-------------+---------|\n| Encoded4To2ButtonConfig   |  60/ 73/ 80 |    1831 |\n| Encoded8To3ButtonConfig   | 168/196/204 |    1645 |\n| EncodedButtonConfig       |  84/110/116 |    1769 |\n| LadderButtonConfig        | 184/211/288 |    1625 |\n+---------------------------+-------------+---------+\n```\n\nESP8266:\n\n```\n+---------------------------+-------------+---------+\n| Button Event              | min/avg/max | samples |\n|---------------------------+-------------+---------|\n| idle                      |   6/  8/ 62 |    1920 |\n| press/release             |   6/  8/ 45 |    1921 |\n| click                     |   6/  7/ 18 |    1921 |\n| double_click              |   6/  7/ 12 |    1922 |\n| long_press/repeat_press   |   6/  8/ 12 |    1920 |\n|---------------------------+-------------+---------|\n| Encoded4To2ButtonConfig   |  22/ 27/ 46 |    1879 |\n| Encoded8To3ButtonConfig   |  56/ 67/ 76 |    1810 |\n| EncodedButtonConfig       |  43/ 54/ 70 |    1841 |\n| LadderButtonConfig        |  81/ 93/212 |    1772 |\n+---------------------------+-------------+---------+\n```\n\n\u003ca name=\"SystemRequirements\"\u003e\u003c/a\u003e\n## System Requirements\n\n\u003ca name=\"Hardware\"\u003e\u003c/a\u003e\n### Hardware\n\n**Tier 1: Fully Supported**\n\nThese boards are tested on each release:\n\n* Arduino Nano (16 MHz ATmega328P)\n* SparkFun Pro Micro (16 MHz ATmega32U4)\n* Seeeduino XIAO M0 (SAMD21, 48 MHz ARM Cortex-M0+)\n* STM32 Blue Pill (STM32F103C8, 72 MHz ARM Cortex-M3)\n* Adafruit ItsyBitsy M4 (SAMD51, 120 MHz ARM Cortext-M4)\n* NodeMCU 1.0 (ESP-12E module, 80MHz ESP8266)\n* WeMos D1 Mini (ESP-12E module, 80 MHz ESP8266)\n* ESP32 Dev Module (ESP-WROOM-32 module, 240MHz dual core Tensilica LX6)\n\n**Tier 2: Should work**\n\nThese boards should work but I don't test them as often:\n\n* ATtiny85 (8 MHz ATtiny85)\n* Arduino Pro Mini (16 MHz ATmega328P)\n* Mini Mega 2560 (Arduino Mega 2560 compatible, 16 MHz ATmega2560)\n* Teensy LC (48 MHz ARM Cortex-M0+)\n* Teensy 3.2 (96 MHz ARM Cortex-M4)\n\n**Tier 3: May work, but not supported**\n\n* Any platform using the\n  [ArduinoCore-API](https://github.com/arduino/ArduinoCore-api). For example:\n    * Arduino Nano Every\n    * Arduino MKRZero\n    * Arduino UNO R4\n    * Raspberry Pi Pico RP2040\n\nMost of my libraries are *not* compatible with the ArduinoCore-API. However\nAceButton is an exception because it is simple enough that it may still work on\nthese boards.\n\n\u003ca name=\"ToolChain\"\u003e\u003c/a\u003e\n### Tool Chain\n\nThis library was developed and tested using:\n\n* [Arduino IDE 1.8.19](https://www.arduino.cc/en/Main/Software)\n* [Arduino CLI 0.31.0](https://arduino.github.io/arduino-cli)\n* [SpenceKonde ATTinyCore 1.5.2](https://github.com/SpenceKonde/ATTinyCore)\n* [Arduino AVR Boards 1.8.6](https://github.com/arduino/ArduinoCore-avr)\n* [SparkFun AVR Boards 1.1.13](https://github.com/sparkfun/Arduino_Boards)\n* [Arduino SAMD Boards 1.8.9](https://github.com/arduino/ArduinoCore-samd)\n* [Adafruit SAMD Boards 1.7.11](https://github.com/adafruit/ArduinoCore-samd)\n* [Seeeduino SAMD Boards 1.8.4](https://github.com/Seeed-Studio/ArduinoCore-samd)\n* [STM32duino 2.5.0](https://github.com/stm32duino/Arduino_Core_STM32)\n* [ESP8266 Arduino Core 3.0.2](https://github.com/esp8266/Arduino)\n* [ESP32 Arduino Core 2.0.9](https://github.com/espressif/arduino-esp32)\n* [Teensyduino 1.56](https://www.pjrc.com/teensy/td_download.html)\n\nIt should work with [PlatformIO](https://platformio.org/) but I have\nnot tested it.\n\nThe library works on Linux or MacOS (using both g++ and clang++ compilers) using\nthe [EpoxyDuino](https://github.com/bxparks/EpoxyDuino) emulation layer.\n\n\u003ca name=\"OperatingSystem\"\u003e\u003c/a\u003e\n### Operating System\n\nI use Ubuntu Linux 22.04 or its variants (i.e. Linux Mint) for most of my\ndevelopment.\n\n\u003ca name=\"BackgroundMotivation\"\u003e\u003c/a\u003e\n## Background Motivation\n\nThere are numerous \"button\" libraries out there for the Arduino. Why write\nanother one? I wanted to add a button to an addressable strip LED controller,\nwhich was being refreshed at 120 Hz. I had a number of requirements:\n\n* the button needed to support a LongPress event, in addition to the simple\n  Press and Release events\n* the button code must not interfere with the LED refresh code which was\n  updating the LEDs at 120 Hz\n* well-tested, I didn't want to be hunting down random and  obscure bugs\n\nSince the LED refresh code needed to run while the button code was waiting for\na \"LongPress\" delay, it seemed that the cleanest API for a button library\nwould use an event handler callback mechanism. This reduced the number of\ncandidate libraries to a handful. Of these, only a few of them supported a\nLongPress event. I did not find the remaining ones flexible enough for my\nbutton needs in the future. Finally, I knew that it was tricky to write correct\ncode for debouncing and detecting various events (e.g. DoubleClick, LongPress,\nRepeatPress). I looked for a library that contained unit tests, and I found\nnone.\n\nI decided to write my own and use the opportunity to learn how to create and\npublish an Arduino library.\n\n\u003ca name=\"NonGoals\"\u003e\u003c/a\u003e\n### Non-goals\n\nAn Arduino UNO or Nano has 16 times more flash memory (32KB) than static memory\n(2KB), so the library is optimized to minimize the static memory usage. The\nAceButton library is not optimized to create a small program size (i.e. flash\nmemory), or for small CPU cycles (i.e. high execution speed). I assumed that if\nyou are seriously optimizing for program size or CPU cycles, you will probably\nwant to write everything yourself from scratch.\n\nThat said, [examples/MemoryBenchmark](examples/MemoryBenchmark/) shows that the\nlibrary consumes between 970-2180  bytes of flash memory, and\n[AutoBenchmark](examples/AutoBenchmark) shows that `AceButton::check()` takes\nbetween ~15 microseconds on a 16MHz ATmega328P chip and 2-3 microseconds on an\nESP32. Hopefully that is small enough and fast enough for the vast majority of\npeople.\n\nWith v1.9, I started using AceButton on an ATtiny85 which has only 8kB of flash\nand 0.5 kB of static RAM. Flash memory consumption became more important and I\ncreated the `ButtonConfigFast1`, `ButtonConfigFast2`, and `ButtonConfigFast3`\nclasses to decrease the flash memory consumption by using one of the\n`\u003cdigitalWriteFast.h\u003e` 3rd party libraries. See\n[Digital Write Fast](#DigitalWriteFast) for more info.\n\n\u003ca name=\"BugsAndLimitations\"\u003e\u003c/a\u003e\n## Bugs and Limitations\n\nThis is the first Arduino library that I ever created. It has grown organically\nover time, while maintaining backwards compatibility as much as possible. There\nare some early design decisions that could have been better with infinite\ninsight. Here are some limitations and bugs.\n\n* The `ButtonConfig` class should have been named `ButtonGroup`.\n    * This was not obvious until I had implemented the `EncodedButtonConfig` and\n      the `LadderButtonConfig` classes.\n    * But I cannot change the names without breaking backwards compatibility.\n* The [Single Button Simplifications](#SingleButtonSimplifications) was probably\n  a mistake.\n    * It makes the API more complex and cluttered than it could be.\n    * It causes an automatic creation of the SystemButtonConfig instance of\n      `ButtonConfig` even when it is not needed by the application, unless\n      special (non-obvious) precautions are taken.\n    * On the other hand, it makes the simplest `HelloButton` program very\n      simple.\n* Embedding multiple `AceButton` objects in an array or a `struct` is difficult\n  if not impossible.\n    * See [Discussion#106](https://github.com/bxparks/AceButton/discussions/106)\n      for some details.\n    * The C++ rules for object initialization are so complicated, and have\n      changed with different versions of C++ (C++11, C++14, C++17, C++20,\n      C++23??), that I cannot understand them anymore.\n    * Probably will never be fixed because I'm tired of the complexity of\n      the C++ language.\n* AceButton does not provide built-in support for simultaneous buttons.\n    * See [Discussion#83](https://github.com/bxparks/AceButton/discussions/83),\n      [Discussion#94](https://github.com/bxparks/AceButton/discussions/94), and\n      [Discussion#96](https://github.com/bxparks/AceButton/discussions/96).\n    * The [examples/SimultaneousButtons](examples/SimultaneousButtons) program\n      shows how it could be detected using a custom `IEventHandler`. However, it\n      has not been extensively tested. I don't even remember writing it 2 years\n      ago.\n    * This remains an open problem because I don't use simultaneous buttons in\n      my applications, and I have not spent much time thinking about how to\n      handle all the combinations of events and their timing interactions that\n      are possible with 2 buttons.\n* The `EventHandler` and `IEventHandler` send an `AceButton*` pointer into\n  the arguments, instead of a `const AceButton*` pointer.\n    * Too late to change that without breaking backwards compatibility.\n* All internal timing variables are `uint16_t` instead of `uint32_t`.\n    * This means that various timing parameters (ClickDelay, LongPressDelay,\n      etc) can be maximum of 65535 milliseconds.\n    * Using 16-bit integers saves RAM, at least 8 bytes for each instance of\n      `AceButton`, and 14 bytes for `ButtonConfig` and its subclasses. And\n      probably saves flash memory on 8-bit processors because fewer machine\n      instruction are needed to operate on 16-bit variables compared to 32-bit\n      variables.\n    * Client applications were assumed to use as many as 10-20 buttons. That's a\n      savings of 80-160 bytes which makes a difference on 8-bit AVR processors\n      with only 2 kB of RAM.\n* The library does not support the mirror-image version of `kEventLongPressed`\n  for the Released state.\n    * In other words, an event that is triggered when a button has been released\n      for a long time (several seconds).\n    * The current `kEventLongReleased` is a different type of event.\n    * See\n      [Discussion#118](https://github.com/bxparks/AceButton/discussions/118)\n      for info.\n    * An alternative solution might be to use the `kEventHeartBeat` and a custom\n      `IEventHandler` to generate a custom event. See\n      [examples/HeartBeat](examples/HeartBeat) for a example.\n* The [Event Supression](#EventSuppression) features are complicated, hard\n  to understand and remember.\n    * I wrote the code and I cannot remember how they all work. I have to\n      refer to this README.md each time I want to use them.\n    * These features grew organically. Maybe there is a better, more consistent\n      way of implementing them, but it was not obvious during the development of\n      these features.\n\n\u003ca name=\"License\"\u003e\u003c/a\u003e\n## License\n\n* Versions 1.0 to 1.0.6:\n  [Apache License 2.0](https://www.apache.org/licenses/LICENSE-2.0)\n* Versions 1.1 and above: [MIT License](https://opensource.org/licenses/MIT)\n\nI changed to the MIT License starting with version 1.1 because the MIT License\nis so simple to understand. I could not be sure that I understood what the\nApache License 2.0 meant.\n\n\u003ca name=\"FeedbackAndSupport\"\u003e\u003c/a\u003e\n## Feedback and Support\n\nIf you have any questions, comments, or feature requests for this library,\nplease use the [GitHub\nDiscussions](https://github.com/bxparks/AceButton/discussions) for this project.\nIf you have a bug report, please file a ticket in [GitHub\nIssues](https://github.com/bxparks/AceButton/issues). Feature requests should\ngo into Discussions first because they often have alternative solutions which\nare useful to remain visible, instead of disappearing from the default view of\nthe Issue tracker after the ticket is closed.\n\nPlease refrain from emailing me directly unless the content is sensitive. The\nproblem with email is that I cannot reference the email conversation when other\npeople ask similar questions later.\n\n\u003ca name=\"Author\"\u003e\u003c/a\u003e\n## Author\n\nCreated by Brian T. Park (brian@xparks.net).\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fbxparks%2Facebutton","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fbxparks%2Facebutton","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fbxparks%2Facebutton/lists"}