Skip to content

fix(nrf52): keep BLE active during reset cleanup - #10873

Merged
thebentern merged 1 commit into
meshtastic:developfrom
RCGV1:codex/issue-10851-nrf52-reset-fix
Jul 3, 2026
Merged

fix(nrf52): keep BLE active during reset cleanup#10873
thebentern merged 1 commit into
meshtastic:developfrom
RCGV1:codex/issue-10851-nrf52-reset-fix

Conversation

@RCGV1

@RCGV1 RCGV1 commented Jul 3, 2026

Copy link
Copy Markdown
Member

Summary

Fixes #10851.

nRF52 factory/config reset paths were stopping Bluetooth before running reset cleanup. On current firmware that cleanup can perform flash-backed work before scheduling reboot, including the nRF52840 warm-node raw-flash erase added in #10705. Keeping BLE/SoftDevice active until after the reset cleanup lets those flash operations complete normally, then advertising is stopped immediately before the scheduled reboot.

This moves disableBluetooth() after the reset cleanup for:

  • admin factory config reset / full factory reset / NodeDB reset
  • input-broker full factory reset
  • screen-menu NodeDB reset

Hardware repro / verification

Bench target: Muzi Base nRF52840 on /dev/cu.usbmodem31201, MUZI_BASE, firmware 2.8.0.84b1af0.

Before fix, on upstream develop:

  • bluetooth.enabled=false followed by admin reboot: PASS, metadata returned immediately.
  • meshtastic --factory-reset: FAIL, serial API timed out repeatedly for more than two minutes. USB stayed enumerated, but Meshtastic metadata never returned until bootloader/PlatformIO recovery.
  • Checked the issue's proposed reset cause: Power::reboot() uses NVIC_SystemReset() in v2.7.22.96dd647, v2.7.26.54e0d8d, and current develop; no sd_nvic_SystemReset() change exists in that path.

After fix, final artifact flashed to the same Muzi Base:

  • meshtastic --factory-reset: PASS. Poll 1 saw expected USB disconnect during reboot; poll 2 returned metadata successfully with no recovery.
  • Restored bench config afterward: bluetooth.enabled=True, lora.region=13, metadata healthy.

Tests

  • trunk fmt src/modules/AdminModule.cpp src/modules/SystemCommandsModule.cpp src/graphics/draw/MenuHandler.cpp
  • git diff --check
  • pio run -e muzi-base
  • pio run -e muzi-base -t upload --upload-port /dev/cu.usbmodem31201
  • Hardware repro above on Muzi Base nRF52840

Native test note:

  • ./bin/run-tests.sh does not run on this macOS checkout because it uses GNU/bash features not available here (mapfile, find -printf).
  • Raw fallback python -m platformio test -e native fails before tests on existing unrelated native compile errors in src/graphics/draw/NotificationRenderer.cpp variable-length array initializers and Linux input headers.

Summary by CodeRabbit

  • Bug Fixes
    • Improved the order of reset actions so Bluetooth is turned off only after reset steps begin or complete, helping reset flows finish more reliably.
    • Updated factory and node database reset behavior to better preserve the correct screen state during confirmation and reset actions.
    • Refined admin and system reset handling to keep reset messages and actions in a clearer, more consistent order.

@github-actions

github-actions Bot commented Jul 3, 2026

Copy link
Copy Markdown
Contributor

⚡ Try this PR in the Web Flasher

Note

Building this pull request… the flash button, badges and supported-board
list will appear here automatically once CI finishes.

@github-actions github-actions Bot added the bugfix Pull request that fixes bugs label Jul 3, 2026
@coderabbitai

coderabbitai Bot commented Jul 3, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: aca24562-7ee5-472c-934d-39e594703dd4

📥 Commits

Reviewing files that changed from the base of the PR and between 84b1af0 and a16434b.

📒 Files selected for processing (3)
  • src/graphics/draw/MenuHandler.cpp
  • src/modules/AdminModule.cpp
  • src/modules/SystemCommandsModule.cpp

📝 Walkthrough

Walkthrough

Three source files reorder operations so nodeDB->factoryReset() or nodeDB->resetNodes() execute before disableBluetooth() is called, reversing the previous sequence in MenuHandler.cpp, AdminModule.cpp, and SystemCommandsModule.cpp reset/factory-reset handling paths.

Changes

Reset/Bluetooth Ordering Fix

