diff --git a/boards/t-deck_pro_max.json b/boards/t-deck_pro_max.json new file mode 100644 index 0000000..ef57d9a --- /dev/null +++ b/boards/t-deck_pro_max.json @@ -0,0 +1,40 @@ +{ + "build": { + "arduino": { + "ldscript": "esp32s3_out.ld", + "memory_type": "qio_qspi", + "partitions": "default_16MB.csv" + }, + "core": "esp32", + "extra_flags": [ + "-DBOARD_HAS_PSRAM", + "-DARDUINO_USB_CDC_ON_BOOT=1", + "-DARDUINO_USB_MODE=1", + "-DARDUINO_RUNNING_CORE=1", + "-DARDUINO_EVENT_RUNNING_CORE=1" + ], + "f_cpu": "240000000L", + "f_flash": "80000000L", + "flash_mode": "qio", + "hwids": [["0x303A", "0x1001"]], + "mcu": "esp32s3", + "variant": "esp32s3" + }, + "connectivity": ["wifi", "bluetooth", "lora"], + "debug": { + "default_tool": "esp-builtin", + "onboard_tools": ["esp-builtin"], + "openocd_target": "esp32s3.cfg" + }, + "frameworks": ["arduino", "espidf"], + "name": "LilyGo T-Deck Pro MAX (16MB Flash 8MB QSPI PSRAM)", + "upload": { + "flash_size": "16MB", + "maximum_ram_size": 327680, + "maximum_size": 16777216, + "require_upload_port": true, + "speed": 921600 + }, + "url": "https://www.lilygo.cc/products/t-deck-pro", + "vendor": "LilyGo" +} diff --git a/platformio.ini b/platformio.ini index eff84da..bca4486 100644 --- a/platformio.ini +++ b/platformio.ini @@ -11,6 +11,8 @@ [platformio] extra_configs = variants/*/platformio.ini + variants/LilyGo_TDeck_Pro/platformio.ini + variants/LilyGo_T5S3_EPaper_Pro/platformio.ini [arduino_base] framework = arduino diff --git a/variants/lilygo_tdeck_pro_max/TDeckProMaxBoard.cpp b/variants/lilygo_tdeck_pro_max/TDeckProMaxBoard.cpp new file mode 100644 index 0000000..7a4898b --- /dev/null +++ b/variants/lilygo_tdeck_pro_max/TDeckProMaxBoard.cpp @@ -0,0 +1,340 @@ +#include +#include "variant.h" +#include "TDeckProMaxBoard.h" +#include // For MESH_DEBUG_PRINTLN + +// ============================================================================= +// TDeckProMaxBoard::begin() — Boot sequence for T-Deck Pro MAX V0.1 +// +// Critical ordering: +// 1. I2C bus init (XL9555, BQ27220, and all sensors share this bus) +// 2. XL9555 init (must be up before ANY peripheral that depends on it) +// 3. Touch reset pulse via XL9555 (needed before touch driver init) +// 4. Keyboard reset pulse via XL9555 (clean keyboard state) +// 5. LoRa power enable via XL9555 (must be on before SPI radio init) +// 6. GPS power + UART init +// 7. Parent class init (ESP32Board::begin) +// 8. LoRa SPI pin config + deep sleep wake handling +// 9. BQ27220 fuel gauge check +// 10. Low-voltage protection +// +// NOTE: We do NOT call TDeckBoard::begin() — we reimplement the boot sequence +// to handle XL9555-routed pins. BQ27220 methods are inherited unchanged. +// ============================================================================= + +void TDeckProMaxBoard::begin() { + + MESH_DEBUG_PRINTLN("TDeckProMaxBoard::begin() - T-Deck Pro MAX V0.1"); + + // ------ Step 1: I2C bus ------ + // All I2C devices (XL9555, BQ27220, TCA8418, CST328, DRV2605, ES8311, + // BQ25896, BHI260AP) share SDA=13, SCL=14. + Wire.begin(I2C_SDA, I2C_SCL); + Wire.setClock(100000); // 100kHz — safe for all devices on the bus + MESH_DEBUG_PRINTLN(" I2C initialized (SDA=%d SCL=%d)", I2C_SDA, I2C_SCL); + + // ------ Step 2: XL9555 I/O Expander ------ + // This must happen before anything that needs peripheral power or resets. + if (!xl9555_init()) { + Serial.println("CRITICAL: XL9555 init failed — peripherals will not work!"); + // Continue anyway; some things (display, keyboard INT) might still work + // without XL9555, but LoRa/GPS/modem will be dead. + } + + // ------ 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. + touchReset(); + + // ------ Step 4: Keyboard reset pulse ------ + keyboardReset(); + + // ------ Step 5: Parent class init ------ + // ESP32Board::begin() handles common ESP32 setup. + // We skip TDeckBoard::begin() because it uses PIN_PERF_POWERON and + // direct GPIO for LoRa/GPS power that don't exist on MAX. + ESP32Board::begin(); + + // ------ Step 6: GPS UART init ------ + // GPS power was already enabled by XL9555 boot defaults (GPS_EN HIGH). + // Now init the UART with the MAX-specific pins. + #if HAS_GPS + Serial2.begin(GPS_BAUDRATE, SERIAL_8N1, GPS_RX_PIN, GPS_TX_PIN); + MESH_DEBUG_PRINTLN(" GPS Serial2 initialized (RX=%d TX=%d @ %d baud)", + GPS_RX_PIN, GPS_TX_PIN, GPS_BAUDRATE); + #endif + + // ------ Step 7: Configure user button ------ + pinMode(PIN_USER_BTN, INPUT); + + // ------ Step 8: Configure LoRa SPI pins ------ + // LoRa power is already enabled via XL9555 (LORA_EN HIGH in boot defaults). + pinMode(P_LORA_MISO, INPUT_PULLUP); + + // ------ Step 9: Handle wake from deep sleep ------ + esp_reset_reason_t reason = esp_reset_reason(); + if (reason == ESP_RST_DEEPSLEEP) { + uint64_t wakeup_source = esp_sleep_get_ext1_wakeup_status(); + if (wakeup_source & (1ULL << P_LORA_DIO_1)) { + startup_reason = BD_STARTUP_RX_PACKET; + } + rtc_gpio_hold_dis((gpio_num_t)P_LORA_NSS); + rtc_gpio_deinit((gpio_num_t)P_LORA_DIO_1); + } + + // ------ Step 10: BQ27220 fuel gauge ------ + #if HAS_BQ27220 + uint16_t voltage = getBattMilliVolts(); + MESH_DEBUG_PRINTLN(" Battery voltage: %d mV", voltage); + configureFuelGauge(); // Inherited from TDeckBoard — sets 1500 mAh + #endif + + // ------ Step 11: Early low-voltage protection ------ + #if HAS_BQ27220 && defined(AUTO_SHUTDOWN_MILLIVOLTS) + { + uint16_t bootMv = getBattMilliVolts(); + if (bootMv > 0 && bootMv < AUTO_SHUTDOWN_MILLIVOLTS) { + Serial.printf("CRITICAL: Boot voltage %dmV < %dmV — sleeping immediately\n", + bootMv, AUTO_SHUTDOWN_MILLIVOLTS); + esp_sleep_disable_wakeup_source(ESP_SLEEP_WAKEUP_ALL); + esp_sleep_enable_ext1_wakeup(1ULL << PIN_USER_BTN, ESP_EXT1_WAKEUP_ANY_HIGH); + esp_deep_sleep_start(); + } + } + #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 + ledcAttach(PIN_EINK_BL, 1000, 8); // 1kHz, 8-bit resolution + ledcWrite(PIN_EINK_BL, 0); // Off by default + MESH_DEBUG_PRINTLN(" Backlight PWM configured on IO%d", PIN_EINK_BL); + #endif + + MESH_DEBUG_PRINTLN("TDeckProMaxBoard::begin() - complete"); +} + + +// ============================================================================= +// XL9555 I/O Expander — Lightweight I2C Driver +// ============================================================================= + +bool TDeckProMaxBoard::xl9555_writeReg(uint8_t reg, uint8_t val) { + Wire.beginTransmission(I2C_ADDR_XL9555); + Wire.write(reg); + Wire.write(val); + return Wire.endTransmission() == 0; +} + +uint8_t TDeckProMaxBoard::xl9555_readReg(uint8_t reg) { + Wire.beginTransmission(I2C_ADDR_XL9555); + Wire.write(reg); + Wire.endTransmission(false); + Wire.requestFrom((uint8_t)I2C_ADDR_XL9555, (uint8_t)1); + return Wire.available() ? Wire.read() : 0xFF; +} + +bool TDeckProMaxBoard::xl9555_init() { + MESH_DEBUG_PRINTLN(" XL9555: Initializing I/O expander at 0x%02X", I2C_ADDR_XL9555); + + // Verify XL9555 is present on the bus + Wire.beginTransmission(I2C_ADDR_XL9555); + if (Wire.endTransmission() != 0) { + Serial.println(" XL9555: NOT FOUND on I2C bus!"); + _xlReady = false; + return false; + } + + // Set ALL pins as outputs (config register: 0 = output) + // Port 0 (pins 0-7): all output + if (!xl9555_writeReg(XL9555_REG_CONFIG_0, 0x00)) return false; + // Port 1 (pins 8-15): all output + if (!xl9555_writeReg(XL9555_REG_CONFIG_1, 0x00)) return false; + + // Apply boot defaults + _xlPort0 = XL9555_BOOT_PORT0; + _xlPort1 = XL9555_BOOT_PORT1; + if (!xl9555_writeReg(XL9555_REG_OUTPUT_0, _xlPort0)) return false; + if (!xl9555_writeReg(XL9555_REG_OUTPUT_1, _xlPort1)) return false; + + _xlReady = true; + + MESH_DEBUG_PRINTLN(" XL9555: Ready (Port0=0x%02X Port1=0x%02X)", _xlPort0, _xlPort1); + MESH_DEBUG_PRINTLN(" XL9555: LoRa=%s GPS=%s 1V8=%s Modem=%s Antenna=%s", + (_xlPort0 & (1 << XL_PIN_LORA_EN)) ? "ON" : "OFF", + (_xlPort0 & (1 << XL_PIN_GPS_EN)) ? "ON" : "OFF", + (_xlPort0 & (1 << XL_PIN_1V8_EN)) ? "ON" : "OFF", + (_xlPort0 & (1 << XL_PIN_6609_EN)) ? "ON" : "OFF", + (_xlPort0 & (1 << XL_PIN_LORA_SEL)) ? "internal" : "external"); + + return true; +} + +void TDeckProMaxBoard::xl9555_digitalWrite(uint8_t pin, bool value) { + if (!_xlReady) return; + + if (pin < 8) { + // Port 0 + if (value) _xlPort0 |= (1 << pin); + else _xlPort0 &= ~(1 << pin); + xl9555_writeReg(XL9555_REG_OUTPUT_0, _xlPort0); + } else if (pin < 16) { + // Port 1 (subtract 8 for bit position) + uint8_t bit = pin - 8; + if (value) _xlPort1 |= (1 << bit); + else _xlPort1 &= ~(1 << bit); + xl9555_writeReg(XL9555_REG_OUTPUT_1, _xlPort1); + } +} + +bool TDeckProMaxBoard::xl9555_digitalRead(uint8_t pin) const { + if (pin < 8) return (_xlPort0 >> pin) & 1; + if (pin < 16) return (_xlPort1 >> (pin - 8)) & 1; + return false; +} + +void TDeckProMaxBoard::xl9555_writePort0(uint8_t val) { + _xlPort0 = val; + if (_xlReady) xl9555_writeReg(XL9555_REG_OUTPUT_0, val); +} + +void TDeckProMaxBoard::xl9555_writePort1(uint8_t val) { + _xlPort1 = val; + if (_xlReady) xl9555_writeReg(XL9555_REG_OUTPUT_1, val); +} + + +// ============================================================================= +// High-level peripheral control +// ============================================================================= + +// ---- Modem (A7682E) ---- + +void TDeckProMaxBoard::modemPowerOn() { + MESH_DEBUG_PRINTLN(" XL9555: Modem power ON (6609_EN HIGH)"); + xl9555_digitalWrite(XL_PIN_6609_EN, HIGH); + delay(100); // Allow SGM6609 boost to stabilise +} + +void TDeckProMaxBoard::modemPowerOff() { + MESH_DEBUG_PRINTLN(" XL9555: Modem power OFF (6609_EN LOW)"); + xl9555_digitalWrite(XL_PIN_6609_EN, LOW); +} + +void TDeckProMaxBoard::modemPwrkeyPulse() { + // A7682E power-on sequence: pulse PWRKEY LOW for >= 500ms + // (Some datasheets say pull HIGH then LOW; LilyGo factory sets HIGH then toggles.) + MESH_DEBUG_PRINTLN(" XL9555: Modem PWRKEY pulse"); + xl9555_digitalWrite(XL_PIN_PWRKEY_EN, HIGH); + delay(100); + xl9555_digitalWrite(XL_PIN_PWRKEY_EN, LOW); + delay(1200); + xl9555_digitalWrite(XL_PIN_PWRKEY_EN, HIGH); +} + +// ---- Audio output selection ---- + +void TDeckProMaxBoard::selectAudioES8311() { + MESH_DEBUG_PRINTLN(" XL9555: Audio select → ES8311"); + xl9555_digitalWrite(XL_PIN_AUDIO_SEL, LOW); +} + +void TDeckProMaxBoard::selectAudioModem() { + MESH_DEBUG_PRINTLN(" XL9555: Audio select → A7682E"); + xl9555_digitalWrite(XL_PIN_AUDIO_SEL, HIGH); +} + +void TDeckProMaxBoard::amplifierEnable() { + xl9555_digitalWrite(XL_PIN_AMPLIFIER, HIGH); +} + +void TDeckProMaxBoard::amplifierDisable() { + xl9555_digitalWrite(XL_PIN_AMPLIFIER, LOW); +} + +// ---- LoRa antenna selection ---- + +void TDeckProMaxBoard::loraAntennaInternal() { + MESH_DEBUG_PRINTLN(" XL9555: LoRa antenna → internal"); + xl9555_digitalWrite(XL_PIN_LORA_SEL, HIGH); +} + +void TDeckProMaxBoard::loraAntennaExternal() { + MESH_DEBUG_PRINTLN(" XL9555: LoRa antenna → external"); + xl9555_digitalWrite(XL_PIN_LORA_SEL, LOW); +} + +// ---- Motor (DRV2605) ---- + +void TDeckProMaxBoard::motorEnable() { + xl9555_digitalWrite(XL_PIN_MOTOR_EN, HIGH); +} + +void TDeckProMaxBoard::motorDisable() { + xl9555_digitalWrite(XL_PIN_MOTOR_EN, LOW); +} + +// ---- Touch reset ---- + +void TDeckProMaxBoard::touchReset() { + if (!_xlReady) return; + MESH_DEBUG_PRINTLN(" XL9555: Touch reset pulse"); + xl9555_digitalWrite(XL_PIN_TOUCH_RST, LOW); + delay(20); + xl9555_digitalWrite(XL_PIN_TOUCH_RST, HIGH); + delay(50); // Allow touch controller to come out of reset +} + +// ---- Keyboard reset ---- + +void TDeckProMaxBoard::keyboardReset() { + if (!_xlReady) return; + MESH_DEBUG_PRINTLN(" XL9555: Keyboard reset pulse"); + xl9555_digitalWrite(XL_PIN_KEY_RST, LOW); + delay(20); + xl9555_digitalWrite(XL_PIN_KEY_RST, HIGH); + delay(50); +} + +// ---- GPS power ---- + +void TDeckProMaxBoard::gpsPowerOn() { + xl9555_digitalWrite(XL_PIN_GPS_EN, HIGH); + delay(100); +} + +void TDeckProMaxBoard::gpsPowerOff() { + xl9555_digitalWrite(XL_PIN_GPS_EN, LOW); +} + +// ---- LoRa power ---- + +void TDeckProMaxBoard::loraPowerOn() { + xl9555_digitalWrite(XL_PIN_LORA_EN, HIGH); + delay(10); +} + +void TDeckProMaxBoard::loraPowerOff() { + xl9555_digitalWrite(XL_PIN_LORA_EN, LOW); +} + +// ---- E-ink backlight (working on MAX!) ---- + +void TDeckProMaxBoard::backlightOn() { + #ifdef PIN_EINK_BL + ledcWrite(PIN_EINK_BL, 255); + #endif +} + +void TDeckProMaxBoard::backlightOff() { + #ifdef PIN_EINK_BL + ledcWrite(PIN_EINK_BL, 0); + #endif +} + +void TDeckProMaxBoard::backlightSetBrightness(uint8_t duty) { + #ifdef PIN_EINK_BL + ledcWrite(PIN_EINK_BL, duty); + #endif +} diff --git a/variants/lilygo_tdeck_pro_max/TDeckProMaxBoard.h b/variants/lilygo_tdeck_pro_max/TDeckProMaxBoard.h new file mode 100644 index 0000000..9112628 --- /dev/null +++ b/variants/lilygo_tdeck_pro_max/TDeckProMaxBoard.h @@ -0,0 +1,108 @@ +#pragma once + +// ============================================================================= +// TDeckProMaxBoard — Board support for LilyGo T-Deck Pro MAX V0.1 +// +// Extends TDeckBoard (which provides all BQ27220 fuel gauge methods) with: +// - XL9555 I/O expander initialisation and control +// - XL9555-routed peripheral power management +// - Touch/keyboard reset via XL9555 +// - Modem power/PWRKEY via XL9555 +// - LoRa antenna selection via XL9555 +// - Audio output mux (ES8311 vs A7682E) via XL9555 +// - Speaker amplifier enable via XL9555 +// +// The XL9555 must be initialised before LoRa, GPS, modem, or touch are used. +// All power enables, resets, and switches go through I2C — not direct GPIO. +// ============================================================================= + +#include "variant.h" +#include "TDeckBoard.h" // Inherits BQ27220 fuel gauge, deep sleep, power management + +class TDeckProMaxBoard : public TDeckBoard { +public: + void begin(); + + const char* getManufacturerName() const { + return "LilyGo T-Deck Pro MAX"; + } + + // ------------------------------------------------------------------------- + // XL9555 I/O Expander — lightweight inline driver + // + // The XL9555 has 16 I/O pins across two 8-bit ports. + // Pin 0-7 = Port 0, Pin 8-15 = Port 1. + // We shadow the output state in _xlPort0/_xlPort1 to allow + // single-bit set/clear without read-modify-write over I2C. + // ------------------------------------------------------------------------- + + // Initialise XL9555: set all used pins as outputs, apply boot defaults. + // Returns true if I2C communication with XL9555 succeeded. + bool xl9555_init(); + + // Set a single XL9555 pin HIGH or LOW (pin 0-15). + void xl9555_digitalWrite(uint8_t pin, bool value); + + // Read the current output state of a pin (from shadow, not I2C read). + bool xl9555_digitalRead(uint8_t pin) const; + + // Write raw port values (for batch updates). + void xl9555_writePort0(uint8_t val); + void xl9555_writePort1(uint8_t val); + + // ------------------------------------------------------------------------- + // High-level peripheral control (delegates to XL9555) + // ------------------------------------------------------------------------- + + // Modem (A7682E) power control + void modemPowerOn(); // Enable SGM6609 boost (6609_EN HIGH) + void modemPowerOff(); // Disable SGM6609 boost (6609_EN LOW) + void modemPwrkeyPulse(); // Toggle PWRKEY: HIGH 100ms → LOW 1200ms → HIGH + + // Audio output selection + void selectAudioES8311(); // AUDIO_SEL LOW → ES8311 output to speaker/headphones + void selectAudioModem(); // AUDIO_SEL HIGH → A7682E output to speaker/headphones + void amplifierEnable(); // NS4150B amplifier ON (louder speaker) + void amplifierDisable(); // NS4150B amplifier OFF (saves power) + + // LoRa antenna selection (SKY13453 RF switch) + void loraAntennaInternal(); // LORA_SEL HIGH → internal PCB antenna (default) + void loraAntennaExternal(); // LORA_SEL LOW → external IPEX antenna + + // Motor (DRV2605) power + void motorEnable(); // MOTOR_EN HIGH + void motorDisable(); // MOTOR_EN LOW + + // Touch controller reset via XL9555 + void touchReset(); // Pulse TOUCH_RST: LOW 20ms → HIGH, then 50ms settle + + // Keyboard reset via XL9555 + void keyboardReset(); // Pulse KEY_RST: LOW 20ms → HIGH, then 50ms settle + + // GPS power control via XL9555 + void gpsPowerOn(); // GPS_EN HIGH + void gpsPowerOff(); // GPS_EN LOW + + // LoRa power control via XL9555 + void loraPowerOn(); // LORA_EN HIGH + void loraPowerOff(); // LORA_EN LOW + + // ------------------------------------------------------------------------- + // E-ink front-light control + // On MAX, IO41 has a working backlight circuit (boost converter + LEDs). + // PWM control for brightness is possible via ledc. + // ------------------------------------------------------------------------- + void backlightOn(); + void backlightOff(); + void backlightSetBrightness(uint8_t duty); // 0-255, via LEDC PWM + +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; + + // Low-level I2C helpers + bool xl9555_writeReg(uint8_t reg, uint8_t val); + uint8_t xl9555_readReg(uint8_t reg); +}; diff --git a/variants/lilygo_tdeck_pro_max/platformio.ini b/variants/lilygo_tdeck_pro_max/platformio.ini new file mode 100644 index 0000000..783fb77 --- /dev/null +++ b/variants/lilygo_tdeck_pro_max/platformio.ini @@ -0,0 +1,228 @@ +; ============================================================================= +; T-Deck Pro MAX V0.1 — Meck Build Environments +; +; Hardware: ESP32-S3 + XL9555 I/O expander + combined 4G (A7682E) + Audio (ES8311) +; +; Key differences from LilyGo_TDeck_Pro (V1.1): +; - Peripheral power controlled via XL9555 (not direct GPIO) +; - 4G modem and ES8311 audio coexist (no longer mutually exclusive) +; - ES8311 I2C codec replaces PCM5102A (different I2S pins, needs I2C config) +; - Several GPIO reassignments (see variant.h for full map) +; - 1500 mAh battery (was 1400) +; - Working e-ink front-light on IO41 +; +; WHAT WORKS OUT OF THE BOX: +; LoRa mesh, keyboard, e-ink display, GPS, touchscreen, battery management, +; SD card, text reader, notes, contacts, channels, settings, discovery, +; last heard, repeater admin, web reader (WiFi builds), OTA update. +; +; NEEDS ADAPTATION (future work): +; - HAS_4G_MODEM: ModemManager uses direct GPIO for MODEM_POWER_EN/PWRKEY +; which are XL9555-routed on MAX. Needs board.modemPowerOn() etc. +; - MECK_AUDIO_VARIANT: ES8311 needs I2C codec init (PCM5102A didn't). +; I2S pins are different. AudiobookPlayerScreen needs ES8311 driver. +; - Combined 4G+audio: existing #ifdef guards treat them as mutually +; exclusive. Needs restructuring for coexistence. +; ============================================================================= + +; --------------------------------------------------------------------------- +; Base environment for T-Deck Pro MAX +; --------------------------------------------------------------------------- +[LilyGo_TDeck_Pro_Max] +extends = esp32_base +extra_scripts = post:merge_firmware.py +board = t-deck_pro_max +board_build.flash_mode = qio +board_build.f_flash = 80000000L +board_build.arduino.memory_type = qio_qspi +board_upload.flash_size = 16MB +build_flags = + ${esp32_base.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_Pro + ; Both defines needed: LilyGo_TDeck_Pro for existing UI code guards, + ; LilyGo_TDeck_Pro_Max for MAX-specific code paths + -D LilyGo_TDeck_Pro + -D LilyGo_TDeck_Pro_Max + -D HAS_XL9555=1 + -D HAS_GPS=1 + -D BOARD_HAS_PSRAM=1 + -D CORE_DEBUG_LEVEL=1 + -D FORMAT_SPIFFS_IF_FAILED=1 + -D FORMAT_LITTLEFS_IF_FAILED=1 + -D ARDUINO_USB_CDC_ON_BOOT=1 + -D RADIO_CLASS=CustomSX1262 + -D WRAPPER_CLASS=CustomSX1262Wrapper + -D LORA_TX_POWER=22 + -D SX126X_DIO2_AS_RF_SWITCH + -D SX126X_CURRENT_LIMIT=140 + -D SX126X_RX_BOOSTED_GAIN=1 + -D SX126X_DIO3_TCXO_VOLTAGE=2.4f + ; LoRa SPI pins (direct GPIO — unchanged from V1.1) + -D P_LORA_DIO_1=5 + -D P_LORA_NSS=3 + -D P_LORA_RESET=4 + -D P_LORA_BUSY=6 + -D P_LORA_SCLK=36 + -D P_LORA_MISO=47 + -D P_LORA_MOSI=33 + ; P_LORA_EN deliberately NOT defined — LoRa power via XL9555 in board.begin() + ; GPS pins (direct GPIO — changed from V1.1!) + -D ENV_INCLUDE_GPS=1 + -D ENV_SKIP_GPS_DETECT=1 + -D PIN_GPS_RX=2 + -D PIN_GPS_TX=16 + -D GPS_BAUD_RATE=38400 + ; Sensor exclusions (same as V1.1) + -D ENV_INCLUDE_AHTX0=0 + -D ENV_INCLUDE_BME280=0 + -D ENV_INCLUDE_BMP280=0 + -D ENV_INCLUDE_SHTC3=0 + -D ENV_INCLUDE_SHT4X=0 + -D ENV_INCLUDE_LPS22HB=0 + -D ENV_INCLUDE_INA3221=0 + -D ENV_INCLUDE_INA219=0 + -D ENV_INCLUDE_INA226=0 + -D ENV_INCLUDE_INA260=0 + -D ENV_INCLUDE_MLX90614=0 + -D ENV_INCLUDE_VL53L0X=0 + -D ENV_INCLUDE_BME680=0 + -D ENV_INCLUDE_BMP085=0 + ; E-ink display (pin changes from V1.1: RST=9, BL=41) + -D USE_EINK + -D DISPLAY_CLASS=GxEPDDisplay + -D EINK_DISPLAY_MODEL=GxEPD2_310_GDEQ031T10 + -D EINK_WIDTH=240 + -D EINK_HEIGHT=320 + -D EINK_CS=34 + -D EINK_DC=35 + -D EINK_RST=9 + -D EINK_BUSY=37 + -D EINK_SCLK=36 + -D EINK_MOSI=33 + -D EINK_BL=41 + -D EINK_NOT_HIBERNATE=1 + ; Battery (1500 mAh on MAX, was 1400 on V1.1) + -D HAS_BQ27220=1 + -D AUTO_SHUTDOWN_MILLIVOLTS=2800 + ; Display rendering parameters + -D EINK_LIMIT_FASTREFRESH=10 + -D EINK_LIMIT_GHOSTING_PX=2000 + -D DISPLAY_ROTATION=0 + -D EINK_ROTATION=0 + -D EINK_SCALE_X=1.875f + -D EINK_SCALE_Y=2.5f + -D EINK_X_OFFSET=0 + -D EINK_Y_OFFSET=5 + ; Legacy display pin aliases (for GxEPDDisplay.cpp) + -D PIN_DISPLAY_CS=34 + -D PIN_DISPLAY_DC=35 + -D PIN_DISPLAY_RST=9 + -D PIN_DISPLAY_BUSY=37 + -D PIN_DISPLAY_SCLK=36 + -D PIN_DISPLAY_MISO=-1 + -D PIN_DISPLAY_MOSI=33 + -D PIN_DISPLAY_BL=41 + -D PIN_USER_BTN=0 + ; Touch (INT is direct GPIO; RST is XL9555, handled by board class) + -D HAS_TOUCHSCREEN=1 + -D CST328_PIN_INT=12 + -D CST328_PIN_RST=-1 + -D ARDUINO_LOOP_STACK_SIZE=32768 +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 ALL MAX variant files (TDeckProMaxBoard, target, etc.) + +<../variants/LilyGo_TDeck_Pro_Max> + + +lib_deps = + ${esp32_base.lib_deps} + ${sensor_base.lib_deps} + zinggjm/GxEPD2@^1.5.9 + adafruit/Adafruit GFX Library@^1.11.0 + bitbank2/PNGdec@^1.0.1 + WebServer + Update + + +; =========================================================================== +; Meck MAX builds — LoRa mesh works out of the box on all variants. +; 4G modem and ES8311 audio need adaptation before they can be enabled. +; =========================================================================== + +; MAX + BLE companion (standard BLE phone bridging) +; Both 4G + audio hardware present but not yet enabled in firmware. +; BLE_PIN_CODE limit: MAX_CONTACTS=500 (BLE protocol ceiling). +[env:meck_max_ble] +extends = LilyGo_TDeck_Pro_Max +build_flags = + ${LilyGo_TDeck_Pro_Max.build_flags} + -I examples/companion_radio/ui-new + -D MAX_CONTACTS=500 + -D MAX_GROUP_CHANNELS=20 + -D BLE_PIN_CODE=123456 + -D OFFLINE_QUEUE_SIZE=256 + -D MECK_WEB_READER=1 + -D MECK_OTA_UPDATE=1 + -D FIRMWARE_VERSION='"Meck v1.3.MAX"' +build_src_filter = ${LilyGo_TDeck_Pro_Max.build_src_filter} + + + + + +<../examples/companion_radio/*.cpp> + +<../examples/companion_radio/ui-new/*.cpp> + + +lib_deps = + ${LilyGo_TDeck_Pro_Max.lib_deps} + densaugeo/base64 @ ~1.4.0 + +; MAX + WiFi companion (WiFi app bridging — no BLE, higher contact limit) +; WiFi credentials loaded from SD card (/web/wifi.cfg). +; Connect via MeshCore web app, meshcore.js, or Python CLI. +[env:meck_max_wifi] +extends = LilyGo_TDeck_Pro_Max +build_flags = + ${LilyGo_TDeck_Pro_Max.build_flags} + -I examples/companion_radio/ui-new + -D MAX_CONTACTS=1500 + -D MAX_GROUP_CHANNELS=20 + -D MECK_WIFI_COMPANION=1 + -D TCP_PORT=5000 + -D WIFI_DEBUG_LOGGING=1 + -D OFFLINE_QUEUE_SIZE=256 + -D MECK_WEB_READER=1 + -D MECK_OTA_UPDATE=1 + -D FIRMWARE_VERSION='"Meck v1.3.MAX.WiFi"' +build_src_filter = ${LilyGo_TDeck_Pro_Max.build_src_filter} + + + + + +<../examples/companion_radio/*.cpp> + +<../examples/companion_radio/ui-new/*.cpp> + + +lib_deps = + ${LilyGo_TDeck_Pro_Max.lib_deps} + densaugeo/base64 @ ~1.4.0 + +; MAX standalone (no BLE/WiFi — maximum battery life, LoRa mesh only) +; Contacts in PSRAM (1500 capacity). OTA enabled (WiFi AP on demand). +[env:meck_max_standalone] +extends = LilyGo_TDeck_Pro_Max +build_flags = + ${LilyGo_TDeck_Pro_Max.build_flags} + -I examples/companion_radio/ui-new + -D MAX_CONTACTS=1500 + -D MAX_GROUP_CHANNELS=20 + -D OFFLINE_QUEUE_SIZE=1 + -D MECK_OTA_UPDATE=1 + -D FIRMWARE_VERSION='"Meck v1.3.MAX.SA"' +build_src_filter = ${LilyGo_TDeck_Pro_Max.build_src_filter} + + + + + +<../examples/companion_radio/*.cpp> + +<../examples/companion_radio/ui-new/*.cpp> + + +lib_deps = + ${LilyGo_TDeck_Pro_Max.lib_deps} + densaugeo/base64 @ ~1.4.0 diff --git a/variants/lilygo_tdeck_pro_max/target.cpp b/variants/lilygo_tdeck_pro_max/target.cpp new file mode 100644 index 0000000..5ee9cde --- /dev/null +++ b/variants/lilygo_tdeck_pro_max/target.cpp @@ -0,0 +1,90 @@ +#include +#include "variant.h" +#include "target.h" + +TDeckProMaxBoard board; + +#if defined(P_LORA_SCLK) + static SPIClass loraSpi(HSPI); + RADIO_CLASS radio = new Module(P_LORA_NSS, P_LORA_DIO_1, P_LORA_RESET, P_LORA_BUSY, loraSpi); +#else + RADIO_CLASS radio = new Module(P_LORA_NSS, P_LORA_DIO_1, P_LORA_RESET, P_LORA_BUSY); +#endif + +WRAPPER_CLASS radio_driver(radio, board); + +ESP32RTCClock fallback_clock; +AutoDiscoverRTCClock rtc_clock(fallback_clock); + +#if HAS_GPS + // Wrap Serial2 with a sentence counter so the UI can show NMEA throughput. + // MicroNMEALocationProvider reads through this wrapper transparently. + GPSStreamCounter gpsStream(Serial2); + MicroNMEALocationProvider gps(gpsStream, &rtc_clock); + EnvironmentSensorManager sensors(gps); +#else + SensorManager sensors; +#endif + +#ifdef DISPLAY_CLASS + DISPLAY_CLASS display; + MomentaryButton user_btn(PIN_USER_BTN, 1000, true); +#endif + +bool radio_init() { + MESH_DEBUG_PRINTLN("radio_init() - starting"); + + // NOTE: board.begin() is called by main.cpp setup() before radio_init() + // I2C is already initialized there with correct pins + + fallback_clock.begin(); + MESH_DEBUG_PRINTLN("radio_init() - fallback_clock started"); + + // Wire already initialized in board.begin() - just use it for RTC + rtc_clock.begin(Wire); + MESH_DEBUG_PRINTLN("radio_init() - rtc_clock started"); + +#if defined(P_LORA_SCLK) + MESH_DEBUG_PRINTLN("radio_init() - initializing LoRa SPI..."); + loraSpi.begin(P_LORA_SCLK, P_LORA_MISO, P_LORA_MOSI, P_LORA_NSS); + MESH_DEBUG_PRINTLN("radio_init() - SPI initialized, calling radio.std_init()..."); + bool result = radio.std_init(&loraSpi); + MESH_DEBUG_PRINTLN("radio_init() - radio.std_init() returned: %s", result ? "SUCCESS" : "FAILED"); + return result; +#else + MESH_DEBUG_PRINTLN("radio_init() - calling radio.std_init() without custom SPI..."); + bool result = radio.std_init(); + return result; +#endif +} + +uint32_t radio_get_rng_seed() { + return radio.random(0x7FFFFFFF); +} + +void radio_set_params(float freq, float bw, uint8_t sf, uint8_t cr) { + radio.setFrequency(freq); + radio.setSpreadingFactor(sf); + radio.setBandwidth(bw); + radio.setCodingRate(cr); + + // Longer preamble for low SF improves reliability — each symbol is shorter + // at low SF, so more symbols are needed for reliable detection. + // SF <= 8 gets 32 symbols (~65ms at SF7/62.5kHz); SF >= 9 keeps 16 (already ~131ms+). + uint16_t preamble = (sf <= 8) ? 32 : 16; + radio.setPreambleLength(preamble); + MESH_DEBUG_PRINTLN("radio_set_params() - bw=%.1f sf=%u preamble=%u", bw, sf, preamble); +} + +void radio_set_tx_power(uint8_t dbm) { + radio.setOutputPower(dbm); +} + +mesh::LocalIdentity radio_new_identity() { + RadioNoiseListener rng(radio); + return mesh::LocalIdentity(&rng); +} + +void radio_reset_agc() { + radio.setRxBoostedGainMode(true); +} diff --git a/variants/lilygo_tdeck_pro_max/target.h b/variants/lilygo_tdeck_pro_max/target.h new file mode 100644 index 0000000..02a4cf4 --- /dev/null +++ b/variants/lilygo_tdeck_pro_max/target.h @@ -0,0 +1,12 @@ +#pragma once + +// ============================================================================= +// target.h — T-Deck Pro MAX V0.1 variant +// +// Overrides the default target.h to use TDeckProMaxBoard instead of TDeckBoard. +// This file shadows variants/LilyGo_TDeck_Pro/target.h in the include path. +// ============================================================================= + +#include + +extern TDeckProMaxBoard board; diff --git a/variants/lilygo_tdeck_pro_max/variant.h b/variants/lilygo_tdeck_pro_max/variant.h new file mode 100644 index 0000000..2f48ce5 --- /dev/null +++ b/variants/lilygo_tdeck_pro_max/variant.h @@ -0,0 +1,301 @@ +#pragma once + +// ============================================================================= +// LilyGo T-Deck Pro MAX V0.1 - Pin Definitions +// Hardware revision: HD-V3-250911 +// +// KEY DIFFERENCES FROM T-Deck Pro V1.1: +// - XL9555 I/O expander (0x20) controls peripheral power, resets, and switches +// (LoRa EN, GPS EN, modem power, touch RST, keyboard RST, antenna sel, etc.) +// - 4G (A7682E) and audio (ES8311) coexist on ONE board — no longer mutually exclusive +// - ES8311 I2C codec replaces PCM5102A (needs I2C config, different I2S pins) +// - E-ink RST moved: IO9 (was IO16) +// - E-ink BL moved: IO41 (was IO45, now has working front-light hardware!) +// - GPS UART moved: RX=IO2, TX=IO16 (was RX=IO44, TX=IO43) +// - GPS/LoRa power via XL9555 (was direct GPIO 39/46) +// - Touch RST via XL9555 IO07 (was GPIO 38) +// - Modem power/PWRKEY via XL9555 (was direct GPIO 41/40) +// - No PIN_PERF_POWERON (IO10 is now modem UART RX) +// - Battery: 1500 mAh (was 1400 mAh) +// - LoRa antenna switch (SKY13453) controlled by XL9555 IO04 +// - Audio output mux (A7682E vs ES8311) controlled by XL9555 IO12 +// - Speaker amplifier (NS4150B) enable via XL9555 IO06 +// ============================================================================= + +// ----------------------------------------------------------------------------- +// E-Ink Display (GDEQ031T10 - 240x320) +// E-ink SHARES the SPI bus with LoRa and SD card (SCK=36, MOSI=33, MISO=47) +// They use different chip selects: E-ink CS=34, LoRa CS=3, SD CS=48 +// ----------------------------------------------------------------------------- +#define PIN_EINK_CS 34 +#define PIN_EINK_DC 35 +#define PIN_EINK_RES 9 // MAX: IO9 (was IO16 on V1.1) +#define PIN_EINK_BUSY 37 +#define PIN_EINK_SCLK 36 // Shared with LoRa + SD +#define PIN_EINK_MOSI 33 // Shared with LoRa + SD +#define PIN_EINK_BL 41 // MAX: IO41 — working front-light! (was IO45 non-functional on V1.1) + +// Legacy aliases for MeshCore compatibility +#define PIN_DISPLAY_CS PIN_EINK_CS +#define PIN_DISPLAY_DC PIN_EINK_DC +#define PIN_DISPLAY_RST PIN_EINK_RES +#define PIN_DISPLAY_BUSY PIN_EINK_BUSY +#define PIN_DISPLAY_SCLK PIN_EINK_SCLK +#define PIN_DISPLAY_MOSI PIN_EINK_MOSI + +// Display dimensions - native resolution of GDEQ031T10 +#define LCD_HOR_SIZE 240 +#define LCD_VER_SIZE 320 + +// E-ink model for GxEPD2 +#define EINK_DISPLAY_MODEL GxEPD2_310_GDEQ031T10 + +// ----------------------------------------------------------------------------- +// SPI Bus - Shared by LoRa, SD Card, AND E-ink display +// ----------------------------------------------------------------------------- +#define BOARD_SPI_SCLK 36 +#define BOARD_SPI_MISO 47 +#define BOARD_SPI_MOSI 33 + +// ----------------------------------------------------------------------------- +// I2C Bus +// ----------------------------------------------------------------------------- +#define I2C_SDA 13 +#define I2C_SCL 14 + +// Aliases for ESP32Board base class compatibility +#define PIN_BOARD_SDA I2C_SDA +#define PIN_BOARD_SCL I2C_SCL + +// I2C Device Addresses +#define I2C_ADDR_ES8311 0x18 // ES8311 audio codec (NEW on MAX) +#define I2C_ADDR_TOUCH 0x1A // CST328 +#define I2C_ADDR_XL9555 0x20 // XL9555 I/O expander (NEW on MAX) +#define I2C_ADDR_GYROSCOPE 0x28 // BHI260AP +#define I2C_ADDR_KEYBOARD 0x34 // TCA8418 +#define I2C_ADDR_BQ27220 0x55 // Fuel gauge +#define I2C_ADDR_DRV2605 0x5A // Motor driver (haptic) +#define I2C_ADDR_BQ25896 0x6B // Charger + +// ----------------------------------------------------------------------------- +// XL9555 I/O Expander — Pin Assignments +// +// The XL9555 replaces direct GPIO control of peripheral power enables, +// resets, and switches. It must be initialised over I2C before LoRa, GPS, +// modem, or touch can be used. +// +// Port 0: pins 0-7, registers 0x02 (output) / 0x06 (direction) +// Port 1: pins 8-15, registers 0x03 (output) / 0x07 (direction) +// Direction: 0 = output, 1 = input +// ----------------------------------------------------------------------------- +#define HAS_XL9555 1 + +// XL9555 I2C registers +#define XL9555_REG_INPUT_0 0x00 +#define XL9555_REG_INPUT_1 0x01 +#define XL9555_REG_OUTPUT_0 0x02 +#define XL9555_REG_OUTPUT_1 0x03 +#define XL9555_REG_INVERT_0 0x04 +#define XL9555_REG_INVERT_1 0x05 +#define XL9555_REG_CONFIG_0 0x06 // 0=output, 1=input +#define XL9555_REG_CONFIG_1 0x07 + +// XL9555 pin assignments (0-7 = Port 0, 8-15 = Port 1) +#define XL_PIN_6609_EN 0 // HIGH: Enable A7682E power supply (SGM6609 boost) +#define XL_PIN_LORA_EN 1 // HIGH: Enable SX1262 power supply +#define XL_PIN_GPS_EN 2 // HIGH: Enable GPS power supply +#define XL_PIN_1V8_EN 3 // HIGH: Enable BHI260AP 1.8V power supply +#define XL_PIN_LORA_SEL 4 // HIGH: internal antenna, LOW: external antenna (SKY13453) +#define XL_PIN_MOTOR_EN 5 // HIGH: Enable DRV2605 power supply +#define XL_PIN_AMPLIFIER 6 // HIGH: Enable NS4150B speaker power amplifier +#define XL_PIN_TOUCH_RST 7 // LOW: Reset touch controller (active-low) +#define XL_PIN_PWRKEY_EN 8 // HIGH: A7682E POWERKEY toggle +#define XL_PIN_KEY_RST 9 // LOW: Reset keyboard (active-low) +#define XL_PIN_AUDIO_SEL 10 // HIGH: A7682E audio out, LOW: ES8311 audio out +// Pins 11-15 are reserved + +// Default XL9555 output state at boot (all power enables ON, resets de-asserted) +// Bit layout: [P07..P00] = TOUCH_RST=1, AMP=0, MOTOR_EN=0, LORA_SEL=1, 1V8=1, GPS=1, LORA=1, 6609=0 +// [P17..P10] = reserved=0, AUDIO_SEL=0, KEY_RST=1, PWRKEY=0 +// +// Conservative boot defaults for Meck: +// - LoRa ON, GPS ON, 1.8V ON, internal antenna +// - Modem OFF (6609_EN LOW), PWRKEY LOW (toggled later if needed) +// - Motor OFF, Amplifier OFF (saves power, enabled on demand) +// - Touch RST HIGH (not resetting), Keyboard RST HIGH (not resetting) +// - Audio select LOW (ES8311 by default — Meck controls this when needed) +#define XL9555_BOOT_PORT0 0b10011110 // 0x9E: T_RST=1, AMP=0, MOT=0, LSEL=1, 1V8=1, GPS=1, LORA=1, 6609=0 +#define XL9555_BOOT_PORT1 0b00000010 // 0x02: ..., ASEL=0, KRST=1, PKEY=0 + +// ----------------------------------------------------------------------------- +// Touch Controller (CST328) +// NOTE: Touch RST is via XL9555 pin 7, NOT a direct GPIO! +// CST328_PIN_RST is defined as -1 to signal "not a direct GPIO". +// The board class handles touch reset via XL9555 in begin(). +// ----------------------------------------------------------------------------- +#define HAS_TOUCHSCREEN 1 +#define CST328_PIN_INT 12 +#define CST328_PIN_RST -1 // MAX: Routed through XL9555 IO07 — handled by board class + +// ----------------------------------------------------------------------------- +// GPS +// NOTE: GPS power enable is via XL9555 pin 2, NOT a direct GPIO! +// PIN_GPS_EN is intentionally NOT defined — the board class handles it via XL9555. +// ----------------------------------------------------------------------------- +#define HAS_GPS 1 +#define GPS_BAUDRATE 38400 +// #define PIN_GPS_EN — NOT a direct GPIO on MAX (XL9555 IO02) +#define GPS_RX_PIN 2 // MAX: IO2 (was IO44 on V1.1) — ESP32 receives from GPS +#define GPS_TX_PIN 16 // MAX: IO16 (was IO43 on V1.1) — ESP32 sends to GPS +#define PIN_GPS_PPS 1 + +// ----------------------------------------------------------------------------- +// Buttons & Controls +// ----------------------------------------------------------------------------- +#define BUTTON_PIN 0 +#define PIN_USER_BTN 0 + +// Vibration Motor — DRV2605 driver (same as V1.1) +// Motor power enable is via XL9555 pin 5, not a direct GPIO. +#define HAS_DRV2605 1 + +// ----------------------------------------------------------------------------- +// SD Card +// ----------------------------------------------------------------------------- +#define HAS_SDCARD +#define SDCARD_USE_SPI1 +#define SPI_MOSI 33 +#define SPI_SCK 36 +#define SPI_MISO 47 +#define SPI_CS 48 +#define SDCARD_CS SPI_CS + +// ----------------------------------------------------------------------------- +// Keyboard (TCA8418) +// NOTE: Keyboard RST is via XL9555 pin 9 (active-low). +// The board class handles keyboard reset via XL9555 in begin(). +// ----------------------------------------------------------------------------- +#define KB_BL_PIN 42 +#define BOARD_KEYBOARD_INT 15 +#define HAS_PHYSICAL_KEYBOARD 1 + +// ----------------------------------------------------------------------------- +// Audio — ES8311 I2C Codec (NEW on MAX — replaces PCM5102A) +// +// ES8311 is an I2C-controlled audio codec (unlike PCM5102A which needed no config). +// It requires I2C register setup for input source, gain, volume, etc. +// Speaker/headphone output is shared with A7682E modem audio, selected via +// XL9555 pin AUDIO_SEL: LOW = ES8311, HIGH = A7682E. +// Power amplifier (NS4150B) for speaker enabled via XL9555 pin AMPLIFIER. +// +// I2S pin mapping for ES8311 (completely different from V1.1 PCM5102A!): +// MCLK = IO38 (master clock — ES8311 needs this, PCM5102A didn't) +// SCLK = IO39 (bit clock, aka BCLK) +// LRCK = IO18 (word select, aka LRC/WS) +// DSDIN = IO17 (DAC serial data in — ESP32 sends audio TO codec) +// ASDOUT= IO40 (ADC serial data out — codec sends mic audio TO ESP32) +// ----------------------------------------------------------------------------- +#define HAS_ES8311_AUDIO 1 + +#define BOARD_ES8311_MCLK 38 +#define BOARD_ES8311_SCLK 39 +#define BOARD_ES8311_LRCK 18 +#define BOARD_ES8311_DSDIN 17 // ESP32 → ES8311 (speaker/headphone output) +#define BOARD_ES8311_ASDOUT 40 // ES8311 → ESP32 (microphone input) + +// Compatibility aliases for ESP32-audioI2S library (setPinout expects BCLK, LRC, DOUT) +#define BOARD_I2S_BCLK BOARD_ES8311_SCLK // IO39 +#define BOARD_I2S_LRC BOARD_ES8311_LRCK // IO18 +#define BOARD_I2S_DOUT BOARD_ES8311_DSDIN // IO17 +#define BOARD_I2S_MCLK BOARD_ES8311_MCLK // IO38 (ESP32-audioI2S may need setMCLK) + +// Microphone — ES8311 built-in ADC (replaces separate PDM mic on V1.1) +// Mic data comes through I2S ASDOUT pin, not a separate PDM interface. +#define BOARD_MIC_I2S_DIN BOARD_ES8311_ASDOUT // IO40 + +// ----------------------------------------------------------------------------- +// Sensors +// ----------------------------------------------------------------------------- +#define HAS_BHI260AP // Gyroscope/IMU (1.8V power via XL9555 IO03) +#define BOARD_GYRO_INT 21 + +// ----------------------------------------------------------------------------- +// Power Management +// ----------------------------------------------------------------------------- +#define HAS_BQ27220 1 +#define BQ27220_I2C_ADDR 0x55 +#define BQ27220_I2C_SDA I2C_SDA +#define BQ27220_I2C_SCL I2C_SCL +#define BQ27220_DESIGN_CAPACITY 1500 // MAX: 1500 mAh (was 1400 on V1.1) +#define BQ27220_DESIGN_CAPACITY_MAH 1500 // Alias used by TDeckBoard.h + +#define HAS_PPM 1 +#define XPOWERS_CHIP_BQ25896 + +// ----------------------------------------------------------------------------- +// LoRa Radio (SX1262) +// NOTE: LoRa power enable is via XL9555 pin 1, NOT GPIO 46! +// The board class enables LoRa power via XL9555 in begin(). +// P_LORA_EN is intentionally NOT defined here — handled by board class. +// Antenna selection: XL9555 pin 4 (HIGH=internal, LOW=external via SKY13453). +// ----------------------------------------------------------------------------- +#define USE_SX1262 +#define USE_SX1268 + +// LORA_EN is NOT a direct GPIO on MAX — omit the define entirely. +// If any code references P_LORA_EN, it must be guarded with #ifndef HAS_XL9555. +// #define LORA_EN — NOT DEFINED (was GPIO 46 on V1.1) + +#define LORA_SCK 36 +#define LORA_MISO 47 +#define LORA_MOSI 33 // Shared with e-ink and SD card +#define LORA_CS 3 +#define LORA_RESET 4 +#define LORA_DIO0 -1 // Not connected on SX1262 +#define LORA_DIO1 5 // SX1262 IRQ +#define LORA_DIO2 6 // SX1262 BUSY + +// SX126X driver aliases (Meshtastic compatibility) +#define SX126X_CS LORA_CS +#define SX126X_DIO1 LORA_DIO1 +#define SX126X_BUSY LORA_DIO2 +#define SX126X_RESET LORA_RESET + +// RadioLib/MeshCore compatibility aliases +#define P_LORA_NSS LORA_CS +#define P_LORA_DIO_1 LORA_DIO1 +#define P_LORA_RESET LORA_RESET +#define P_LORA_BUSY LORA_DIO2 +#define P_LORA_SCLK LORA_SCK +#define P_LORA_MISO LORA_MISO +#define P_LORA_MOSI LORA_MOSI +// P_LORA_EN is NOT defined — LoRa power is via XL9555, handled in board begin() + +// ----------------------------------------------------------------------------- +// 4G Modem — A7682E (ALWAYS PRESENT on MAX — no longer optional!) +// +// On V1.1, 4G and audio were mutually exclusive hardware configurations. +// On MAX, both coexist. The XL9555 controls: +// - 6609_EN (XL pin 0): modem power supply (SGM6609 boost converter) +// - PWRKEY (XL pin 8): modem power key toggle +// Audio output from modem vs ES8311 is selected by AUDIO_SEL (XL pin 10). +// +// MODEM_POWER_EN and MODEM_PWRKEY are NOT direct GPIOs — ModemManager +// needs MAX-aware paths (see integration guide). +// MODEM_RST does not exist on MAX (IO9 is now LCD_RST). +// ----------------------------------------------------------------------------- +// Direct GPIO modem pins (still accessible as regular GPIO): +#define MODEM_RI 7 // Ring indicator (interrupt input) +#define MODEM_DTR 8 // Data terminal ready (output) +#define MODEM_RX 10 // UART RX (ESP32 receives from modem) +#define MODEM_TX 11 // UART TX (ESP32 sends to modem) + +// XL9555-routed modem pins — these are NOT direct GPIO! +// MODEM_POWER_EN and MODEM_PWRKEY are intentionally NOT defined. +// Existing code guarded by #ifdef MODEM_POWER_EN / #ifdef HAS_4G_MODEM will +// be skipped. Use board.modemPowerOn()/modemPwrkeyPulse() instead. +// MODEM_RST does not exist on MAX (IO9 is LCD_RST). + +// 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