diff --git a/lib/hal/HalGPIO.cpp b/lib/hal/HalGPIO.cpp index 911d5543..7cefa64d 100644 --- a/lib/hal/HalGPIO.cpp +++ b/lib/hal/HalGPIO.cpp @@ -309,22 +309,29 @@ void HalGPIO::verifyPowerButtonWakeup(uint16_t requiredDurationMs, bool shortPre bool HalGPIO::isUsbConnected() const { if (deviceIsX3()) { - // X3: infer USB/charging via BQ27220 Current() register (0x0C, signed mA). - // Positive current means charging. + // X3: GPIO20 is repurposed as I2C SDA, so the X4 pin-level USB detect is + // unusable here — the I2C pull-ups would always report HIGH. Probe the + // BQ27220 fuel gauge instead. Using just Current() mis-reports "not + // connected" when the battery is full (current ~= 0 mA); combine it with + // the Flags() DSG bit so we report true whenever the charger is present + // (DSG=0 means charging or fully charged, not discharging). for (uint8_t attempt = 0; attempt < 2; ++attempt) { - int16_t currentMa = 0; - if (X3GPIO::readBQ27220CurrentMA(¤tMa)) { - if (currentMa > 0) { + uint16_t flags = 0; + if (X3GPIO::readI2CReg16LE(I2C_ADDR_BQ27220, BQ27220_FLAGS_REG, &flags)) { + if ((flags & BQ27220_FLAG_DSG) == 0) { return true; } - break; + int16_t currentMa = 0; + if (X3GPIO::readBQ27220CurrentMA(¤tMa) && currentMa > 0) { + return true; + } + return false; } delay(2); } - // Fall back to the same USB pin-level detection used by X4. - return digitalRead(UART0_RXD) == HIGH; + return false; } - // U0RXD/GPIO20 reads HIGH when USB is connected + // X4: U0RXD/GPIO20 reads HIGH when USB is connected return digitalRead(UART0_RXD) == HIGH; } @@ -336,6 +343,13 @@ HalGPIO::WakeupReason HalGPIO::getWakeupReason() const { LOG_DBG("GPIO", "getWakeupReason: wakeupCause=%d, resetReason=%d, usbConnected=%d", static_cast(wakeupCause), static_cast(resetReason), usbConnected); + // X3: USB alone cannot cold-boot the MCU — the battery-latch MOSFET must be + // closed by a physical power-button press. So any POWERON reset on X3 is a + // button press, regardless of whether USB happens to be plugged in as well. + if (deviceIsX3() && wakeupCause == ESP_SLEEP_WAKEUP_UNDEFINED && resetReason == ESP_RST_POWERON) { + return WakeupReason::PowerButton; + } + if ((wakeupCause == ESP_SLEEP_WAKEUP_UNDEFINED && resetReason == ESP_RST_POWERON && !usbConnected) || (wakeupCause == ESP_SLEEP_WAKEUP_GPIO && resetReason == ESP_RST_DEEPSLEEP && usbConnected)) { return WakeupReason::PowerButton; diff --git a/lib/hal/HalGPIO.h b/lib/hal/HalGPIO.h index 3653a730..26079ab4 100644 --- a/lib/hal/HalGPIO.h +++ b/lib/hal/HalGPIO.h @@ -23,10 +23,12 @@ #define X3_I2C_FREQ 400000 // TI BQ27220 Fuel gauge I2C -#define I2C_ADDR_BQ27220 0x55 // Fuel gauge I2C address -#define BQ27220_SOC_REG 0x2C // StateOfCharge() command code (%) -#define BQ27220_CUR_REG 0x0C // Current() command code (signed mA) -#define BQ27220_VOLT_REG 0x08 // Voltage() command code (mV) +#define I2C_ADDR_BQ27220 0x55 // Fuel gauge I2C address +#define BQ27220_SOC_REG 0x2C // StateOfCharge() command code (%) +#define BQ27220_CUR_REG 0x0C // Current() command code (signed mA) +#define BQ27220_VOLT_REG 0x08 // Voltage() command code (mV) +#define BQ27220_FLAGS_REG 0x0A // BatteryStatus() / Flags() command code (bit0=DSG) +#define BQ27220_FLAG_DSG 0x0001 // DSG bit: 1 = discharging, 0 = charging or at rest // Analog DS3231 RTC I2C #define I2C_ADDR_DS3231 0x68 // RTC I2C address