Layer / File(s) Summary
Menu-triggered reset ordering
src/graphics/draw/MenuHandler.cpp
resetNodeDBMenu() sets frame focus before reset branches and moves disableBluetooth() into each branch after the corresponding resetNodes() call.
AdminModule factory/nodedb reset ordering
src/modules/AdminModule.cpp
handleReceivedProtobuf reorders factory_reset_config_tag and nodedb_reset_tag handling so factoryReset()/resetNodes() and logging occur before disableBluetooth().
SystemCommandsModule factory reset ordering
src/modules/SystemCommandsModule.cpp
handleInputEvent reorders full factory reset handling so logging and factoryReset(true) run before disableBluetooth().

Estimated code review effort: 2 (Simple) | ~10 minutes

Sequence Diagram(s)

sequenceDiagram
  participant User
  participant Module as AdminModule / MenuHandler / SystemCommandsModule
  participant NodeDB
  participant Bluetooth

  User->>Module: Trigger factory/node reset
  Module->>NodeDB: factoryReset() / resetNodes()
  NodeDB-->>Module: Reset complete
  Module->>Bluetooth: disableBluetooth()
  Module->>Module: Schedule reboot
Loading

Poem

A hop, a reset, a job well done,
Bluetooth waits till the reset's won! 🐇
No more freezing in the dark,
Reboot lands right on the mark.
Carrots for order, done just right! 🥕✨

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly summarizes the main change: delaying BLE shutdown during nRF52 reset cleanup.
Description check ✅ Passed The description is mostly complete with a summary, verification, and tests, though it omits the template's attestation checkboxes.
Linked Issues check ✅ Passed The changes address #10851 by keeping Bluetooth active until reset cleanup finishes, matching the reported freeze scenario.
Out of Scope Changes check ✅ Passed All edits stay within reset/BLE ordering logic for the affected modules and do not introduce unrelated changes.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.

Comment @coderabbitai help to get the list of available commands.

@thebentern
thebentern merged commit 6085a4f into meshtastic:develop Jul 3, 2026
84 of 85 checks passed
@github-actions

github-actions Bot commented Jul 4, 2026

Copy link
Copy Markdown
Contributor

Firmware Size Report

22 targets | vs develop: 21 increased, 1 decreased, net +363,748 (+355.2 KB)

Target Size vs develop
picow 1,271,936 📈 +27,056 (+26.4 KB)
pico2w 1,246,620 📈 +26,104 (+25.5 KB)
rak11310 831,224 📈 +25,800 (+25.2 KB)
pico 808,472 📈 +25,784 (+25.2 KB)
seeed_xiao_rp2040 806,688 📈 +25,784 (+25.2 KB)
Show 17 more target(s)
Target Size vs develop
pico2 794,744 📈 +24,968 (+24.4 KB)
seeed_xiao_rp2350 792,888 📈 +24,952 (+24.4 KB)
t-deck-tft 3,829,872 📈 +19,200 (+18.8 KB)
heltec-vision-master-e213-inkhud 2,245,136 📈 +17,776 (+17.4 KB)
elecrow-adv-35-tft 3,433,344 📈 +16,720 (+16.3 KB)
rak11200 1,875,728 📈 +15,120 (+14.8 KB)
seeed-xiao-s3 2,290,192 📈 +14,048 (+13.7 KB)
tlora-c6 2,382,288 📈 +13,984 (+13.7 KB)
heltec-v3 2,277,472 📈 +13,392 (+13.1 KB)
heltec-ht62-esp32c3-sx1262 2,148,256 📈 +13,264 (+13.0 KB)
rak3312 2,285,408 📈 +13,168 (+12.9 KB)
station-g2 2,278,832 📈 +12,480 (+12.2 KB)
station-g3 2,278,832 📈 +12,480 (+12.2 KB)
t-eth-elite 2,503,872 📈 +12,384 (+12.1 KB)
heltec-v4 2,289,568 📈 +12,272 (+12.0 KB)
rak3172 182,492 📉 -3,804 (-3.7 KB)
wio-e5 239,444 📈 +816

Updated for fa3b1a1

jeremiah-k added a commit to jeremiah-k/meshtastic-firmware that referenced this pull request Aug 31, 2026
Sending AdminMessage.factory_reset_device to a TBeam over USB serial ends
in 'Guru Meditation Error: Core 0 panic'ed (LoadProhibited)' inside
npl_freertos_eventq_remove, the NimBLE FreeRTOS port event-queue layer,
followed by a slow crash-reboot. Defaults do get applied, but the panic
beats the admin ACK and wedges clients.

