|
| 1 | +/* ✔ */ |
| 2 | + |
| 3 | +#pragma once |
| 4 | + |
| 5 | +#include <AH/Settings/Warnings.hpp> |
| 6 | +AH_DIAGNOSTIC_WERROR() // Enable errors on warnings |
| 7 | + |
| 8 | +#include "Button.hpp" |
| 9 | + |
| 10 | +BEGIN_AH_NAMESPACE |
| 11 | + |
| 12 | +/** |
| 13 | + * @brief Class for detecting short/long button presses and double clicks. |
| 14 | + */ |
| 15 | +class MultiPurposeButton { |
| 16 | + public: |
| 17 | + MultiPurposeButton(pin_t pin) : button(pin) {} |
| 18 | + |
| 19 | + enum Event { |
| 20 | + None, ///< Nothing changed |
| 21 | + PressStart, ///< The button was just pressed |
| 22 | + ShortPressRelease, ///< The button was released after a short press |
| 23 | + LongPress, ///< The button has been pressed for some time |
| 24 | + LongPressRelease, ///< The button was released after a long press |
| 25 | + MultiPress, ///< The button was pressed in quick succession of |
| 26 | + ///< the previous release. |
| 27 | + MultiPressDone, ///< The button has been released for long enough to |
| 28 | + ///< rule out another MultiPress. |
| 29 | + }; |
| 30 | + |
| 31 | + /// @see @ref Button::begin() |
| 32 | + void begin() { button.begin(); } |
| 33 | + |
| 34 | + /// Read the button state and return the @ref Event (if any). |
| 35 | + Event update() { |
| 36 | + Event event = None; |
| 37 | + auto now = millis(); |
| 38 | + auto stableTime = button.stableTime(now); |
| 39 | + switch (button.update()) { |
| 40 | + case Button::Released: { |
| 41 | + if (multiPressCountNew > 0 && stableTime > multiPressDelay) { |
| 42 | + multiPressCount = multiPressCountNew; |
| 43 | + multiPressCountNew = 0; |
| 44 | + event = MultiPressDone; |
| 45 | + } |
| 46 | + } break; |
| 47 | + case Button::Falling: { |
| 48 | + event = (stableTime <= multiPressDelay) ? MultiPress // |
| 49 | + : PressStart; |
| 50 | + multiPressCountNew += event == MultiPress; |
| 51 | + } break; |
| 52 | + case Button::Pressed: { |
| 53 | + if (not longPress && stableTime >= longPressDelay) { |
| 54 | + event = LongPress; |
| 55 | + longPress = true; |
| 56 | + } |
| 57 | + } break; |
| 58 | + case Button::Rising: { |
| 59 | + event = longPress ? LongPressRelease : ShortPressRelease; |
| 60 | + longPress = false; |
| 61 | + } break; |
| 62 | + } |
| 63 | + return event; |
| 64 | + } |
| 65 | + |
| 66 | + /// Get the number of times the button was pressed in quick succession |
| 67 | + /// (after MultiPressDone), this is the final count. |
| 68 | + uint8_t getMultiPressCount() const { return multiPressCount; } |
| 69 | + /// Get the number of times the button was pressed in quick succession, |
| 70 | + /// while the button is being pressed (before MultiPressDone). |
| 71 | + /// The count could still increase if the user keeps on pressing the button. |
| 72 | + uint8_t getCurrentMultiPressCount() const { return multiPressCountNew; } |
| 73 | + |
| 74 | + /// Get the number of milliseconds after which a press is considered a long |
| 75 | + /// press. |
| 76 | + unsigned long getLongPressDelay() const { return longPressDelay; } |
| 77 | + /// Set the number of milliseconds after which a press is considered a long |
| 78 | + /// press. |
| 79 | + void setLongPressDelay(unsigned long longPressDelay) { |
| 80 | + this->longPressDelay = longPressDelay; |
| 81 | + } |
| 82 | + |
| 83 | + /// Get the number of milliseconds between multipresses. |
| 84 | + unsigned long getMultiPressDelay() const { return multiPressDelay; } |
| 85 | + /// Set the number of milliseconds between multipresses. |
| 86 | + void setMultiPressDelay(unsigned long multiPressDelay) { |
| 87 | + this->multiPressDelay = multiPressDelay; |
| 88 | + } |
| 89 | + |
| 90 | + /// @see @ref Button::previousBounceTime() |
| 91 | + unsigned long previousBounceTime() const { |
| 92 | + return button.previousBounceTime(); |
| 93 | + } |
| 94 | + /// @see @ref Button::stableTime() |
| 95 | + unsigned long stableTime() const { return button.stableTime(); } |
| 96 | + /// @see @ref Button::stableTime(unsigned long) |
| 97 | + unsigned long stableTime(unsigned long now) const { |
| 98 | + return button.stableTime(now); |
| 99 | + } |
| 100 | + |
| 101 | + /// Get the number of milliseconds the button has been pressed for. Returns |
| 102 | + /// 0 if the button is not currently pressed. |
| 103 | + unsigned long getPressedTime() const { |
| 104 | + return getButtonState() == Button::Pressed ? stableTime() : 0; |
| 105 | + } |
| 106 | + /// Get the number of milliseconds the button has been pressed for. Returns |
| 107 | + /// 0 if the button is not currently pressed or if the current press is not |
| 108 | + /// a long press (yet). |
| 109 | + unsigned long getLongPressedTime() const { |
| 110 | + return longPress ? stableTime() : 0; |
| 111 | + } |
| 112 | + |
| 113 | + /// Return the name of the event as a string. |
| 114 | + static FlashString_t getName(Event ev) { return to_string(ev); } |
| 115 | + |
| 116 | + /// @see @ref Button::getState() |
| 117 | + Button::State getButtonState() const { return button.getState(); } |
| 118 | + /// @see @ref Button::invert() |
| 119 | + void invert() { button.invert(); } |
| 120 | + |
| 121 | + protected: |
| 122 | + Button button; |
| 123 | + unsigned long longPressDelay = 1000; |
| 124 | + unsigned long multiPressDelay = 400; |
| 125 | + bool longPress = false; |
| 126 | + uint8_t multiPressCountNew = 0; |
| 127 | + uint8_t multiPressCount = 0; |
| 128 | + |
| 129 | + public: |
| 130 | + friend FlashString_t to_string(Event ev) { |
| 131 | + switch (ev) { |
| 132 | + case None: return F("None"); |
| 133 | + case PressStart: return F("PressStart"); |
| 134 | + case ShortPressRelease: return F("ShortPressRelease"); |
| 135 | + case LongPress: return F("LongPress"); |
| 136 | + case LongPressRelease: return F("LongPressRelease"); |
| 137 | + case MultiPress: return F("MultiPress"); |
| 138 | + case MultiPressDone: return F("MultiPressDone"); |
| 139 | + } |
| 140 | + return F("<invalid>"); |
| 141 | + } |
| 142 | +}; |
| 143 | + |
| 144 | +END_AH_NAMESPACE |
| 145 | + |
| 146 | +AH_DIAGNOSTIC_POP() |
0 commit comments