Stability: WDT yield, BLE mutex fixes, time-based desync recovery

Reduces crash rate from every 60-85s to 1 reboot per 6+ minutes.
Zero WDT triggers in 10-minute stability test.

BLE mutex fixes (BLEInterface.cpp):
- Release _mutex before blocking GATT ops in onConnected() and
  onServicesDiscovered() — prevents 5-30s main-loop stalls during
  service discovery, notification subscribe, identity exchange
- Non-blocking try_lock() for peerCount(), getConnectedPeerSummaries(),
  get_stats() — returns empty/default if BLE task holds mutex
- Write-without-response in initiateHandshake()

WDT and persistence (main.cpp, sdkconfig.defaults, microReticulum):
- 30s WDT timeout (up from 10s) for SPIFFS flash I/O headroom
- Register Identity::set_persist_yield_callback() to feed WDT every
  5 entries during save_known_destinations() (70+ entries = 30-50s)
- WDT feeds between reticulum and identity persist calls

BLE host desync recovery (NimBLEPlatform):
- Time-based desync tracking instead of aggressive counter-based reboot
- 60s tolerance without connections, 5 minutes with active connections
  (data still flows over existing BLE mesh links)
- Remove immediate recoverBLEStack() from 574 handler and
  enterErrorRecovery() — let startScan() manage reboot decision
- Increase CONNECTION_COOLDOWN from 3s to 10s to reduce 574 risk
- Increase SCAN_FAIL_RECOVERY_THRESHOLD from 5 to 10

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
torlando-tech
2026-02-23 12:30:30 -05:00
co-authored by Claude Opus 4.6
parent 3ca27f53f6
commit e343caf2d2
7 changed files with 116 additions and 64 deletions
+14 -5
View File
@@ -1194,11 +1194,15 @@ void setup() {
INFO("╚══════════════════════════════════════╝");
INFO("");
// Subscribe main loop to Task Watchdog — detects hangs/deadlocks
// If loop() blocks for >10s (CONFIG_ESP_TASK_WDT_TIMEOUT_S), WDT fires
// with a backtrace showing exactly where the hang is
esp_task_wdt_add(NULL); // NULL = current task (loopTask)
INFO("Task Watchdog: loopTask subscribed");
// Reconfigure Task Watchdog with 30s timeout (default 10s is too tight
// for SPIFFS flash I/O — identity persistence writes 40-50 entries and
// can take 5-15s with sector erases and garbage collection)
esp_task_wdt_init(30, true); // 30s timeout, panic on trigger
esp_task_wdt_add(NULL); // Subscribe loopTask
INFO("Task Watchdog: loopTask subscribed (30s timeout)");
// Feed WDT during long Identity persistence (71+ entries to SPIFFS can take >30s)
Identity::set_persist_yield_callback([]() { esp_task_wdt_reset(); });
// Show startup message
INFO("Press any key to start messaging");
@@ -1275,10 +1279,15 @@ void loop() {
reticulum->loop();
// Periodically persist identity/transport data (display names, paths, etc.)
// NOTE: Identity persistence writes 40-50 entries to SPIFFS flash, which
// involves sector erases (100ms each) and can take 5-15s total.
// WDT feeds between calls prevent timeout during heavy flash I/O.
LOOP_STEP(5); // persist data
reticulum->should_persist_data();
esp_task_wdt_reset();
// Fast-persist known destinations (5s after dirty) to survive crashes
Identity::should_persist_data();
esp_task_wdt_reset();
// Process TCP interface
LOOP_STEP(6); // TCP loop