mod: fix clock bugs, add NTP auto-sync, show clock in all headers

- Fix SetTimeActivity immediately dismissing by changing wasReleased to
  wasPressed for all button inputs (matching other subactivities)
- Extract NTP sync into shared TimeSync utility (startNtpSync,
  waitForNtpSync, stopNtpSync) and trigger non-blocking NTP sync on
  every WiFi connection
- Move clock rendering into drawHeader (BaseTheme + LyraTheme) so it
  appears on all screens with a header, positioned symmetrically with
  the battery icon (12px margin, same Y offset, SMALL_FONT_ID)
- Add per-minute auto-refresh on home screen so clock updates without
  button press
- Add RTC time debug log on boot to verify time persistence across
  deep sleep

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
cottongin
2026-02-17 02:13:10 -05:00
parent ab4540b26f
commit 38a87298f3
10 changed files with 145 additions and 54 deletions

View File

@@ -192,6 +192,19 @@ void HomeActivity::freeCoverBuffer() {
}
void HomeActivity::loop() {
// Refresh the screen when the displayed minute changes (clock update)
if (SETTINGS.homeScreenClock != CrossPointSettings::CLOCK_OFF) {
time_t now = time(nullptr);
struct tm* t = localtime(&now);
if (t != nullptr && t->tm_year > 100) {
const int currentMinute = t->tm_hour * 60 + t->tm_min;
if (lastRenderedMinute >= 0 && currentMinute != lastRenderedMinute) {
requestUpdate();
}
lastRenderedMinute = currentMinute;
}
}
const int menuCount = getMenuItemCount();
buttonNavigator.onNext([this, menuCount] {
@@ -240,23 +253,6 @@ void HomeActivity::render(Activity::RenderLock&&) {
GUI.drawHeader(renderer, Rect{0, metrics.topPadding, pageWidth, metrics.homeTopPadding}, nullptr);
// Draw clock in the header area (left side) if enabled
if (SETTINGS.homeScreenClock != CrossPointSettings::CLOCK_OFF) {
time_t now = time(nullptr);
struct tm* t = localtime(&now);
if (t != nullptr && t->tm_year > 100) {
char timeBuf[16];
if (SETTINGS.homeScreenClock == CrossPointSettings::CLOCK_24H) {
snprintf(timeBuf, sizeof(timeBuf), "%02d:%02d", t->tm_hour, t->tm_min);
} else {
int hour12 = t->tm_hour % 12;
if (hour12 == 0) hour12 = 12;
snprintf(timeBuf, sizeof(timeBuf), "%d:%02d %s", hour12, t->tm_min, t->tm_hour >= 12 ? "PM" : "AM");
}
renderer.drawText(SMALL_FONT_ID, metrics.contentSidePadding, metrics.topPadding, timeBuf, true);
}
}
GUI.drawRecentBookCover(renderer, Rect{0, metrics.homeTopPadding, pageWidth, metrics.homeCoverTileHeight},
recentBooks, selectorIndex, coverRendered, coverBufferStored, bufferRestored,
std::bind(&HomeActivity::storeCoverBuffer, this));

View File

@@ -16,6 +16,7 @@ class HomeActivity final : public Activity {
bool recentsLoaded = false;
bool firstRenderDone = false;
bool hasOpdsUrl = false;
int lastRenderedMinute = -1; // Track displayed minute for clock auto-update
bool coverRendered = false; // Track if cover has been rendered once
bool coverBufferStored = false; // Track if cover buffer is stored
uint8_t* coverBuffer = nullptr; // HomeActivity's own buffer for cover image

View File

@@ -12,6 +12,7 @@
#include "activities/util/KeyboardEntryActivity.h"
#include "components/UITheme.h"
#include "fontIds.h"
#include "util/TimeSync.h"
void WifiSelectionActivity::onEnter() {
Activity::onEnter();
@@ -243,6 +244,9 @@ void WifiSelectionActivity::checkConnectionStatus() {
connectedIP = ipStr;
autoConnecting = false;
// Start NTP time sync in the background (non-blocking)
TimeSync::startNtpSync();
// Save this as the last connected network - SD card operations need lock as
// we use SPI for both
{

View File

@@ -4,7 +4,6 @@
#include <I18n.h>
#include <Logging.h>
#include <WiFi.h>
#include <esp_sntp.h>
#include "KOReaderCredentialStore.h"
#include "KOReaderDocumentId.h"
@@ -12,34 +11,7 @@
#include "activities/network/WifiSelectionActivity.h"
#include "components/UITheme.h"
#include "fontIds.h"
namespace {
void syncTimeWithNTP() {
// Stop SNTP if already running (can't reconfigure while running)
if (esp_sntp_enabled()) {
esp_sntp_stop();
}
// Configure SNTP
esp_sntp_setoperatingmode(ESP_SNTP_OPMODE_POLL);
esp_sntp_setservername(0, "pool.ntp.org");
esp_sntp_init();
// Wait for time to sync (with timeout)
int retry = 0;
const int maxRetries = 50; // 5 seconds max
while (sntp_get_sync_status() != SNTP_SYNC_STATUS_COMPLETED && retry < maxRetries) {
vTaskDelay(100 / portTICK_PERIOD_MS);
retry++;
}
if (retry < maxRetries) {
LOG_DBG("KOSync", "NTP time synced");
} else {
LOG_DBG("KOSync", "NTP sync timeout, using fallback");
}
}
} // namespace
#include "util/TimeSync.h"
void KOReaderSyncActivity::onWifiSelectionComplete(const bool success) {
exitActivity();
@@ -59,8 +31,8 @@ void KOReaderSyncActivity::onWifiSelectionComplete(const bool success) {
}
requestUpdate();
// Sync time with NTP before making API requests
syncTimeWithNTP();
// Wait for NTP sync before making API requests (blocks up to 5s)
TimeSync::waitForNtpSync();
{
RenderLock lock(*this);
@@ -199,8 +171,8 @@ void KOReaderSyncActivity::onEnter() {
xTaskCreate(
[](void* param) {
auto* self = static_cast<KOReaderSyncActivity*>(param);
// Sync time first
syncTimeWithNTP();
// Wait for NTP sync before making API requests
TimeSync::waitForNtpSync();
{
RenderLock lock(*self);
self->statusMessage = tr(STR_CALC_HASH);

View File

@@ -34,25 +34,25 @@ void SetTimeActivity::onExit() { Activity::onExit(); }
void SetTimeActivity::loop() {
// Back button: discard and exit
if (mappedInput.wasReleased(MappedInputManager::Button::Back)) {
if (mappedInput.wasPressed(MappedInputManager::Button::Back)) {
onBack();
return;
}
// Confirm button: apply time and exit
if (mappedInput.wasReleased(MappedInputManager::Button::Confirm)) {
if (mappedInput.wasPressed(MappedInputManager::Button::Confirm)) {
applyTime();
onBack();
return;
}
// Left/Right: switch between hour and minute fields
if (mappedInput.wasReleased(MappedInputManager::Button::Left)) {
if (mappedInput.wasPressed(MappedInputManager::Button::Left)) {
selectedField = 0;
requestUpdate();
return;
}
if (mappedInput.wasReleased(MappedInputManager::Button::Right)) {
if (mappedInput.wasPressed(MappedInputManager::Button::Right)) {
selectedField = 1;
requestUpdate();
return;