mirror of
https://github.com/daveallie/crosspoint-reader.git
synced 2026-02-06 15:47:39 +03:00
Merge pull request #1 from ckorhonen/ckorhonen/ottawa
Add Calendar Mode for automated e-ink calendar display
This commit is contained in:
commit
7e7c51949e
@ -16,7 +16,8 @@ class CrossPointSettings {
|
|||||||
CrossPointSettings& operator=(const CrossPointSettings&) = delete;
|
CrossPointSettings& operator=(const CrossPointSettings&) = delete;
|
||||||
|
|
||||||
// Should match with SettingsActivity text
|
// Should match with SettingsActivity text
|
||||||
enum SLEEP_SCREEN_MODE { DARK = 0, LIGHT = 1, CUSTOM = 2, COVER = 3, BLANK = 4 };
|
// Note: CALENDAR is internal-only, not user-selectable in Settings
|
||||||
|
enum SLEEP_SCREEN_MODE { DARK = 0, LIGHT = 1, CUSTOM = 2, COVER = 3, BLANK = 4, CALENDAR = 5 };
|
||||||
enum SLEEP_SCREEN_COVER_MODE { FIT = 0, CROP = 1 };
|
enum SLEEP_SCREEN_COVER_MODE { FIT = 0, CROP = 1 };
|
||||||
|
|
||||||
// Status bar display type enum
|
// Status bar display type enum
|
||||||
@ -96,7 +97,7 @@ class CrossPointSettings {
|
|||||||
// Calendar mode settings
|
// Calendar mode settings
|
||||||
uint8_t calendarModeEnabled = 0; // 0 = disabled, 1 = enabled
|
uint8_t calendarModeEnabled = 0; // 0 = disabled, 1 = enabled
|
||||||
uint8_t calendarRefreshHours = 4; // Refresh interval in hours (1-24)
|
uint8_t calendarRefreshHours = 4; // Refresh interval in hours (1-24)
|
||||||
char calendarServerUrl[256] = ""; // URL to fetch BMP image from
|
char calendarServerUrl[256] = ""; // URL to fetch BMP image from
|
||||||
|
|
||||||
~CrossPointSettings() = default;
|
~CrossPointSettings() = default;
|
||||||
|
|
||||||
|
|||||||
@ -5,7 +5,7 @@
|
|||||||
#include <Serialization.h>
|
#include <Serialization.h>
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
constexpr uint8_t STATE_FILE_VERSION = 2;
|
constexpr uint8_t STATE_FILE_VERSION = 3;
|
||||||
constexpr char STATE_FILE[] = "/.crosspoint/state.bin";
|
constexpr char STATE_FILE[] = "/.crosspoint/state.bin";
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
@ -20,6 +20,7 @@ bool CrossPointState::saveToFile() const {
|
|||||||
serialization::writePod(outputFile, STATE_FILE_VERSION);
|
serialization::writePod(outputFile, STATE_FILE_VERSION);
|
||||||
serialization::writeString(outputFile, openEpubPath);
|
serialization::writeString(outputFile, openEpubPath);
|
||||||
serialization::writePod(outputFile, lastSleepImage);
|
serialization::writePod(outputFile, lastSleepImage);
|
||||||
|
serialization::writePod(outputFile, lastCalendarFetch);
|
||||||
outputFile.close();
|
outputFile.close();
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@ -44,6 +45,9 @@ bool CrossPointState::loadFromFile() {
|
|||||||
} else {
|
} else {
|
||||||
lastSleepImage = 0;
|
lastSleepImage = 0;
|
||||||
}
|
}
|
||||||
|
if (version >= 3) {
|
||||||
|
serialization::readPod(inputFile, lastCalendarFetch);
|
||||||
|
}
|
||||||
|
|
||||||
inputFile.close();
|
inputFile.close();
|
||||||
return true;
|
return true;
|
||||||
|
|||||||
@ -9,6 +9,7 @@ class CrossPointState {
|
|||||||
public:
|
public:
|
||||||
std::string openEpubPath;
|
std::string openEpubPath;
|
||||||
uint8_t lastSleepImage;
|
uint8_t lastSleepImage;
|
||||||
|
uint32_t lastCalendarFetch = 0; // Unix epoch of last successful calendar fetch
|
||||||
~CrossPointState() = default;
|
~CrossPointState() = default;
|
||||||
|
|
||||||
// Get singleton instance
|
// Get singleton instance
|
||||||
|
|||||||
@ -20,6 +20,10 @@ void SleepActivity::onEnter() {
|
|||||||
return renderBlankSleepScreen();
|
return renderBlankSleepScreen();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (SETTINGS.sleepScreen == CrossPointSettings::SLEEP_SCREEN_MODE::CALENDAR) {
|
||||||
|
return renderCalendarSleepScreen();
|
||||||
|
}
|
||||||
|
|
||||||
if (SETTINGS.sleepScreen == CrossPointSettings::SLEEP_SCREEN_MODE::CUSTOM) {
|
if (SETTINGS.sleepScreen == CrossPointSettings::SLEEP_SCREEN_MODE::CUSTOM) {
|
||||||
return renderCustomSleepScreen();
|
return renderCustomSleepScreen();
|
||||||
}
|
}
|
||||||
@ -119,6 +123,21 @@ void SleepActivity::renderCustomSleepScreen() const {
|
|||||||
renderDefaultSleepScreen();
|
renderDefaultSleepScreen();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void SleepActivity::renderCalendarSleepScreen() const {
|
||||||
|
// Calendar mode: read /sleep.bmp directly, bypassing /sleep/ directory
|
||||||
|
FsFile file;
|
||||||
|
if (SdMan.openFileForRead("SLP", "/sleep.bmp", file)) {
|
||||||
|
Bitmap bitmap(file, true);
|
||||||
|
if (bitmap.parseHeaders() == BmpReaderError::Ok) {
|
||||||
|
Serial.printf("[%lu] [SLP] Loading calendar: /sleep.bmp\n", millis());
|
||||||
|
renderBitmapSleepScreen(bitmap);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Fallback if no calendar image
|
||||||
|
renderDefaultSleepScreen();
|
||||||
|
}
|
||||||
|
|
||||||
void SleepActivity::renderDefaultSleepScreen() const {
|
void SleepActivity::renderDefaultSleepScreen() const {
|
||||||
const auto pageWidth = renderer.getScreenWidth();
|
const auto pageWidth = renderer.getScreenWidth();
|
||||||
const auto pageHeight = renderer.getScreenHeight();
|
const auto pageHeight = renderer.getScreenHeight();
|
||||||
|
|||||||
@ -13,6 +13,7 @@ class SleepActivity final : public Activity {
|
|||||||
void renderPopup(const char* message) const;
|
void renderPopup(const char* message) const;
|
||||||
void renderDefaultSleepScreen() const;
|
void renderDefaultSleepScreen() const;
|
||||||
void renderCustomSleepScreen() const;
|
void renderCustomSleepScreen() const;
|
||||||
|
void renderCalendarSleepScreen() const;
|
||||||
void renderCoverSleepScreen() const;
|
void renderCoverSleepScreen() const;
|
||||||
void renderBitmapSleepScreen(const Bitmap& bitmap) const;
|
void renderBitmapSleepScreen(const Bitmap& bitmap) const;
|
||||||
void renderBlankSleepScreen() const;
|
void renderBlankSleepScreen() const;
|
||||||
|
|||||||
@ -5,16 +5,18 @@
|
|||||||
#include <SDCardManager.h>
|
#include <SDCardManager.h>
|
||||||
#include <WiFi.h>
|
#include <WiFi.h>
|
||||||
#include <esp_sleep.h>
|
#include <esp_sleep.h>
|
||||||
|
#include <time.h>
|
||||||
|
|
||||||
#include "../../CrossPointSettings.h"
|
#include "../../CrossPointSettings.h"
|
||||||
|
#include "../../CrossPointState.h"
|
||||||
#include "../../WifiCredentialStore.h"
|
#include "../../WifiCredentialStore.h"
|
||||||
#include "../../fontIds.h"
|
#include "../../fontIds.h"
|
||||||
#include "../boot_sleep/SleepActivity.h"
|
|
||||||
|
|
||||||
// External functions from main.cpp
|
// External functions from main.cpp
|
||||||
extern void exitActivity();
|
extern void exitActivity();
|
||||||
extern void enterNewActivity(Activity* activity);
|
extern void enterNewActivity(Activity* activity);
|
||||||
extern void enterDeepSleep();
|
extern void enterDeepSleep();
|
||||||
|
extern void enterCalendarDeepSleep(uint8_t refreshHours);
|
||||||
|
|
||||||
void CalendarActivity::onEnter() {
|
void CalendarActivity::onEnter() {
|
||||||
Activity::onEnter();
|
Activity::onEnter();
|
||||||
@ -23,7 +25,8 @@ void CalendarActivity::onEnter() {
|
|||||||
|
|
||||||
Serial.printf("[%lu] [CAL] Calendar mode starting\n", millis());
|
Serial.printf("[%lu] [CAL] Calendar mode starting\n", millis());
|
||||||
|
|
||||||
// Begin WiFi connection
|
// Show status and begin WiFi connection
|
||||||
|
renderStatus("Connecting...");
|
||||||
startWifiConnection();
|
startWifiConnection();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -125,42 +128,6 @@ bool CalendarActivity::fetchAndSaveImage() {
|
|||||||
return totalWritten > 0;
|
return totalWritten > 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
void CalendarActivity::renderSleepScreen() {
|
|
||||||
Serial.printf("[%lu] [CAL] Rendering sleep screen\n", millis());
|
|
||||||
|
|
||||||
// Force sleep screen mode to CUSTOM to use our downloaded image
|
|
||||||
SETTINGS.sleepScreen = CrossPointSettings::CUSTOM;
|
|
||||||
|
|
||||||
// Create and enter SleepActivity to render the image
|
|
||||||
exitActivity();
|
|
||||||
enterNewActivity(new SleepActivity(renderer, mappedInput));
|
|
||||||
}
|
|
||||||
|
|
||||||
void CalendarActivity::scheduleWakeAndSleep() {
|
|
||||||
Serial.printf("[%lu] [CAL] Scheduling wake in %d hours\n", millis(), SETTINGS.calendarRefreshHours);
|
|
||||||
|
|
||||||
// Disconnect WiFi to save power
|
|
||||||
WiFi.disconnect(true);
|
|
||||||
WiFi.mode(WIFI_OFF);
|
|
||||||
|
|
||||||
// Calculate sleep duration in microseconds
|
|
||||||
uint64_t sleepDurationUs = (uint64_t)SETTINGS.calendarRefreshHours * 60ULL * 60ULL * 1000000ULL;
|
|
||||||
|
|
||||||
Serial.printf("[%lu] [CAL] Sleep duration: %llu us (%d hours)\n", millis(), sleepDurationUs,
|
|
||||||
SETTINGS.calendarRefreshHours);
|
|
||||||
|
|
||||||
// Enable timer wakeup
|
|
||||||
esp_sleep_enable_timer_wakeup(sleepDurationUs);
|
|
||||||
|
|
||||||
// Also keep GPIO wakeup for power button (allows manual wake for normal use)
|
|
||||||
esp_deep_sleep_enable_gpio_wakeup(1ULL << 3, ESP_GPIO_WAKEUP_GPIO_LOW); // Power button pin
|
|
||||||
|
|
||||||
Serial.printf("[%lu] [CAL] Entering deep sleep\n", millis());
|
|
||||||
|
|
||||||
// Enter deep sleep
|
|
||||||
esp_deep_sleep_start();
|
|
||||||
}
|
|
||||||
|
|
||||||
void CalendarActivity::handleError(const char* message) {
|
void CalendarActivity::handleError(const char* message) {
|
||||||
Serial.printf("[%lu] [CAL] Error: %s\n", millis(), message);
|
Serial.printf("[%lu] [CAL] Error: %s\n", millis(), message);
|
||||||
errorMessage = message;
|
errorMessage = message;
|
||||||
@ -171,8 +138,11 @@ void CalendarActivity::handleError(const char* message) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void CalendarActivity::renderStatus(const char* status) {
|
void CalendarActivity::renderStatus(const char* status) {
|
||||||
// Minimal status rendering - just log for now
|
|
||||||
Serial.printf("[%lu] [CAL] Status: %s\n", millis(), status);
|
Serial.printf("[%lu] [CAL] Status: %s\n", millis(), status);
|
||||||
|
|
||||||
|
renderer.clearScreen();
|
||||||
|
renderer.drawCenteredText(UI_12_FONT_ID, renderer.getScreenHeight() / 2, status, true, EpdFontFamily::BOLD);
|
||||||
|
renderer.displayBuffer();
|
||||||
}
|
}
|
||||||
|
|
||||||
void CalendarActivity::loop() {
|
void CalendarActivity::loop() {
|
||||||
@ -184,6 +154,15 @@ void CalendarActivity::loop() {
|
|||||||
case CalendarState::CONNECTING_WIFI:
|
case CalendarState::CONNECTING_WIFI:
|
||||||
if (checkWifiConnection()) {
|
if (checkWifiConnection()) {
|
||||||
Serial.printf("[%lu] [CAL] WiFi connected, IP: %s\n", millis(), WiFi.localIP().toString().c_str());
|
Serial.printf("[%lu] [CAL] WiFi connected, IP: %s\n", millis(), WiFi.localIP().toString().c_str());
|
||||||
|
|
||||||
|
// Sync time via NTP
|
||||||
|
configTime(0, 0, "pool.ntp.org", "time.nist.gov");
|
||||||
|
struct tm timeinfo;
|
||||||
|
if (getLocalTime(&timeinfo, 5000)) {
|
||||||
|
Serial.printf("[%lu] [CAL] NTP time synced\n", millis());
|
||||||
|
}
|
||||||
|
|
||||||
|
renderStatus("Fetching...");
|
||||||
state = CalendarState::FETCHING_IMAGE;
|
state = CalendarState::FETCHING_IMAGE;
|
||||||
stateStartTime = millis();
|
stateStartTime = millis();
|
||||||
} else if (millis() - stateStartTime > WIFI_TIMEOUT_MS) {
|
} else if (millis() - stateStartTime > WIFI_TIMEOUT_MS) {
|
||||||
@ -194,6 +173,17 @@ void CalendarActivity::loop() {
|
|||||||
case CalendarState::FETCHING_IMAGE:
|
case CalendarState::FETCHING_IMAGE:
|
||||||
if (fetchAndSaveImage()) {
|
if (fetchAndSaveImage()) {
|
||||||
Serial.printf("[%lu] [CAL] Image saved successfully\n", millis());
|
Serial.printf("[%lu] [CAL] Image saved successfully\n", millis());
|
||||||
|
|
||||||
|
// Save fetch timestamp
|
||||||
|
time_t now;
|
||||||
|
time(&now);
|
||||||
|
if (now > 1700000000) { // Sanity check - after Nov 2023
|
||||||
|
APP_STATE.lastCalendarFetch = now;
|
||||||
|
APP_STATE.saveToFile();
|
||||||
|
Serial.printf("[%lu] [CAL] Saved fetch timestamp: %lu\n", millis(), (unsigned long)now);
|
||||||
|
}
|
||||||
|
|
||||||
|
renderStatus("Image saved!");
|
||||||
state = CalendarState::RENDERING;
|
state = CalendarState::RENDERING;
|
||||||
} else {
|
} else {
|
||||||
// Check if we have a cached image
|
// Check if we have a cached image
|
||||||
@ -207,14 +197,9 @@ void CalendarActivity::loop() {
|
|||||||
break;
|
break;
|
||||||
|
|
||||||
case CalendarState::RENDERING:
|
case CalendarState::RENDERING:
|
||||||
renderSleepScreen();
|
Serial.printf("[%lu] [CAL] Rendering complete, entering deep sleep\n", millis());
|
||||||
// After SleepActivity renders, we need to schedule sleep
|
enterCalendarDeepSleep(SETTINGS.calendarRefreshHours);
|
||||||
state = CalendarState::SCHEDULING_SLEEP;
|
// Never reaches here - device enters deep sleep
|
||||||
break;
|
|
||||||
|
|
||||||
case CalendarState::SCHEDULING_SLEEP:
|
|
||||||
scheduleWakeAndSleep();
|
|
||||||
// Should never reach here - device sleeps
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case CalendarState::ERROR:
|
case CalendarState::ERROR:
|
||||||
@ -224,7 +209,7 @@ void CalendarActivity::loop() {
|
|||||||
state = CalendarState::RENDERING;
|
state = CalendarState::RENDERING;
|
||||||
} else {
|
} else {
|
||||||
// No cached image - just sleep with default screen and try again later
|
// No cached image - just sleep with default screen and try again later
|
||||||
scheduleWakeAndSleep();
|
enterCalendarDeepSleep(SETTINGS.calendarRefreshHours);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|||||||
@ -19,15 +19,7 @@
|
|||||||
* 7. Enter deep sleep
|
* 7. Enter deep sleep
|
||||||
*/
|
*/
|
||||||
|
|
||||||
enum class CalendarState {
|
enum class CalendarState { INIT, CONNECTING_WIFI, FETCHING_IMAGE, RENDERING, ERROR };
|
||||||
INIT,
|
|
||||||
CONNECTING_WIFI,
|
|
||||||
FETCHING_IMAGE,
|
|
||||||
SAVING_IMAGE,
|
|
||||||
RENDERING,
|
|
||||||
SCHEDULING_SLEEP,
|
|
||||||
ERROR
|
|
||||||
};
|
|
||||||
|
|
||||||
class CalendarActivity final : public Activity {
|
class CalendarActivity final : public Activity {
|
||||||
public:
|
public:
|
||||||
@ -47,8 +39,6 @@ class CalendarActivity final : public Activity {
|
|||||||
void startWifiConnection();
|
void startWifiConnection();
|
||||||
bool checkWifiConnection();
|
bool checkWifiConnection();
|
||||||
bool fetchAndSaveImage();
|
bool fetchAndSaveImage();
|
||||||
void renderSleepScreen();
|
|
||||||
void scheduleWakeAndSleep();
|
|
||||||
void handleError(const char* message);
|
void handleError(const char* message);
|
||||||
void renderStatus(const char* status);
|
void renderStatus(const char* status);
|
||||||
|
|
||||||
|
|||||||
@ -10,10 +10,11 @@
|
|||||||
#include "MappedInputManager.h"
|
#include "MappedInputManager.h"
|
||||||
#include "OtaUpdateActivity.h"
|
#include "OtaUpdateActivity.h"
|
||||||
#include "fontIds.h"
|
#include "fontIds.h"
|
||||||
|
#include "../calendar/CalendarActivity.h"
|
||||||
|
|
||||||
// Define the static settings list
|
// Define the static settings list
|
||||||
namespace {
|
namespace {
|
||||||
constexpr int settingsCount = 20;
|
constexpr int settingsCount = 23;
|
||||||
const SettingInfo settingsList[settingsCount] = {
|
const SettingInfo settingsList[settingsCount] = {
|
||||||
// Should match with SLEEP_SCREEN_MODE
|
// Should match with SLEEP_SCREEN_MODE
|
||||||
SettingInfo::Enum("Sleep Screen", &CrossPointSettings::sleepScreen, {"Dark", "Light", "Custom", "Cover", "None"}),
|
SettingInfo::Enum("Sleep Screen", &CrossPointSettings::sleepScreen, {"Dark", "Light", "Custom", "Cover", "None"}),
|
||||||
@ -41,6 +42,10 @@ const SettingInfo settingsList[settingsCount] = {
|
|||||||
{"1 min", "5 min", "10 min", "15 min", "30 min"}),
|
{"1 min", "5 min", "10 min", "15 min", "30 min"}),
|
||||||
SettingInfo::Enum("Refresh Frequency", &CrossPointSettings::refreshFrequency,
|
SettingInfo::Enum("Refresh Frequency", &CrossPointSettings::refreshFrequency,
|
||||||
{"1 page", "5 pages", "10 pages", "15 pages", "30 pages"}),
|
{"1 page", "5 pages", "10 pages", "15 pages", "30 pages"}),
|
||||||
|
// Calendar mode settings
|
||||||
|
SettingInfo::Toggle("Calendar Mode", &CrossPointSettings::calendarModeEnabled),
|
||||||
|
SettingInfo::Value("Calendar Refresh (hrs)", &CrossPointSettings::calendarRefreshHours, {1, 24, 1}),
|
||||||
|
SettingInfo::Action("Test Calendar Now"),
|
||||||
SettingInfo::Action("Calibre Settings"),
|
SettingInfo::Action("Calibre Settings"),
|
||||||
SettingInfo::Action("Check for updates")};
|
SettingInfo::Action("Check for updates")};
|
||||||
} // namespace
|
} // namespace
|
||||||
@ -155,6 +160,11 @@ void SettingsActivity::toggleCurrentSetting() {
|
|||||||
updateRequired = true;
|
updateRequired = true;
|
||||||
}));
|
}));
|
||||||
xSemaphoreGive(renderingMutex);
|
xSemaphoreGive(renderingMutex);
|
||||||
|
} else if (strcmp(setting.name, "Test Calendar Now") == 0) {
|
||||||
|
xSemaphoreTake(renderingMutex, portMAX_DELAY);
|
||||||
|
exitActivity();
|
||||||
|
enterNewActivity(new CalendarActivity(renderer, mappedInput));
|
||||||
|
xSemaphoreGive(renderingMutex);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// Only toggle if it's a toggle type and has a value pointer
|
// Only toggle if it's a toggle type and has a value pointer
|
||||||
|
|||||||
56
src/main.cpp
56
src/main.cpp
@ -7,6 +7,7 @@
|
|||||||
#include <SPI.h>
|
#include <SPI.h>
|
||||||
#include <builtinFonts/all.h>
|
#include <builtinFonts/all.h>
|
||||||
#include <esp_sleep.h>
|
#include <esp_sleep.h>
|
||||||
|
#include <WiFi.h>
|
||||||
|
|
||||||
#include <cstring>
|
#include <cstring>
|
||||||
|
|
||||||
@ -198,6 +199,11 @@ void waitForPowerRelease() {
|
|||||||
|
|
||||||
// Enter deep sleep mode
|
// Enter deep sleep mode
|
||||||
void enterDeepSleep() {
|
void enterDeepSleep() {
|
||||||
|
// If calendar mode enabled, use calendar sleep screen to show /sleep.bmp
|
||||||
|
if (SETTINGS.calendarModeEnabled) {
|
||||||
|
SETTINGS.sleepScreen = CrossPointSettings::CALENDAR;
|
||||||
|
}
|
||||||
|
|
||||||
exitActivity();
|
exitActivity();
|
||||||
enterNewActivity(new SleepActivity(renderer, mappedInputManager));
|
enterNewActivity(new SleepActivity(renderer, mappedInputManager));
|
||||||
|
|
||||||
@ -211,6 +217,34 @@ void enterDeepSleep() {
|
|||||||
esp_deep_sleep_start();
|
esp_deep_sleep_start();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Calendar mode deep sleep - renders custom sleep screen and schedules timer wake
|
||||||
|
void enterCalendarDeepSleep(uint8_t refreshHours) {
|
||||||
|
// Set sleep screen to CALENDAR to use /sleep.bmp directly (bypasses /sleep/ directory)
|
||||||
|
SETTINGS.sleepScreen = CrossPointSettings::CALENDAR;
|
||||||
|
|
||||||
|
// Transition to SleepActivity to render the custom image
|
||||||
|
exitActivity();
|
||||||
|
enterNewActivity(new SleepActivity(renderer, mappedInputManager));
|
||||||
|
|
||||||
|
// Put display to sleep
|
||||||
|
einkDisplay.deepSleep();
|
||||||
|
|
||||||
|
// Disconnect WiFi to save power
|
||||||
|
WiFi.disconnect(true);
|
||||||
|
WiFi.mode(WIFI_OFF);
|
||||||
|
|
||||||
|
// Calculate sleep duration
|
||||||
|
uint64_t sleepDurationUs = (uint64_t)refreshHours * 60ULL * 60ULL * 1000000ULL;
|
||||||
|
Serial.printf("[%lu] [CAL] Sleep duration: %llu us (%d hours)\n", millis(), sleepDurationUs, refreshHours);
|
||||||
|
|
||||||
|
// Enable timer + GPIO wakeup
|
||||||
|
esp_sleep_enable_timer_wakeup(sleepDurationUs);
|
||||||
|
esp_deep_sleep_enable_gpio_wakeup(1ULL << InputManager::POWER_BUTTON_PIN, ESP_GPIO_WAKEUP_GPIO_LOW);
|
||||||
|
|
||||||
|
Serial.printf("[%lu] [CAL] Entering deep sleep\n", millis());
|
||||||
|
esp_deep_sleep_start(); // Never returns
|
||||||
|
}
|
||||||
|
|
||||||
void onGoHome();
|
void onGoHome();
|
||||||
void onGoToReader(const std::string& initialEpubPath) {
|
void onGoToReader(const std::string& initialEpubPath) {
|
||||||
exitActivity();
|
exitActivity();
|
||||||
@ -291,9 +325,30 @@ void setup() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
SETTINGS.loadFromFile();
|
SETTINGS.loadFromFile();
|
||||||
|
APP_STATE.loadFromFile();
|
||||||
|
|
||||||
// Check if this is a timer wake for calendar mode
|
// Check if this is a timer wake for calendar mode
|
||||||
esp_sleep_wakeup_cause_t wakeupCause = esp_sleep_get_wakeup_cause();
|
esp_sleep_wakeup_cause_t wakeupCause = esp_sleep_get_wakeup_cause();
|
||||||
|
|
||||||
|
// Check if calendar needs refresh (backup for power loss)
|
||||||
|
if (SETTINGS.calendarModeEnabled && wakeupCause != ESP_SLEEP_WAKEUP_TIMER) {
|
||||||
|
// Not a timer wake - check if we need a backup fetch
|
||||||
|
time_t now;
|
||||||
|
time(&now);
|
||||||
|
uint32_t elapsed = (now > APP_STATE.lastCalendarFetch) ?
|
||||||
|
(now - APP_STATE.lastCalendarFetch) : UINT32_MAX;
|
||||||
|
uint32_t thresholdSec = SETTINGS.calendarRefreshHours * 3600;
|
||||||
|
|
||||||
|
// If time is valid and elapsed > threshold, trigger backup fetch
|
||||||
|
if (now > 1700000000 && elapsed > thresholdSec) {
|
||||||
|
Serial.printf("[%lu] [ ] Calendar stale, triggering backup fetch\n", millis());
|
||||||
|
setupDisplayAndFonts();
|
||||||
|
exitActivity();
|
||||||
|
enterNewActivity(new CalendarActivity(renderer, mappedInputManager));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (wakeupCause == ESP_SLEEP_WAKEUP_TIMER && SETTINGS.calendarModeEnabled) {
|
if (wakeupCause == ESP_SLEEP_WAKEUP_TIMER && SETTINGS.calendarModeEnabled) {
|
||||||
Serial.printf("[%lu] [ ] Timer wake detected - entering calendar mode\n", millis());
|
Serial.printf("[%lu] [ ] Timer wake detected - entering calendar mode\n", millis());
|
||||||
setupDisplayAndFonts();
|
setupDisplayAndFonts();
|
||||||
@ -313,7 +368,6 @@ void setup() {
|
|||||||
exitActivity();
|
exitActivity();
|
||||||
enterNewActivity(new BootActivity(renderer, mappedInputManager));
|
enterNewActivity(new BootActivity(renderer, mappedInputManager));
|
||||||
|
|
||||||
APP_STATE.loadFromFile();
|
|
||||||
if (APP_STATE.openEpubPath.empty()) {
|
if (APP_STATE.openEpubPath.empty()) {
|
||||||
onGoHome();
|
onGoHome();
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user