From d18f3f7a658817b35a3ab746d8522c1136890785 Mon Sep 17 00:00:00 2001 From: viric Date: Tue, 18 Nov 2025 18:23:39 +0100 Subject: [PATCH] Allow deepsleep in rak4630 and make it restart well when power comes back (#7882) * Make RAK4631 nodes power back on deep sleep The devices will hang if the VBAT goes under 1.7V (Brown-out reset) and they will never come back unless power supply goes completely off. This kills unattended nodes. Using the SystemOff the LPCOMP we can get the nodes back again when power comes back, even if VBAT goes under 1.7V, which moreover is more unlikely because the device is off. * Adding support for heltec t114 And moved particularities to variant.h * Remove old cpp comment that belongs to variant.h It was a leftover. * Trunk fix --------- Co-authored-by: Tom Fifield --- src/Power.cpp | 15 +++++-------- src/platform/nrf52/main-nrf52.cpp | 22 ++++++++++++++++++- src/power.h | 2 ++ .../nrf52840/heltec_mesh_node_t114/variant.h | 10 +++++++++ variants/nrf52840/rak4631/variant.h | 14 ++++++++++++ 5 files changed, 52 insertions(+), 11 deletions(-) diff --git a/src/Power.cpp b/src/Power.cpp index d7fd5b33b..fa8661d01 100644 --- a/src/Power.cpp +++ b/src/Power.cpp @@ -194,7 +194,7 @@ static HasBatteryLevel *batteryLevel; // Default to NULL for no battery level se #ifdef BATTERY_PIN -static void adcEnable() +void battery_adcEnable() { #ifdef ADC_CTRL // enable adc voltage divider when we need to read #ifdef ADC_USE_PULLUP @@ -214,7 +214,7 @@ static void adcEnable() #endif } -static void adcDisable() +static void battery_adcDisable() { #ifdef ADC_CTRL // disable adc voltage divider when we need to read #ifdef ADC_USE_PULLUP @@ -320,7 +320,7 @@ class AnalogBatteryLevel : public HasBatteryLevel uint32_t raw = 0; float scaled = 0; - adcEnable(); + battery_adcEnable(); #ifdef ARCH_ESP32 // ADC block for espressif platforms raw = espAdcRead(); scaled = esp_adc_cal_raw_to_voltage(raw, adc_characs); @@ -332,7 +332,7 @@ class AnalogBatteryLevel : public HasBatteryLevel raw = raw / BATTERY_SENSE_SAMPLES; scaled = operativeAdcMultiplier * ((1000 * AREF_VOLTAGE) / pow(2, BATTERY_SENSE_RESOLUTION_BITS)) * raw; #endif - adcDisable(); + battery_adcDisable(); if (!initial_read_done) { // Flush the smoothing filter with an ADC reading, if the reading is plausibly correct @@ -906,13 +906,8 @@ void Power::readPowerStatus() low_voltage_counter++; LOG_DEBUG("Low voltage counter: %d/10", low_voltage_counter); if (low_voltage_counter > 10) { -#ifdef ARCH_NRF52 - // We can't trigger deep sleep on NRF52, it's freezing the board - LOG_DEBUG("Low voltage detected, but not trigger deep sleep"); -#else LOG_INFO("Low voltage detected, trigger deep sleep"); powerFSM.trigger(EVENT_LOW_BATTERY); -#endif } } else { low_voltage_counter = 0; @@ -1552,4 +1547,4 @@ bool Power::meshSolarInit() { return false; } -#endif \ No newline at end of file +#endif diff --git a/src/platform/nrf52/main-nrf52.cpp b/src/platform/nrf52/main-nrf52.cpp index 8ce74d5f7..f29def72e 100644 --- a/src/platform/nrf52/main-nrf52.cpp +++ b/src/platform/nrf52/main-nrf52.cpp @@ -14,6 +14,9 @@ #include "error.h" #include "main.h" #include "meshUtils.h" +#include "power.h" + +#include #ifdef BQ25703A_ADDR #include "BQ25713.h" @@ -389,6 +392,23 @@ void cpuDeepSleep(uint32_t msecToWake) nrf_gpio_cfg_sense_set(BUTTON_PIN, sense); // Apply SENSE to wake up the device from the deep sleep #endif +#ifdef BATTERY_LPCOMP_INPUT + // Wake up if power rises again + nrf_lpcomp_config_t c; + c.reference = BATTERY_LPCOMP_THRESHOLD; + c.detection = NRF_LPCOMP_DETECT_UP; + c.hyst = NRF_LPCOMP_HYST_NOHYST; + nrf_lpcomp_configure(NRF_LPCOMP, &c); + nrf_lpcomp_input_select(NRF_LPCOMP, BATTERY_LPCOMP_INPUT); + nrf_lpcomp_enable(NRF_LPCOMP); + + battery_adcEnable(); + + nrf_lpcomp_task_trigger(NRF_LPCOMP, NRF_LPCOMP_TASK_START); + while (!nrf_lpcomp_event_check(NRF_LPCOMP, NRF_LPCOMP_EVENT_READY)) + ; +#endif + auto ok = sd_power_system_off(); if (ok != NRF_SUCCESS) { LOG_ERROR("FIXME: Ignoring soft device (EasyDMA pending?) and forcing system-off!"); @@ -420,4 +440,4 @@ void enterDfuMode() #else enterUf2Dfu(); #endif -} \ No newline at end of file +} diff --git a/src/power.h b/src/power.h index cdbdd3ea0..f9ccb08aa 100644 --- a/src/power.h +++ b/src/power.h @@ -144,4 +144,6 @@ class Power : private concurrency::OSThread #endif }; +void battery_adcEnable(); + extern Power *power; diff --git a/variants/nrf52840/heltec_mesh_node_t114/variant.h b/variants/nrf52840/heltec_mesh_node_t114/variant.h index 7e82733aa..b6082fdc6 100644 --- a/variants/nrf52840/heltec_mesh_node_t114/variant.h +++ b/variants/nrf52840/heltec_mesh_node_t114/variant.h @@ -210,6 +210,16 @@ No longer populated on PCB #define VBAT_AR_INTERNAL AR_INTERNAL_3_0 #define ADC_MULTIPLIER (4.916F) +// rf52840 AIN2 = Pin 4 +#define BATTERY_LPCOMP_INPUT NRF_LPCOMP_INPUT_2 + +// We have AIN2 with a VBAT divider so AIN2 = VBAT * (100/490) +// We have the device going deep sleep under 3.1V, which is AIN2 = 0.63V +// So we can wake up when VBAT>=VDD is restored to 3.3V, where AIN2 = 0.67V +// Ratio 0.67/3.3 = 0.20, so we can pick a bit higher, 2/8 VDD, which means +// VBAT=4.04V +#define BATTERY_LPCOMP_THRESHOLD NRF_LPCOMP_REF_SUPPLY_2_8 + #define HAS_RTC 0 #ifdef __cplusplus } diff --git a/variants/nrf52840/rak4631/variant.h b/variants/nrf52840/rak4631/variant.h index f5ec11ef2..302e531d5 100644 --- a/variants/nrf52840/rak4631/variant.h +++ b/variants/nrf52840/rak4631/variant.h @@ -267,6 +267,20 @@ SO GPIO 39/TXEN MAY NOT BE DEFINED FOR SUCCESSFUL OPERATION OF THE SX1262 - TG #define VBAT_AR_INTERNAL AR_INTERNAL_3_0 #define ADC_MULTIPLIER 1.73 +// RAK4630 AIN0 = nrf52840 AIN3 = Pin 5 +#define BATTERY_LPCOMP_INPUT NRF_LPCOMP_INPUT_3 + +// We have AIN3 with a VBAT divider so AIN3 = VBAT * (1.5/2.5) +// We have the device going deep sleep under 3.1V, which is AIN3 = 1.86V +// So we can wake up when VBAT>=VDD is restored to 3.3V, where AIN3 = 1.98V +// 1.98/3.3 = 6/10, but that's close to the VBAT divider, so we +// pick 6/8VDD, which means VBAT=4.1V. +// Reference: +// VDD=3.3V AIN3=5/8*VDD=2.06V VBAT=1.66*AIN3=3.41V +// VDD=3.3V AIN3=11/16*VDD=2.26V VBAT=1.66*AIN3=3.76V +// VDD=3.3V AIN3=6/8*VDD=2.47V VBAT=1.66*AIN3=4.1V +#define BATTERY_LPCOMP_THRESHOLD NRF_LPCOMP_REF_SUPPLY_11_16 + #define HAS_RTC 1 #define HAS_ETHERNET 1