⚠ This page is served via a proxy. Original site: https://github.com
This service does not collect credentials or authentication data.
Skip to content

Conversation

@entr0p1
Copy link

@entr0p1 entr0p1 commented Jan 17, 2026

This PR implements initial stages of power management functionality for nRF52840 nodes to prevent lockups and flash corruption associated with low voltage. This is designed to be modular and enabled per-variant.

Phase 1 (this PR) includes:

  • Startup lockout - minimum voltage reading from VBAT is required in order to proceed with the boot. If voltage is deemed too low, the board will enter the shutdown state. This is gated if there is external power present (e.g. 5V, USB) to prevent erroneous lockouts if the board is charging.
  • LPCOMP wake - when voltage readings from the battery return to a healthy state (i.e. indicating charging), a board reset is issued and the board will boot back up without external intervention. This is enabled when shutdown is initiated from Startup lockout.

@entr0p1
Copy link
Author

entr0p1 commented Jan 17, 2026

Leaving this in a draft state so it can be tested as this is a major refactor to centralise more of the code away from individual variants, and tidy up general sloppiness/hacky code from the v1 implementation.

@mattzzw
Copy link
Contributor

mattzzw commented Jan 18, 2026

Would this work also e.g. with nfr52 based systems that do not have voltage divider resistors for the ADC port in place? (Thinking e.g. of ProMicro)

@entr0p1
Copy link
Author

entr0p1 commented Jan 18, 2026

Would this work also e.g. with nfr52 based systems that do not have voltage divider resistors for the ADC port in place? (Thinking e.g. of ProMicro)

Great question, likely not (at least in the current implementation). I've been trying to track down a concrete schematic for the promicro but because there are so many iterations of it, I can't get a reliable view of the board. If you happen to have one, that would go a long way to getting a more definitive answer.

@mattzzw
Copy link
Contributor

mattzzw commented Jan 18, 2026