Root cause: the reset ordering ran the destructive work while the BLE
stack was still live. nodeDB->factoryReset(true) calls nvs_flash_erase()
(NodeDB.cpp:874) while the NimBLE host task, its host-timer callout and
the NVS-backed bond store (ble_store_config, the 'nimble_bond' namespace;
see NimbleBluetooth::purgeIncompatibleBleBonds) are all running. Only then
does disableBluetooth() tear the stack down (NimbleBluetooth::deinit ->
BLEDevice::deinit -> nimble_port_stop/nimble_port_deinit), and the
teardown dereferences host state invalidated by the erase under it,
panicking in npl_freertos_eventq_remove. meshtastic#10873 moved disableBluetooth()
after the reset cleanup for nRF52's sake (the nRF52840 warm-node raw-
flash erase needs the SoftDevice alive); on ESP32 that ordering is
exactly backwards.

Fix: on ESP32 call disableBluetooth() before nodeDB->factoryReset() /
factoryReset(true) so nvs_flash_erase() runs against a quiesced stack;
nRF52 keeps the meshtastic#10873 ordering (BLE stays active during its flash
cleanup and is disabled after). disableBluetooth() self-guards on
HAS_BLUETOOTH, so BLE-less ESP32 builds are unaffected. Applied to the
admin factory_reset_config and factory_reset_device handlers and to the
INPUT_BROKER_FACTORY_RST path in SystemCommandsModule, which shares the
same nvs_flash_erase hazard. MenuHandler's node-db reset only clears the
in-RAM/file node list and keeps the safe post-reset order.

Build check: pio run -e tbeam SUCCESS.
jk-aibot pushed a commit to jk-aibot/firmware that referenced this pull request Sep 1, 2026
Sending AdminMessage.factory_reset_device to a TBeam over USB serial ends
in 'Guru Meditation Error: Core 0 panic'ed (LoadProhibited)' inside
npl_freertos_eventq_remove, the NimBLE FreeRTOS port event-queue layer,
followed by a slow crash-reboot. Defaults do get applied, but the panic
beats the admin ACK and wedges clients.

Root cause: the reset ordering ran the destructive work while the BLE
stack was still live. nodeDB->factoryReset(true) calls nvs_flash_erase()
(NodeDB.cpp:874) while the NimBLE host task, its host-timer callout and
the NVS-backed bond store (ble_store_config, the 'nimble_bond' namespace;
see NimbleBluetooth::purgeIncompatibleBleBonds) are all running. Only then
does disableBluetooth() tear the stack down (NimbleBluetooth::deinit ->
BLEDevice::deinit -> nimble_port_stop/nimble_port_deinit), and the
teardown dereferences host state invalidated by the erase under it,
panicking in npl_freertos_eventq_remove. meshtastic#10873 moved disableBluetooth()
after the reset cleanup for nRF52's sake (the nRF52840 warm-node raw-
flash erase needs the SoftDevice alive); on ESP32 that ordering is
exactly backwards.

Fix: on ESP32 call disableBluetooth() before nodeDB->factoryReset() /
factoryReset(true) so nvs_flash_erase() runs against a quiesced stack;
nRF52 keeps the meshtastic#10873 ordering (BLE stays active during its flash
cleanup and is disabled after). disableBluetooth() self-guards on
HAS_BLUETOOTH, so BLE-less ESP32 builds are unaffected. Applied to the
admin factory_reset_config and factory_reset_device handlers and to the
INPUT_BROKER_FACTORY_RST path in SystemCommandsModule, which shares the
same nvs_flash_erase hazard. MenuHandler's node-db reset only clears the
in-RAM/file node list and keeps the safe post-reset order.

Build check: pio run -e tbeam SUCCESS.
jk-aibot pushed a commit to jk-aibot/firmware that referenced this pull request Sep 3, 2026
Sending AdminMessage.factory_reset_device to a TBeam over USB serial ends
in 'Guru Meditation Error: Core 0 panic'ed (LoadProhibited)' inside
npl_freertos_eventq_remove, the NimBLE FreeRTOS port event-queue layer,
followed by a slow crash-reboot. Defaults do get applied, but the panic
beats the admin ACK and wedges clients.

