diff --git a/docs/companion_protocol.md b/docs/companion_protocol.md index 9d45b59ef..1d9cf8264 100644 --- a/docs/companion_protocol.md +++ b/docs/companion_protocol.md @@ -587,11 +587,12 @@ def parse_device_info(data): **PACKET_BATTERY** (0x0C): ``` Byte 0: 0x0C -Bytes 1-2: Battery Level (16-bit little-endian, percentage 0-100) +Bytes 1-2: Battery Milli Volts (16-bit little-endian) Optional (if data size > 3): Bytes 3-6: Used Storage (32-bit little-endian, KB) Bytes 7-10: Total Storage (32-bit little-endian, KB) +Bytes 11: Charging/External Power Flag (uint8, 0=no, 1=yes) ``` **Parsing Pseudocode**: @@ -600,15 +601,18 @@ def parse_battery(data): if len(data) < 3: return None - level = int.from_bytes(data[1:3], 'little') - info = {'level': level} + milli_volts = int.from_bytes(data[1:3], 'little') + info = {'milli_volts': milli_volts} - if len(data) > 3: + if len(data) >= 11: used_kb = int.from_bytes(data[3:7], 'little') total_kb = int.from_bytes(data[7:11], 'little') info['used_kb'] = used_kb info['total_kb'] = total_kb + if len(data) >= 12: + info['is_charging'] = data[11] == 1 + return info ``` diff --git a/examples/companion_radio/MyMesh.cpp b/examples/companion_radio/MyMesh.cpp index 87d3091a0..e83a1df84 100644 --- a/examples/companion_radio/MyMesh.cpp +++ b/examples/companion_radio/MyMesh.cpp @@ -1309,15 +1309,19 @@ void MyMesh::handleCmdFrame(size_t len) { } board.reboot(); } else if (cmd_frame[0] == CMD_GET_BATT_AND_STORAGE) { - uint8_t reply[11]; + uint8_t reply[12]; int i = 0; reply[i++] = RESP_CODE_BATT_AND_STORAGE; uint16_t battery_millivolts = board.getBattMilliVolts(); uint32_t used = _store->getStorageUsedKb(); uint32_t total = _store->getStorageTotalKb(); + // Optional extra byte for companion clients: + // 0 = not externally powered, 1 = externally powered/charging source present. + uint8_t is_charging = board.isExternalPowered() ? 1 : 0; memcpy(&reply[i], &battery_millivolts, 2); i += 2; memcpy(&reply[i], &used, 4); i += 4; memcpy(&reply[i], &total, 4); i += 4; + reply[i++] = is_charging; _serial->writeFrame(reply, i); } else if (cmd_frame[0] == CMD_EXPORT_PRIVATE_KEY) { #if ENABLE_PRIVATE_KEY_EXPORT diff --git a/variants/t1000-e/T1000eBoard.cpp b/variants/t1000-e/T1000eBoard.cpp index 0d390f798..e73e03516 100644 --- a/variants/t1000-e/T1000eBoard.cpp +++ b/variants/t1000-e/T1000eBoard.cpp @@ -20,4 +20,23 @@ void T1000eBoard::begin() { Wire.begin(); delay(10); // give sx1262 some time to power up -} \ No newline at end of file +} + +bool T1000eBoard::isExternalPowered() { + // T1000-E exposes dedicated detect lines for external power and charge state. + // Use these first, then fall back to NRF52 USB VBUS detection. + bool externalPowerDetected = false; + bool chargingDetected = false; + +#ifdef EXT_PWR_DETECT + // EXT_PWR_DETECT is high when external power rail is present. + externalPowerDetected = digitalRead(EXT_PWR_DETECT) == HIGH; +#endif + +#ifdef EXT_CHRG_DETECT + // Charge detect is typically active-low on PMIC status lines. + chargingDetected = digitalRead(EXT_CHRG_DETECT) == LOW; +#endif + + return externalPowerDetected || chargingDetected || NRF52Board::isExternalPowered(); +} diff --git a/variants/t1000-e/T1000eBoard.h b/variants/t1000-e/T1000eBoard.h index 492236077..aa00de5d0 100644 --- a/variants/t1000-e/T1000eBoard.h +++ b/variants/t1000-e/T1000eBoard.h @@ -11,6 +11,7 @@ class T1000eBoard : public NRF52BoardDCDC { public: T1000eBoard() : NRF52Board("T1000E_OTA") {} void begin(); + bool isExternalPowered() override; uint16_t getBattMilliVolts() override { #ifdef BATTERY_PIN