diff --git a/src/main.cpp b/src/main.cpp index c53877e37..e44e938bc 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -108,6 +108,10 @@ NRF52Bluetooth *nrf52Bluetooth = nullptr; ButtonThread *TouchButtonThread = nullptr; #endif +#if defined(TTGO_T_ECHO) && !defined(MESHTASTIC_INCLUDE_NICHE_GRAPHICS) +#include "../variants/nrf52840/t-echo/TEchoBacklight.h" +#endif + #if defined(BUTTON_PIN) || defined(ARCH_PORTDUINO) ButtonThread *UserButtonThread = nullptr; #endif @@ -981,8 +985,13 @@ void setup() BaseType_t higherWake = 0; mainDelay.interruptFromISR(&higherWake); }; +#if defined(TTGO_T_ECHO) && !defined(MESHTASTIC_INCLUDE_NICHE_GRAPHICS) + touchConfig.singlePress = INPUT_BROKER_NONE; + touchConfig.longPress = INPUT_BROKER_NONE; +#else touchConfig.singlePress = INPUT_BROKER_NONE; touchConfig.longPress = INPUT_BROKER_BACK; +#endif TouchButtonThread->initButton(touchConfig); #endif @@ -1456,6 +1465,14 @@ void setup() // We manually run this to update the NodeStatus nodeDB->notifyObservers(true); + +#if defined(TTGO_T_ECHO) && !defined(MESHTASTIC_INCLUDE_NICHE_GRAPHICS) + if (!tEchoBacklight) { + tEchoBacklight = new TEchoBacklight(); + tEchoBacklight->setPin(PIN_EINK_EN); + tEchoBacklight->start(); + } +#endif } #endif diff --git a/variants/nrf52840/t-echo/TEchoBacklight.cpp b/variants/nrf52840/t-echo/TEchoBacklight.cpp new file mode 100644 index 000000000..8fca7b49d --- /dev/null +++ b/variants/nrf52840/t-echo/TEchoBacklight.cpp @@ -0,0 +1,174 @@ +#include "TEchoBacklight.h" + +#if defined(TTGO_T_ECHO) && !defined(MESHTASTIC_INCLUDE_NICHE_GRAPHICS) + +#include "RadioLibInterface.h" +#include "configuration.h" + +TEchoBacklight *tEchoBacklight = nullptr; + +TEchoBacklight::TEchoBacklight() : OSThread("TEchoBacklight") +{ + setInterval(POLL_INTERVAL_MS); + OSThread::disable(); +} + +void TEchoBacklight::setPin(uint8_t pin) +{ + pinMode(pin, OUTPUT); + off(); +} + +void TEchoBacklight::start() +{ + pinMode(PIN_BUTTON_TOUCH, INPUT_PULLUP); + attachInterrupt(PIN_BUTTON_TOUCH, touchISR, FALLING); +} + +int32_t TEchoBacklight::runOnce() +{ + bool awaitingRelease = false; + + switch (state) { + case REST: + break; + + case IRQ: + if (!RadioLibInterface::instance || RadioLibInterface::instance->isSending()) { + LOG_INFO("TEchoBacklight: Touch ignored - radio transmitting"); + state = REST; + break; + } + LOG_INFO("TEchoBacklight: Touch detected - peek()"); + peek(); + state = POLLING_UNFIRED; + awaitingRelease = true; + break; + + case POLLING_UNFIRED: { + uint32_t length = millis() - irqAtMillis; + + if (!isTouchPressed()) { + state = REST; + if (length > DEBOUNCE_MS && length < LATCH_TIME_MS) { + LOG_INFO("TEchoBacklight: Short press (%lums) - off()", length); + off(); + } else { + LOG_INFO("TEchoBacklight: Touch released too quick (%lums) - debounced", length); + } + } else { + awaitingRelease = true; + if (length >= LATCH_TIME_MS) { + LOG_INFO("TEchoBacklight: Long press (%lums) - starting latch blink", length); + state = BLINKING; + blinkStartTime = millis(); + blinkStep = 0; + setBacklight(false); + } + } + break; + } + + case BLINKING: { + uint32_t elapsed = millis() - blinkStartTime; + if (elapsed >= BLINK_DELAY_MS) { + blinkStep++; + blinkStartTime = millis(); + + setBacklight(blinkStep % 2 == 1); + + if (blinkStep == BLINK_STEPS) { + backlightLatched = true; + state = POLLING_FIRED; + LOG_INFO("TEchoBacklight: Blink complete - latched ON"); + } + } + awaitingRelease = true; + break; + } + + case POLLING_FIRED: + if (!isTouchPressed()) { + LOG_INFO("TEchoBacklight: Long press released"); + state = REST; + } else { + awaitingRelease = true; + } + break; + } + + if (!awaitingRelease) { + stopThread(); + } + + return POLL_INTERVAL_MS; +} + +void TEchoBacklight::setBacklight(bool on) +{ + digitalWrite(PIN_EINK_EN, on ? HIGH : LOW); +} + +bool TEchoBacklight::isTouchPressed() +{ + return digitalRead(PIN_BUTTON_TOUCH) == LOW; +} + +void TEchoBacklight::peek() +{ + setBacklight(true); + backlightLatched = false; +} + +void TEchoBacklight::latch() +{ + if (backlightLatched) { + LOG_INFO("TEchoBacklight: latch() - turning OFF"); + backlightLatched = false; + setBacklight(false); + } else { + LOG_INFO("TEchoBacklight: latch() - turning ON"); + backlightLatched = true; + setBacklight(true); + } +} + +void TEchoBacklight::off() +{ + backlightLatched = false; + setBacklight(false); +} + +void TEchoBacklight::touchISR() +{ + static volatile bool isrRunning = false; + + if (!isrRunning && tEchoBacklight) { + isrRunning = true; + if (tEchoBacklight->state == REST) { + tEchoBacklight->state = IRQ; + tEchoBacklight->irqAtMillis = millis(); + tEchoBacklight->startThread(); + LOG_INFO("TEchoBacklight: ISR triggered - starting thread"); + } + isrRunning = false; + } +} + +void TEchoBacklight::startThread() +{ + if (!OSThread::enabled) { + OSThread::setInterval(POLL_INTERVAL_MS); + OSThread::enabled = true; + } +} + +void TEchoBacklight::stopThread() +{ + if (OSThread::enabled) { + OSThread::disable(); + } + state = REST; +} + +#endif diff --git a/variants/nrf52840/t-echo/TEchoBacklight.h b/variants/nrf52840/t-echo/TEchoBacklight.h new file mode 100644 index 000000000..9ca6e0996 --- /dev/null +++ b/variants/nrf52840/t-echo/TEchoBacklight.h @@ -0,0 +1,43 @@ +#pragma once +#include "configuration.h" + +#if defined(TTGO_T_ECHO) && !defined(MESHTASTIC_INCLUDE_NICHE_GRAPHICS) + +#include "concurrency/OSThread.h" + +class TEchoBacklight : public concurrency::OSThread +{ + public: + TEchoBacklight(); + int32_t runOnce() override; + void setPin(uint8_t pin); + void start(); + void peek(); + void latch(); + void off(); + + private: + static constexpr uint32_t LATCH_TIME_MS = 5000; + static constexpr uint32_t POLL_INTERVAL_MS = 10; + static constexpr uint32_t DEBOUNCE_MS = 50; + static constexpr uint32_t BLINK_DELAY_MS = 25; + static constexpr uint8_t BLINK_STEPS = 3; + + enum State { REST, IRQ, POLLING_UNFIRED, POLLING_FIRED, BLINKING }; + + bool backlightLatched = false; + uint32_t irqAtMillis = 0; + State state = REST; + uint32_t blinkStartTime = 0; + uint8_t blinkStep = 0; + + void setBacklight(bool on); + bool isTouchPressed(); + static void touchISR(); + void startThread(); + void stopThread(); +}; + +extern TEchoBacklight *tEchoBacklight; + +#endif diff --git a/variants/nrf52840/t-echo/variant.cpp b/variants/nrf52840/t-echo/variant.cpp index cae079b74..1248c5a38 100644 --- a/variants/nrf52840/t-echo/variant.cpp +++ b/variants/nrf52840/t-echo/variant.cpp @@ -22,6 +22,7 @@ #include "nrf.h" #include "wiring_constants.h" #include "wiring_digital.h" +#include "TEchoBacklight.h" const uint32_t g_ADigitalPinMap[] = { // P0 - pins 0 and 1 are hardwired for xtal and should never be enabled