acid-drop- Hacking the planet from a LilyGo T-Deck using custom firmware |
git clone git://git.acid.vegas/acid-drop.git |
Log | Files | Refs | Archive | README | LICENSE |
AceButton.h (18480B)
1 /* 2 MIT License 3 4 Copyright (c) 2018 Brian T. Park 5 6 Permission is hereby granted, free of charge, to any person obtaining a copy 7 of this software and associated documentation files (the "Software"), to deal 8 in the Software without restriction, including without limitation the rights 9 to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 copies of the Software, and to permit persons to whom the Software is 11 furnished to do so, subject to the following conditions: 12 13 The above copyright notice and this permission notice shall be included in all 14 copies or substantial portions of the Software. 15 16 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 SOFTWARE. 23 */ 24 25 #ifndef ACE_BUTTON_ACE_BUTTON_H 26 #define ACE_BUTTON_ACE_BUTTON_H 27 28 #if !defined(RASPBERRY_PI) 29 #include <Arduino.h> 30 #else 31 #include <raspi/raspi.h> 32 #endif /* RASPBERRY_PI */ 33 34 #include "ButtonConfig.h" 35 36 namespace ace_button { 37 38 /** 39 * An Adjustable Compact Event-driven (ACE) Button library that debounces and 40 * dispatches button events to a user-defined event handler. Supported events 41 * types are: 42 * 43 * - kEventPressed 44 * - kEventReleased 45 * - kEventClicked 46 * - kEventDoubleClicked 47 * - kEventLongPressed 48 * - kEventRepeatPressed 49 * 50 * The check() method should be called from the loop() at least 2-3 times during 51 * the debouncing time period. For 20 ms delay, the check() method should be 52 * called at a minimum of every 5 ms. The execution time of check() on a 16 53 * MHz Arduino ATmega328P MCU seems to about about 12-14 microseconds. 54 */ 55 class AceButton { 56 public: 57 // The supported event types. 58 59 /** Button was pressed. */ 60 static const uint8_t kEventPressed = 0; 61 62 /** Button was released. */ 63 static const uint8_t kEventReleased = 1; 64 65 /** 66 * Button was clicked (Pressed and Released within 67 * ButtonConfig::getClickDelay()). 68 */ 69 static const uint8_t kEventClicked = 2; 70 71 /** 72 * Button was double-clicked. (Two clicks within 73 * ButtonConfig::getDoubleClickDelay()). 74 */ 75 static const uint8_t kEventDoubleClicked = 3; 76 77 /** 78 * Button was held down for longer than 79 * ButtonConfig::getLongPressDelay()). 80 */ 81 static const uint8_t kEventLongPressed = 4; 82 83 /** 84 * Button was held down and auto generated multiple presses. The first event 85 * is triggered after ButtonConfig::getRepeatPressDelay(), then the event 86 * fires repeatedly every ButtonConfig::getRepeatPressInterval() until the 87 * button is released. 88 */ 89 static const uint8_t kEventRepeatPressed = 5; 90 91 /** 92 * Button state is unknown. This is a third state (different from LOW or 93 * HIGH) used when the class is first initialized upon reboot. 94 */ 95 static const uint8_t kButtonStateUnknown = 2; 96 97 /** 98 * Constructor defines parameters of the button that changes from button to 99 * button. These parameters don't change during the runtime of the program. 100 * Another way to initialize the object is to create an instance using 101 * an empty constructor, then use the init() method to initialize the 102 * object with these parameters. 103 * 104 * Using the constructor often reads better for simple situations where only 105 * a single button is used, and it doesn't need to be configured 106 * significantly. Using the init() method can make the code be more readable 107 * when multiple buttons are used, and they need to be significantly 108 * customized. The init() method allows the button configuration code to 109 * appear in close proximity to the pinMode() methods which sets up the 110 * hardware pins. 111 * 112 * @param pin The pin number of the button. Default 0. Normally the pin 113 * number should be given at construction time. However, the pin number 114 * the pin number can be omitted so that the pin number can be assigned at 115 * setup() time using the setPin() method. 116 * 117 * @param defaultReleasedState The pin state when the button is in the 118 * initial released position. Default HIGH. When using a pullup resistor 119 * (either external or internal) and the button is connected to ground, this 120 * should be set to HIGH. When using an external pulldown resistor and the 121 * button is connected to Vcc (5V or 3.3V), this should be set to LOW. The 122 * defaultReleasedState can be assigned using the constructor or the 123 * init() method. 124 * 125 * @param id This is an optional user-defined identifier for the 126 * button. For example, this could be an index into an array of data that is 127 * associated with the button. 128 */ 129 explicit AceButton(uint8_t pin = 0, uint8_t defaultReleasedState = HIGH, 130 uint8_t id = 0); 131 132 /** 133 * Constructor that accepts a ButtonConfig as a dependency. Dependency 134 * injection using this constructor is now recommended over using the 135 * setButtonConfig() method because it makes the dependency more clear. 136 */ 137 explicit AceButton(ButtonConfig* buttonConfig); 138 139 /** 140 * Reset the button to the initial constructed state. In particular, 141 * getLastButtonState() returns kButtonStateUnknown. The parameters are 142 * identical as the parameters in the AceButton() constructor. 143 */ 144 void init(uint8_t pin = 0, uint8_t defaultReleasedState = HIGH, 145 uint8_t id = 0); 146 147 /** Get the ButtonConfig associated with this Button. */ 148 ButtonConfig* getButtonConfig() ACE_BUTTON_INLINE { 149 return mButtonConfig; 150 } 151 152 /** 153 * Set the ButtonConfig associated with this Button. It is recommended that 154 * the AceButton(ButtonConfig*) constructor is used instead to make the 155 * dependency to ButtonConfig more explicit. 156 */ 157 void setButtonConfig(ButtonConfig* buttonConfig) ACE_BUTTON_INLINE { 158 mButtonConfig = buttonConfig; 159 } 160 161 /** 162 * Convenience method to set the event handler. Event handlers are stored in 163 * the ButtonConfig object, not in the AceButton object, to save 164 * memory. (Multiple buttons are likely to share the same event handler.) So 165 * this method is just a pass-through to ButtonConfig::setEventHandler(). If 166 * you are using multiple ButtonConfig objects, you should call the 167 * ButtonConfig::setEventHandler() method on those objects directly, instead 168 * of using this method. 169 */ 170 void setEventHandler(ButtonConfig::EventHandler eventHandler) 171 ACE_BUTTON_INLINE { 172 mButtonConfig->setEventHandler(eventHandler); 173 } 174 175 /** Get the button's pin number. */ 176 uint8_t getPin() ACE_BUTTON_INLINE { return mPin; } 177 178 /** Get the custom identifier of the button. */ 179 uint8_t getId() ACE_BUTTON_INLINE { return mId; } 180 181 /** Get the initial released state of the button, HIGH or LOW. */ 182 uint8_t getDefaultReleasedState(); 183 184 /** 185 * Return the button state that was last valid. This is a tri-state 186 * function. It may return HIGH, LOW or kButtonStateUnknown to indicate that 187 * the last state is not known. This method is **not** for public 188 * consumption, it is exposed only for testing purposes. Consider it to be a 189 * private method. Use the buttonState parameter provided to the 190 * EventHandler. 191 * 192 * In a more general multi-threaded environment (which the Arduino is not, 193 * fortunately or unfortunately), the getLastButtonState() may have changed 194 * from the value of buttonState provided to the event handler. In other 195 * words, there is a race-condition. 196 */ 197 uint8_t getLastButtonState() ACE_BUTTON_INLINE { 198 return mLastButtonState; 199 } 200 201 /** 202 * Check state of button and trigger event processing. This method should be 203 * called from the loop() method in Arduino every 4-5 times during the 204 * getDebounceDelay() time (default 20 ms), so about every 5 ms. If this 205 * is called less often than that, the debouncing algorithm may not work 206 * correctly, which may cause other event detection algorithms to fail. 207 */ 208 void check(); 209 210 /** 211 * Returns true if the given buttonState represents a 'Released' state for 212 * the button. Returns false if the buttonState is 'Pressed' or 213 * kButtonStateUnknown. 214 * 215 * The HIGH or LOW logical value of buttonState represents different a 216 * button position depending on whether the button is wired with a pull-up 217 * or a pull-down resistor. This method translates the logical level to the 218 * physical position which allows the client code to be independent of the 219 * physical wiring. 220 * 221 * Normally, the eventType given to the EventHandler should be sufficient 222 * because the value of the eventType already encodes this information. 223 * This method is provided just in case. 224 */ 225 bool isReleased(uint8_t buttonState) ACE_BUTTON_INLINE { 226 return buttonState == getDefaultReleasedState(); 227 } 228 229 /** 230 * Read the button state directly using ButtonConfig and return true if the 231 * button is in the Pressed state. This method is intended to be used in the 232 * global setup() to determine if the button was pressed while the device 233 * was booted. This method does not use the check() method, does not perform 234 * any debouncing, and does not dispatch events to the EventHandler. 235 */ 236 bool isPressedRaw() ACE_BUTTON_INLINE { 237 return !isReleased(mButtonConfig->readButton(mPin)); 238 } 239 240 // Some of these private methods may be useful to the calling client but I 241 // don't want to release them to the public because I want to keep the API as 242 // small as possible for easier long term maintenance. (Once a method is 243 // released to the public, it must be supported forever to ensure backwards 244 // compatibility with older client code.) 245 246 private: 247 // Disable copy-constructor and assignment operator 248 AceButton(const AceButton&) = delete; 249 AceButton& operator=(const AceButton&) = delete; 250 251 /** Set the pin number of the button. */ 252 void setPin(uint8_t pin) ACE_BUTTON_INLINE { mPin = pin; } 253 254 /** 255 * Set the initial released state of the button. 256 * 257 * @param state If a pull up resistor is used, this should be HIGH. If a 258 * pull down resistor is used, this should be LOW. The behavior is undefined 259 * for any other values of 'state'. 260 */ 261 void setDefaultReleasedState(uint8_t state); 262 263 /** Set the identifier of the button. */ 264 void setId(uint8_t id) ACE_BUTTON_INLINE { mId = id; } 265 266 // Various bit masks to store a boolean flag in the 'mFlags' field. 267 // We use bit masks to save static RAM. If we had used a 'bool' type, each 268 // of these would consume one byte. 269 static const uint8_t kFlagDefaultReleasedState = 0x01; 270 static const uint8_t kFlagDebouncing = 0x02; 271 static const uint8_t kFlagPressed = 0x04; 272 static const uint8_t kFlagClicked = 0x08; 273 static const uint8_t kFlagDoubleClicked = 0x10; 274 static const uint8_t kFlagLongPressed = 0x20; 275 static const uint8_t kFlagRepeatPressed = 0x40; 276 static const uint8_t kFlagClickPostponed = 0x80; 277 278 // Methods for accessing the button's internal states. 279 // I don't expect these to be useful to the outside world. 280 281 // If this is set, then mLastDebounceTime is valid. 282 bool isDebouncing() ACE_BUTTON_INLINE { 283 return mFlags & kFlagDebouncing; 284 } 285 286 void setDebouncing() ACE_BUTTON_INLINE { 287 mFlags |= kFlagDebouncing; 288 } 289 290 void clearDebouncing() ACE_BUTTON_INLINE { 291 mFlags &= ~kFlagDebouncing; 292 } 293 294 // If this is set, then mLastPressTime is valid. 295 bool isPressed() ACE_BUTTON_INLINE { 296 return mFlags & kFlagPressed; 297 } 298 299 void setPressed() ACE_BUTTON_INLINE { 300 mFlags |= kFlagPressed; 301 } 302 303 void clearPressed() ACE_BUTTON_INLINE { 304 mFlags &= ~kFlagPressed; 305 } 306 307 // If this is set, then mLastClickTime is valid. 308 bool isClicked() ACE_BUTTON_INLINE { 309 return mFlags & kFlagClicked; 310 } 311 312 void setClicked() ACE_BUTTON_INLINE { 313 mFlags |= kFlagClicked; 314 } 315 316 void clearClicked() ACE_BUTTON_INLINE { 317 mFlags &= ~kFlagClicked; 318 } 319 320 // A double click was detected. No need to store the last double-clicked 321 // time because we don't support a triple-click event (yet). 322 bool isDoubleClicked() ACE_BUTTON_INLINE { 323 return mFlags & kFlagDoubleClicked; 324 } 325 326 void setDoubleClicked() ACE_BUTTON_INLINE { 327 mFlags |= kFlagDoubleClicked; 328 } 329 330 void clearDoubleClicked() ACE_BUTTON_INLINE { 331 mFlags &= ~kFlagDoubleClicked; 332 } 333 334 // If this is set, then mLastPressTime can be treated as the start 335 // of a long press. 336 bool isLongPressed() ACE_BUTTON_INLINE { 337 return mFlags & kFlagLongPressed; 338 } 339 340 void setLongPressed() ACE_BUTTON_INLINE { 341 mFlags |= kFlagLongPressed; 342 } 343 344 void clearLongPressed() ACE_BUTTON_INLINE { 345 mFlags &= ~kFlagLongPressed; 346 } 347 348 // If this is set, then mLastRepeatPressTime is valid. 349 bool isRepeatPressed() ACE_BUTTON_INLINE { 350 return mFlags & kFlagRepeatPressed; 351 } 352 353 void setRepeatPressed() ACE_BUTTON_INLINE { 354 mFlags |= kFlagRepeatPressed; 355 } 356 357 void clearRepeatPressed() ACE_BUTTON_INLINE { 358 mFlags &= ~kFlagRepeatPressed; 359 } 360 361 bool isClickPostponed() ACE_BUTTON_INLINE { 362 return mFlags & kFlagClickPostponed; 363 } 364 365 void setClickPostponed() ACE_BUTTON_INLINE { 366 mFlags |= kFlagClickPostponed; 367 } 368 369 void clearClickPostponed() ACE_BUTTON_INLINE { 370 mFlags &= ~kFlagClickPostponed; 371 } 372 373 /** 374 * Return true if debouncing succeeded and the buttonState value can be 375 * used. Return false if buttonState should be ignored until debouncing 376 * phase is complete. 377 */ 378 bool checkDebounced(uint16_t now, uint8_t buttonState); 379 380 /** 381 * Return true if the button was already initialzed and determined to be in 382 * a HIGH or LOW state. Return false if the button was previously in 383 * kButtonStateUnknown state which implies that the event handler should 384 * NOT be fired. 385 */ 386 bool checkInitialized(uint16_t buttonState); 387 388 /** Categorize the button event. */ 389 void checkEvent(uint16_t now, uint8_t buttonState); 390 391 /** Check for a long press event and dispatch to event handler. */ 392 void checkLongPress(uint16_t now, uint8_t buttonState); 393 394 /** Check for a repeat press event and dispatch to event handler. */ 395 void checkRepeatPress(uint16_t now, uint8_t buttonState); 396 397 /** Check for onChange event and check for Press or Release events. */ 398 void checkChanged(uint16_t now, uint8_t buttonState); 399 400 /** 401 * Check for Released and Click events and dispatch to respective 402 * handlers. 403 */ 404 void checkReleased(uint16_t now, uint8_t buttonState); 405 406 /** Check for Pressed event and dispatch to handler. */ 407 void checkPressed(uint16_t now, uint8_t buttonState); 408 409 /** Check for a single click event and dispatch to handler. */ 410 void checkClicked(uint16_t now); 411 412 /** 413 * Check for a double click event and dispatch to handler. Return true if 414 * double click detected. 415 */ 416 void checkDoubleClicked(uint16_t now); 417 418 /** 419 * Check for an orphaned click that did not generate a double click and 420 * clean up internal state. If we don't do this, the second click may be 421 * generated after the uint16_t rolls over in 65.5 seconds, causing an 422 * unwanted double-click. Even if we used the full 'unsigned long' to store 423 * the 'lastClickTime', we'd still need this function to prevent a rollover 424 * of the 32-bit number in 49.7 days. 425 */ 426 void checkOrphanedClick(uint16_t now); 427 428 /** 429 * Check if a click message has been postponed because of 430 * ButtonConfig::kFeatureSuppressClickBeforeDoubleClick. 431 */ 432 void checkPostponedClick(uint16_t now); 433 434 /** 435 * Dispatch to the event handler defined in the mButtonConfig. 436 * 437 * This method will always be called and it's up to the user-provided 438 * handler to ignore the events which aren't interesting. 439 * 440 * An alternative might be to provide a bitmask filter to select only 441 * events which should are registered to trigger the event handler. For 442 * example, add the following method: 443 * 444 * @code 445 * setEventSelection(uint8_t eventSelection) { 446 * mEventSelection = eventSelection; 447 * } 448 * @endcode 449 * 450 * Set the event selector at setup(): 451 * 452 * @code 453 * setEventSelection(kEventSelectPressed | kEventSelectReleased); 454 * @endcode 455 * 456 * where 457 * 458 * @code 459 * kEventSelectPressed = (0x1 << kEventPressed); 460 * kEventSelectReleased = (0x1 << kEventReleased); 461 * ... 462 * @endcode 463 * 464 * Then change the handleEvent() method to something like: 465 * 466 * @code 467 * void handleEvent(uint8_t eventType) { 468 * if (mEventHandler && (eventSelections & (0x1 << eventType))) { 469 * handleEvent(this, eventType); 470 * } 471 * } 472 * @endcode 473 * 474 * But it is possible that the evaluation of the if-condition above takes 475 * longer to evaluate than an empty function call, so we should do some 476 * profiling before making this change. 477 * 478 * @param eventType the type of event given by the kEvent* constants 479 */ 480 void handleEvent(uint8_t eventType); 481 482 uint8_t mPin; // button pin number 483 uint8_t mId; // identifier, e.g. an index into an array 484 485 // Internal states of the button debouncing and event handling. 486 // NOTE: We don't keep track of the lastDoubleClickTime, because we 487 // don't support a TripleClicked event. That may change in the future. 488 uint16_t mLastDebounceTime; // ms 489 uint16_t mLastClickTime; // ms 490 uint16_t mLastPressTime; // ms 491 uint16_t mLastRepeatPressTime; // ms 492 493 /** Internal flags. Bit masks are defined by the kFlag* constants. */ 494 uint8_t mFlags; 495 496 /** 497 * Last button state. This is a tri-state variable: LOW, HIGH or 498 * kButtonStateUnknown. 499 */ 500 uint8_t mLastButtonState; 501 502 /** ButtonConfig associated with this button. */ 503 ButtonConfig* mButtonConfig; 504 }; 505 506 } 507 #endif