# nRF52 Power Management

## nRF52 Power Management

### Overview

The nRF52 Power Management module provides battery protection features to prevent over-discharge, minimise likelihood of brownout and flash corruption conditions existing, and enable safe voltage-based recovery.

### Features

#### Boot Voltage Protection

- Checks battery voltage immediately after boot and before mesh operations commence
- If voltage is below a configurable threshold (e.g., 3300mV), the device configures voltage wake (LPCOMP + VBUS) and enters protective shutdown (SYSTEMOFF)
- Prevents boot loops when battery is critically low
- Skipped when external power (USB VBUS) is detected

#### Voltage Wake (LPCOMP + VBUS)

- Configures the nRF52's Low Power Comparator (LPCOMP) before entering SYSTEMOFF
- Enables USB VBUS detection so external power can wake the device
- Device automatically wakes when battery voltage rises above recovery threshold or when VBUS is detected

#### Early Boot Register Capture

- Captures RESETREAS (reset reason) and GPREGRET2 (shutdown reason) before SystemInit() clears them
- Allows firmware to determine why it booted (cold boot, watchdog, LPCOMP wake, etc.)
- Allows firmware to determine why it last shut down (user request, low voltage, boot protection)

#### Shutdown Reason Tracking

Shutdown reason codes (stored in GPREGRET2):

<table id="bkmrk-codenamedescription-"><thead><tr><th>Code</th><th>Name</th><th>Description</th></tr></thead><tbody><tr><td>0x00</td><td>NONE</td><td>Normal boot / no previous shutdown</td></tr><tr><td>0x4C</td><td>LOW\_VOLTAGE</td><td>Runtime low voltage threshold reached</td></tr><tr><td>0x55</td><td>USER</td><td>User requested powerOff()</td></tr><tr><td>0x42</td><td>BOOT\_PROTECT</td><td>Boot voltage protection triggered</td></tr></tbody></table>

### Supported Boards

<table id="bkmrk-boardimplementedlpco"><thead><tr><th>Board</th><th>Implemented</th><th>LPCOMP wake</th><th>VBUS wake</th></tr></thead><tbody><tr><td>Seeed Studio XIAO nRF52840 (`xiao_nrf52`)</td><td>Yes</td><td>Yes</td><td>Yes</td></tr><tr><td>RAK4631 (`rak4631`)</td><td>Yes</td><td>Yes</td><td>Yes</td></tr><tr><td>Heltec T114 (`heltec_t114`)</td><td>Yes</td><td>Yes</td><td>Yes</td></tr><tr><td>Promicro nRF52840</td><td>No</td><td>No</td><td>No</td></tr><tr><td>RAK WisMesh Tag</td><td>No</td><td>No</td><td>No</td></tr><tr><td>Heltec Mesh Solar</td><td>No</td><td>No</td><td>No</td></tr><tr><td>LilyGo T-Echo / T-Echo Lite</td><td>No</td><td>No</td><td>No</td></tr><tr><td>SenseCAP Solar</td><td>Yes</td><td>Yes</td><td>Yes</td></tr><tr><td>WIO Tracker L1 / L1 E-Ink</td><td>No</td><td>No</td><td>No</td></tr><tr><td>WIO WM1110</td><td>No</td><td>No</td><td>No</td></tr><tr><td>Mesh Pocket</td><td>No</td><td>No</td><td>No</td></tr><tr><td>Nano G2 Ultra</td><td>No</td><td>No</td><td>No</td></tr><tr><td>ThinkNode M1/M3/M6</td><td>No</td><td>No</td><td>No</td></tr><tr><td>T1000-E</td><td>No</td><td>No</td><td>No</td></tr><tr><td>Ikoka Nano/Stick/Handheld (nRF)</td><td>No</td><td>No</td><td>No</td></tr><tr><td>Keepteen LT1</td><td>No</td><td>No</td><td>No</td></tr><tr><td>Minewsemi ME25LS01</td><td>No</td><td>No</td><td>No</td></tr></tbody></table>

Notes:

- "Implemented" reflects Phase 1 (boot lockout + shutdown reason capture).
- User power-off on Heltec T114 does not enable LPCOMP wake.
- VBUS detection is used to skip boot lockout on external power, and VBUS wake is configured alongside LPCOMP when supported hardware exposes VBUS to the nRF52.

