Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
33 changes: 26 additions & 7 deletions src/helpers/NRF52Board.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -130,10 +130,17 @@ bool NRF52Board::checkBootVoltage(const PowerMgtConfig* config) {
// Only trigger shutdown if reading is valid (>1000mV) AND below threshold
// This prevents spurious shutdowns on ADC glitches or uninitialized reads
if (boot_voltage_mv > 1000 && boot_voltage_mv < config->voltage_bootlock) {
MESH_DEBUG_PRINTLN("PWRMGT: Boot voltage too low - entering protective shutdown");
// First reading below threshold - wait for DCDC to stabilize and re-read
delay(50);
boot_voltage_mv = getBattMilliVolts();
MESH_DEBUG_PRINTLN("PWRMGT: Boot voltage (confirmed) = %u mV (threshold = %u mV)",
boot_voltage_mv, config->voltage_bootlock);

initiateShutdown(SHUTDOWN_REASON_BOOT_PROTECT);
return false; // Should never reach this
if (boot_voltage_mv > 1000 && boot_voltage_mv < config->voltage_bootlock) {
MESH_DEBUG_PRINTLN("PWRMGT: Boot voltage too low - entering protective shutdown");
initiateShutdown(SHUTDOWN_REASON_BOOT_PROTECT);
return false; // Should never reach this
}
}

return true;
Expand Down Expand Up @@ -177,7 +184,7 @@ void NRF52Board::enterSystemOff(uint8_t reason) {
NVIC_SystemReset();
}

void NRF52Board::configureVoltageWake(uint8_t ain_channel, uint8_t refsel) {
bool NRF52Board::configureVoltageWake(uint8_t ain_channel, uint8_t refsel) {
// LPCOMP is not managed by SoftDevice - direct register access required
// Halt and disable before reconfiguration
NRF_LPCOMP->TASKS_STOP = 1;
Expand Down Expand Up @@ -213,6 +220,14 @@ void NRF52Board::configureVoltageWake(uint8_t ain_channel, uint8_t refsel) {
delayMicroseconds(50);
}

// Safety: if voltage is already above threshold, UP detection will never fire
if (NRF_LPCOMP->RESULT & LPCOMP_RESULT_RESULT_Msk) {
MESH_DEBUG_PRINTLN("PWRMGT: LPCOMP shows voltage above threshold - unsafe for SYSTEMOFF");
NRF_LPCOMP->TASKS_STOP = 1;
NRF_LPCOMP->ENABLE = LPCOMP_ENABLE_ENABLE_Disabled;
return false;
}

if (refsel == 7) {
MESH_DEBUG_PRINTLN("PWRMGT: LPCOMP wake configured (AIN%d, ref=ARef)", ain_channel);
} else if (refsel <= 6) {
Expand All @@ -235,19 +250,23 @@ void NRF52Board::configureVoltageWake(uint8_t ain_channel, uint8_t refsel) {
}

MESH_DEBUG_PRINTLN("PWRMGT: VBUS wake configured");

return true;
}
#endif

void NRF52BoardDCDC::begin() {
NRF52Board::begin();

// Enable DC/DC converter for improved power efficiency
// Enable DC/DC converter only when SoftDevice is managing the POWER peripheral.
// Without SoftDevice, NRF_POWER->DCDCEN = 1 leaves the DC/DC permanently on,
// which causes boot failures on some boards when running on battery alone.
// The SoftDevice manages DC/DC dynamically (on during radio, off during idle),
// which is the safe way to use it.
uint8_t sd_enabled = 0;
sd_softdevice_is_enabled(&sd_enabled);
if (sd_enabled) {
sd_power_dcdc_mode_set(NRF_POWER_DCDC_ENABLE);
} else {
NRF_POWER->DCDCEN = 1;
Copy link
Copy Markdown
Contributor

@fschrempf fschrempf Mar 4, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I guess this will increase the power consumption of all NRF52 repeaters (running without softdevice) by something like 20 to 30%.

I have a board with RAK4630 (uart.cz Solar Mesh Baseboard) running with DCDC enabled for several months now without any issues. And a lot of other people are running similar RAK4630 and RAK4631 hardware without such issues.

So I really wonder what is different for @Emmpunkt in #1878 and if this change is justified.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If "powersaving on" is used, repeaters dont need this DC2DC to achieve 6mA.

This DC2DC is more meaningful to companions and repeaters without powersaving on.

I guess DC2DC is less reliable at low voltage than the default LDO way.

}
}

Expand Down
2 changes: 1 addition & 1 deletion src/helpers/NRF52Board.h
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ class NRF52Board : public mesh::MainBoard {

bool checkBootVoltage(const PowerMgtConfig* config);
void enterSystemOff(uint8_t reason);
void configureVoltageWake(uint8_t ain_channel, uint8_t refsel);
bool configureVoltageWake(uint8_t ain_channel, uint8_t refsel);
virtual void initiateShutdown(uint8_t reason);
#endif

Expand Down
4 changes: 3 additions & 1 deletion variants/heltec_t114/T114Board.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,9 @@ void T114Board::initiateShutdown(uint8_t reason) {
digitalWrite(PIN_BAT_CTL, enable_lpcomp ? HIGH : LOW);

if (enable_lpcomp) {
configureVoltageWake(power_config.lpcomp_ain_channel, power_config.lpcomp_refsel);
if (!configureVoltageWake(power_config.lpcomp_ain_channel, power_config.lpcomp_refsel)) {
NVIC_SystemReset();
}
}

enterSystemOff(reason);
Expand Down
38 changes: 36 additions & 2 deletions variants/rak4631/RAK4631Board.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,22 @@

#include "RAK4631Board.h"

#ifdef BOOT_DIAG
// Diagnostic LED blink: N short blinks on green LED to indicate boot stage
static void diag_blink(uint8_t count) {
pinMode(PIN_LED1, OUTPUT);
for (uint8_t i = 0; i < count; i++) {
digitalWrite(PIN_LED1, HIGH);
delay(100);
digitalWrite(PIN_LED1, LOW);
delay(150);
}
delay(300);
}
#else
#define diag_blink(n) ((void)0)
#endif

#ifdef NRF52_POWER_MANAGEMENT
// Static configuration for power management
// Values set in variant.h defines
Expand All @@ -18,15 +34,26 @@ void RAK4631Board::initiateShutdown(uint8_t reason) {

if (reason == SHUTDOWN_REASON_LOW_VOLTAGE ||
reason == SHUTDOWN_REASON_BOOT_PROTECT) {
configureVoltageWake(power_config.lpcomp_ain_channel, power_config.lpcomp_refsel);
if (!configureVoltageWake(power_config.lpcomp_ain_channel, power_config.lpcomp_refsel)) {
NVIC_SystemReset();
}
}

enterSystemOff(reason);
}
#endif // NRF52_POWER_MANAGEMENT

void RAK4631Board::begin() {
diag_blink(1); // Stage 1: entering begin()

#ifdef DISABLE_DCDC
NRF52Board::begin();
#else
NRF52BoardDCDC::begin();
#endif

diag_blink(2); // Stage 2: board base init done

pinMode(PIN_VBAT_READ, INPUT);
#ifdef PIN_USER_BTN
pinMode(PIN_USER_BTN, INPUT_PULLUP);
Expand All @@ -42,12 +69,19 @@ void RAK4631Board::begin() {

Wire.begin();

diag_blink(3); // Stage 3: I2C done, about to configure LoRa power

pinMode(SX126X_POWER_EN, OUTPUT);
#ifdef NRF52_POWER_MANAGEMENT
#if defined(NRF52_POWER_MANAGEMENT) && !defined(DISABLE_BOOT_PROTECTION)
// Boot voltage protection check (may not return if voltage too low)
// We need to call this after we configure SX126X_POWER_EN as output but before we pull high
checkBootVoltage(&power_config);
#endif

diag_blink(4); // Stage 4: power check passed

digitalWrite(SX126X_POWER_EN, HIGH);
delay(10); // give sx1262 some time to power up

diag_blink(5); // Stage 5: board.begin() complete
}
17 changes: 17 additions & 0 deletions variants/rak4631/platformio.ini
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,23 @@ build_src_filter = ${rak4631.build_src_filter}
+<helpers/ui/SSD1306Display.cpp>
+<../examples/simple_repeater>

; Diagnostic build: LED blinks at boot stages + boot protection disabled
[env:RAK_4631_repeater_diag]
extends = env:RAK_4631_repeater
build_flags =
${env:RAK_4631_repeater.build_flags}
-D BOOT_DIAG
-D DISABLE_BOOT_PROTECTION

; Diagnostic build: LED blinks + boot protection disabled + DC/DC disabled
[env:RAK_4631_repeater_diag_no_dcdc]
extends = env:RAK_4631_repeater
build_flags =
${env:RAK_4631_repeater.build_flags}
-D BOOT_DIAG
-D DISABLE_BOOT_PROTECTION
-D DISABLE_DCDC

[env:RAK_4631_repeater_bridge_rs232_serial1]
extends = rak4631
build_flags =
Expand Down
4 changes: 3 additions & 1 deletion variants/xiao_nrf52/XiaoNrf52Board.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,9 @@ void XiaoNrf52Board::initiateShutdown(uint8_t reason) {
digitalWrite(VBAT_ENABLE, enable_lpcomp ? LOW : HIGH);

if (enable_lpcomp) {
configureVoltageWake(power_config.lpcomp_ain_channel, power_config.lpcomp_refsel);
if (!configureVoltageWake(power_config.lpcomp_ain_channel, power_config.lpcomp_refsel)) {
NVIC_SystemReset();
}
}

enterSystemOff(reason);
Expand Down
Loading