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
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -17,3 +17,4 @@ compile_commands.json
.venv/
venv/
platformio.local.ini
Pipfile
7 changes: 7 additions & 0 deletions boards/nrf52840_s140_v6.ld
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,13 @@ SECTIONS
KEEP(*(.fs_data))
PROVIDE(__stop_fs_data = .);
} > RAM

.noinit (NOLOAD) :
{
. = ALIGN(4);
KEEP(*(.noinit))
. = ALIGN(4);
} > RAM
} INSERT AFTER .data;

INCLUDE "nrf52_common.ld"
7 changes: 7 additions & 0 deletions boards/nrf52840_s140_v6_extrafs.ld
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,13 @@ SECTIONS
KEEP(*(.fs_data))
PROVIDE(__stop_fs_data = .);
} > RAM

.noinit (NOLOAD) :
{
. = ALIGN(4);
KEEP(*(.noinit))
. = ALIGN(4);
} > RAM
} INSERT AFTER .data;

INCLUDE "nrf52_common.ld"
7 changes: 7 additions & 0 deletions boards/nrf52840_s140_v7.ld
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,13 @@ SECTIONS
KEEP(*(.fs_data))
PROVIDE(__stop_fs_data = .);
} > RAM

.noinit (NOLOAD) :
{
. = ALIGN(4);
KEEP(*(.noinit))
. = ALIGN(4);
} > RAM
} INSERT AFTER .data;

INCLUDE "nrf52_common.ld"
7 changes: 7 additions & 0 deletions boards/nrf52840_s140_v7_extrafs.ld
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,13 @@ SECTIONS
KEEP(*(.fs_data))
PROVIDE(__stop_fs_data = .);
} > RAM

.noinit (NOLOAD) :
{
. = ALIGN(4);
KEEP(*(.noinit))
. = ALIGN(4);
} > RAM
} INSERT AFTER .data;

INCLUDE "nrf52_common.ld"
17 changes: 17 additions & 0 deletions docs/cli_commands.md
Original file line number Diff line number Diff line change
Expand Up @@ -1124,3 +1124,20 @@ region save
**Note:** Returns an error on boards without power management support.

---

#### View the last radio initialisation status
**Usage:** `get radio.init_status`

---

#### View the radio initialisation attempt count
**Usage:** `get radio.init_attempts`

---

#### View compact boot diagnostics
**Usage:** `get diag.boot`

**Note:** Returns current and previous reset-retained boot diagnostics. `cur_sd` and `prev_sd` are raw shutdown/fault markers, including low voltage, boot protection, user shutdown, and radio initialisation failure.

---
4 changes: 4 additions & 0 deletions docs/nrf52_power_management.md
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ Shutdown reason codes (stored in GPREGRET2):
| 0x4C | LOW_VOLTAGE | Runtime low voltage threshold reached |
| 0x55 | USER | User requested powerOff() |
| 0x42 | BOOT_PROTECT | Boot voltage protection triggered |
| 0x52 | RADIO_INIT_FAIL | Radio initialisation failed before startup completed |

## Supported Boards

Expand Down Expand Up @@ -185,12 +186,15 @@ Power management status can be queried via the CLI:
| `get pwrmgt.source` | Returns current power source - "battery" or "external" (5V/USB power) |
| `get pwrmgt.bootreason` | Returns reset and shutdown reason strings |
| `get pwrmgt.bootmv` | Returns boot voltage in millivolts |
| `get diag.boot` | Returns current and previous reset-retained boot/radio diagnostics |

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

`get diag.boot` includes raw current and previous boot records. The shutdown fields include all `GPREGRET2` markers, not only radio initialisation failure, so low-voltage and boot-protection context is preserved when diagnosing reset loops. The previous slot is retained across MCU resets where RAM is preserved; it is not a flash-backed history and may be lost after a complete power loss.

## Debug Output

When `MESH_DEBUG=1` is enabled, the power management module outputs:
Expand Down
7 changes: 6 additions & 1 deletion examples/companion_radio/main.cpp
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
#include <Arduino.h> // needed for PlatformIO
#include <Mesh.h>
#include <helpers/RadioInitDiagnostics.h>
#include "MyMesh.h"

// Believe it or not, this std C function is busted on some platforms!
Expand Down Expand Up @@ -129,7 +130,11 @@ void setup() {
}
#endif

if (!radio_init()) { halt(); }
radioInitSetBootStage(RADIO_BOOT_STAGE_RADIO_INIT_ENTERED);
if (!radio_init()) {
MESH_DEBUG_PRINTLN("Radio init failed!");
radioInitRebootAfterFault(board, RADIO_INIT_FAULT_RADIO_INIT_FAIL);
}

