Browse Source

Merge f9db92333a into adc631f315

pull/2019/merge
Wessel 19 hours ago
committed by GitHub
parent
commit
ad794d359b
No known key found for this signature in database GPG Key ID: B5690EEEBB952194
  1. 1
      examples/companion_radio/main.cpp
  2. 1
      examples/kiss_modem/main.cpp
  3. 2
      examples/simple_repeater/main.cpp
  4. 2
      examples/simple_room_server/main.cpp
  5. 1
      examples/simple_secure_chat/main.cpp
  6. 2
      examples/simple_sensor/main.cpp
  7. 1
      src/MeshCore.h
  8. 88
      src/helpers/NRF52Board.cpp
  9. 19
      src/helpers/NRF52Board.h
  10. 4
      variants/gat562_mesh_tracker_pro/GAT562MeshTrackerProBoard.cpp
  11. 4
      variants/heltec_t114/T114Board.cpp
  12. 4
      variants/rak4631/RAK4631Board.cpp
  13. 4
      variants/sensecap_solar/SenseCapSolarBoard.cpp
  14. 4
      variants/xiao_nrf52/XiaoNrf52Board.cpp

1
examples/companion_radio/main.cpp

@ -262,6 +262,7 @@ void setup() {
}
void loop() {
board.loop();
the_mesh.loop();
sensors.loop();
#ifdef DISPLAY_CLASS

1
examples/kiss_modem/main.cpp

@ -130,6 +130,7 @@ void setup() {
}
void loop() {
board.loop();
modem->loop();
if (!modem->isActuallyTransmitting() && !modem->isHostOutputBackedUp()) {

2
examples/simple_repeater/main.cpp

@ -123,6 +123,8 @@ void setup() {
}
void loop() {
board.loop();
// Handle Serial CLI
int len = strlen(command);
while (Serial.available() && len < sizeof(command)-1) {

2
examples/simple_room_server/main.cpp

@ -104,6 +104,8 @@ void setup() {
}
void loop() {
board.loop();
int len = strlen(command);
while (Serial.available() && len < sizeof(command)-1) {
char c = Serial.read();

1
examples/simple_secure_chat/main.cpp

@ -593,6 +593,7 @@ void setup() {
}
void loop() {
board.loop();
the_mesh.loop();
rtc_clock.tick();
#ifdef HAS_EXTERNAL_WATCHDOG

2
examples/simple_sensor/main.cpp

@ -121,6 +121,8 @@ void setup() {
}
void loop() {
board.loop();
int len = strlen(command);
while (Serial.available() && len < sizeof(command)-1) {
char c = Serial.read();

1
src/MeshCore.h

@ -67,6 +67,7 @@ public:
virtual bool setLoRaFemLnaEnabled(bool enable) { return false; }
virtual bool canControlLoRaFemLna() const { return false; }
virtual bool isLoRaFemLnaEnabled() const { return false; }
virtual void loop() { /* no op */ }
// Power management interface (boards with power management override these)
virtual bool isExternalPowered() { return false; }

88
src/helpers/NRF52Board.cpp

@ -99,15 +99,29 @@ const char* NRF52Board::getShutdownReasonString(uint8_t reason) {
bool NRF52Board::checkBootVoltage(const PowerMgtConfig* config) {
initPowerMgr();
// Store config for runtime use (voltage monitoring, WDT)
_power_config = config;
_last_voltage_check_ms = 0;
_low_voltage_count = 0;
// Read boot voltage
boot_voltage_mv = getBattMilliVolts();
if (config->voltage_bootlock == 0) return true; // Protection disabled
if (config->voltage_bootlock == 0) {
// Boot protection disabled, but still init WDT if configured
if (config->wdt_timeout_ms > 0) {
initWatchdog(config->wdt_timeout_ms);
}
return true;
}
// Skip check if externally powered
if (isExternalPowered()) {
MESH_DEBUG_PRINTLN("PWRMGT: Boot check skipped (external power)");
boot_voltage_mv = getBattMilliVolts();
if (config->wdt_timeout_ms > 0) {
initWatchdog(config->wdt_timeout_ms);
}
return true;
}
@ -123,6 +137,11 @@ bool NRF52Board::checkBootVoltage(const PowerMgtConfig* config) {
return false; // Should never reach this
}
// Boot voltage OK — start WDT if configured
if (config->wdt_timeout_ms > 0) {
initWatchdog(config->wdt_timeout_ms);
}
return true;
}
@ -223,7 +242,66 @@ void NRF52Board::configureVoltageWake(uint8_t ain_channel, uint8_t refsel) {
MESH_DEBUG_PRINTLN("PWRMGT: VBUS wake configured");
}
#define VOLTAGE_CHECK_INTERVAL_MS 30000
#define LOW_VOLTAGE_COUNT_THRESHOLD 3
void NRF52Board::initWatchdog(uint32_t timeout_ms) {
// Configure WDT via direct register access (same pattern as LPCOMP/POWER registers)
NRF_WDT->CONFIG = WDT_CONFIG_SLEEP_Run << WDT_CONFIG_SLEEP_Pos; // Keep running during WFE sleep
NRF_WDT->CRV = (uint32_t)((uint64_t)timeout_ms * 32768 / 1000); // Timeout in 32.768kHz ticks
NRF_WDT->RREN = WDT_RREN_RR0_Enabled << WDT_RREN_RR0_Pos; // Enable reload register 0
NRF_WDT->TASKS_START = 1; // Start — cannot be stopped once started
// Initial feed
NRF_WDT->RR[0] = WDT_RR_RR_Reload;
MESH_DEBUG_PRINTLN("PWRMGT: WDT started (%lu ms)", (unsigned long)timeout_ms);
}
void NRF52Board::feedWatchdog() {
if (NRF_WDT->RUNSTATUS) {
NRF_WDT->RR[0] = WDT_RR_RR_Reload;
}
}
void NRF52Board::checkRuntimeVoltage() {
if (_power_config == nullptr || _power_config->voltage_runtime == 0) return;
uint32_t now = millis();
if (now - _last_voltage_check_ms < VOLTAGE_CHECK_INTERVAL_MS) return;
_last_voltage_check_ms = now;
if (isExternalPowered()) {
_low_voltage_count = 0;
return;
}
uint16_t mv = getBattMilliVolts();
// Ignore ADC glitch readings
if (mv < 1000) return;
if (mv < _power_config->voltage_runtime) {
_low_voltage_count++;
MESH_DEBUG_PRINTLN("PWRMGT: Low voltage %u mV (%u/%u)",
mv, _low_voltage_count, LOW_VOLTAGE_COUNT_THRESHOLD);
if (_low_voltage_count >= LOW_VOLTAGE_COUNT_THRESHOLD) {
MESH_DEBUG_PRINTLN("PWRMGT: Runtime voltage too low - shutting down");
initiateShutdown(SHUTDOWN_REASON_LOW_VOLTAGE);
}
} else {
_low_voltage_count = 0;
}
}
#endif
void NRF52Board::loop() {
#ifdef NRF52_POWER_MANAGEMENT
feedWatchdog();
checkRuntimeVoltage();
#endif
}
void NRF52BoardDCDC::begin() {
NRF52Board::begin();
@ -253,10 +331,14 @@ bool NRF52Board::isExternalPowered() {
}
void NRF52Board::sleep(uint32_t secs) {
#ifdef NRF52_POWER_MANAGEMENT
feedWatchdog();
#endif
// Clear FPU interrupt flags to avoid insomnia
// see errata 87 for details https://docs.nordicsemi.com/bundle/errata_nRF52840_Rev3/page/ERR/nRF52840/Rev3/latest/anomaly_840_87.html
#if (__FPU_USED == 1)
__set_FPSCR(__get_FPSCR() & ~(0x0000009F));
__set_FPSCR(__get_FPSCR() & ~(0x0000009F));
(void) __get_FPSCR();
NVIC_ClearPendingIRQ(FPU_IRQn);
#endif

19
src/helpers/NRF52Board.h

@ -21,6 +21,11 @@ struct PowerMgtConfig {
// Boot protection voltage threshold (millivolts)
// Set to 0 to disable boot protection
uint16_t voltage_bootlock;
// Runtime low voltage shutdown threshold (millivolts), 0=disabled
uint16_t voltage_runtime;
// Watchdog timer timeout (ms), 0=disabled
uint32_t wdt_timeout_ms;
};
#endif
@ -38,14 +43,25 @@ protected:
uint8_t shutdown_reason; // GPREGRET value (why we entered last SYSTEMOFF)
uint16_t boot_voltage_mv; // Battery voltage at boot (millivolts)
const PowerMgtConfig* _power_config;
uint32_t _last_voltage_check_ms;
uint8_t _low_voltage_count;
bool checkBootVoltage(const PowerMgtConfig* config);
void enterSystemOff(uint8_t reason);
void configureVoltageWake(uint8_t ain_channel, uint8_t refsel);
virtual void initiateShutdown(uint8_t reason);
void initWatchdog(uint32_t timeout_ms);
void feedWatchdog();
void checkRuntimeVoltage();
#endif
public:
NRF52Board(char *otaname) : ota_name(otaname) {}
NRF52Board(char *otaname) : ota_name(otaname)
#ifdef NRF52_POWER_MANAGEMENT
, _power_config(nullptr), _last_voltage_check_ms(0), _low_voltage_count(0)
#endif
{}
virtual void begin();
virtual uint8_t getStartupReason() const override { return startup_reason; }
virtual float getMCUTemperature() override;
@ -56,6 +72,7 @@ public:
virtual bool startOTAUpdate(const char *id, char reply[]) override;
virtual void sleep(uint32_t secs) override;
bool isExternalPowered() override;
virtual void loop() override;
#ifdef NRF52_POWER_MANAGEMENT
uint16_t getBootVoltage() override { return boot_voltage_mv; }

4
variants/gat562_mesh_tracker_pro/GAT562MeshTrackerProBoard.cpp

@ -10,7 +10,9 @@
const PowerMgtConfig power_config = {
.lpcomp_ain_channel = PWRMGT_LPCOMP_AIN,
.lpcomp_refsel = PWRMGT_LPCOMP_REFSEL,
.voltage_bootlock = PWRMGT_VOLTAGE_BOOTLOCK
.voltage_bootlock = PWRMGT_VOLTAGE_BOOTLOCK,
.voltage_runtime = PWRMGT_VOLTAGE_BOOTLOCK - 200,
.wdt_timeout_ms = 60000
};

4
variants/heltec_t114/T114Board.cpp

@ -9,7 +9,9 @@
const PowerMgtConfig power_config = {
.lpcomp_ain_channel = PWRMGT_LPCOMP_AIN,
.lpcomp_refsel = PWRMGT_LPCOMP_REFSEL,
.voltage_bootlock = PWRMGT_VOLTAGE_BOOTLOCK
.voltage_bootlock = PWRMGT_VOLTAGE_BOOTLOCK,
.voltage_runtime = PWRMGT_VOLTAGE_BOOTLOCK - 200,
.wdt_timeout_ms = 60000
};
void T114Board::initiateShutdown(uint8_t reason) {

4
variants/rak4631/RAK4631Board.cpp

@ -22,7 +22,9 @@ static void __attribute__((constructor(102))) rak4631_early_poe_power() {
const PowerMgtConfig power_config = {
.lpcomp_ain_channel = PWRMGT_LPCOMP_AIN,
.lpcomp_refsel = PWRMGT_LPCOMP_REFSEL,
.voltage_bootlock = PWRMGT_VOLTAGE_BOOTLOCK
.voltage_bootlock = PWRMGT_VOLTAGE_BOOTLOCK,
.voltage_runtime = PWRMGT_VOLTAGE_BOOTLOCK - 200,
.wdt_timeout_ms = 60000
};
void RAK4631Board::initiateShutdown(uint8_t reason) {

4
variants/sensecap_solar/SenseCapSolarBoard.cpp

@ -7,7 +7,9 @@
const PowerMgtConfig power_config = {
.lpcomp_ain_channel = PWRMGT_LPCOMP_AIN,
.lpcomp_refsel = PWRMGT_LPCOMP_REFSEL,
.voltage_bootlock = PWRMGT_VOLTAGE_BOOTLOCK
.voltage_bootlock = PWRMGT_VOLTAGE_BOOTLOCK,
.voltage_runtime = PWRMGT_VOLTAGE_BOOTLOCK - 200,
.wdt_timeout_ms = 60000
};
void SenseCapSolarBoard::initiateShutdown(uint8_t reason) {

4
variants/xiao_nrf52/XiaoNrf52Board.cpp

@ -11,7 +11,9 @@
const PowerMgtConfig power_config = {
.lpcomp_ain_channel = PWRMGT_LPCOMP_AIN,
.lpcomp_refsel = PWRMGT_LPCOMP_REFSEL,
.voltage_bootlock = PWRMGT_VOLTAGE_BOOTLOCK
.voltage_bootlock = PWRMGT_VOLTAGE_BOOTLOCK,
.voltage_runtime = PWRMGT_VOLTAGE_BOOTLOCK - 200,
.wdt_timeout_ms = 60000
};
void XiaoNrf52Board::initiateShutdown(uint8_t reason) {

Loading…
Cancel
Save