### Technical Details

#### Architecture

The power management functionality is integrated into the `NRF52Board` base class in `src/helpers/NRF52Board.cpp`. Board variants provide hardware-specific configuration via a `PowerMgtConfig` struct and override `initiateShutdown(uint8_t reason)` to perform board-specific power-down work and conditionally enable voltage wake (LPCOMP + VBUS).

#### Early Boot Capture

A static constructor with priority 101 in `NRF52Board.cpp` captures the RESETREAS and GPREGRET2 registers before:

- SystemInit() (priority 102) - which clears RESETREAS
- Static C++ constructors (default priority 65535)

This ensures we capture the true reset reason before any initialisation code runs.

#### Board Implementation

To enable power management on a board variant:

1. **Enable in platformio.ini**:

```ini

-D NRF52\_POWER\_MANAGEMENT

```

1. **Define configuration in variant.h**:

```c

\#define PWRMGT\_VOLTAGE\_BOOTLOCK 3300 // Won't boot below this voltage (mV)

\#define PWRMGT\_LPCOMP\_AIN 7 // AIN channel for voltage sensing

\#define PWRMGT\_LPCOMP\_REFSEL 2 // REFSEL (0-6=1/8..7/8, 7=ARef, 8-15=1/16..15/16)

```

1. **Implement in board .cpp file**:

```cpp

\#ifdef NRF52\_POWER\_MANAGEMENT

const PowerMgtConfig power\_config = {

.lpcomp\_ain\_channel = PWRMGT\_LPCOMP\_AIN,

.lpcomp\_refsel = PWRMGT\_LPCOMP\_REFSEL,

.voltage\_bootlock = PWRMGT\_VOLTAGE\_BOOTLOCK

};

void MyBoard::initiateShutdown(uint8\_t reason) {

// Board-specific shutdown preparation (e.g., disable peripherals)

bool enable\_lpcomp = (reason == SHUTDOWN\_REASON\_LOW\_VOLTAGE ||

reason == SHUTDOWN\_REASON\_BOOT\_PROTECT);

if (enable\_lpcomp) {

configureVoltageWake(power\_config.lpcomp\_ain\_channel, power\_config.lpcomp\_refsel);

}

enterSystemOff(reason);

}

\#endif

void MyBoard::begin() {

NRF52Board::begin(); // or NRF52BoardDCDC::begin()

// ... board setup ...

\#ifdef NRF52\_POWER\_MANAGEMENT

checkBootVoltage(&amp;power\_config);

\#endif

}

```

For user-initiated shutdowns, `powerOff()` remains board-specific. Power management only arms LPCOMP for automated shutdown reasons (boot protection/low voltage).

1. **Declare override in board .h file**:

```cpp

\#ifdef NRF52\_POWER\_MANAGEMENT

void initiateShutdown(uint8\_t reason) override;

\#endif

```

#### Voltage Wake Configuration

The LPCOMP (Low Power Comparator) is configured to:

- Monitor the specified AIN channel (0-7 corresponding to P0.02-P0.05, P0.28-P0.31)
- Compare against VDD fraction reference (REFSEL: 0-6=1/8..7/8, 7=ARef, 8-15=1/16..15/16)
- Detect UP events (voltage rising above threshold)
- Use 50mV hysteresis for noise immunity
- Wake the device from SYSTEMOFF when triggered

VBUS wake is enabled via the POWER peripheral USBDETECTED event whenever `configureVoltageWake()` is used. This requires USB VBUS to be routed to the nRF52 (typical on nRF52840 boards with native USB).

**LPCOMP Reference Selection (PWRMGT\_LPCOMP\_REFSEL)**:

