fix: prevent idle freeze by hardening NTP and clock power management

BootNtpSync now acquires a HalPowerManager::Lock for the entire WiFi/NTP
task lifecycle, keeping the CPU at full speed during scan, connect, sync,
and teardown. The clock refresh logic in the main loop now explicitly
restores CPU frequency and resets the activity timer before requesting a
render, preventing display SPI operations from running at 10 MHz.

Made-with: Cursor
This commit is contained in:
cottongin
2026-03-09 05:19:48 -04:00
parent 4851016c47
commit 6cf212d12a
3 changed files with 34 additions and 0 deletions

View File

@@ -0,0 +1,28 @@
# Fix Idle Freeze: NTP Power Lock and Clock Refresh Hardening
**Date**: 2026-03-09
## Task Description
Fix device freeze during idle, where the device stops responding to button presses after idling on the Home screen and requires a hard reset. Root cause was the mod-specific clock refresh logic in the main loop triggering a display render while the CPU is at reduced frequency (10 MHz low-power mode), combined with the background BootNtpSync task running WiFi/NTP operations without holding a power lock.
## Root Cause
The freeze coincides with the clock minute boundary. The mod's clock refresh code detects the minute change and calls `activityManager.requestUpdate()`, triggering a Home screen render while the CPU is at 10 MHz. SPI display operations at reduced APB frequency can deadlock the display communication. Additionally, `BootNtpSync` runs WiFi/NTP on a background task with no power lock, risking instability during WiFi teardown when the main loop may enter low-power mode.
## Changes Made
### 1. `src/util/BootNtpSync.cpp`
- Added `#include <HalPowerManager.h>`
- Added `HalPowerManager::Lock powerLock;` at the top of `taskFunc()` to keep the CPU at full speed for the entire duration of WiFi scanning, connection, NTP sync, and teardown
### 2. `src/main.cpp` (clock refresh block, ~lines 408-428)
- In the `sawInvalidTime` branch: added `lastActivityTime = millis()` and `powerManager.setPowerSaving(false)` before calling `requestUpdate()`
- In the minute-change branch: added `lastActivityTime = millis()` and `powerManager.setPowerSaving(false)` before calling `requestUpdate()`
- This ensures the CPU is restored to 160 MHz before any render-related code executes and prevents immediate re-entry into low-power mode
## Follow-up Items
- Verify on device that the freeze no longer occurs after idling for extended periods
- Monitor heap usage to confirm the power lock doesn't introduce memory issues
- Test that NTP sync still completes successfully with the power lock in place

View File

@@ -416,9 +416,13 @@ void loop() {
if (lastRenderedMinute < 0) {
lastRenderedMinute = currentMinute;
if (sawInvalidTime) {
lastActivityTime = millis();
powerManager.setPowerSaving(false);
activityManager.requestUpdate();
}
} else if (currentMinute != lastRenderedMinute) {
lastActivityTime = millis();
powerManager.setPowerSaving(false);
activityManager.requestUpdate();
lastRenderedMinute = currentMinute;
}

View File

@@ -10,6 +10,7 @@
#include "CrossPointSettings.h"
#include "WifiCredentialStore.h"
#include <HalPowerManager.h>
#include "util/TimeSync.h"
namespace BootNtpSync {
@@ -93,6 +94,7 @@ static bool tryConnectToSavedNetwork(const TaskParams& params) {
}
static void taskFunc(void* param) {
HalPowerManager::Lock powerLock;
auto* params = static_cast<TaskParams*>(param);
bool connected = tryConnectToSavedNetwork(*params);