Root cause: the reset ordering ran the destructive work while the BLE
stack was still live. nodeDB->factoryReset(true) calls nvs_flash_erase()
(NodeDB.cpp:874) while the NimBLE host task, its host-timer callout and
the NVS-backed bond store (ble_store_config, the 'nimble_bond' namespace;
see NimbleBluetooth::purgeIncompatibleBleBonds) are all running. Only then
does disableBluetooth() tear the stack down (NimbleBluetooth::deinit ->
BLEDevice::deinit -> nimble_port_stop/nimble_port_deinit), and the
teardown dereferences host state invalidated by the erase under it,
panicking in npl_freertos_eventq_remove. meshtastic#10873 moved disableBluetooth()
after the reset cleanup for nRF52's sake (the nRF52840 warm-node raw-
flash erase needs the SoftDevice alive); on ESP32 that ordering is
exactly backwards.

Fix: on ESP32 call disableBluetooth() before nodeDB->factoryReset() /
factoryReset(true) so nvs_flash_erase() runs against a quiesced stack;
nRF52 keeps the meshtastic#10873 ordering (BLE stays active during its flash
cleanup and is disabled after). disableBluetooth() self-guards on
HAS_BLUETOOTH, so BLE-less ESP32 builds are unaffected. Applied to the
admin factory_reset_config and factory_reset_device handlers and to the
INPUT_BROKER_FACTORY_RST path in SystemCommandsModule, which shares the
same nvs_flash_erase hazard. MenuHandler's node-db reset only clears the
in-RAM/file node list and keeps the safe post-reset order.

Build check: pio run -e tbeam SUCCESS.
jk-aibot pushed a commit to jk-aibot/firmware that referenced this pull request Sep 3, 2026
Sending AdminMessage.factory_reset_device to a TBeam over USB serial ends
in 'Guru Meditation Error: Core 0 panic'ed (LoadProhibited)' inside
npl_freertos_eventq_remove, the NimBLE FreeRTOS port event-queue layer,
followed by a slow crash-reboot. Defaults do get applied, but the panic
beats the admin ACK and wedges clients.

Root cause: the reset ordering ran the destructive work while the BLE
stack was still live. nodeDB->factoryReset(true) calls nvs_flash_erase()
(NodeDB.cpp:874) while the NimBLE host task, its host-timer callout and
the NVS-backed bond store (ble_store_config, the 'nimble_bond' namespace;
see NimbleBluetooth::purgeIncompatibleBleBonds) are all running. Only then
does disableBluetooth() tear the stack down (NimbleBluetooth::deinit ->
BLEDevice::deinit -> nimble_port_stop/nimble_port_deinit), and the
teardown dereferences host state invalidated by the erase under it,
panicking in npl_freertos_eventq_remove. meshtastic#10873 moved disableBluetooth()
after the reset cleanup for nRF52's sake (the nRF52840 warm-node raw-
flash erase needs the SoftDevice alive); on ESP32 that ordering is
exactly backwards.

Fix: on ESP32 call disableBluetooth() before nodeDB->factoryReset() /
factoryReset(true) so nvs_flash_erase() runs against a quiesced stack;
nRF52 keeps the meshtastic#10873 ordering (BLE stays active during its flash
cleanup and is disabled after). disableBluetooth() self-guards on
HAS_BLUETOOTH, so BLE-less ESP32 builds are unaffected. Applied to the
admin factory_reset_config and factory_reset_device handlers and to the
INPUT_BROKER_FACTORY_RST path in SystemCommandsModule, which shares the
same nvs_flash_erase hazard. MenuHandler's node-db reset only clears the
in-RAM/file node list and keeps the safe post-reset order.

Build check: pio run -e tbeam SUCCESS.
Itzdavid01 pushed a commit to Itzdavid01/firmware that referenced this pull request Sep 5, 2026
jeremiah-k added a commit to jeremiah-k/meshtastic-firmware that referenced this pull request Sep 6, 2026
Sending AdminMessage.factory_reset_device to a TBeam over USB serial ends
in 'Guru Meditation Error: Core 0 panic'ed (LoadProhibited)' inside
npl_freertos_eventq_remove, the NimBLE FreeRTOS port event-queue layer,
followed by a slow crash-reboot. Defaults do get applied, but the panic
beats the admin ACK and wedges clients.

Root cause: the reset ordering ran the destructive work while the BLE
stack was still live. nodeDB->factoryReset(true) calls nvs_flash_erase()
(NodeDB.cpp:874) while the NimBLE host task, its host-timer callout and
the NVS-backed bond store (ble_store_config, the 'nimble_bond' namespace;
see NimbleBluetooth::purgeIncompatibleBleBonds) are all running. Only then
does disableBluetooth() tear the stack down (NimbleBluetooth::deinit ->
BLEDevice::deinit -> nimble_port_stop/nimble_port_deinit), and the
teardown dereferences host state invalidated by the erase under it,
panicking in npl_freertos_eventq_remove. meshtastic#10873 moved disableBluetooth()
after the reset cleanup for nRF52's sake (the nRF52840 warm-node raw-
flash erase needs the SoftDevice alive); on ESP32 that ordering is
exactly backwards.

