diff --git a/src/input/InputBroker.h b/src/input/InputBroker.h index 022101f7d..c55d7fa53 100644 --- a/src/input/InputBroker.h +++ b/src/input/InputBroker.h @@ -53,6 +53,7 @@ typedef struct _InputEvent { class InputPollable { public: + virtual ~InputPollable() = default; virtual void pollOnce() = 0; }; diff --git a/src/input/RotaryEncoderImpl.cpp b/src/input/RotaryEncoderImpl.cpp index 7b43fa256..cc1222595 100644 --- a/src/input/RotaryEncoderImpl.cpp +++ b/src/input/RotaryEncoderImpl.cpp @@ -3,6 +3,9 @@ #include "RotaryEncoderImpl.h" #include "InputBroker.h" #include "RotaryEncoder.h" +#ifdef ARCH_ESP32 +#include "sleep.h" +#endif #define ORIGIN_NAME "RotaryEncoder" @@ -11,6 +14,20 @@ RotaryEncoderImpl *rotaryEncoderImpl; RotaryEncoderImpl::RotaryEncoderImpl() { rotary = nullptr; +#ifdef ARCH_ESP32 + isFirstInit = true; +#endif +} + +RotaryEncoderImpl::~RotaryEncoderImpl() +{ + LOG_DEBUG("RotaryEncoderImpl destructor"); + detachRotaryEncoderInterrupts(); + + if (rotary != nullptr) { + delete rotary; + rotary = nullptr; + } } bool RotaryEncoderImpl::init() @@ -25,15 +42,22 @@ bool RotaryEncoderImpl::init() eventCcw = static_cast(moduleConfig.canned_message.inputbroker_event_ccw); eventPressed = static_cast(moduleConfig.canned_message.inputbroker_event_press); - rotary = new RotaryEncoder(moduleConfig.canned_message.inputbroker_pin_a, moduleConfig.canned_message.inputbroker_pin_b, - moduleConfig.canned_message.inputbroker_pin_press); - rotary->resetButton(); + if (rotary == nullptr) { + rotary = new RotaryEncoder(moduleConfig.canned_message.inputbroker_pin_a, moduleConfig.canned_message.inputbroker_pin_b, + moduleConfig.canned_message.inputbroker_pin_press); + } - interruptInstance = this; - auto interruptHandler = []() { inputBroker->requestPollSoon(interruptInstance); }; - attachInterrupt(moduleConfig.canned_message.inputbroker_pin_a, interruptHandler, CHANGE); - attachInterrupt(moduleConfig.canned_message.inputbroker_pin_b, interruptHandler, CHANGE); - attachInterrupt(moduleConfig.canned_message.inputbroker_pin_press, interruptHandler, CHANGE); + attachRotaryEncoderInterrupts(); + +#ifdef ARCH_ESP32 + // Register callbacks for before and after lightsleep + // Used to detach and reattach interrupts + if (isFirstInit) { + lsObserver.observe(¬ifyLightSleep); + lsEndObserver.observe(¬ifyLightSleepEnd); + isFirstInit = false; + } +#endif LOG_INFO("RotaryEncoder initialized pins(%d, %d, %d), events(%d, %d, %d)", moduleConfig.canned_message.inputbroker_pin_a, moduleConfig.canned_message.inputbroker_pin_b, moduleConfig.canned_message.inputbroker_pin_press, eventCw, eventCcw, @@ -71,6 +95,50 @@ void RotaryEncoderImpl::pollOnce() } } +void RotaryEncoderImpl::detachRotaryEncoderInterrupts() +{ + LOG_DEBUG("RotaryEncoderImpl detach button interrupts"); + if (interruptInstance == this) { + detachInterrupt(moduleConfig.canned_message.inputbroker_pin_a); + detachInterrupt(moduleConfig.canned_message.inputbroker_pin_b); + detachInterrupt(moduleConfig.canned_message.inputbroker_pin_press); + interruptInstance = nullptr; + } else { + LOG_WARN("RotaryEncoderImpl: interrupts already detached"); + } +} + +void RotaryEncoderImpl::attachRotaryEncoderInterrupts() +{ + LOG_DEBUG("RotaryEncoderImpl attach button interrupts"); + if (rotary != nullptr && interruptInstance == nullptr) { + rotary->resetButton(); + + interruptInstance = this; + auto interruptHandler = []() { inputBroker->requestPollSoon(interruptInstance); }; + attachInterrupt(moduleConfig.canned_message.inputbroker_pin_a, interruptHandler, CHANGE); + attachInterrupt(moduleConfig.canned_message.inputbroker_pin_b, interruptHandler, CHANGE); + attachInterrupt(moduleConfig.canned_message.inputbroker_pin_press, interruptHandler, CHANGE); + } else { + LOG_WARN("RotaryEncoderImpl: interrupts already attached"); + } +} + +#ifdef ARCH_ESP32 + +int RotaryEncoderImpl::beforeLightSleep(void *unused) +{ + detachRotaryEncoderInterrupts(); + return 0; // Indicates success; +} + +int RotaryEncoderImpl::afterLightSleep(esp_sleep_wakeup_cause_t cause) +{ + attachRotaryEncoderInterrupts(); + return 0; // Indicates success; +} +#endif + RotaryEncoderImpl *RotaryEncoderImpl::interruptInstance; #endif \ No newline at end of file diff --git a/src/input/RotaryEncoderImpl.h b/src/input/RotaryEncoderImpl.h index 6f8e9fe5f..ec8a064bd 100644 --- a/src/input/RotaryEncoderImpl.h +++ b/src/input/RotaryEncoderImpl.h @@ -8,12 +8,18 @@ class RotaryEncoder; -class RotaryEncoderImpl : public InputPollable +class RotaryEncoderImpl final : public InputPollable { public: RotaryEncoderImpl(); - bool init(void); + ~RotaryEncoderImpl() override; + bool init(); virtual void pollOnce() override; + // Disconnect and reconnect interrupts for light sleep +#ifdef ARCH_ESP32 + int beforeLightSleep(void *unused); + int afterLightSleep(esp_sleep_wakeup_cause_t cause); +#endif protected: static RotaryEncoderImpl *interruptInstance; @@ -23,6 +29,21 @@ class RotaryEncoderImpl : public InputPollable input_broker_event eventPressed = INPUT_BROKER_NONE; RotaryEncoder *rotary; + + private: +#ifdef ARCH_ESP32 + bool isFirstInit; +#endif + void detachRotaryEncoderInterrupts(); + void attachRotaryEncoderInterrupts(); + +#ifdef ARCH_ESP32 + // Get notified when lightsleep begins and ends + CallbackObserver lsObserver = + CallbackObserver(this, &RotaryEncoderImpl::beforeLightSleep); + CallbackObserver lsEndObserver = + CallbackObserver(this, &RotaryEncoderImpl::afterLightSleep); +#endif }; extern RotaryEncoderImpl *rotaryEncoderImpl;