<table id="bkmrk-refselfractionvbat-%40"><thead><tr><th>REFSEL</th><th>Fraction</th><th>VBAT @ 1M/1M divider (VDD=3.0-3.3)</th><th>VBAT @ 1.5M/1M divider (VDD=3.0-3.3)</th></tr></thead><tbody><tr><td>0</td><td>1/8</td><td>0.75-0.82 V</td><td>0.94-1.03 V</td></tr><tr><td>1</td><td>2/8</td><td>1.50-1.65 V</td><td>1.88-2.06 V</td></tr><tr><td>2</td><td>3/8</td><td>2.25-2.47 V</td><td>2.81-3.09 V</td></tr><tr><td>3</td><td>4/8</td><td>3.00-3.30 V</td><td>3.75-4.12 V</td></tr><tr><td>4</td><td>5/8</td><td>3.75-4.12 V</td><td>4.69-5.16 V</td></tr><tr><td>5</td><td>6/8</td><td>4.50-4.95 V</td><td>5.62-6.19 V</td></tr><tr><td>6</td><td>7/8</td><td>5.25-5.77 V</td><td>6.56-7.22 V</td></tr><tr><td>7</td><td>ARef</td><td>-</td><td>-</td></tr><tr><td>8</td><td>1/16</td><td>0.38-0.41 V</td><td>0.47-0.52 V</td></tr><tr><td>9</td><td>3/16</td><td>1.12-1.24 V</td><td>1.41-1.55 V</td></tr><tr><td>10</td><td>5/16</td><td>1.88-2.06 V</td><td>2.34-2.58 V</td></tr><tr><td>11</td><td>7/16</td><td>2.62-2.89 V</td><td>3.28-3.61 V</td></tr><tr><td>12</td><td>9/16</td><td>3.38-3.71 V</td><td>4.22-4.64 V</td></tr><tr><td>13</td><td>11/16</td><td>4.12-4.54 V</td><td>5.16-5.67 V</td></tr><tr><td>14</td><td>13/16</td><td>4.88-5.36 V</td><td>6.09-6.70 V</td></tr><tr><td>15</td><td>15/16</td><td>5.62-6.19 V</td><td>7.03-7.73 V</td></tr></tbody></table>

**Important**: For boards with a voltage divider on the battery sense pin, LPCOMP measures the divided voltage. Use:

`VBAT_threshold ≈ (VDD <em> fraction) </em> divider_scale`, where `divider_scale = (Rtop + Rbottom) / Rbottom` (e.g., 2.0 for 1M/1M, 2.5 for 1.5M/1M, 3.0 for XIAO).

#### SoftDevice Compatibility

The power management code checks whether SoftDevice is enabled and uses the appropriate API:

- When SD enabled: `sd_power_*` functions
- When SD disabled: Direct register access (NRF\_POWER-&gt;\*)

This ensures compatibility regardless of BLE stack state.

### CLI Commands

Power management status can be queried via the CLI:

<table id="bkmrk-commanddescription-g"><thead><tr><th>Command</th><th>Description</th></tr></thead><tbody><tr><td>`get pwrmgt.support`</td><td>Returns "supported" or "unsupported"</td></tr><tr><td>`get pwrmgt.source`</td><td>Returns current power source - "battery" or "external" (5V/USB power)</td></tr><tr><td>`get pwrmgt.bootreason`</td><td>Returns reset and shutdown reason strings</td></tr><tr><td>`get pwrmgt.bootmv`</td><td>Returns boot voltage in millivolts</td></tr></tbody></table>

On boards without power management enabled, all commands except `get pwrmgt.support` return:

```
ERROR: Power management not supported
```

### Debug Output

When `MESH_DEBUG=1` is enabled, the power management module outputs:

```
DEBUG: PWRMGT: Reset = Wake from LPCOMP (0x20000); Shutdown = Low Voltage (0x4C)
DEBUG: PWRMGT: Boot voltage = 3450 mV (threshold = 3300 mV)
DEBUG: PWRMGT: LPCOMP wake configured (AIN7, ref=3/8 VDD)
```

### Phase 2 (Planned)

- Runtime voltage monitoring
- Voltage state machine (Normal -&gt; Warning -&gt; Critical -&gt; Shutdown)
- Configurable thresholds
- Load shedding callbacks for power reduction
- Deep sleep integration
- Scheduled wake-up
- Extended sleep with periodic monitoring

### References

- [nRF52840 Product Specification - POWER](https://infocenter.nordicsemi.com/topic/ps_nrf52840/power.html)
- [nRF52840 Product Specification - LPCOMP](https://infocenter.nordicsemi.com/topic/ps_nrf52840/lpcomp.html)
- [SoftDevice S140 API - Power Management](https://infocenter.nordicsemi.com/topic/sdk_nrf5_v17.1.0/group)