From d31e104679b0d4898db2d40b2adfad1999387358 Mon Sep 17 00:00:00 2001 From: whywilson Date: Sat, 23 Aug 2025 15:26:05 +0800 Subject: [PATCH] Add longPress event for RotaryEncoder Press. --- src/graphics/draw/NotificationRenderer.cpp | 2 - src/input/RotaryEncoderInterruptBase.cpp | 45 ++++++++++++++++++---- src/input/RotaryEncoderInterruptBase.h | 14 ++++++- src/input/RotaryEncoderInterruptImpl1.cpp | 5 ++- 4 files changed, 55 insertions(+), 11 deletions(-) diff --git a/src/graphics/draw/NotificationRenderer.cpp b/src/graphics/draw/NotificationRenderer.cpp index c2ca93243..b53cd2f3f 100644 --- a/src/graphics/draw/NotificationRenderer.cpp +++ b/src/graphics/draw/NotificationRenderer.cpp @@ -687,8 +687,6 @@ void NotificationRenderer::drawTextInput(OLEDDisplay *display, OLEDDisplayUiStat inEvent.inputEvent = INPUT_BROKER_NONE; } - // Continuous long-press repeat removed: no per-frame ticking needed - // Clear the screen to avoid overlapping with underlying frames or overlays display->setColor(BLACK); display->fillRect(0, 0, display->getWidth(), display->getHeight()); diff --git a/src/input/RotaryEncoderInterruptBase.cpp b/src/input/RotaryEncoderInterruptBase.cpp index 0557bc180..d542740c5 100644 --- a/src/input/RotaryEncoderInterruptBase.cpp +++ b/src/input/RotaryEncoderInterruptBase.cpp @@ -8,22 +8,24 @@ RotaryEncoderInterruptBase::RotaryEncoderInterruptBase(const char *name) : concu void RotaryEncoderInterruptBase::init( uint8_t pinA, uint8_t pinB, uint8_t pinPress, input_broker_event eventCw, input_broker_event eventCcw, - input_broker_event eventPressed, + input_broker_event eventPressed, input_broker_event eventPressedLong, // std::function onIntA, std::function onIntB, std::function onIntPress) : void (*onIntA)(), void (*onIntB)(), void (*onIntPress)()) { this->_pinA = pinA; this->_pinB = pinB; + this->_pinPress = pinPress; this->_eventCw = eventCw; this->_eventCcw = eventCcw; this->_eventPressed = eventPressed; + this->_eventPressedLong = eventPressedLong; pinMode(pinPress, INPUT_PULLUP); pinMode(this->_pinA, INPUT_PULLUP); pinMode(this->_pinB, INPUT_PULLUP); - // attachInterrupt(pinPress, onIntPress, RISING); - attachInterrupt(pinPress, onIntPress, RISING); + // Use FALLING edge for active-low press button to start long-press timing immediately + attachInterrupt(pinPress, onIntPress, FALLING); attachInterrupt(this->_pinA, onIntA, CHANGE); attachInterrupt(this->_pinB, onIntB, CHANGE); @@ -37,10 +39,37 @@ int32_t RotaryEncoderInterruptBase::runOnce() InputEvent e; e.inputEvent = INPUT_BROKER_NONE; e.source = this->_originName; + unsigned long now = millis(); + // Handle press long/short detection if (this->action == ROTARY_ACTION_PRESSED) { - LOG_DEBUG("Rotary event Press"); - e.inputEvent = this->_eventPressed; + bool buttonPressed = !digitalRead(_pinPress); + if (!pressDetected && buttonPressed) { + pressDetected = true; + pressStartTime = now; + } + + if (pressDetected) { + uint32_t duration = now - pressStartTime; + if (!buttonPressed) { + // released -> if short press, send short, else already sent long + if (duration < LONG_PRESS_DURATION && now - lastPressKeyTime >= pressDebounceMs) { + lastPressKeyTime = now; + LOG_DEBUG("Rotary event Press short"); + e.inputEvent = this->_eventPressed; + } + pressDetected = false; + pressStartTime = 0; + lastPressLongEventTime = 0; + this->action = ROTARY_ACTION_NONE; + } else if (duration >= LONG_PRESS_DURATION && this->_eventPressedLong != INPUT_BROKER_NONE && + lastPressLongEventTime == 0) { + // fire single-shot long press + lastPressLongEventTime = now; + LOG_DEBUG("Rotary event Press long"); + e.inputEvent = this->_eventPressedLong; + } + } } else if (this->action == ROTARY_ACTION_CW) { LOG_DEBUG("Rotary event CW"); e.inputEvent = this->_eventCw; @@ -53,7 +82,9 @@ int32_t RotaryEncoderInterruptBase::runOnce() this->notifyObservers(&e); } - this->action = ROTARY_ACTION_NONE; + if (!pressDetected) { + this->action = ROTARY_ACTION_NONE; + } return INT32_MAX; } @@ -61,7 +92,7 @@ int32_t RotaryEncoderInterruptBase::runOnce() void RotaryEncoderInterruptBase::intPressHandler() { this->action = ROTARY_ACTION_PRESSED; - setIntervalFromNow(20); // TODO: this modifies a non-volatile variable! + setIntervalFromNow(20); // start checking for long/short } void RotaryEncoderInterruptBase::intAHandler() diff --git a/src/input/RotaryEncoderInterruptBase.h b/src/input/RotaryEncoderInterruptBase.h index 9bdab4730..4f9757609 100644 --- a/src/input/RotaryEncoderInterruptBase.h +++ b/src/input/RotaryEncoderInterruptBase.h @@ -13,7 +13,7 @@ class RotaryEncoderInterruptBase : public Observable, public public: explicit RotaryEncoderInterruptBase(const char *name); void init(uint8_t pinA, uint8_t pinB, uint8_t pinPress, input_broker_event eventCw, input_broker_event eventCcw, - input_broker_event eventPressed, + input_broker_event eventPressed, input_broker_event eventPressedLong, // std::function onIntA, std::function onIntB, std::function onIntPress); void (*onIntA)(), void (*onIntB)(), void (*onIntPress)()); void intPressHandler(); @@ -33,10 +33,22 @@ class RotaryEncoderInterruptBase : public Observable, public volatile RotaryEncoderInterruptBaseActionType action = ROTARY_ACTION_NONE; private: + // pins and events uint8_t _pinA = 0; uint8_t _pinB = 0; + uint8_t _pinPress = 0; input_broker_event _eventCw = INPUT_BROKER_NONE; input_broker_event _eventCcw = INPUT_BROKER_NONE; input_broker_event _eventPressed = INPUT_BROKER_NONE; + input_broker_event _eventPressedLong = INPUT_BROKER_NONE; const char *_originName; + + // Long press detection variables + uint32_t pressStartTime = 0; + bool pressDetected = false; + uint32_t lastPressLongEventTime = 0; + unsigned long lastPressKeyTime = 0; + static const uint32_t LONG_PRESS_DURATION = 300; // ms + static const uint32_t LONG_PRESS_REPEAT_INTERVAL = 0; // 0 = single-shot for rotary select + const unsigned long pressDebounceMs = 200; // ms }; diff --git a/src/input/RotaryEncoderInterruptImpl1.cpp b/src/input/RotaryEncoderInterruptImpl1.cpp index 4f19c8b0b..12cbc36fb 100644 --- a/src/input/RotaryEncoderInterruptImpl1.cpp +++ b/src/input/RotaryEncoderInterruptImpl1.cpp @@ -1,5 +1,6 @@ #include "RotaryEncoderInterruptImpl1.h" #include "InputBroker.h" +extern bool osk_found; RotaryEncoderInterruptImpl1 *rotaryEncoderInterruptImpl1; @@ -19,12 +20,14 @@ bool RotaryEncoderInterruptImpl1::init() input_broker_event eventCw = static_cast(moduleConfig.canned_message.inputbroker_event_cw); input_broker_event eventCcw = static_cast(moduleConfig.canned_message.inputbroker_event_ccw); input_broker_event eventPressed = static_cast(moduleConfig.canned_message.inputbroker_event_press); + input_broker_event eventPressedLong = INPUT_BROKER_SELECT_LONG; // moduleConfig.canned_message.ext_notification_module_output - RotaryEncoderInterruptBase::init(pinA, pinB, pinPress, eventCw, eventCcw, eventPressed, + RotaryEncoderInterruptBase::init(pinA, pinB, pinPress, eventCw, eventCcw, eventPressed, eventPressedLong, RotaryEncoderInterruptImpl1::handleIntA, RotaryEncoderInterruptImpl1::handleIntB, RotaryEncoderInterruptImpl1::handleIntPressed); inputBroker->registerSource(this); + osk_found = true; return true; }