fast_rng.begin(radio_driver.getRngSeed());

Expand Down
5 changes: 4 additions & 1 deletion examples/kiss_modem/main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
#include <target.h>
#include <helpers/ArduinoHelpers.h>
#include <helpers/IdentityStore.h>
#include <helpers/RadioInitDiagnostics.h>
#include "KissModem.h"

#if defined(NRF52_PLATFORM)
Expand Down Expand Up @@ -76,8 +77,10 @@ void onGetStats(uint32_t* rx, uint32_t* tx, uint32_t* errors) {
void setup() {
board.begin();

radioInitSetBootStage(RADIO_BOOT_STAGE_RADIO_INIT_ENTERED);
if (!radio_init()) {
halt();
MESH_DEBUG_PRINTLN("Radio init failed!");
radioInitRebootAfterFault(board, RADIO_INIT_FAULT_RADIO_INIT_FAIL);
}

radio_driver.begin();
Expand Down
4 changes: 3 additions & 1 deletion examples/simple_repeater/main.cpp
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
#include <Arduino.h> // needed for PlatformIO
#include <Mesh.h>
#include <helpers/RadioInitDiagnostics.h>

#include "MyMesh.h"

Expand Down Expand Up @@ -48,9 +49,10 @@ void setup() {
}
#endif

radioInitSetBootStage(RADIO_BOOT_STAGE_RADIO_INIT_ENTERED);
if (!radio_init()) {
MESH_DEBUG_PRINTLN("Radio init failed!");
halt();
radioInitRebootAfterFault(board, RADIO_INIT_FAULT_RADIO_INIT_FAIL);
}

fast_rng.begin(radio_driver.getRngSeed());
Expand Down
7 changes: 6 additions & 1 deletion examples/simple_room_server/main.cpp
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
#include <Arduino.h> // needed for PlatformIO
#include <Mesh.h>
#include <helpers/RadioInitDiagnostics.h>

#include "MyMesh.h"

Expand Down Expand Up @@ -33,7 +34,11 @@ void setup() {
}
#endif

if (!radio_init()) { halt(); }
radioInitSetBootStage(RADIO_BOOT_STAGE_RADIO_INIT_ENTERED);
if (!radio_init()) {
MESH_DEBUG_PRINTLN("Radio init failed!");
radioInitRebootAfterFault(board, RADIO_INIT_FAULT_RADIO_INIT_FAIL);
}

fast_rng.begin(radio_driver.getRngSeed());

Expand Down
7 changes: 6 additions & 1 deletion examples/simple_secure_chat/main.cpp
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
#include <Arduino.h> // needed for PlatformIO
#include <Mesh.h>
#include <helpers/RadioInitDiagnostics.h>

#if defined(NRF52_PLATFORM)
#include <InternalFileSystem.h>
Expand Down Expand Up @@ -560,7 +561,11 @@ void setup() {

board.begin();

if (!radio_init()) { halt(); }
radioInitSetBootStage(RADIO_BOOT_STAGE_RADIO_INIT_ENTERED);
if (!radio_init()) {
MESH_DEBUG_PRINTLN("Radio init failed!");
radioInitRebootAfterFault(board, RADIO_INIT_FAULT_RADIO_INIT_FAIL);
}

fast_rng.begin(radio_driver.getRngSeed());

Expand Down
7 changes: 6 additions & 1 deletion examples/simple_sensor/main.cpp
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
#include "SensorMesh.h"
#include <helpers/RadioInitDiagnostics.h>

#ifdef DISPLAY_CLASS
#include "UITask.h"
Expand Down Expand Up @@ -66,7 +67,11 @@ void setup() {
}
#endif

if (!radio_init()) { halt(); }
radioInitSetBootStage(RADIO_BOOT_STAGE_RADIO_INIT_ENTERED);
if (!radio_init()) {
MESH_DEBUG_PRINTLN("Radio init failed!");
radioInitRebootAfterFault(board, RADIO_INIT_FAULT_RADIO_INIT_FAIL);
}

fast_rng.begin(radio_driver.getRngSeed());

Expand Down
5 changes: 5 additions & 0 deletions src/helpers/AutoDiscoverRTCClock.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
#include "RTClib.h"
#include <Melopero_RV3028.h>
#include "RTC_RX8130CE.h"
#include "RadioInitDiagnostics.h"

static RTC_DS3231 rtc_3231;
static bool ds3231_success = false;
Expand All @@ -27,6 +28,8 @@ bool AutoDiscoverRTCClock::i2c_probe(TwoWire& wire, uint8_t addr) {
}

void AutoDiscoverRTCClock::begin(TwoWire& wire) {
radioInitSetBootStage(RADIO_BOOT_STAGE_RTC_BEGIN_ENTERED);

#if !defined(DISABLE_DS3231_PROBE)
if (i2c_probe(wire, DS3231_ADDRESS)) {
ds3231_success = rtc_3231.begin(&wire);
Expand All @@ -51,6 +54,8 @@ void AutoDiscoverRTCClock::begin(TwoWire& wire) {
rtc_8130_success = true;
MESH_DEBUG_PRINTLN("RX8130CE: Initialized");
}

radioInitSetBootStage(RADIO_BOOT_STAGE_RTC_BEGIN_RETURNED);
}

uint32_t AutoDiscoverRTCClock::getCurrentTime() {
Expand Down
19 changes: 19 additions & 0 deletions src/helpers/CommonCLI.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
#include "TxtDataHelpers.h"
#include "AdvertDataHelpers.h"
#include "TxtDataHelpers.h"
#include "RadioInitDiagnostics.h"
#include <RTClib.h>

#ifndef BRIDGE_MAX_BAUD
Expand Down Expand Up @@ -926,6 +927,24 @@ void CommonCLI::handleGetCmd(uint32_t sender_timestamp, char* command, char* rep
#else
strcpy(reply, "ERROR: Power management not supported");
#endif
} else if (memcmp(config, "radio.init_status", 17) == 0) {
sprintf(reply, "> %d", (int)g_last_radio_init_status);
} else if (memcmp(config, "radio.init_attempts", 19) == 0) {
sprintf(reply, "> %u", (unsigned)g_radio_init_attempts);
} else if (memcmp(config, "diag.boot", 9) == 0) {
RadioInitBootRecord cur = radioInitCurrentBootRecord();
RadioInitBootRecord prev = radioInitPreviousBootRecord();
sprintf(reply, "> cur_rr=0x%08lX cur_sd=0x%02X cur_st=0x%02X cur_radio=%d cur_att=%u prev_rr=0x%08lX prev_sd=0x%02X prev_st=0x%02X prev_radio=%d prev_att=%u",
(unsigned long)cur.reset_reason,
(unsigned)cur.shutdown_reason,
(unsigned)cur.boot_stage,
(int)cur.radio_status,
(unsigned)cur.attempts,
(unsigned long)prev.reset_reason,
(unsigned)prev.shutdown_reason,
(unsigned)prev.boot_stage,
(int)prev.radio_status,
(unsigned)prev.attempts);
} else {
sprintf(reply, "??: %s", config);
}
Expand Down
3 changes: 3 additions & 0 deletions src/helpers/NRF52Board.cpp
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
#if defined(NRF52_PLATFORM)
#include "NRF52Board.h"
#include "RadioInitDiagnostics.h"

#include <bluefruit.h>
#include <nrf_soc.h>
Expand Down Expand Up @@ -41,6 +42,7 @@ void NRF52Board::initPowerMgr() {
// Copy early-captured register values
reset_reason = g_nrf52_reset_reason;
shutdown_reason = g_nrf52_shutdown_reason;
radioInitCaptureBoot(reset_reason, shutdown_reason);
boot_voltage_mv = 0; // Will be set by checkBootVoltage()

// Clear registers for next boot
Expand Down Expand Up @@ -91,6 +93,7 @@ const char* NRF52Board::getShutdownReasonString(uint8_t reason) {
case SHUTDOWN_REASON_LOW_VOLTAGE: return "Low Voltage";
case SHUTDOWN_REASON_USER: return "User Request";
case SHUTDOWN_REASON_BOOT_PROTECT: return "Boot Protection";
case SHUTDOWN_REASON_RADIO_INIT_FAIL: return "Radio Init Fail";
}
return "Unknown";
}
Expand Down
3 changes: 2 additions & 1 deletion src/helpers/NRF52Board.h
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
#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
#define SHUTDOWN_REASON_RADIO_INIT_FAIL 0x52 // 'R' - Radio init failed before startup completed

// Boards provide this struct with their hardware-specific settings and callbacks.
struct PowerMgtConfig {
Expand Down Expand Up @@ -76,4 +77,4 @@ class NRF52BoardDCDC : virtual public NRF52Board {
NRF52BoardDCDC() {}
virtual void begin() override;
};
#endif
#endif
Loading