Fix: on ESP32 call disableBluetooth() before nodeDB->factoryReset() /
factoryReset(true) so nvs_flash_erase() runs against a quiesced stack;
nRF52 keeps the meshtastic#10873 ordering (BLE stays active during its flash
cleanup and is disabled after). disableBluetooth() self-guards on
HAS_BLUETOOTH, so BLE-less ESP32 builds are unaffected. Applied to the
admin factory_reset_config and factory_reset_device handlers and to the
INPUT_BROKER_FACTORY_RST path in SystemCommandsModule, which shares the
same nvs_flash_erase hazard. MenuHandler's node-db reset only clears the
in-RAM/file node list and keeps the safe post-reset order.

Build check: pio run -e tbeam SUCCESS.
jeremiah-k added a commit to jeremiah-k/meshtastic-firmware that referenced this pull request Sep 6, 2026
Sending AdminMessage.factory_reset_device to a TBeam over USB serial ends
in 'Guru Meditation Error: Core 0 panic'ed (LoadProhibited)' inside
npl_freertos_eventq_remove, the NimBLE FreeRTOS port event-queue layer,
followed by a slow crash-reboot. Defaults do get applied, but the panic
beats the admin ACK and wedges clients.

Root cause: the reset ordering ran the destructive work while the BLE
stack was still live. nodeDB->factoryReset(true) calls nvs_flash_erase()
(NodeDB.cpp:874) while the NimBLE host task, its host-timer callout and
the NVS-backed bond store (ble_store_config, the 'nimble_bond' namespace;
see NimbleBluetooth::purgeIncompatibleBleBonds) are all running. Only then
does disableBluetooth() tear the stack down (NimbleBluetooth::deinit ->
BLEDevice::deinit -> nimble_port_stop/nimble_port_deinit), and the
teardown dereferences host state invalidated by the erase under it,
panicking in npl_freertos_eventq_remove. meshtastic#10873 moved disableBluetooth()
after the reset cleanup for nRF52's sake (the nRF52840 warm-node raw-
flash erase needs the SoftDevice alive); on ESP32 that ordering is
exactly backwards.

Fix: on ESP32 call disableBluetooth() before nodeDB->factoryReset() /
factoryReset(true) so nvs_flash_erase() runs against a quiesced stack;
nRF52 keeps the meshtastic#10873 ordering (BLE stays active during its flash
cleanup and is disabled after). disableBluetooth() self-guards on
HAS_BLUETOOTH, so BLE-less ESP32 builds are unaffected. Applied to the
admin factory_reset_config and factory_reset_device handlers and to the
INPUT_BROKER_FACTORY_RST path in SystemCommandsModule, which shares the
same nvs_flash_erase hazard. MenuHandler's node-db reset only clears the
in-RAM/file node list and keeps the safe post-reset order.

Build check: pio run -e tbeam SUCCESS.
jeremiah-k added a commit to jeremiah-k/meshtastic-firmware that referenced this pull request Sep 6, 2026
Sending AdminMessage.factory_reset_device to a TBeam over USB serial ends
in 'Guru Meditation Error: Core 0 panic'ed (LoadProhibited)' inside
npl_freertos_eventq_remove, the NimBLE FreeRTOS port event-queue layer,
followed by a slow crash-reboot. Defaults do get applied, but the panic
beats the admin ACK and wedges clients.

Root cause: the reset ordering ran the destructive work while the BLE
stack was still live. nodeDB->factoryReset(true) calls nvs_flash_erase()
(NodeDB.cpp:874) while the NimBLE host task, its host-timer callout and
the NVS-backed bond store (ble_store_config, the 'nimble_bond' namespace;
see NimbleBluetooth::purgeIncompatibleBleBonds) are all running. Only then
does disableBluetooth() tear the stack down (NimbleBluetooth::deinit ->
BLEDevice::deinit -> nimble_port_stop/nimble_port_deinit), and the
teardown dereferences host state invalidated by the erase under it,
panicking in npl_freertos_eventq_remove. meshtastic#10873 moved disableBluetooth()
after the reset cleanup for nRF52's sake (the nRF52840 warm-node raw-
flash erase needs the SoftDevice alive); on ESP32 that ordering is
exactly backwards.

