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:
@@ -400,6 +400,7 @@ enum class StrId : uint16_t {
|
|||||||
STR_INDEXING_STATUS_ICON,
|
STR_INDEXING_STATUS_ICON,
|
||||||
STR_SYNC_CLOCK,
|
STR_SYNC_CLOCK,
|
||||||
STR_TIME_SYNCED,
|
STR_TIME_SYNCED,
|
||||||
|
STR_AUTO_NTP_SYNC,
|
||||||
STR_MANAGE_BOOK,
|
STR_MANAGE_BOOK,
|
||||||
STR_ARCHIVE_BOOK,
|
STR_ARCHIVE_BOOK,
|
||||||
STR_UNARCHIVE_BOOK,
|
STR_UNARCHIVE_BOOK,
|
||||||
|
|||||||
@@ -343,3 +343,4 @@ STR_INDEXING_STATUS_TEXT: "Text stavového řádku"
|
|||||||
STR_INDEXING_STATUS_ICON: "Ikona stavového řádku"
|
STR_INDEXING_STATUS_ICON: "Ikona stavového řádku"
|
||||||
STR_SYNC_CLOCK: "Sync Clock"
|
STR_SYNC_CLOCK: "Sync Clock"
|
||||||
STR_TIME_SYNCED: "Time synced!"
|
STR_TIME_SYNCED: "Time synced!"
|
||||||
|
STR_AUTO_NTP_SYNC: "Auto Sync on Boot"
|
||||||
|
|||||||
@@ -364,6 +364,7 @@ STR_INDEXING_STATUS_TEXT: "Status Bar Text"
|
|||||||
STR_INDEXING_STATUS_ICON: "Status Bar Icon"
|
STR_INDEXING_STATUS_ICON: "Status Bar Icon"
|
||||||
STR_SYNC_CLOCK: "Sync Clock"
|
STR_SYNC_CLOCK: "Sync Clock"
|
||||||
STR_TIME_SYNCED: "Time synced!"
|
STR_TIME_SYNCED: "Time synced!"
|
||||||
|
STR_AUTO_NTP_SYNC: "Auto Sync on Boot"
|
||||||
STR_MANAGE_BOOK: "Manage Book"
|
STR_MANAGE_BOOK: "Manage Book"
|
||||||
STR_ARCHIVE_BOOK: "Archive Book"
|
STR_ARCHIVE_BOOK: "Archive Book"
|
||||||
STR_UNARCHIVE_BOOK: "Unarchive Book"
|
STR_UNARCHIVE_BOOK: "Unarchive Book"
|
||||||
|
|||||||
@@ -343,3 +343,4 @@ STR_INDEXING_STATUS_TEXT: "Texte barre d'état"
|
|||||||
STR_INDEXING_STATUS_ICON: "Icône barre d'état"
|
STR_INDEXING_STATUS_ICON: "Icône barre d'état"
|
||||||
STR_SYNC_CLOCK: "Sync Clock"
|
STR_SYNC_CLOCK: "Sync Clock"
|
||||||
STR_TIME_SYNCED: "Time synced!"
|
STR_TIME_SYNCED: "Time synced!"
|
||||||
|
STR_AUTO_NTP_SYNC: "Auto Sync on Boot"
|
||||||
|
|||||||
@@ -343,3 +343,4 @@ STR_INDEXING_STATUS_TEXT: "Statusleistentext"
|
|||||||
STR_INDEXING_STATUS_ICON: "Statusleistensymbol"
|
STR_INDEXING_STATUS_ICON: "Statusleistensymbol"
|
||||||
STR_SYNC_CLOCK: "Sync Clock"
|
STR_SYNC_CLOCK: "Sync Clock"
|
||||||
STR_TIME_SYNCED: "Time synced!"
|
STR_TIME_SYNCED: "Time synced!"
|
||||||
|
STR_AUTO_NTP_SYNC: "Auto Sync on Boot"
|
||||||
|
|||||||
@@ -343,3 +343,4 @@ STR_INDEXING_STATUS_TEXT: "Texto da barra"
|
|||||||
STR_INDEXING_STATUS_ICON: "Ícone da barra"
|
STR_INDEXING_STATUS_ICON: "Ícone da barra"
|
||||||
STR_SYNC_CLOCK: "Sync Clock"
|
STR_SYNC_CLOCK: "Sync Clock"
|
||||||
STR_TIME_SYNCED: "Time synced!"
|
STR_TIME_SYNCED: "Time synced!"
|
||||||
|
STR_AUTO_NTP_SYNC: "Auto Sync on Boot"
|
||||||
|
|||||||
@@ -318,3 +318,4 @@ STR_EMBEDDED_STYLE: "Stil încorporat"
|
|||||||
STR_OPDS_SERVER_URL: "URL server OPDS"
|
STR_OPDS_SERVER_URL: "URL server OPDS"
|
||||||
STR_SYNC_CLOCK: "Sync Clock"
|
STR_SYNC_CLOCK: "Sync Clock"
|
||||||
STR_TIME_SYNCED: "Time synced!"
|
STR_TIME_SYNCED: "Time synced!"
|
||||||
|
STR_AUTO_NTP_SYNC: "Auto Sync on Boot"
|
||||||
|
|||||||
@@ -343,3 +343,4 @@ STR_INDEXING_STATUS_TEXT: "Текст в строке"
|
|||||||
STR_INDEXING_STATUS_ICON: "Иконка в строке"
|
STR_INDEXING_STATUS_ICON: "Иконка в строке"
|
||||||
STR_SYNC_CLOCK: "Sync Clock"
|
STR_SYNC_CLOCK: "Sync Clock"
|
||||||
STR_TIME_SYNCED: "Time synced!"
|
STR_TIME_SYNCED: "Time synced!"
|
||||||
|
STR_AUTO_NTP_SYNC: "Auto Sync on Boot"
|
||||||
|
|||||||
@@ -343,3 +343,4 @@ STR_INDEXING_STATUS_TEXT: "Texto barra estado"
|
|||||||
STR_INDEXING_STATUS_ICON: "Icono barra estado"
|
STR_INDEXING_STATUS_ICON: "Icono barra estado"
|
||||||
STR_SYNC_CLOCK: "Sync Clock"
|
STR_SYNC_CLOCK: "Sync Clock"
|
||||||
STR_TIME_SYNCED: "Time synced!"
|
STR_TIME_SYNCED: "Time synced!"
|
||||||
|
STR_AUTO_NTP_SYNC: "Auto Sync on Boot"
|
||||||
|
|||||||
@@ -343,3 +343,4 @@ STR_INDEXING_STATUS_TEXT: "Statusfältstext"
|
|||||||
STR_INDEXING_STATUS_ICON: "Statusfältsikon"
|
STR_INDEXING_STATUS_ICON: "Statusfältsikon"
|
||||||
STR_SYNC_CLOCK: "Sync Clock"
|
STR_SYNC_CLOCK: "Sync Clock"
|
||||||
STR_TIME_SYNCED: "Time synced!"
|
STR_TIME_SYNCED: "Time synced!"
|
||||||
|
STR_AUTO_NTP_SYNC: "Auto Sync on Boot"
|
||||||
|
|||||||
@@ -144,6 +144,7 @@ uint8_t CrossPointSettings::writeSettings(FsFile& file, bool count_only) const {
|
|||||||
writer.writeItem(file, timezone);
|
writer.writeItem(file, timezone);
|
||||||
writer.writeItem(file, timezoneOffsetHours);
|
writer.writeItem(file, timezoneOffsetHours);
|
||||||
writer.writeItem(file, indexingDisplay);
|
writer.writeItem(file, indexingDisplay);
|
||||||
|
writer.writeItem(file, autoNtpSync);
|
||||||
|
|
||||||
return writer.item_count;
|
return writer.item_count;
|
||||||
}
|
}
|
||||||
@@ -288,6 +289,8 @@ bool CrossPointSettings::loadFromFile() {
|
|||||||
if (++settingsRead >= fileSettingsCount) break;
|
if (++settingsRead >= fileSettingsCount) break;
|
||||||
readAndValidate(inputFile, indexingDisplay, INDEXING_DISPLAY_COUNT);
|
readAndValidate(inputFile, indexingDisplay, INDEXING_DISPLAY_COUNT);
|
||||||
if (++settingsRead >= fileSettingsCount) break;
|
if (++settingsRead >= fileSettingsCount) break;
|
||||||
|
serialization::readPod(inputFile, autoNtpSync);
|
||||||
|
if (++settingsRead >= fileSettingsCount) break;
|
||||||
} while (false);
|
} while (false);
|
||||||
|
|
||||||
if (frontButtonMappingRead) {
|
if (frontButtonMappingRead) {
|
||||||
|
|||||||
@@ -227,6 +227,9 @@ class CrossPointSettings {
|
|||||||
// Custom timezone offset in hours from UTC (-12 to +14)
|
// Custom timezone offset in hours from UTC (-12 to +14)
|
||||||
int8_t timezoneOffsetHours = 0;
|
int8_t timezoneOffsetHours = 0;
|
||||||
|
|
||||||
|
// Automatically sync time via NTP on boot using saved WiFi credentials
|
||||||
|
uint8_t autoNtpSync = 0;
|
||||||
|
|
||||||
~CrossPointSettings() = default;
|
~CrossPointSettings() = default;
|
||||||
|
|
||||||
// Get singleton instance
|
// Get singleton instance
|
||||||
|
|||||||
@@ -91,6 +91,8 @@ inline std::vector<SettingInfo> getSettingsList() {
|
|||||||
{StrId::STR_TZ_UTC, StrId::STR_TZ_EASTERN, StrId::STR_TZ_CENTRAL, StrId::STR_TZ_MOUNTAIN,
|
{StrId::STR_TZ_UTC, StrId::STR_TZ_EASTERN, StrId::STR_TZ_CENTRAL, StrId::STR_TZ_MOUNTAIN,
|
||||||
StrId::STR_TZ_PACIFIC, StrId::STR_TZ_ALASKA, StrId::STR_TZ_HAWAII, StrId::STR_TZ_CUSTOM},
|
StrId::STR_TZ_PACIFIC, StrId::STR_TZ_ALASKA, StrId::STR_TZ_HAWAII, StrId::STR_TZ_CUSTOM},
|
||||||
"timezone", StrId::STR_CAT_CLOCK),
|
"timezone", StrId::STR_CAT_CLOCK),
|
||||||
|
SettingInfo::Toggle(StrId::STR_AUTO_NTP_SYNC, &CrossPointSettings::autoNtpSync, "autoNtpSync",
|
||||||
|
StrId::STR_CAT_CLOCK),
|
||||||
|
|
||||||
// --- Reader ---
|
// --- Reader ---
|
||||||
SettingInfo::DynamicEnum(
|
SettingInfo::DynamicEnum(
|
||||||
|
|||||||
@@ -13,6 +13,7 @@
|
|||||||
#include "MappedInputManager.h"
|
#include "MappedInputManager.h"
|
||||||
#include "NetworkModeSelectionActivity.h"
|
#include "NetworkModeSelectionActivity.h"
|
||||||
#include "WifiSelectionActivity.h"
|
#include "WifiSelectionActivity.h"
|
||||||
|
#include "util/BootNtpSync.h"
|
||||||
#include "activities/network/CalibreConnectActivity.h"
|
#include "activities/network/CalibreConnectActivity.h"
|
||||||
#include "components/UITheme.h"
|
#include "components/UITheme.h"
|
||||||
#include "fontIds.h"
|
#include "fontIds.h"
|
||||||
@@ -35,6 +36,8 @@ constexpr uint16_t DNS_PORT = 53;
|
|||||||
void CrossPointWebServerActivity::onEnter() {
|
void CrossPointWebServerActivity::onEnter() {
|
||||||
ActivityWithSubactivity::onEnter();
|
ActivityWithSubactivity::onEnter();
|
||||||
|
|
||||||
|
BootNtpSync::cancel();
|
||||||
|
|
||||||
LOG_DBG("WEBACT", "Free heap at onEnter: %d bytes", ESP.getFreeHeap());
|
LOG_DBG("WEBACT", "Free heap at onEnter: %d bytes", ESP.getFreeHeap());
|
||||||
|
|
||||||
// Reset state
|
// Reset state
|
||||||
|
|||||||
@@ -12,11 +12,14 @@
|
|||||||
#include "activities/util/KeyboardEntryActivity.h"
|
#include "activities/util/KeyboardEntryActivity.h"
|
||||||
#include "components/UITheme.h"
|
#include "components/UITheme.h"
|
||||||
#include "fontIds.h"
|
#include "fontIds.h"
|
||||||
|
#include "util/BootNtpSync.h"
|
||||||
#include "util/TimeSync.h"
|
#include "util/TimeSync.h"
|
||||||
|
|
||||||
void WifiSelectionActivity::onEnter() {
|
void WifiSelectionActivity::onEnter() {
|
||||||
Activity::onEnter();
|
Activity::onEnter();
|
||||||
|
|
||||||
|
BootNtpSync::cancel();
|
||||||
|
|
||||||
// Load saved WiFi credentials - SD card operations need lock as we use SPI
|
// Load saved WiFi credentials - SD card operations need lock as we use SPI
|
||||||
// for both
|
// for both
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -11,6 +11,7 @@
|
|||||||
#include "activities/network/WifiSelectionActivity.h"
|
#include "activities/network/WifiSelectionActivity.h"
|
||||||
#include "components/UITheme.h"
|
#include "components/UITheme.h"
|
||||||
#include "fontIds.h"
|
#include "fontIds.h"
|
||||||
|
#include "util/BootNtpSync.h"
|
||||||
#include "util/TimeSync.h"
|
#include "util/TimeSync.h"
|
||||||
|
|
||||||
void KOReaderSyncActivity::onWifiSelectionComplete(const bool success) {
|
void KOReaderSyncActivity::onWifiSelectionComplete(const bool success) {
|
||||||
@@ -155,6 +156,8 @@ void KOReaderSyncActivity::performUpload() {
|
|||||||
void KOReaderSyncActivity::onEnter() {
|
void KOReaderSyncActivity::onEnter() {
|
||||||
ActivityWithSubactivity::onEnter();
|
ActivityWithSubactivity::onEnter();
|
||||||
|
|
||||||
|
BootNtpSync::cancel();
|
||||||
|
|
||||||
// Check for credentials first
|
// Check for credentials first
|
||||||
if (!KOREADER_STORE.hasCredentials()) {
|
if (!KOREADER_STORE.hasCredentials()) {
|
||||||
state = NO_CREDENTIALS;
|
state = NO_CREDENTIALS;
|
||||||
|
|||||||
@@ -8,6 +8,7 @@
|
|||||||
#include "KOReaderSyncClient.h"
|
#include "KOReaderSyncClient.h"
|
||||||
#include "MappedInputManager.h"
|
#include "MappedInputManager.h"
|
||||||
#include "activities/network/WifiSelectionActivity.h"
|
#include "activities/network/WifiSelectionActivity.h"
|
||||||
|
#include "util/BootNtpSync.h"
|
||||||
#include "components/UITheme.h"
|
#include "components/UITheme.h"
|
||||||
#include "fontIds.h"
|
#include "fontIds.h"
|
||||||
|
|
||||||
@@ -53,6 +54,8 @@ void KOReaderAuthActivity::performAuthentication() {
|
|||||||
void KOReaderAuthActivity::onEnter() {
|
void KOReaderAuthActivity::onEnter() {
|
||||||
ActivityWithSubactivity::onEnter();
|
ActivityWithSubactivity::onEnter();
|
||||||
|
|
||||||
|
BootNtpSync::cancel();
|
||||||
|
|
||||||
// Turn on WiFi
|
// Turn on WiFi
|
||||||
WiFi.mode(WIFI_STA);
|
WiFi.mode(WIFI_STA);
|
||||||
|
|
||||||
|
|||||||
@@ -10,6 +10,7 @@
|
|||||||
#include "activities/network/WifiSelectionActivity.h"
|
#include "activities/network/WifiSelectionActivity.h"
|
||||||
#include "components/UITheme.h"
|
#include "components/UITheme.h"
|
||||||
#include "fontIds.h"
|
#include "fontIds.h"
|
||||||
|
#include "util/BootNtpSync.h"
|
||||||
#include "util/TimeSync.h"
|
#include "util/TimeSync.h"
|
||||||
|
|
||||||
static constexpr unsigned long AUTO_DISMISS_MS = 5000;
|
static constexpr unsigned long AUTO_DISMISS_MS = 5000;
|
||||||
@@ -52,6 +53,7 @@ void NtpSyncActivity::onWifiSelectionComplete(const bool success) {
|
|||||||
void NtpSyncActivity::onEnter() {
|
void NtpSyncActivity::onEnter() {
|
||||||
ActivityWithSubactivity::onEnter();
|
ActivityWithSubactivity::onEnter();
|
||||||
|
|
||||||
|
BootNtpSync::cancel();
|
||||||
LOG_DBG("NTP", "Turning on WiFi...");
|
LOG_DBG("NTP", "Turning on WiFi...");
|
||||||
WiFi.mode(WIFI_STA);
|
WiFi.mode(WIFI_STA);
|
||||||
|
|
||||||
|
|||||||
@@ -9,6 +9,7 @@
|
|||||||
#include "components/UITheme.h"
|
#include "components/UITheme.h"
|
||||||
#include "fontIds.h"
|
#include "fontIds.h"
|
||||||
#include "network/OtaUpdater.h"
|
#include "network/OtaUpdater.h"
|
||||||
|
#include "util/BootNtpSync.h"
|
||||||
|
|
||||||
void OtaUpdateActivity::onWifiSelectionComplete(const bool success) {
|
void OtaUpdateActivity::onWifiSelectionComplete(const bool success) {
|
||||||
exitActivity();
|
exitActivity();
|
||||||
@@ -58,6 +59,8 @@ void OtaUpdateActivity::onWifiSelectionComplete(const bool success) {
|
|||||||
void OtaUpdateActivity::onEnter() {
|
void OtaUpdateActivity::onEnter() {
|
||||||
ActivityWithSubactivity::onEnter();
|
ActivityWithSubactivity::onEnter();
|
||||||
|
|
||||||
|
BootNtpSync::cancel();
|
||||||
|
|
||||||
// Turn on WiFi immediately
|
// Turn on WiFi immediately
|
||||||
LOG_DBG("OTA", "Turning on WiFi...");
|
LOG_DBG("OTA", "Turning on WiFi...");
|
||||||
WiFi.mode(WIFI_STA);
|
WiFi.mode(WIFI_STA);
|
||||||
|
|||||||
15
src/main.cpp
15
src/main.cpp
@@ -32,6 +32,7 @@
|
|||||||
#include "activities/util/FullScreenMessageActivity.h"
|
#include "activities/util/FullScreenMessageActivity.h"
|
||||||
#include "components/UITheme.h"
|
#include "components/UITheme.h"
|
||||||
#include "fontIds.h"
|
#include "fontIds.h"
|
||||||
|
#include "util/BootNtpSync.h"
|
||||||
#include "util/ButtonNavigator.h"
|
#include "util/ButtonNavigator.h"
|
||||||
|
|
||||||
HalDisplay display;
|
HalDisplay display;
|
||||||
@@ -342,6 +343,7 @@ void setup() {
|
|||||||
|
|
||||||
I18N.loadSettings();
|
I18N.loadSettings();
|
||||||
KOREADER_STORE.loadFromFile();
|
KOREADER_STORE.loadFromFile();
|
||||||
|
BootNtpSync::start();
|
||||||
UITheme::getInstance().reload();
|
UITheme::getInstance().reload();
|
||||||
ButtonNavigator::setMappedInputManager(mappedInputManager);
|
ButtonNavigator::setMappedInputManager(mappedInputManager);
|
||||||
|
|
||||||
@@ -459,14 +461,23 @@ void loop() {
|
|||||||
// Refresh screen when the displayed minute changes (clock in header)
|
// Refresh screen when the displayed minute changes (clock in header)
|
||||||
if (SETTINGS.clockFormat != CrossPointSettings::CLOCK_OFF && currentActivity) {
|
if (SETTINGS.clockFormat != CrossPointSettings::CLOCK_OFF && currentActivity) {
|
||||||
static int lastRenderedMinute = -1;
|
static int lastRenderedMinute = -1;
|
||||||
|
static bool sawInvalidTime = false;
|
||||||
time_t now = time(nullptr);
|
time_t now = time(nullptr);
|
||||||
struct tm* t = localtime(&now);
|
struct tm* t = localtime(&now);
|
||||||
if (t != nullptr && t->tm_year > 100) {
|
if (t != nullptr && t->tm_year > 100) {
|
||||||
const int currentMinute = t->tm_hour * 60 + t->tm_min;
|
const int currentMinute = t->tm_hour * 60 + t->tm_min;
|
||||||
if (lastRenderedMinute >= 0 && currentMinute != lastRenderedMinute) {
|
if (lastRenderedMinute < 0) {
|
||||||
|
lastRenderedMinute = currentMinute;
|
||||||
|
if (sawInvalidTime) {
|
||||||
|
// Time just became valid (e.g. background NTP sync completed)
|
||||||
|
currentActivity->requestUpdate();
|
||||||
|
}
|
||||||
|
} else if (currentMinute != lastRenderedMinute) {
|
||||||
currentActivity->requestUpdate();
|
currentActivity->requestUpdate();
|
||||||
|
lastRenderedMinute = currentMinute;
|
||||||
}
|
}
|
||||||
lastRenderedMinute = currentMinute;
|
} else {
|
||||||
|
sawInvalidTime = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
163
src/util/BootNtpSync.cpp
Normal file
163
src/util/BootNtpSync.cpp
Normal file
@@ -0,0 +1,163 @@
|
|||||||
|
#include "BootNtpSync.h"
|
||||||
|
|
||||||
|
#include <Logging.h>
|
||||||
|
#include <WiFi.h>
|
||||||
|
#include <freertos/FreeRTOS.h>
|
||||||
|
#include <freertos/task.h>
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
#include "CrossPointSettings.h"
|
||||||
|
#include "WifiCredentialStore.h"
|
||||||
|
#include "util/TimeSync.h"
|
||||||
|
|
||||||
|
namespace BootNtpSync {
|
||||||
|
|
||||||
|
static volatile bool running = false;
|
||||||
|
static TaskHandle_t taskHandle = nullptr;
|
||||||
|
|
||||||
|
struct TaskParams {
|
||||||
|
std::vector<WifiCredential> credentials;
|
||||||
|
std::string lastConnectedSsid;
|
||||||
|
};
|
||||||
|
|
||||||
|
static bool tryConnectToSavedNetwork(const TaskParams& params) {
|
||||||
|
WiFi.mode(WIFI_STA);
|
||||||
|
WiFi.disconnect();
|
||||||
|
vTaskDelay(100 / portTICK_PERIOD_MS);
|
||||||
|
|
||||||
|
LOG_DBG("BNTP", "Scanning WiFi networks...");
|
||||||
|
int16_t count = WiFi.scanNetworks();
|
||||||
|
if (count <= 0) {
|
||||||
|
LOG_DBG("BNTP", "Scan returned %d networks", count);
|
||||||
|
WiFi.scanDelete();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
LOG_DBG("BNTP", "Found %d networks, matching against %zu saved credentials", count, params.credentials.size());
|
||||||
|
|
||||||
|
// Find best match: prefer lastConnectedSsid, otherwise first saved match
|
||||||
|
const WifiCredential* bestMatch = nullptr;
|
||||||
|
for (int i = 0; i < count; i++) {
|
||||||
|
std::string ssid = WiFi.SSID(i).c_str();
|
||||||
|
for (const auto& cred : params.credentials) {
|
||||||
|
if (cred.ssid == ssid) {
|
||||||
|
if (!bestMatch || cred.ssid == params.lastConnectedSsid) {
|
||||||
|
bestMatch = &cred;
|
||||||
|
}
|
||||||
|
if (cred.ssid == params.lastConnectedSsid) {
|
||||||
|
break; // Can't do better than lastConnected
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (bestMatch && bestMatch->ssid == params.lastConnectedSsid) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
WiFi.scanDelete();
|
||||||
|
|
||||||
|
if (!bestMatch) {
|
||||||
|
LOG_DBG("BNTP", "No saved network found in scan results");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
LOG_DBG("BNTP", "Connecting to %s", bestMatch->ssid.c_str());
|
||||||
|
if (!bestMatch->password.empty()) {
|
||||||
|
WiFi.begin(bestMatch->ssid.c_str(), bestMatch->password.c_str());
|
||||||
|
} else {
|
||||||
|
WiFi.begin(bestMatch->ssid.c_str());
|
||||||
|
}
|
||||||
|
|
||||||
|
const unsigned long start = millis();
|
||||||
|
constexpr unsigned long CONNECT_TIMEOUT_MS = 10000;
|
||||||
|
while (WiFi.status() != WL_CONNECTED && millis() - start < CONNECT_TIMEOUT_MS) {
|
||||||
|
if (!running) return false;
|
||||||
|
vTaskDelay(100 / portTICK_PERIOD_MS);
|
||||||
|
|
||||||
|
wl_status_t status = WiFi.status();
|
||||||
|
if (status == WL_CONNECT_FAILED || status == WL_NO_SSID_AVAIL) {
|
||||||
|
LOG_DBG("BNTP", "Connection failed (status=%d)", status);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (WiFi.status() == WL_CONNECTED) {
|
||||||
|
LOG_DBG("BNTP", "Connected to %s", bestMatch->ssid.c_str());
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
LOG_DBG("BNTP", "Connection timed out");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void taskFunc(void* param) {
|
||||||
|
auto* params = static_cast<TaskParams*>(param);
|
||||||
|
|
||||||
|
bool connected = tryConnectToSavedNetwork(*params);
|
||||||
|
|
||||||
|
if (!connected && running) {
|
||||||
|
LOG_DBG("BNTP", "First scan failed, retrying in 3s...");
|
||||||
|
vTaskDelay(3000 / portTICK_PERIOD_MS);
|
||||||
|
if (running) {
|
||||||
|
connected = tryConnectToSavedNetwork(*params);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (connected && running) {
|
||||||
|
LOG_DBG("BNTP", "Starting NTP sync...");
|
||||||
|
bool synced = TimeSync::waitForNtpSync(5000);
|
||||||
|
TimeSync::stopNtpSync();
|
||||||
|
if (synced) {
|
||||||
|
LOG_DBG("BNTP", "NTP sync successful");
|
||||||
|
} else {
|
||||||
|
LOG_DBG("BNTP", "NTP sync timed out, continuing without time");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
WiFi.disconnect(false);
|
||||||
|
vTaskDelay(100 / portTICK_PERIOD_MS);
|
||||||
|
WiFi.mode(WIFI_OFF);
|
||||||
|
vTaskDelay(100 / portTICK_PERIOD_MS);
|
||||||
|
|
||||||
|
delete params;
|
||||||
|
running = false;
|
||||||
|
taskHandle = nullptr;
|
||||||
|
LOG_DBG("BNTP", "Boot NTP task complete");
|
||||||
|
vTaskDelete(nullptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
void start() {
|
||||||
|
if (!SETTINGS.autoNtpSync) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
WIFI_STORE.loadFromFile();
|
||||||
|
const auto& creds = WIFI_STORE.getCredentials();
|
||||||
|
if (creds.empty()) {
|
||||||
|
LOG_DBG("BNTP", "No saved WiFi credentials, skipping boot NTP sync");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto* params = new TaskParams{creds, WIFI_STORE.getLastConnectedSsid()};
|
||||||
|
|
||||||
|
running = true;
|
||||||
|
xTaskCreate(taskFunc, "BootNTP", 4096, params, 1, &taskHandle);
|
||||||
|
LOG_DBG("BNTP", "Boot NTP sync task started");
|
||||||
|
}
|
||||||
|
|
||||||
|
void cancel() {
|
||||||
|
if (!running) return;
|
||||||
|
LOG_DBG("BNTP", "Cancelling boot NTP sync...");
|
||||||
|
running = false;
|
||||||
|
// Wait for the task to notice and clean up (up to 2s)
|
||||||
|
for (int i = 0; i < 20 && taskHandle != nullptr; i++) {
|
||||||
|
delay(100);
|
||||||
|
}
|
||||||
|
LOG_DBG("BNTP", "Boot NTP sync cancelled");
|
||||||
|
}
|
||||||
|
|
||||||
|
bool isRunning() { return running; }
|
||||||
|
|
||||||
|
} // namespace BootNtpSync
|
||||||
16
src/util/BootNtpSync.h
Normal file
16
src/util/BootNtpSync.h
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
namespace BootNtpSync {
|
||||||
|
|
||||||
|
// Spawn a background FreeRTOS task that scans for saved WiFi networks,
|
||||||
|
// connects, syncs NTP, then tears down WiFi. Non-blocking; does nothing
|
||||||
|
// if autoNtpSync is disabled or no credentials are stored.
|
||||||
|
void start();
|
||||||
|
|
||||||
|
// Signal the background task to abort and wait for it to finish.
|
||||||
|
// Call before starting any other WiFi operation.
|
||||||
|
void cancel();
|
||||||
|
|
||||||
|
bool isRunning();
|
||||||
|
|
||||||
|
} // namespace BootNtpSync
|
||||||
Reference in New Issue
Block a user