The ProMicro target is based on the "faketec" PCB (https://github.com/gargomoma/fakeTec_pcb). There are multiple variations of the PCB but mostly adding peripherals like FETs, power regulators, solar chargers etc.
I doubt that the circuitry for measuring battery voltage is different for the multiple PCB versions.

I was only able to find the schematic for V5
V5: https://github.com/gargomoma/fakeTec_pcb/blob/main/design_files/ShimonHoranek_fakeTecv5schematics.pdf

@entr0p1
Copy link
Author

entr0p1 commented Jan 18, 2026

OK so short answer is yes it should work:

  • According to the schematic the pin mapping should match the Xiao nRF52840 that I've been testing with (PIN_VBAT_READ (17) -> P0.31 = AIN7). It will work with LPCOMP wake.
  • If the caveats below prove problematic, there is actually a hardware BMS in the schematic which provides over-discharge protection (so you can fall back on this). Not sure if it auto-recovers though but you would think it would.

There are however a couple of caveats (citation needed from people who know this stuff better):

  • The voltage divider is high-impedance (20MOhm) which may cause it to be a tad inaccurate but I'm not certain
  • The current ADC_MULTIPLIER value in our code is "1.815" which, if the above is true, we should actually have it set to 2.0

@entr0p1
Copy link
Author

entr0p1 commented Jan 18, 2026

Sometimes we just need to send it and see. I'll add in code for the promicro variant if you're willing to test.

@entr0p1
Copy link
Author

entr0p1 commented Jan 18, 2026

I've found some duplication in the code so will undo the commits, fix that up and force push the branch back up to keep things tidy. I'll add the promicro support at the same time.

@oltaco
Copy link
Contributor

oltaco commented Jan 19, 2026

Would this work also e.g. with nfr52 based systems that do not have voltage divider resistors for the ADC port in place? (Thinking e.g. of ProMicro)

If you can't measure battery voltage then it's not going to work. I personally haven't seen any promicro-based boards that don't implement a voltage divider for battery reading though

@oltaco
Copy link
Contributor

oltaco commented Jan 19, 2026

  • The current ADC_MULTIPLIER value in our code is "1.815" which, if the above is true, we should actually have it set to 2.0

The thing is that promicro based boards don't necessarily all use the same voltage dividers. Promicro repeaters do allow setting the adc.multiplier value via the CLI though 👍

Comment on lines 10 to 14
// Shutdown Reason Codes (stored in GPREGRET before SYSTEMOFF)
#define SHUTDOWN_REASON_NONE 0x00
#define SHUTDOWN_REASON_LOW_VOLTAGE 0x4C // 'L' - Runtime low voltage threshold
#define SHUTDOWN_REASON_USER 0x55 // 'U' - User requested powerOff()
#define SHUTDOWN_REASON_BOOT_PROTECT 0x42 // 'B' - Boot voltage protection
Copy link
Contributor

Choose a reason for hiding this comment

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

Need to be careful here, as the bootloader uses GPREGRET as well, but I think these values will be OK.

#define DFU_MAGIC_OTA_APPJUM BOOTLOADER_DFU_START // 0xB1
#define DFU_MAGIC_OTA_RESET 0xA8
#define DFU_MAGIC_SERIAL_ONLY_RESET 0x4e
#define DFU_MAGIC_UF2_RESET 0x57
#define DFU_MAGIC_SKIP 0x6d

Copy link
Author

Choose a reason for hiding this comment

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

Found this out while going over it again, there's apparently a GPREGRET2 we can use. Just looking into that.

@entr0p1 entr0p1 force-pushed the powermgt-nrf52840-v2 branch from ee73ddb to 94376f8 Compare January 19, 2026 13:58
@entr0p1
Copy link
Author

entr0p1 commented Jan 19, 2026

Cleaned up and simplfied the code (thanks to those who helped behind the scenes). Have switched GPREGRET over to GPREGRET2 and run a manual shutdown via the same route a boot lockout shutdown would take, and confirmed the LPCOMP wake works and so does the GPREGRET2 and RESETREAS value reads.

Ready for a review I think. I've also added the promicro board support for others to test.

@entr0p1 entr0p1 marked this pull request as ready for review January 19, 2026 14:02
@entr0p1
Copy link
Author

entr0p1 commented Jan 20, 2026

BTW - once the reviews are done and the devs are happy, I'll clean up the commits into one.

Copy link
Contributor

@oltaco oltaco left a comment

Choose a reason for hiding this comment

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

Looks good, just a few comments/suggestions 👍

Comment on lines 43 to 46
#ifdef NRF52_POWER_MANAGEMENT
// Boot voltage protection check (may not return if voltage too low)
checkBootVoltage(&power_config);
#endif
Copy link
Contributor

Choose a reason for hiding this comment

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

Should this happen before the power is supplied to the radio a few lines above?

Copy link
Author

Choose a reason for hiding this comment

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

Good call, I missed that. Fixed up on local and will be in the next commit.

#ifdef NRF52_POWER_MANAGEMENT
bool isExternalPowered() override;
uint16_t getBootVoltage() override { return boot_voltage_mv; }
virtual uint32_t getResetReason() const override { return reset_reason; }
Copy link
Contributor

Choose a reason for hiding this comment

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

as above can just use readResetReason()

Comment on lines +30 to +36
uint8_t g_nrf52_shutdown_reason = 0; // Shutdown reason

// Early constructor - runs before SystemInit() clears the registers
// Priority 101 ensures this runs before SystemInit (102) and before
// any C++ static constructors (default 65535)
static void __attribute__((constructor(101))) nrf52_early_reset_capture() {
g_nrf52_reset_reason = NRF_POWER->RESETREAS;
Copy link
Contributor

Choose a reason for hiding this comment

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

reset reason can be accessed by readResetReason(), adafruit caches the value at early startup before it gets cleared.
GPREGRET2 shouldn't get cleared so you can access that at anytime, but if you use readResetReason() you probably don't need to force priority

Copy link
Author

Choose a reason for hiding this comment

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

I noticed anecdotally online that the adafruit version can be a bit clunky/unreliable sometimes, have you found it to work OK? If so, I can change it over. GPREGRET2 is configured now though and working.

Comment on lines +58 to +60
configureLpcompWake(power_config.lpcomp_ain_channel, power_config.lpcomp_ref_eighths);
enterSystemOff(SHUTDOWN_REASON_USER);
Copy link
Contributor

Choose a reason for hiding this comment

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

Should LPCOMP wake be disabled when the user has explicitly requested shutdown via the UI?

Copy link
Author

@entr0p1 entr0p1 Jan 20, 2026

Choose a reason for hiding this comment

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

I was in two minds about this when I added it and still am. What I'm trying to achieve is a means for the user to force a power management state change early or if they wish to test how it will respond with their setup.

I wanted to differentiate this without people getting arthritis in their thumbs from typing some long arduous command like "set pwrmgt.state shutdownandwake". I think there's probably merit in allowing both ways to exist; shutdown with and without LPCOMP wake.

What do you think?

- Added NRF52 power‑management core (early reset/shutdown capture, boot‑voltage lockout, LPCOMP wake config, system‑off entry, power source detection) in NRF52Board.cpp and NRF52Board.h, plus interface hooks in MeshCore.h.
- Exposed CLI queries for power‑management status (support/source/boot reason/boot voltage) in CommonCLI.cpp
- Added documentation in docs/nrf52_power_management.md.
- Enabled power management in Xiao nRF52840, RAK4631, Promicro, Heltec T114 boards (with per‑board PWRMGT config macros in each variant.h)
- Implemented per‑board shutdown prep, boot‑lock checks, and power‑off flows in respective board files (*Board.cpp, *Board.h), including LoRa power gating and divider handling (Note: T114 user power‑off preserves the old behavior but now records GPREGRET2).
@entr0p1 entr0p1 force-pushed the powermgt-nrf52840-v2 branch from 94376f8 to 346c9d3 Compare January 21, 2026 12:32
@entr0p1
Copy link
Author

entr0p1 commented Jan 21, 2026

Alright, commits cleaned up and have cleaned up the code a bit more and fixed some sequencing issues for some boards. Its now implemented for the Heltec T114, Xiao nRF52840, RAK4631, and Promicro. Let me know how this one looks, thanks everyone for all your effort going over this with me 100 times :)

@entr0p1
Copy link
Author

entr0p1 commented Jan 21, 2026

FYI some testing is showing reliability issues for the Promicro. I'm working on it, will mark this as a draft again just so we don't merge bad code. Should have a solution in the next ~24hrs.

@entr0p1 entr0p1 marked this pull request as draft January 21, 2026 22:31
@oltaco
Copy link
Contributor

oltaco commented Jan 21, 2026

As noted by @entr0p1 I had some issues during initial testing with a promicro last night. Low voltage boot lockout works as expected but LPCOMP doesn't wake reliably on rise using a lab power supply hooked up to the battery input. I will test some more with Xiao NRF52 / RAK4631 / Heltec T114 tonight and see what the results are.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants