From 3bcfb70141983b7b94b64108cac1cc6092b1a04c Mon Sep 17 00:00:00 2001 From: F4FPR Date: Fri, 14 Aug 2026 00:42:57 +0200 Subject: [PATCH 1/8] Update MeshCore.h --- src/MeshCore.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/MeshCore.h b/src/MeshCore.h index e67371ef17..229794761a 100644 --- a/src/MeshCore.h +++ b/src/MeshCore.h @@ -74,6 +74,8 @@ class MainBoard { // Power management interface (boards with power management override these) virtual bool isExternalPowered() { return false; } + // True while a board can identify an active battery-charging source. + virtual bool isChargerActive() { return false; } virtual uint16_t getBootVoltage() { return 0; } virtual uint32_t getResetReason() const { return 0; } virtual const char* getResetReasonString(uint32_t reason) { return "Not available"; } From 77057aefdd392c09c744497f6103c3003f30329f Mon Sep 17 00:00:00 2001 From: F4FPR Date: Fri, 14 Aug 2026 00:43:33 +0200 Subject: [PATCH 2/8] Update CommonCLI.cpp --- src/helpers/CommonCLI.cpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/helpers/CommonCLI.cpp b/src/helpers/CommonCLI.cpp index b318bb58e8..d2a8b7076b 100644 --- a/src/helpers/CommonCLI.cpp +++ b/src/helpers/CommonCLI.cpp @@ -273,6 +273,11 @@ void CommonCLI::handleCommand(uint32_t sender_timestamp, char* command, char* re sprintf(reply, "%s (Build: %s)", _callbacks->getFirmwareVer(), _callbacks->getBuildDate()); } else if (memcmp(command, "board", 5) == 0) { sprintf(reply, "%s", _board->getManufacturerName()); + } else if (strcmp(command, "power") == 0) { + snprintf(reply, 160, "batt:%u mV ext:%s charger:%s", + (unsigned)_board->getBattMilliVolts(), + _board->isExternalPowered() ? "yes" : "no", + _board->isChargerActive() ? "yes" : "no"); } else if (memcmp(command, "sensor get ", 11) == 0) { const char* key = command + 11; const char* val = _sensors->getSettingByKey(key); From 4c4497d077da23304c53890dcdee31872fb55268 Mon Sep 17 00:00:00 2001 From: F4FPR Date: Fri, 14 Aug 2026 00:44:16 +0200 Subject: [PATCH 3/8] Update ThinkNodeM6Board.cpp --- variants/thinknode_m6/ThinkNodeM6Board.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/variants/thinknode_m6/ThinkNodeM6Board.cpp b/variants/thinknode_m6/ThinkNodeM6Board.cpp index 8ebae64c64..f83f274f96 100644 --- a/variants/thinknode_m6/ThinkNodeM6Board.cpp +++ b/variants/thinknode_m6/ThinkNodeM6Board.cpp @@ -15,6 +15,9 @@ void ThinkNodeM6Board::begin() { digitalWrite(P_LORA_TX_LED, LOW); #endif + pinMode(EXT_PWR_DETECT, INPUT); + pinMode(EXT_CHRG_DETECT, INPUT); + delay(10); // give sx1262 some time to power up } From 33239520e13a384149ab920a76995175d836738d Mon Sep 17 00:00:00 2001 From: F4FPR Date: Fri, 14 Aug 2026 00:44:43 +0200 Subject: [PATCH 4/8] Update ThinkNodeM6Board.h --- variants/thinknode_m6/ThinkNodeM6Board.h | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/variants/thinknode_m6/ThinkNodeM6Board.h b/variants/thinknode_m6/ThinkNodeM6Board.h index 78815e2c9c..1af952d2f1 100644 --- a/variants/thinknode_m6/ThinkNodeM6Board.h +++ b/variants/thinknode_m6/ThinkNodeM6Board.h @@ -36,6 +36,21 @@ class ThinkNodeM6Board : public NRF52BoardDCDC { return "Elecrow ThinkNode M6"; } + bool isExternalPowered() override { + if (digitalRead(EXT_PWR_DETECT) == EXT_PWR_DETECT_VALUE || + digitalRead(EXT_CHRG_DETECT) == EXT_CHRG_DETECT_VALUE) { + return true; + } + return NRF52Board::isExternalPowered(); + } + + bool isChargerActive() override { + // The charge-status line covers solar input; external USB input is also a + // charging source, although the board cannot report charge completion. + return digitalRead(EXT_CHRG_DETECT) == EXT_CHRG_DETECT_VALUE || + digitalRead(EXT_PWR_DETECT) == EXT_PWR_DETECT_VALUE; + } + void powerOff() override { // turn off all leds, sd_power_system_off will not do this for us From bb7a324471f4070966530bc3c6b7b6680164119a Mon Sep 17 00:00:00 2001 From: F4FPR Date: Fri, 14 Aug 2026 00:45:22 +0200 Subject: [PATCH 5/8] Update variant.h --- variants/thinknode_m6/variant.h | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/variants/thinknode_m6/variant.h b/variants/thinknode_m6/variant.h index 0a4025f3bd..3428fcd646 100644 --- a/variants/thinknode_m6/variant.h +++ b/variants/thinknode_m6/variant.h @@ -29,6 +29,12 @@ #define AREF_VOLTAGE (3.0) +// External power and charger status lines. +#define EXT_PWR_DETECT (13) +#define EXT_PWR_DETECT_VALUE HIGH +#define EXT_CHRG_DETECT (15) +#define EXT_CHRG_DETECT_VALUE LOW + //////////////////////////////////////////////////////////////////////////////// // Number of pins From da863e555d811155c97bd3cda5517b42d9096bda Mon Sep 17 00:00:00 2001 From: F4FPR Date: Tue, 18 Aug 2026 19:38:23 +0200 Subject: [PATCH 6/8] Update MeshCore.h --- src/MeshCore.h | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/MeshCore.h b/src/MeshCore.h index 229794761a..ca12daee57 100644 --- a/src/MeshCore.h +++ b/src/MeshCore.h @@ -76,6 +76,14 @@ class MainBoard { virtual bool isExternalPowered() { return false; } // True while a board can identify an active battery-charging source. virtual bool isChargerActive() { return false; } + + // Optional, source-specific power detection. Boards that can distinguish a USB + // supply from a solar charger override these; defaults keep every other board + // unaffected (the generic CLI prints "n/a" when a capability is absent). + virtual bool hasUsbPowerDetect() const { return false; } + virtual bool isUsbPowered() { return false; } + virtual bool hasSolarChargerDetect() const { return false; } + virtual bool isSolarChargerActive() { return false; } virtual uint16_t getBootVoltage() { return 0; } virtual uint32_t getResetReason() const { return 0; } virtual const char* getResetReasonString(uint32_t reason) { return "Not available"; } From 6b696a4ef6db2e757f300b1d55efa34705f4e142 Mon Sep 17 00:00:00 2001 From: F4FPR Date: Tue, 18 Aug 2026 19:39:21 +0200 Subject: [PATCH 7/8] Update CommonCLI.cpp --- src/helpers/CommonCLI.cpp | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/src/helpers/CommonCLI.cpp b/src/helpers/CommonCLI.cpp index d2a8b7076b..f2daf3cd85 100644 --- a/src/helpers/CommonCLI.cpp +++ b/src/helpers/CommonCLI.cpp @@ -274,8 +274,16 @@ void CommonCLI::handleCommand(uint32_t sender_timestamp, char* command, char* re } else if (memcmp(command, "board", 5) == 0) { sprintf(reply, "%s", _board->getManufacturerName()); } else if (strcmp(command, "power") == 0) { - snprintf(reply, 160, "batt:%u mV ext:%s charger:%s", + const char* usb_state = _board->hasUsbPowerDetect() + ? (_board->isUsbPowered() ? "yes" : "no") + : "n/a"; + const char* solar_state = _board->hasSolarChargerDetect() + ? (_board->isSolarChargerActive() ? "yes" : "no") + : "n/a"; + snprintf(reply, 160, "batt:%u mV usb:%s solar_chg:%s ext:%s charger:%s", (unsigned)_board->getBattMilliVolts(), + usb_state, + solar_state, _board->isExternalPowered() ? "yes" : "no", _board->isChargerActive() ? "yes" : "no"); } else if (memcmp(command, "sensor get ", 11) == 0) { From 2940ead5049604e75fcedf963a3b3cfb94ea9ee2 Mon Sep 17 00:00:00 2001 From: F4FPR Date: Tue, 18 Aug 2026 19:40:42 +0200 Subject: [PATCH 8/8] Update ThinkNodeM6Board.h --- variants/thinknode_m6/ThinkNodeM6Board.h | 26 ++++++++++++++++++------ 1 file changed, 20 insertions(+), 6 deletions(-) diff --git a/variants/thinknode_m6/ThinkNodeM6Board.h b/variants/thinknode_m6/ThinkNodeM6Board.h index 1af952d2f1..934794c9af 100644 --- a/variants/thinknode_m6/ThinkNodeM6Board.h +++ b/variants/thinknode_m6/ThinkNodeM6Board.h @@ -36,19 +36,33 @@ class ThinkNodeM6Board : public NRF52BoardDCDC { return "Elecrow ThinkNode M6"; } + bool hasUsbPowerDetect() const override { return true; } + + bool isUsbPowered() override { + // P0.13 is the dedicated USB / external power-detect line on the M6 PCB + // (same pin the Meshtastic M6 variant uses for USB detection). + return digitalRead(EXT_PWR_DETECT) == EXT_PWR_DETECT_VALUE; + } + + bool hasSolarChargerDetect() const override { return true; } + + bool isSolarChargerActive() override { + // P0.15 is the active-low solar-charger status line. It reports the + // charger's state, not photovoltaic voltage or current. + return digitalRead(EXT_CHRG_DETECT) == EXT_CHRG_DETECT_VALUE; + } + bool isExternalPowered() override { - if (digitalRead(EXT_PWR_DETECT) == EXT_PWR_DETECT_VALUE || - digitalRead(EXT_CHRG_DETECT) == EXT_CHRG_DETECT_VALUE) { + if (isUsbPowered() || isSolarChargerActive()) { return true; } return NRF52Board::isExternalPowered(); } bool isChargerActive() override { - // The charge-status line covers solar input; external USB input is also a - // charging source, although the board cannot report charge completion. - return digitalRead(EXT_CHRG_DETECT) == EXT_CHRG_DETECT_VALUE || - digitalRead(EXT_PWR_DETECT) == EXT_PWR_DETECT_VALUE; + // Preserve a generic charging indication while also exposing both + // source-specific states (USB / solar) separately through the CLI. + return isSolarChargerActive() || isUsbPowered(); } void powerOff() override {