diff --git a/src/platform/nrf52/main-nrf52.cpp b/src/platform/nrf52/main-nrf52.cpp index 87246ba21..ae12ef4a0 100644 --- a/src/platform/nrf52/main-nrf52.cpp +++ b/src/platform/nrf52/main-nrf52.cpp @@ -58,6 +58,15 @@ void variant_shutdown() {} void variant_nrf52LoopHook(void) __attribute__((weak)); void variant_nrf52LoopHook(void) {} +// Return false to skip LPCOMP wake when entering System OFF (e.g. user CLI shutdown). +// noinline: weak default and call site are in this file; without it GCC may inline the +// weak body and never link the strong override from variant.cpp. +__attribute__((noinline)) bool variant_enableBatteryLpcompWake() __attribute__((weak)); +__attribute__((noinline)) bool variant_enableBatteryLpcompWake() +{ + return true; +} + static nrfx_wdt_t nrfx_wdt = NRFX_WDT_INSTANCE(0); static nrfx_wdt_channel_id nrfx_wdt_channel_id_nrf52_main; @@ -496,20 +505,23 @@ void cpuDeepSleep(uint32_t msecToWake) // https://devzone.nordicsemi.com/f/nordic-q-a/48919/ram-retention-settings-with-softdevice-enabled #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); + // Only enable LPCOMP wake if the variant allows it + if (variant_enableBatteryLpcompWake()) { + // 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(); + battery_adcEnable(); - nrf_lpcomp_task_trigger(NRF_LPCOMP, NRF_LPCOMP_TASK_START); - while (!nrf_lpcomp_event_check(NRF_LPCOMP, NRF_LPCOMP_EVENT_READY)) - ; + 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(); diff --git a/variants/nrf52840/rak3401_1watt/platformio.ini b/variants/nrf52840/rak3401_1watt/platformio.ini index 889a17ed6..0220042d1 100644 --- a/variants/nrf52840/rak3401_1watt/platformio.ini +++ b/variants/nrf52840/rak3401_1watt/platformio.ini @@ -36,6 +36,19 @@ lib_deps = # renovate: datasource=git-refs depName=RAK12034-BMX160 packageName=https://github.com/RAKWireless/RAK12034-BMX160 gitBranch=main https://github.com/RAKWireless/RAK12034-BMX160/archive/dcead07ffa267d3c906e9ca4a1330ab989e957e2.zip +; RAK10729 WisMesh Repeater Mini V2 HP (RAK3401 + RAK19007 + RAK13302 + solar battery) +[env:rak_wismesh_repeater_mini_hp] +extends = env:rak3401-1watt +custom_meshtastic_display_name = RAK WisMesh Repeater Mini V2 HP +custom_meshtastic_images = rak3401.svg +build_flags = + ${env:rak3401-1watt.build_flags} + -D WISMESH_REPEATER_MINI_HP + -DMESHTASTIC_EXCLUDE_GPS=1 + -DLOW_VDD_SYSTEMOFF_DELAY_MS=5000 + -DSAFE_VDD_VOLTAGE_THRESHOLD_MV=2900 + -DSAFE_VDD_VOLTAGE_THRESHOLD_HYST_MV=100 + ; If not set we will default to uploading over serial (first it forces bootloader entry by talking 1200bps to cdcacm) ; Note: as of 6/2013 the serial/bootloader based programming takes approximately 30 seconds ;upload_protocol = jlink diff --git a/variants/nrf52840/rak3401_1watt/variant.cpp b/variants/nrf52840/rak3401_1watt/variant.cpp index a035fbaf0..188b747e8 100644 --- a/variants/nrf52840/rak3401_1watt/variant.cpp +++ b/variants/nrf52840/rak3401_1watt/variant.cpp @@ -23,6 +23,13 @@ #include "wiring_constants.h" #include "wiring_digital.h" +#ifdef LOW_VDD_SYSTEMOFF_DELAY_MS +#include "Arduino.h" +#include "FreeRTOS.h" +#include "power/PowerHAL.h" +#include "sleep.h" +#endif + const uint32_t g_ADigitalPinMap[] = { // P0 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, @@ -40,3 +47,39 @@ void initVariant() pinMode(PIN_3V3_EN, OUTPUT); digitalWrite(PIN_3V3_EN, HIGH); } + +#ifdef LOW_VDD_SYSTEMOFF_DELAY_MS +void variant_nrf52LoopHook(void) +{ + // If VDD stays unsafe for a while (brownout), force System OFF. + // Skip when VBUS present to allow recovery while USB-powered. + if (!powerHAL_isVBUSConnected()) { + // Rate-limit VDD safety checks: powerHAL_isPowerLevelSafe() calls getVDDVoltage() each time. + static constexpr uint32_t POWER_LEVEL_CHECK_INTERVAL_MS = 100; + static uint32_t last_vdd_check_ms = 0; + static bool last_power_level_safe = true; + + const uint32_t now = millis(); + if (last_vdd_check_ms == 0 || (uint32_t)(now - last_vdd_check_ms) >= POWER_LEVEL_CHECK_INTERVAL_MS) { + last_vdd_check_ms = now; + last_power_level_safe = powerHAL_isPowerLevelSafe(); + } + + // Do not use millis()==0 as a sentinel: at boot, millis() may be 0 while VDD is unsafe. + static bool low_vdd_timer_armed = false; + static uint32_t low_vdd_since_ms = 0; + + if (!last_power_level_safe) { + if (!low_vdd_timer_armed) { + low_vdd_since_ms = now; + low_vdd_timer_armed = true; + } + if ((uint32_t)(now - low_vdd_since_ms) >= (uint32_t)LOW_VDD_SYSTEMOFF_DELAY_MS) { + cpuDeepSleep(portMAX_DELAY); + } + } else { + low_vdd_timer_armed = false; + } + } +} +#endif diff --git a/variants/nrf52840/rak3401_1watt/variant.h b/variants/nrf52840/rak3401_1watt/variant.h index a154e6a41..329ec21e4 100644 --- a/variants/nrf52840/rak3401_1watt/variant.h +++ b/variants/nrf52840/rak3401_1watt/variant.h @@ -209,6 +209,13 @@ static const uint8_t SCK = PIN_SPI_SCK; #define AREF_VOLTAGE 3.0 #define VBAT_AR_INTERNAL AR_INTERNAL_3_0 #define ADC_MULTIPLIER 1.73 +#if defined(LOW_VDD_SYSTEMOFF_DELAY_MS) +#if !defined(OCV_ARRAY) +#define OCV_ARRAY 4160, 4020, 3940, 3870, 3810, 3760, 3740, 3720, 3680, 3620, 2990 +#endif +#define BATTERY_LPCOMP_INPUT NRF_LPCOMP_INPUT_3 +#define BATTERY_LPCOMP_THRESHOLD NRF_LPCOMP_REF_SUPPLY_5_8 // VDD=3.3V AIN3=5/8*VDD=2.06V VBAT=1.66*AIN3=3.41V +#endif #define RAK_4631 1 diff --git a/variants/nrf52840/rak4631/platformio.ini b/variants/nrf52840/rak4631/platformio.ini index 179d73e92..2cdaafba4 100644 --- a/variants/nrf52840/rak4631/platformio.ini +++ b/variants/nrf52840/rak4631/platformio.ini @@ -44,6 +44,18 @@ lib_deps = # renovate: datasource=git-refs depName=RAK12034-BMX160 packageName=https://github.com/RAKWireless/RAK12034-BMX160 gitBranch=main https://github.com/RAKWireless/RAK12034-BMX160/archive/dcead07ffa267d3c906e9ca4a1330ab989e957e2.zip +; RAK10728 WisMesh Repeater Mini V2 (RAK4631 + RAK19003 + solar battery + GPS module) +[env:rak_wismesh_repeater_mini] +extends = env:rak4631 +custom_meshtastic_display_name = RAK WisMesh Repeater Mini V2 +custom_meshtastic_images = rak4631.svg +build_flags = + ${env:rak4631.build_flags} + -D WISMESH_REPEATER_MINI + -DLOW_VDD_SYSTEMOFF_DELAY_MS=5000 + -DSAFE_VDD_VOLTAGE_THRESHOLD_MV=2900 + -DSAFE_VDD_VOLTAGE_THRESHOLD_HYST_MV=100 + ; If not set we will default to uploading over serial (first it forces bootloader entry by talking 1200bps to cdcacm) ; Note: as of 6/2013 the serial/bootloader based programming takes approximately 30 seconds ;upload_protocol = jlink diff --git a/variants/nrf52840/rak4631/variant.cpp b/variants/nrf52840/rak4631/variant.cpp index a035fbaf0..eed47eb2c 100644 --- a/variants/nrf52840/rak4631/variant.cpp +++ b/variants/nrf52840/rak4631/variant.cpp @@ -23,6 +23,13 @@ #include "wiring_constants.h" #include "wiring_digital.h" +#ifdef LOW_VDD_SYSTEMOFF_DELAY_MS +#include "Arduino.h" +#include "FreeRTOS.h" +#include "power/PowerHAL.h" +#include "sleep.h" +#endif + const uint32_t g_ADigitalPinMap[] = { // P0 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, @@ -40,3 +47,48 @@ void initVariant() pinMode(PIN_3V3_EN, OUTPUT); digitalWrite(PIN_3V3_EN, HIGH); } + +#ifdef LOW_VDD_SYSTEMOFF_DELAY_MS +static bool lowVddSystemOff = false; // Set to true when VDD is unsafe for a while (brownout), forcing System OFF. +// Strong override; must be noinline (see main-nrf52.cpp weak default). +__attribute__((noinline)) bool variant_enableBatteryLpcompWake() +{ + return lowVddSystemOff; +} + +// Hook called each nrf52Loop(); e.g. for low-VDD System OFF. +void variant_nrf52LoopHook(void) +{ + // If VDD stays unsafe for a while (brownout), force System OFF. + // Skip when VBUS present to allow recovery while USB-powered. + if (!powerHAL_isVBUSConnected()) { + // Rate-limit VDD safety checks: powerHAL_isPowerLevelSafe() calls getVDDVoltage() each time. + static constexpr uint32_t POWER_LEVEL_CHECK_INTERVAL_MS = 100; + static uint32_t last_vdd_check_ms = 0; + static bool last_power_level_safe = true; + + const uint32_t now = millis(); + if (last_vdd_check_ms == 0 || (uint32_t)(now - last_vdd_check_ms) >= POWER_LEVEL_CHECK_INTERVAL_MS) { + last_vdd_check_ms = now; + last_power_level_safe = powerHAL_isPowerLevelSafe(); + } + + // Do not use millis()==0 as a sentinel: at boot, millis() may be 0 while VDD is unsafe. + static bool low_vdd_timer_armed = false; + static uint32_t low_vdd_since_ms = 0; + + if (!last_power_level_safe) { + if (!low_vdd_timer_armed) { + low_vdd_since_ms = now; + low_vdd_timer_armed = true; + } + if ((uint32_t)(now - low_vdd_since_ms) >= (uint32_t)LOW_VDD_SYSTEMOFF_DELAY_MS) { + lowVddSystemOff = true; + cpuDeepSleep(portMAX_DELAY); + } + } else { + low_vdd_timer_armed = false; + } + } +} +#endif