diff --git a/examples/simple_repeater/UITask.cpp b/examples/simple_repeater/UITask.cpp index ce46b9c7c..70fa78912 100644 --- a/examples/simple_repeater/UITask.cpp +++ b/examples/simple_repeater/UITask.cpp @@ -320,6 +320,7 @@ void UITask::toggleDisplay() { #ifdef DISPLAY_ACTIVITY_DASHBOARD _rows_valid = false; #endif + _next_refresh = 0; // redraw at once rather than showing the stale frame _auto_off = millis() + displayTimeoutMillis(); } #endif diff --git a/examples/simple_room_server/UITask.cpp b/examples/simple_room_server/UITask.cpp index 3f124cb1a..2e9818e58 100644 --- a/examples/simple_room_server/UITask.cpp +++ b/examples/simple_room_server/UITask.cpp @@ -297,6 +297,7 @@ void UITask::toggleDisplay() { #ifdef DISPLAY_ACTIVITY_DASHBOARD _rows_valid = false; #endif + _next_refresh = 0; // redraw at once rather than showing the stale frame _auto_off = millis() + displayTimeoutMillis(); } #endif diff --git a/src/helpers/ui/CHSC6XTouch.h b/src/helpers/ui/CHSC6XTouch.h index c4c5cd2e0..fc8f119fc 100644 --- a/src/helpers/ui/CHSC6XTouch.h +++ b/src/helpers/ui/CHSC6XTouch.h @@ -18,7 +18,12 @@ #endif #define CHSC6X_READ_LEN 5 -#define CHSC6X_MAX_POINTS 1 + +// Bounds a single read. The shared bus can wedge, and the ESP32 default lets a +// failed transfer burn ~1 s inside the UI loop. +#ifndef CHSC6X_I2C_TIMEOUT_MS +#define CHSC6X_I2C_TIMEOUT_MS 10 +#endif class CHSC6XTouch { public: @@ -63,32 +68,47 @@ private: TouchTapDetector _detector; bool readPressed() { + // Probe the address first. This controller NACKs while it has nothing to + // report, and going straight to requestFrom() turns that into a logged bus + // error every poll plus, once the bus wedges, a ~1 s timeout stall in the + // UI loop. endTransmission() reports the same NACK quietly and cheaply. + _wire->beginTransmission((uint8_t)CHSC6X_I2C_ADDR); + if (_wire->endTransmission() != 0) return false; + + const uint16_t prev_timeout = _wire->getTimeOut(); + _wire->setTimeOut(CHSC6X_I2C_TIMEOUT_MS); uint8_t got = _wire->requestFrom((uint8_t)CHSC6X_I2C_ADDR, (uint8_t)CHSC6X_READ_LEN); + uint8_t buf[CHSC6X_READ_LEN]; + for (uint8_t i = 0; i < CHSC6X_READ_LEN; i++) { + buf[i] = i < got ? (uint8_t)_wire->read() : 0xFF; + } + while (_wire->available()) _wire->read(); // drain a short read + _wire->setTimeOut(prev_timeout); + if (got != CHSC6X_READ_LEN) { - while (_wire->available()) _wire->read(); // drain a short read logRaw(got, NULL); return false; } - - uint8_t buf[CHSC6X_READ_LEN]; - for (uint8_t i = 0; i < CHSC6X_READ_LEN; i++) buf[i] = (uint8_t)_wire->read(); logRaw(got, buf); - // buf[0] is the reported touch-point count (buf[2]/buf[4] are x/y). It must - // be tested against a *valid* count, not merely against zero: an idle or - // NACKed read can come back as 0xFF, which "non-zero" reads as a finger - // held down forever - the tap detector then fires once and, seeing no - // release, never fires again. - return buf[0] >= 1 && buf[0] <= CHSC6X_MAX_POINTS; + // Measured on the Expansion Kit V2 panel: byte 0 reads 0x00 while idle and + // 0x1F while a finger is down - not the 0x01 point count the reference + // CHSC6X drivers document, so testing for a count of 1 never fires. A + // partly-failed read leaves 0xFF, which must not register as a press. + return buf[0] != 0x00 && buf[0] != 0xFF; } #ifdef DISPLAY_TOUCH_DEBUG int16_t _logged = -1; + uint8_t _log_budget = 5; // always show the first few frames, then on change - // Logs on change only, so a normal boot stays quiet. + // Mostly logs on change, so a normal boot stays quiet - but the opening + // frames are unconditional so an idle read that never changes is still + // visible in the log. void logRaw(uint8_t got, const uint8_t* buf) { int16_t key = buf ? (int16_t)buf[0] : (int16_t)(-2 - (int16_t)got); - if (key == _logged) return; + if (key == _logged && _log_budget == 0) return; + if (_log_budget > 0) _log_budget--; _logged = key; if (!buf) { diff --git a/src/helpers/ui/ST7789LCDDisplay.cpp b/src/helpers/ui/ST7789LCDDisplay.cpp index bcdaf81c8..6ccdd24ba 100644 --- a/src/helpers/ui/ST7789LCDDisplay.cpp +++ b/src/helpers/ui/ST7789LCDDisplay.cpp @@ -50,6 +50,22 @@ ColorVal UIColor::corp_blue = 0x001A; bool ST7789LCDDisplay::begin() { if (!_isOn) { + #ifdef HELTEC_V4_R8_TFT + // turnOff() leaves this panel configured and powered - its reset line is + // shared with the touch controller, so it is never parked low - which makes + // waking just a backlight switch. Re-running the init below would re-enter + // SPI setup and pulse GPIO 21, resetting the touch controller on every wake + // and stalling the UI loop for ~500 ms of reset delays. + if (_panel_ready) { + if (_peripher_power) _peripher_power->claim(); + if (PIN_TFT_LEDA_CTL != -1) { + digitalWrite(PIN_TFT_LEDA_CTL, PIN_TFT_LEDA_CTL_ACTIVE); + } + _isOn = true; + return true; + } + #endif + if (_peripher_power) { _peripher_power->claim(); #ifdef HELTEC_V4_R8_TFT @@ -91,6 +107,7 @@ bool ST7789LCDDisplay::begin() { if (PIN_TFT_LEDA_CTL != -1) { digitalWrite(PIN_TFT_LEDA_CTL, PIN_TFT_LEDA_CTL_ACTIVE); } + _panel_ready = true; #endif _isOn = true; diff --git a/src/helpers/ui/ST7789LCDDisplay.h b/src/helpers/ui/ST7789LCDDisplay.h index f6d7446b8..baa72bf01 100644 --- a/src/helpers/ui/ST7789LCDDisplay.h +++ b/src/helpers/ui/ST7789LCDDisplay.h @@ -13,6 +13,9 @@ class ST7789LCDDisplay : public DisplayDriver { #endif Adafruit_ST7789 display; bool _isOn; +#ifdef HELTEC_V4_R8_TFT + bool _panel_ready = false; // panel configured once; wake is backlight-only +#endif uint16_t _color; RefCountedDigitalPin* _peripher_power; diff --git a/variants/heltec_v4_r8/HeltecV4R8Board.cpp b/variants/heltec_v4_r8/HeltecV4R8Board.cpp index 9a07db92f..434a83cae 100644 --- a/variants/heltec_v4_r8/HeltecV4R8Board.cpp +++ b/variants/heltec_v4_r8/HeltecV4R8Board.cpp @@ -71,9 +71,21 @@ void HeltecV4R8Board::enterDeepSleep(uint32_t secs, int pin_wake_btn) { } void HeltecV4R8Board::powerOff() { - enterDeepSleep(0); +#if defined(HELTEC_V4_R8_TFT) && defined(DISPLAY_CLASS) + display.turnOff(); +#endif + + // Deliberately NOT enterDeepSleep(): that always arms an ext1 wake on + // P_LORA_DIO_1, so a node sitting in live traffic woke - and a deep-sleep + // wake is a full reboot - within seconds of showing "Turning OFF". With every + // wake source disabled the node stays down until RST or a power cycle, which + // is what asking for power-off means. + esp_sleep_disable_wakeup_source(ESP_SLEEP_WAKEUP_ALL); + loRaFEMControl.setRxModeEnableWhenMCUSleep(); + esp_deep_sleep_start(); } + uint16_t HeltecV4R8Board::getBattMilliVolts() { analogReadResolution(12);