Fix: on ESP32 call disableBluetooth() before nodeDB->factoryReset() /
factoryReset(true) so nvs_flash_erase() runs against a quiesced stack;
nRF52 keeps the meshtastic#10873 ordering (BLE stays active during its flash
cleanup and is disabled after). disableBluetooth() self-guards on
HAS_BLUETOOTH, so BLE-less ESP32 builds are unaffected. Applied to the
admin factory_reset_config and factory_reset_device handlers and to the
INPUT_BROKER_FACTORY_RST path in SystemCommandsModule, which shares the
same nvs_flash_erase hazard. MenuHandler's node-db reset only clears the
in-RAM/file node list and keeps the safe post-reset order.

Build check: pio run -e tbeam SUCCESS.
jeremiah-k added a commit to jeremiah-k/meshtastic-firmware that referenced this pull request Sep 6, 2026
Sending AdminMessage.factory_reset_device to a TBeam over USB serial ends
in 'Guru Meditation Error: Core 0 panic'ed (LoadProhibited)' inside
npl_freertos_eventq_remove, the NimBLE FreeRTOS port event-queue layer,
followed by a slow crash-reboot. Defaults do get applied, but the panic
beats the admin ACK and wedges clients.

Root cause: the reset ordering ran the destructive work while the BLE
stack was still live. nodeDB->factoryReset(true) calls nvs_flash_erase()
(NodeDB.cpp:874) while the NimBLE host task, its host-timer callout and
the NVS-backed bond store (ble_store_config, the 'nimble_bond' namespace;
see NimbleBluetooth::purgeIncompatibleBleBonds) are all running. Only then
does disableBluetooth() tear the stack down (NimbleBluetooth::deinit ->
BLEDevice::deinit -> nimble_port_stop/nimble_port_deinit), and the
teardown dereferences host state invalidated by the erase under it,
panicking in npl_freertos_eventq_remove. meshtastic#10873 moved disableBluetooth()
after the reset cleanup for nRF52's sake (the nRF52840 warm-node raw-
flash erase needs the SoftDevice alive); on ESP32 that ordering is
exactly backwards.

Fix: on ESP32 call disableBluetooth() before nodeDB->factoryReset() /
factoryReset(true) so nvs_flash_erase() runs against a quiesced stack;
nRF52 keeps the meshtastic#10873 ordering (BLE stays active during its flash
cleanup and is disabled after). disableBluetooth() self-guards on
HAS_BLUETOOTH, so BLE-less ESP32 builds are unaffected. Applied to the
admin factory_reset_config and factory_reset_device handlers and to the
INPUT_BROKER_FACTORY_RST path in SystemCommandsModule, which shares the
same nvs_flash_erase hazard. MenuHandler's node-db reset only clears the
in-RAM/file node list and keeps the safe post-reset order.

Build check: pio run -e tbeam SUCCESS.
jeremiah-k added a commit to jeremiah-k/meshtastic-firmware that referenced this pull request Sep 6, 2026
Sending AdminMessage.factory_reset_device to a TBeam over USB serial ends
in 'Guru Meditation Error: Core 0 panic'ed (LoadProhibited)' inside
npl_freertos_eventq_remove, the NimBLE FreeRTOS port event-queue layer,
followed by a slow crash-reboot. Defaults do get applied, but the panic
beats the admin ACK and wedges clients.

Root cause: the reset ordering ran the destructive work while the BLE
stack was still live. nodeDB->factoryReset(true) calls nvs_flash_erase()
(NodeDB.cpp:874) while the NimBLE host task, its host-timer callout and
the NVS-backed bond store (ble_store_config, the 'nimble_bond' namespace;
see NimbleBluetooth::purgeIncompatibleBleBonds) are all running. Only then
does disableBluetooth() tear the stack down (NimbleBluetooth::deinit ->
BLEDevice::deinit -> nimble_port_stop/nimble_port_deinit), and the
teardown dereferences host state invalidated by the erase under it,
panicking in npl_freertos_eventq_remove. meshtastic#10873 moved disableBluetooth()
after the reset cleanup for nRF52's sake (the nRF52840 warm-node raw-
flash erase needs the SoftDevice alive); on ESP32 that ordering is
exactly backwards.

