feat: add silent NTP time sync on boot via saved WiFi credentials

New "Auto Sync on Boot" toggle in Clock Settings. When enabled, a
background FreeRTOS task scans for saved WiFi networks at boot,
connects, syncs time via NTP, then tears down WiFi — all without
blocking boot or requiring user interaction. If no saved network is
found after two scan attempts (with a 3-second retry gap), it bails
silently.

Conflict guards (BootNtpSync::cancel()) added to all WiFi-using
activities so the background task cleans up before any user-initiated
WiFi flow. Also fixes clock not appearing in the header until a button
press by detecting the invalid→valid time transition after NTP sync.

Made-with: Cursor
This commit is contained in:
cottongin
2026-02-26 18:21:13 -05:00
parent 2eae521b6a
commit 19b6ad047b
22 changed files with 227 additions and 2 deletions

View File

@@ -13,6 +13,7 @@
#include "MappedInputManager.h"
#include "NetworkModeSelectionActivity.h"
#include "WifiSelectionActivity.h"
#include "util/BootNtpSync.h"
#include "activities/network/CalibreConnectActivity.h"
#include "components/UITheme.h"
#include "fontIds.h"
@@ -35,6 +36,8 @@ constexpr uint16_t DNS_PORT = 53;
void CrossPointWebServerActivity::onEnter() {
ActivityWithSubactivity::onEnter();
BootNtpSync::cancel();
LOG_DBG("WEBACT", "Free heap at onEnter: %d bytes", ESP.getFreeHeap());
// Reset state

View File

@@ -12,11 +12,14 @@
#include "activities/util/KeyboardEntryActivity.h"
#include "components/UITheme.h"
#include "fontIds.h"
#include "util/BootNtpSync.h"
#include "util/TimeSync.h"
void WifiSelectionActivity::onEnter() {
Activity::onEnter();
BootNtpSync::cancel();
// Load saved WiFi credentials - SD card operations need lock as we use SPI
// for both
{

View File

@@ -11,6 +11,7 @@
#include "activities/network/WifiSelectionActivity.h"
#include "components/UITheme.h"
#include "fontIds.h"
#include "util/BootNtpSync.h"
#include "util/TimeSync.h"
void KOReaderSyncActivity::onWifiSelectionComplete(const bool success) {
@@ -155,6 +156,8 @@ void KOReaderSyncActivity::performUpload() {
void KOReaderSyncActivity::onEnter() {
ActivityWithSubactivity::onEnter();
BootNtpSync::cancel();
// Check for credentials first
if (!KOREADER_STORE.hasCredentials()) {
state = NO_CREDENTIALS;

View File

@@ -8,6 +8,7 @@
#include "KOReaderSyncClient.h"
#include "MappedInputManager.h"
#include "activities/network/WifiSelectionActivity.h"
#include "util/BootNtpSync.h"
#include "components/UITheme.h"
#include "fontIds.h"
@@ -53,6 +54,8 @@ void KOReaderAuthActivity::performAuthentication() {
void KOReaderAuthActivity::onEnter() {
ActivityWithSubactivity::onEnter();
BootNtpSync::cancel();
// Turn on WiFi
WiFi.mode(WIFI_STA);

View File

@@ -10,6 +10,7 @@
#include "activities/network/WifiSelectionActivity.h"
#include "components/UITheme.h"
#include "fontIds.h"
#include "util/BootNtpSync.h"
#include "util/TimeSync.h"
static constexpr unsigned long AUTO_DISMISS_MS = 5000;
@@ -52,6 +53,7 @@ void NtpSyncActivity::onWifiSelectionComplete(const bool success) {
void NtpSyncActivity::onEnter() {
ActivityWithSubactivity::onEnter();
BootNtpSync::cancel();
LOG_DBG("NTP", "Turning on WiFi...");
WiFi.mode(WIFI_STA);

View File

@@ -9,6 +9,7 @@
#include "components/UITheme.h"
#include "fontIds.h"
#include "network/OtaUpdater.h"
#include "util/BootNtpSync.h"
void OtaUpdateActivity::onWifiSelectionComplete(const bool success) {
exitActivity();
@@ -58,6 +59,8 @@ void OtaUpdateActivity::onWifiSelectionComplete(const bool success) {
void OtaUpdateActivity::onEnter() {
ActivityWithSubactivity::onEnter();
BootNtpSync::cancel();
// Turn on WiFi immediately
LOG_DBG("OTA", "Turning on WiFi...");
WiFi.mode(WIFI_STA);