diff --git a/examples/companion_radio/DataStore.cpp b/examples/companion_radio/DataStore.cpp index 52d731a3..3a802918 100644 --- a/examples/companion_radio/DataStore.cpp +++ b/examples/companion_radio/DataStore.cpp @@ -292,6 +292,9 @@ void DataStore::loadPrefsInt(const char *filename, NodePrefs& _prefs, double& no if (file.read((uint8_t *)_prefs.channel_notif, sizeof(_prefs.channel_notif)) != sizeof(_prefs.channel_notif)) { memset(_prefs.channel_notif, 0, sizeof(_prefs.channel_notif)); // default: NOTIF_ALL } + if (file.read((uint8_t *)&_prefs.lora_antenna, sizeof(_prefs.lora_antenna)) != sizeof(_prefs.lora_antenna)) { + _prefs.lora_antenna = 0; // default: internal antenna + } // Clamp to valid ranges if (_prefs.dark_mode > 1) _prefs.dark_mode = 0; @@ -305,6 +308,7 @@ void DataStore::loadPrefsInt(const char *filename, NodePrefs& _prefs, double& no for (int i = 0; i < (int)sizeof(_prefs.channel_notif); i++) { if (_prefs.channel_notif[i] > 2) _prefs.channel_notif[i] = 0; } + if (_prefs.lora_antenna > 1) _prefs.lora_antenna = 0; // auto_lock_minutes: only accept known options (0, 2, 5, 10, 15, 30) { uint8_t alm = _prefs.auto_lock_minutes; @@ -365,6 +369,7 @@ void DataStore::savePrefs(const NodePrefs& _prefs, double node_lat, double node_ file.write((uint8_t *)_prefs.default_scope_name, sizeof(_prefs.default_scope_name)); // 106 file.write((uint8_t *)_prefs.default_scope_key, sizeof(_prefs.default_scope_key)); // 137 file.write((uint8_t *)_prefs.channel_notif, sizeof(_prefs.channel_notif)); // 153 + file.write((uint8_t *)&_prefs.lora_antenna, sizeof(_prefs.lora_antenna)); // 174 file.close(); } diff --git a/examples/companion_radio/NodePrefs.h b/examples/companion_radio/NodePrefs.h index 713fd1c8..05ebe490 100644 --- a/examples/companion_radio/NodePrefs.h +++ b/examples/companion_radio/NodePrefs.h @@ -64,6 +64,11 @@ struct NodePrefs { // persisted to file // Defaults to NOTIF_ALL for all channels. uint8_t channel_notif[21]; // 20 group channels + 1 DM slot + // --- LoRa antenna selection (T-Deck Pro MAX only) --- + // 0 = internal antenna (XL9555 LORA_SEL HIGH), 1 = external (LORA_SEL LOW). + // Applied at boot in main.cpp and live on toggle in SettingsScreen. + uint8_t lora_antenna; // 0 = internal (default), 1 = external + // --- Font helpers (inline, no overhead) --- // Returns the DisplayDriver text-size index for "small/body" text. // T-Deck Pro: 0 = built-in 6×8 (or 7pt with custom fonts), 1 = 9pt. diff --git a/examples/companion_radio/main.cpp b/examples/companion_radio/main.cpp index 6cd33679..ce667554 100644 --- a/examples/companion_radio/main.cpp +++ b/examples/companion_radio/main.cpp @@ -2140,6 +2140,12 @@ void setup() { ); MESH_DEBUG_PRINTLN("setup() - the_mesh.begin() done"); +#if defined(LilyGo_TDeck_Pro_Max) + // Restore saved LoRa antenna selection (XL9555 boots to internal by default) + if (the_mesh.getNodePrefs()->lora_antenna) board.loraAntennaExternal(); + else board.loraAntennaInternal(); +#endif + // Boot-time config import: check for /meshcore/import.json on SD #ifdef HAS_SDCARD if (sdCardReady) { @@ -2540,6 +2546,17 @@ void setup() { if (p4) free(p4); if (p10) free(p10); if (p25) free(p25); } MESH_DEBUG_PRINTLN("=== setup() - COMPLETE ==="); + + // DIAGNOSTIC: assert the frontlight at the very end of setup(), after ALL + // init has run. The factory firmware proves analogWrite(41,50) lights this + // panel. If the home screen is lit now (but the early step-12 assert was + // not), the pin was being reconfigured during init and this late assert wins. + // If it lights now and stays until a specific screen is opened, that screen's + // pinMode(41) is the thief. Watch this marker vs the screen you open. +#ifdef PIN_EINK_BL + analogWrite(PIN_EINK_BL, 50); + Serial.println(">>> BL DIAG: analogWrite(41,50) asserted at end of setup()"); +#endif } // --------------------------------------------------------------------------- @@ -4100,7 +4117,16 @@ void handleKeyboardInput() { // Defer contact saves while user is actively pressing keys the_mesh.notifyUserInput(); - + +#if defined(LilyGo_TDeck_Pro_Max) + // Alt+B toggles the e-ink frontlight (MAX only -- working backlight on IO41) + if (key == KB_KEY_BACKLIGHT) { + if (board.isBacklightOn()) board.backlightOff(); + else board.backlightOn(); + return; + } +#endif + // Alarm ringing: ANY key dismisses (highest priority after lock screen) #ifdef MECK_AUDIO_VARIANT { diff --git a/examples/companion_radio/ui-new/Settingsscreen.h b/examples/companion_radio/ui-new/Settingsscreen.h index 89419d98..cc455fe3 100644 --- a/examples/companion_radio/ui-new/Settingsscreen.h +++ b/examples/companion_radio/ui-new/Settingsscreen.h @@ -43,6 +43,11 @@ class UITask; class MyMesh; extern MyMesh the_mesh; +#if defined(LilyGo_TDeck_Pro_Max) +#include "TDeckProMaxBoard.h" +extern TDeckProMaxBoard board; +#endif + // --------------------------------------------------------------------------- // Auto-add config bitmask (mirrored from MyMesh.cpp for UI access) // --------------------------------------------------------------------------- @@ -124,6 +129,9 @@ static inline int findAutoLockIndex(uint8_t minutes) { // Settings row types // --------------------------------------------------------------------------- enum SettingsRowType : uint8_t { +#if defined(LilyGo_TDeck_Pro_Max) + ROW_LORA_ANTENNA, // LoRa antenna select: internal/external (MAX only) +#endif ROW_NAME, // Device name (text editor) ROW_RADIO_PRESET, // Radio preset picker ROW_FREQ, // Frequency (float) @@ -249,14 +257,19 @@ enum FmPhase : uint8_t { #endif // Max rows in the settings list (increased for contact sub-toggles + WiFi) -#if defined(HAS_4G_MODEM) && defined(MECK_WIFI_COMPANION) -#define SETTINGS_MAX_ROWS 63 // Extra rows for IMEI, Carrier, APN, contacts, WiFi, scope, export -#elif defined(HAS_4G_MODEM) -#define SETTINGS_MAX_ROWS 61 // Extra rows for IMEI, Carrier, APN + contacts + scope + export -#elif defined(MECK_WIFI_COMPANION) -#define SETTINGS_MAX_ROWS 57 // Extra rows for contacts + WiFi + scope + export +#if defined(LilyGo_TDeck_Pro_Max) +#define SETTINGS_LORA_ANTENNA_ROWS 1 // LoRa antenna toggle (MAX only) #else -#define SETTINGS_MAX_ROWS 55 // Contacts section + scope + export +#define SETTINGS_LORA_ANTENNA_ROWS 0 +#endif +#if defined(HAS_4G_MODEM) && defined(MECK_WIFI_COMPANION) +#define SETTINGS_MAX_ROWS (63 + SETTINGS_LORA_ANTENNA_ROWS) // Extra rows for IMEI, Carrier, APN, contacts, WiFi, scope, export +#elif defined(HAS_4G_MODEM) +#define SETTINGS_MAX_ROWS (61 + SETTINGS_LORA_ANTENNA_ROWS) // Extra rows for IMEI, Carrier, APN + contacts + scope + export +#elif defined(MECK_WIFI_COMPANION) +#define SETTINGS_MAX_ROWS (57 + SETTINGS_LORA_ANTENNA_ROWS) // Extra rows for contacts + WiFi + scope + export +#else +#define SETTINGS_MAX_ROWS (55 + SETTINGS_LORA_ANTENNA_ROWS) // Contacts section + scope + export #endif #define SETTINGS_TEXT_BUF 33 // 32 chars + null @@ -466,6 +479,9 @@ private: #endif } else { // --- Top-level settings list --- +#if defined(LilyGo_TDeck_Pro_Max) + addRow(ROW_LORA_ANTENNA); +#endif addRow(ROW_NAME); addRow(ROW_RADIO_PRESET); addRow(ROW_FREQ); @@ -1900,6 +1916,14 @@ public: break; } +#if defined(LilyGo_TDeck_Pro_Max) + case ROW_LORA_ANTENNA: + snprintf(tmp, sizeof(tmp), "LoRa Antenna: %s", + _prefs->lora_antenna ? "External" : "Internal"); + display.print(tmp); + break; +#endif + case ROW_DARK_MODE: snprintf(tmp, sizeof(tmp), "Dark Mode: %s", _prefs->dark_mode ? "ON" : "OFF"); @@ -3503,6 +3527,16 @@ public: case ROW_GPS_BAUD: startEditPicker(findGpsBaudIndex(_prefs->gps_baudrate)); break; +#if defined(LilyGo_TDeck_Pro_Max) + case ROW_LORA_ANTENNA: + _prefs->lora_antenna = _prefs->lora_antenna ? 0 : 1; + if (_prefs->lora_antenna) board.loraAntennaExternal(); + else board.loraAntennaInternal(); + the_mesh.savePrefs(); + Serial.printf("Settings: LoRa antenna = %s\n", + _prefs->lora_antenna ? "External" : "Internal"); + break; +#endif case ROW_DARK_MODE: _prefs->dark_mode = _prefs->dark_mode ? 0 : 1; the_mesh.savePrefs(); diff --git a/examples/companion_radio/ui-new/Touchinput.h b/examples/companion_radio/ui-new/Touchinput.h index 837199cc..67e462b2 100644 --- a/examples/companion_radio/ui-new/Touchinput.h +++ b/examples/companion_radio/ui-new/Touchinput.h @@ -32,16 +32,30 @@ #include #include +// The CST328 pulses its INT line low (falling edge) when a new touch report is +// ready. We attach an edge interrupt that sets this flag, and only read/ack the +// controller when it has fired -- mirroring the Hynitron driver, which reads +// only on the INT edge instead of blind-polling. Blind polling at 50 Hz was the +// source of the i2cRead -1/263 errors (reading when no report was pending). +namespace { + volatile bool _touchIrqFired = false; + void IRAM_ATTR _touchIsr() { _touchIrqFired = true; } +} + class TouchInput { public: static const uint8_t TOUCH_ADDR = 0x1A; TouchInput(TwoWire* wire = &Wire) - : _wire(wire), _intPin(-1), _initialized(false), _debugCount(0), _lastPoll(0) {} + : _wire(wire), _intPin(-1), _initialized(false), _debugCount(0) {} bool begin(int intPin) { _intPin = intPin; pinMode(_intPin, INPUT); + // On ESP32 every GPIO is interrupt-capable and digitalPinToInterrupt is an + // identity macro (pin == interrupt number), but it is not always visible in + // this header's include context -- pass the GPIO number directly. + attachInterrupt(_intPin, _touchIsr, FALLING); // Verify the touch controller is present on the bus _wire->beginTransmission(TOUCH_ADDR); @@ -52,38 +66,61 @@ public: } Serial.printf("[Touch] CST328 found at 0x%02X, INT=GPIO%d\n", TOUCH_ADDR, _intPin); + + // Enter normal report mode: write command register 0xD109. The Hynitron + // driver does this once after reset (cst3xx_init -> set_workmode NOMAL_MODE). + _wire->beginTransmission(TOUCH_ADDR); + _wire->write(0xD1); + _wire->write(0x09); + _wire->endTransmission(true); + _initialized = true; return true; } bool isReady() const { return _initialized; } - // Poll for touch. Returns true if a finger is down, fills x and y. - // Coordinates are in physical display space (0-239 X, 0-319 Y). - // NOTE: CST328 INT pin is pulse-based, not level. We cannot rely on - // digitalRead(INT) for touch state. Instead, always read and check buf[0]. + // Returns true if a finger is down, fills x and y (physical display space: + // 0-239 X, 0-319 Y). Reads only when the INT edge interrupt has fired. bool getPoint(int16_t &x, int16_t &y) { if (!_initialized) return false; - // Rate limit: poll at most every 20ms (50 Hz) to avoid I2C bus congestion - unsigned long now = millis(); - if (now - _lastPoll < 20) return false; - _lastPoll = now; + // Only touch the bus when the INT line has signalled a new report. With no + // pending report there is nothing to read, and reading anyway is what + // produced the i2cRead -1/263 errors. + if (!_touchIrqFired) return false; + _touchIrqFired = false; uint8_t buf[7]; memset(buf, 0, sizeof(buf)); - // Write register address 0xD000 + // Write register address 0xD000. + // Use a STOP here (true), not a repeated start (false): the repeated-start + // combined read (i2cWriteReadNonStop) is what was throwing the -1/263 errors + // on this bus, while the keyboard's stop-then-read pattern never errors. _wire->beginTransmission(TOUCH_ADDR); _wire->write(0xD0); _wire->write(0x00); - if (_wire->endTransmission(false) != 0) return false; + if (_wire->endTransmission(true) != 0) return false; // Read 7 bytes of touch data uint8_t received = _wire->requestFrom(TOUCH_ADDR, (uint8_t)7); if (received < 7) return false; for (int i = 0; i < 7; i++) buf[i] = _wire->read(); + // Acknowledge the report: write 0xAB to register 0xD000 so the controller + // releases its buffer for the next frame. Required after EVERY read of + // 0xD000 -- without it the CST328 re-serves stale frames (phantom touches) + // and eventually NAKs the read. Matches the Hynitron driver tail-end write. + _wire->beginTransmission(TOUCH_ADDR); + _wire->write(0xD0); + _wire->write(0x00); + _wire->write(0xAB); + _wire->endTransmission(true); + + // Check byte: a valid frame always has buf[6] == 0xAB. Reject anything else. + if (buf[6] != 0xAB) return false; + // buf[0] == 0xAB means idle (no touch active) if (buf[0] == 0xAB) return false; @@ -121,7 +158,6 @@ private: int _intPin; bool _initialized; int _debugCount; - unsigned long _lastPoll; }; #endif // TOUCH_INPUT_H diff --git a/variants/lilygo_tdeck_pro_max_WIP/TDeckProMaxBoard.cpp b/variants/lilygo_tdeck_max/TDeckProMaxBoard.cpp similarity index 89% rename from variants/lilygo_tdeck_pro_max_WIP/TDeckProMaxBoard.cpp rename to variants/lilygo_tdeck_max/TDeckProMaxBoard.cpp index 9d7e34f2..96abbdde 100644 --- a/variants/lilygo_tdeck_pro_max_WIP/TDeckProMaxBoard.cpp +++ b/variants/lilygo_tdeck_max/TDeckProMaxBoard.cpp @@ -36,6 +36,13 @@ void TDeckProMaxBoard::begin() { // BQ25896, BHI260AP) share SDA=13, SCL=14. Wire.begin(I2C_SDA, I2C_SCL); Wire.setClock(100000); // 100kHz — safe for all devices on the bus + // --- TEMP: charger chip probe (BQ25896 @ 0x6B vs SY6970 @ 0x6A) --- +for (uint8_t a = 0x6A; a <= 0x6B; a++) { + Wire.beginTransmission(a); + uint8_t e = Wire.endTransmission(); + Serial.printf("Charger probe 0x%02X -> %s\n", a, + e == 0 ? (a == 0x6A ? "ACK (SY6970)" : "ACK (BQ25896)") : "no response"); +} MESH_DEBUG_PRINTLN(" I2C initialized (SDA=%d SCL=%d)", I2C_SDA, I2C_SCL); // ------ Step 2: XL9555 I/O Expander ------ @@ -46,6 +53,15 @@ void TDeckProMaxBoard::begin() { // without XL9555, but LoRa/GPS/modem will be dead. } + // Frontlight on, factory-style: a single analogWrite right after XL9555 init, + // exactly as LilyGo's factory firmware does (analogWrite(BOARD_EPD_BL, 50)). + // No rail-forcing, no explicit channel -- this is the clean test of whether + // the core (2.0.14 vs 2.0.17) was the difference. +#ifdef PIN_EINK_BL + analogWrite(PIN_EINK_BL, 50); + Serial.println(">>> BL: analogWrite(41,50) after XL9555 init"); +#endif + // ------ Step 3: Touch reset pulse ------ // The touch controller (CST328) needs a clean reset via XL9555 IO07 // before the touch driver tries to communicate with it. @@ -108,16 +124,10 @@ void TDeckProMaxBoard::begin() { } #endif - // ------ Step 12: E-ink backlight (working on MAX!) ------ - // Configure LEDC PWM for backlight brightness control. - // Start with backlight OFF — UI code can enable it when needed. - #ifdef PIN_EINK_BL - // Arduino ESP32 core 2.x uses channel-based LEDC API - ledcSetup(EINK_BL_LEDC_CHANNEL, 1000, 8); // Channel 0, 1kHz, 8-bit resolution - ledcAttachPin(PIN_EINK_BL, EINK_BL_LEDC_CHANNEL); - ledcWrite(EINK_BL_LEDC_CHANNEL, 0); // Off by default - MESH_DEBUG_PRINTLN(" Backlight PWM configured on IO%d", PIN_EINK_BL); - #endif + // ------ Step 12: E-ink backlight ------ + // Left as no-op during this diagnostic: the early factory-order assert above + // (right after XL9555 init) is what we are testing. Do NOT pull IO41 low here, + // or it would undo that test. MESH_DEBUG_PRINTLN("TDeckProMaxBoard::begin() - complete"); } @@ -330,18 +340,25 @@ void TDeckProMaxBoard::loraPowerOff() { void TDeckProMaxBoard::backlightOn() { #ifdef PIN_EINK_BL - ledcWrite(EINK_BL_LEDC_CHANNEL, 255); + analogWrite(PIN_EINK_BL, 50); #endif + _backlightOn = true; } void TDeckProMaxBoard::backlightOff() { #ifdef PIN_EINK_BL - ledcWrite(EINK_BL_LEDC_CHANNEL, 0); + analogWrite(PIN_EINK_BL, 0); #endif + _backlightOn = false; } void TDeckProMaxBoard::backlightSetBrightness(uint8_t duty) { #ifdef PIN_EINK_BL - ledcWrite(EINK_BL_LEDC_CHANNEL, duty); + analogWrite(PIN_EINK_BL, duty); #endif + _backlightOn = (duty > 0); } + +bool TDeckProMaxBoard::isBacklightOn() const { + return _backlightOn; +} \ No newline at end of file diff --git a/variants/lilygo_tdeck_pro_max_WIP/TDeckProMaxBoard.h b/variants/lilygo_tdeck_max/TDeckProMaxBoard.h similarity index 97% rename from variants/lilygo_tdeck_pro_max_WIP/TDeckProMaxBoard.h rename to variants/lilygo_tdeck_max/TDeckProMaxBoard.h index 91126284..aac7df74 100644 --- a/variants/lilygo_tdeck_pro_max_WIP/TDeckProMaxBoard.h +++ b/variants/lilygo_tdeck_max/TDeckProMaxBoard.h @@ -95,14 +95,16 @@ public: void backlightOn(); void backlightOff(); void backlightSetBrightness(uint8_t duty); // 0-255, via LEDC PWM + bool isBacklightOn() const; private: // Shadow registers for XL9555 output ports (avoid I2C read-modify-write) uint8_t _xlPort0 = XL9555_BOOT_PORT0; uint8_t _xlPort1 = XL9555_BOOT_PORT1; bool _xlReady = false; + bool _backlightOn = false; // tracks frontlight on/off for isBacklightOn() // Low-level I2C helpers bool xl9555_writeReg(uint8_t reg, uint8_t val); uint8_t xl9555_readReg(uint8_t reg); -}; +}; \ No newline at end of file diff --git a/variants/lilygo_tdeck_pro_max_WIP/Tca8418keyboard.h b/variants/lilygo_tdeck_max/Tca8418keyboard.h similarity index 84% rename from variants/lilygo_tdeck_pro_max_WIP/Tca8418keyboard.h rename to variants/lilygo_tdeck_max/Tca8418keyboard.h index 4e5635a6..4b238c88 100644 --- a/variants/lilygo_tdeck_pro_max_WIP/Tca8418keyboard.h +++ b/variants/lilygo_tdeck_max/Tca8418keyboard.h @@ -21,8 +21,10 @@ #define KB_KEY_BACKSPACE '\b' #define KB_KEY_ENTER '\r' #define KB_KEY_SPACE ' ' -#define KB_KEY_EMOJI 0x01 // Non-printable code for $ key (emoji picker) -#define KB_KEY_BACKLIGHT 0x02 // Non-printable code for Alt+B (backlight toggle, MAX only) +#define KB_KEY_EMOJI 0x01 // Non-printable code for $ key (emoji picker) +#define KB_KEY_MIC 0x02 // Mic key press (PTT start / voice screen open) +#define KB_KEY_MIC_RELEASE 0x03 // Mic key release (PTT stop) +#define KB_KEY_BACKLIGHT 0x04 // Alt+B backlight toggle (T-Deck Pro MAX only) class TCA8418Keyboard { private: @@ -35,7 +37,10 @@ private: bool _shiftUsedWhileHeld; // Was shift consumed by any key while held bool _altActive; // Sticky alt (one-shot) bool _symActive; // Sticky sym (one-shot) + bool _micHeld; // Mic key physically held down (for PTT release detection) unsigned long _lastShiftTime; // For Shift+key combos + bool _enterHeld; // Enter key physically held down + unsigned long _enterPressTime; // millis() when Enter was pressed uint8_t readReg(uint8_t reg) { _wire->beginTransmission(_addr); @@ -152,7 +157,8 @@ private: public: TCA8418Keyboard(uint8_t addr = 0x34, TwoWire* wire = &Wire) : _addr(addr), _wire(wire), _initialized(false), - _shiftActive(false), _shiftConsumed(false), _shiftHeld(false), _shiftUsedWhileHeld(false), _altActive(false), _symActive(false), _lastShiftTime(0) {} + _shiftActive(false), _shiftConsumed(false), _shiftHeld(false), _shiftUsedWhileHeld(false), _altActive(false), _symActive(false), _micHeld(false), _lastShiftTime(0), + _enterHeld(false), _enterPressTime(0) {} bool begin() { // Check if device responds @@ -243,7 +249,22 @@ public: return 0; } + // Track mic key release — return KB_KEY_MIC_RELEASE for PTT stop + if (!pressed && keyCode == 34) { + if (_micHeld) { + _micHeld = false; + Serial.println("KB: Mic released -> KB_KEY_MIC_RELEASE"); + return KB_KEY_MIC_RELEASE; + } + return 0; + } + // Only act on key press, not release + // (Enter release tracked for long-press detection) + if (!pressed && keyCode == 21) { + _enterHeld = false; + return 0; + } if (!pressed || keyCode == 0) { return 0; } @@ -267,6 +288,13 @@ public: Serial.println("KB: Sym activated"); return 0; } + + // Track Enter press for long-press detection + if (keyCode == 21) { + _enterHeld = true; + _enterPressTime = millis(); + // Fall through to normal processing — '\r' is returned below + } // Handle dedicated $ key (key code 22, next to M) // Bare press = emoji picker, Sym+$ = literal '$' @@ -280,24 +308,29 @@ public: return KB_KEY_EMOJI; } - // Handle Mic key - always produces '0' (silk-screened on key) - // Sym+Mic also produces '0' (consumes sym so it doesn't leak) + // Handle Mic key — bare press returns KB_KEY_MIC for PTT / voice screen + // Sym+Mic produces '0' (silk-screened on key) for text input if (keyCode == 34) { - _symActive = false; - Serial.println("KB: Mic -> '0'"); - return '0'; + if (_symActive) { + _symActive = false; + Serial.println("KB: Sym+Mic -> '0'"); + return '0'; + } + _micHeld = true; + Serial.println("KB: Mic -> KB_KEY_MIC"); + return KB_KEY_MIC; } - // Get the character - char c = 0; - - // Alt+B -> backlight toggle (T-Deck Pro MAX only — working front-light on IO41) + // Alt+B -> backlight toggle (T-Deck Pro MAX only -- working front-light on IO41) if (_altActive && keyCode == 25) { // keyCode 25 = B _altActive = false; Serial.println("KB: Alt+B -> backlight toggle"); return KB_KEY_BACKLIGHT; } + // Get the character + char c = 0; + if (_altActive) { c = getAltChar(keyCode); _altActive = false; // Reset sticky alt @@ -346,6 +379,7 @@ public: } bool isReady() const { return _initialized; } + bool isMicHeld() const { return _micHeld; } // Check if shift was pressed within the last N milliseconds bool wasShiftRecentlyPressed(unsigned long withinMs = 500) const { @@ -357,4 +391,10 @@ public: bool wasShiftConsumed() const { return _shiftConsumed; } + + // Enter long-press detection + bool isEnterHeld() const { return _enterHeld; } + unsigned long enterHeldMs() const { + return _enterHeld ? (millis() - _enterPressTime) : 0; + } }; \ No newline at end of file diff --git a/variants/lilygo_tdeck_pro_max_WIP/platformio.ini b/variants/lilygo_tdeck_max/platformio.ini similarity index 98% rename from variants/lilygo_tdeck_pro_max_WIP/platformio.ini rename to variants/lilygo_tdeck_max/platformio.ini index ff9fe026..65153d05 100644 --- a/variants/lilygo_tdeck_pro_max_WIP/platformio.ini +++ b/variants/lilygo_tdeck_max/platformio.ini @@ -41,7 +41,7 @@ build_flags = ${sensor_base.build_flags} ; Include MAX variant first (for variant.h, target.h, TDeckProMaxBoard.h) ; then V1.1 variant (for TDeckBoard.h, which TDeckProMaxBoard inherits from) - -I variants/LilyGo_TDeck_Pro_Max + -I variants/lilygo_tdeck_max -I variants/LilyGo_TDeck_Pro ; Both defines needed: LilyGo_TDeck_Pro for existing UI code guards, ; LilyGo_TDeck_Pro_Max for MAX-specific code paths @@ -137,7 +137,7 @@ build_src_filter = ${esp32_base.build_src_filter} ; Include TDeckBoard.cpp from V1.1 (parent class with BQ27220 code) +<../variants/LilyGo_TDeck_Pro/TDeckBoard.cpp> ; Include MAX variant (target.cpp + TDeckProMaxBoard.cpp) - +<../variants/LilyGo_TDeck_Pro_Max> + +<../variants/lilygo_tdeck_max> + lib_deps = ${esp32_base.lib_deps} @@ -146,6 +146,7 @@ lib_deps = adafruit/Adafruit GFX Library@^1.11.0 bitbank2/PNGdec@^1.0.1 WebServer + DNSServer Update @@ -230,4 +231,4 @@ build_src_filter = ${LilyGo_TDeck_Pro_Max.build_src_filter} + lib_deps = ${LilyGo_TDeck_Pro_Max.lib_deps} - densaugeo/base64 @ ~1.4.0 + densaugeo/base64 @ ~1.4.0 \ No newline at end of file diff --git a/variants/lilygo_tdeck_pro_max_WIP/target.cpp b/variants/lilygo_tdeck_max/target.cpp similarity index 100% rename from variants/lilygo_tdeck_pro_max_WIP/target.cpp rename to variants/lilygo_tdeck_max/target.cpp diff --git a/variants/lilygo_tdeck_pro_max_WIP/target.h b/variants/lilygo_tdeck_max/target.h similarity index 100% rename from variants/lilygo_tdeck_pro_max_WIP/target.h rename to variants/lilygo_tdeck_max/target.h diff --git a/variants/lilygo_tdeck_pro_max_WIP/variant.h b/variants/lilygo_tdeck_max/variant.h similarity index 97% rename from variants/lilygo_tdeck_pro_max_WIP/variant.h rename to variants/lilygo_tdeck_max/variant.h index 2f48ce5f..80bd9f45 100644 --- a/variants/lilygo_tdeck_pro_max_WIP/variant.h +++ b/variants/lilygo_tdeck_max/variant.h @@ -230,7 +230,11 @@ #define BQ27220_DESIGN_CAPACITY_MAH 1500 // Alias used by TDeckBoard.h #define HAS_PPM 1 -#define XPOWERS_CHIP_BQ25896 +// This board uses the SY6970 charger @ 0x6A (newer revision -- confirmed by I2C +// probe: ACK at 0x6A, no response at 0x6B). XPowersLib selects the driver class +// from this macro: XPOWERS_CHIP_SY6970 -> PowersSY6970 (0x6A). Older boards with +// the BQ25896 @ 0x6B would use XPOWERS_CHIP_BQ25896 instead. +#define XPOWERS_CHIP_SY6970 // ----------------------------------------------------------------------------- // LoRa Radio (SX1262) @@ -298,4 +302,4 @@ // Compatibility: PIN_PERF_POWERON does not exist on MAX (IO10 is modem UART RX). // Defined as -1 so TDeckBoard.cpp compiles (parent class), but never used at runtime. -#define PIN_PERF_POWERON -1 +#define PIN_PERF_POWERON -1 \ No newline at end of file diff --git a/variants/lilygo_tdeck_pro/platformio.ini b/variants/lilygo_tdeck_pro/platformio.ini index 66dc0b37..132e2989 100644 --- a/variants/lilygo_tdeck_pro/platformio.ini +++ b/variants/lilygo_tdeck_pro/platformio.ini @@ -160,6 +160,9 @@ build_flags = -D MECK_WEB_READER=1 -D MECK_OTA_UPDATE=1 -D FIRMWARE_VERSION='"Meck v1.11.WiFi"' + -D WIFI_DEBUG_LOGGING=1 + -D MESH_PACKET_LOGGING=1 + -D MESH_DEBUG=1 build_src_filter = ${LilyGo_TDeck_Pro.build_src_filter} + -