Fix: on ESP32 call disableBluetooth() before nodeDB->factoryReset() /
factoryReset(true) so nvs_flash_erase() runs against a quiesced stack;
nRF52 keeps the meshtastic#10873 ordering (BLE stays active during its flash
cleanup and is disabled after). disableBluetooth() self-guards on
HAS_BLUETOOTH, so BLE-less ESP32 builds are unaffected. Applied to the
admin factory_reset_config and factory_reset_device handlers and to the
INPUT_BROKER_FACTORY_RST path in SystemCommandsModule, which shares the
same nvs_flash_erase hazard. MenuHandler's node-db reset only clears the
in-RAM/file node list and keeps the safe post-reset order.

Build check: pio run -e tbeam SUCCESS.
jeremiah-k added a commit to jeremiah-k/meshtastic-firmware that referenced this pull request Sep 6, 2026
Sending AdminMessage.factory_reset_device to a TBeam over USB serial ends
in 'Guru Meditation Error: Core 0 panic'ed (LoadProhibited)' inside
npl_freertos_eventq_remove, the NimBLE FreeRTOS port event-queue layer,
followed by a slow crash-reboot. Defaults do get applied, but the panic
beats the admin ACK and wedges clients.

Root cause: the reset ordering ran the destructive work while the BLE
stack was still live. nodeDB->factoryReset(true) calls nvs_flash_erase()
(NodeDB.cpp:874) while the NimBLE host task, its host-timer callout and
the NVS-backed bond store (ble_store_config, the 'nimble_bond' namespace;
see NimbleBluetooth::purgeIncompatibleBleBonds) are all running. Only then
does disableBluetooth() tear the stack down (NimbleBluetooth::deinit ->
BLEDevice::deinit -> nimble_port_stop/nimble_port_deinit), and the
teardown dereferences host state invalidated by the erase under it,
panicking in npl_freertos_eventq_remove. meshtastic#10873 moved disableBluetooth()
after the reset cleanup for nRF52's sake (the nRF52840 warm-node raw-
flash erase needs the SoftDevice alive); on ESP32 that ordering is
exactly backwards.

Fix: on ESP32 call disableBluetooth() before nodeDB->factoryReset() /
factoryReset(true) so nvs_flash_erase() runs against a quiesced stack;
nRF52 keeps the meshtastic#10873 ordering (BLE stays active during its flash
cleanup and is disabled after). disableBluetooth() self-guards on
HAS_BLUETOOTH, so BLE-less ESP32 builds are unaffected. Applied to the
admin factory_reset_config and factory_reset_device handlers and to the
INPUT_BROKER_FACTORY_RST path in SystemCommandsModule, which shares the
same nvs_flash_erase hazard. MenuHandler's node-db reset only clears the
in-RAM/file node list and keeps the safe post-reset order.

Build check: pio run -e tbeam SUCCESS.
jeremiah-k added a commit to jeremiah-k/meshtastic-firmware that referenced this pull request Sep 6, 2026
Sending AdminMessage.factory_reset_device to a TBeam over USB serial ends
in 'Guru Meditation Error: Core 0 panic'ed (LoadProhibited)' inside
npl_freertos_eventq_remove, the NimBLE FreeRTOS port event-queue layer,
followed by a slow crash-reboot. Defaults do get applied, but the panic
beats the admin ACK and wedges clients.

Root cause: the reset ordering ran the destructive work while the BLE
stack was still live. nodeDB->factoryReset(true) calls nvs_flash_erase()
(NodeDB.cpp:874) while the NimBLE host task, its host-timer callout and
the NVS-backed bond store (ble_store_config, the 'nimble_bond' namespace;
see NimbleBluetooth::purgeIncompatibleBleBonds) are all running. Only then
does disableBluetooth() tear the stack down (NimbleBluetooth::deinit ->
BLEDevice::deinit -> nimble_port_stop/nimble_port_deinit), and the
teardown dereferences host state invalidated by the erase under it,
panicking in npl_freertos_eventq_remove. meshtastic#10873 moved disableBluetooth()
after the reset cleanup for nRF52's sake (the nRF52840 warm-node raw-
flash erase needs the SoftDevice alive); on ESP32 that ordering is
exactly backwards.

Fix: on ESP32 call disableBluetooth() before nodeDB->factoryReset() /
factoryReset(true) so nvs_flash_erase() runs against a quiesced stack;
nRF52 keeps the meshtastic#10873 ordering (BLE stays active during its flash
cleanup and is disabled after). disableBluetooth() self-guards on
HAS_BLUETOOTH, so BLE-less ESP32 builds are unaffected. Applied to the
admin factory_reset_config and factory_reset_device handlers and to the
INPUT_BROKER_FACTORY_RST path in SystemCommandsModule, which shares the
same nvs_flash_erase hazard. MenuHandler's node-db reset only clears the
in-RAM/file node list and keeps the safe post-reset order.

Build check: pio run -e tbeam SUCCESS.
jeremiah-k added a commit to jeremiah-k/meshtastic-firmware that referenced this pull request Sep 6, 2026
Sending AdminMessage.factory_reset_device to a TBeam over USB serial ends
in 'Guru Meditation Error: Core 0 panic'ed (LoadProhibited)' inside
npl_freertos_eventq_remove, the NimBLE FreeRTOS port event-queue layer,
followed by a slow crash-reboot. Defaults do get applied, but the panic
beats the admin ACK and wedges clients.

Root cause: the reset ordering ran the destructive work while the BLE
stack was still live. nodeDB->factoryReset(true) calls nvs_flash_erase()
(NodeDB.cpp:874) while the NimBLE host task, its host-timer callout and
the NVS-backed bond store (ble_store_config, the 'nimble_bond' namespace;
see NimbleBluetooth::purgeIncompatibleBleBonds) are all running. Only then
does disableBluetooth() tear the stack down (NimbleBluetooth::deinit ->
BLEDevice::deinit -> nimble_port_stop/nimble_port_deinit), and the
teardown dereferences host state invalidated by the erase under it,
panicking in npl_freertos_eventq_remove. meshtastic#10873 moved disableBluetooth()
after the reset cleanup for nRF52's sake (the nRF52840 warm-node raw-
flash erase needs the SoftDevice alive); on ESP32 that ordering is
exactly backwards.

Fix: on ESP32 call disableBluetooth() before nodeDB->factoryReset() /
factoryReset(true) so nvs_flash_erase() runs against a quiesced stack;
nRF52 keeps the meshtastic#10873 ordering (BLE stays active during its flash
cleanup and is disabled after). disableBluetooth() self-guards on
HAS_BLUETOOTH, so BLE-less ESP32 builds are unaffected. Applied to the
admin factory_reset_config and factory_reset_device handlers and to the
INPUT_BROKER_FACTORY_RST path in SystemCommandsModule, which shares the
same nvs_flash_erase hazard. MenuHandler's node-db reset only clears the
in-RAM/file node list and keeps the safe post-reset order.

Build check: pio run -e tbeam SUCCESS.
jeremiah-k added a commit to jeremiah-k/meshtastic-firmware that referenced this pull request Sep 6, 2026
Sending AdminMessage.factory_reset_device to a TBeam over USB serial ends
in 'Guru Meditation Error: Core 0 panic'ed (LoadProhibited)' inside
npl_freertos_eventq_remove, the NimBLE FreeRTOS port event-queue layer,
followed by a slow crash-reboot. Defaults do get applied, but the panic
beats the admin ACK and wedges clients.

Root cause: the reset ordering ran the destructive work while the BLE
stack was still live. nodeDB->factoryReset(true) calls nvs_flash_erase()
(NodeDB.cpp:874) while the NimBLE host task, its host-timer callout and
the NVS-backed bond store (ble_store_config, the 'nimble_bond' namespace;
see NimbleBluetooth::purgeIncompatibleBleBonds) are all running. Only then
does disableBluetooth() tear the stack down (NimbleBluetooth::deinit ->
BLEDevice::deinit -> nimble_port_stop/nimble_port_deinit), and the
teardown dereferences host state invalidated by the erase under it,
panicking in npl_freertos_eventq_remove. meshtastic#10873 moved disableBluetooth()
after the reset cleanup for nRF52's sake (the nRF52840 warm-node raw-
flash erase needs the SoftDevice alive); on ESP32 that ordering is
exactly backwards.

Fix: on ESP32 call disableBluetooth() before nodeDB->factoryReset() /
factoryReset(true) so nvs_flash_erase() runs against a quiesced stack;
nRF52 keeps the meshtastic#10873 ordering (BLE stays active during its flash
cleanup and is disabled after). disableBluetooth() self-guards on
HAS_BLUETOOTH, so BLE-less ESP32 builds are unaffected. Applied to the
admin factory_reset_config and factory_reset_device handlers and to the
INPUT_BROKER_FACTORY_RST path in SystemCommandsModule, which shares the
same nvs_flash_erase hazard. MenuHandler's node-db reset only clears the
in-RAM/file node list and keeps the safe post-reset order.

Build check: pio run -e tbeam SUCCESS.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bugfix Pull request that fixes bugs

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Bug]: nRF52 device freeze/lockup after Factory Reset (SoftDevice reboot regression)

2 participants