CDC Badge OS
Firmware for the CDC Badge v1.0 hardware security key
Loading...
Searching...
No Matches
SleepManager.cpp
Go to the documentation of this file.
3#include "cdc_ui/ViewStack.h"
6#include "cdc_hal/IDisplay.h"
7#include "cdc_core/EventBus.h"
8#include "usb_badge/usb_cdc.h"
9#include "cdc_log.h"
10#include "esp_timer.h"
11#include <ctime>
12#include <cstdio>
13#include <cstring>
14
15static const char* TAG = "SleepMgr";
16
17namespace cdc::ui {
18
23
28SleepManager& SleepManager::instance() {
29 static SleepManager s_instance;
30 return s_instance;
31}
32
41 sleep_ = sleep;
42 power_ = power;
43 lockScreen_ = lockScreen;
44 lockScreenEnteredMs_ = 0;
45 inLightSleep_ = false;
46}
47
53void SleepManager::resetTimer(uint32_t nowMs) {
54 lockScreenEnteredMs_ = nowMs;
55}
56
62 lockScreenEnteredMs_ = esp_timer_get_time() / 1000;
63}
64
71 // Only on lock screen (depth == 1)
72 if (ViewStack::instance().depth() != 1) return;
73 if (!lockScreen_) return;
74
75 // Initialize timer if needed
76 if (lockScreenEnteredMs_ == 0) {
77 lockScreenEnteredMs_ = nowMs;
78 return;
79 }
80
81 // Skip if USB connected (keep device responsive)
82 if (power_ && power_->isUsbConnected()) {
83 lockScreenEnteredMs_ = nowMs; // Reset timer
84 // Remove light sleep icon if shown
85 if ((lockScreen_->getStatusIcons() & StatusIcon::LIGHT_SLEEP) != StatusIcon::NONE) {
86 lockScreen_->removeStatusIcon(StatusIcon::LIGHT_SLEEP);
87 }
88 return;
89 }
90
91 // Skip if sleep is inhibited (caffeinated mode)
92 if (isSleepInhibited()) {
93 lockScreenEnteredMs_ = nowMs; // Reset timer
94 return;
95 }
96
97 // Check timeout
98 uint32_t elapsed = nowMs - lockScreenEnteredMs_;
99 if (elapsed >= LIGHT_SLEEP_TIMEOUT_MS) {
100 enterLockScreenSleep();
101 }
102}
103
108void SleepManager::enterLockScreenSleep() {
109 if (!lockScreen_ || !sleep_) return;
110
111 auto& bus = cdc::core::EventBus::instance();
113 bus.process();
114
116 inLightSleep_ = true;
117
118 // Render the icon synchronously so the full SPI command/data stream reaches
119 // the panel before light sleep halts the chip; otherwise an in-flight
120 // refresh is frozen mid-transfer and the panel is left half-rendered.
122
123 // Light-sleep loop: timer wakeups refresh the clock and re-enter sleep at
124 // constant stack depth; a key/USB/unknown wakeup exits the loop.
125 do {
126 sleep_->enterLightSleep();
127 } while (handleWakeup());
128
129 // We only sleep while USB is absent; if USB is present after the loop it was
130 // plugged in during sleep, so force a re-enumeration to make the host attach
131 // without a manual re-plug. Covers every exit path, including a key-press
132 // wakeup that happens to discover USB.
133 if (power_ && power_->isUsbConnected()) {
135 }
136}
137
143bool SleepManager::handleWakeup() {
144 if (!sleep_ || !lockScreen_) return false;
145
146 // Immediately ensure backlight is off after wakeup to prevent glitches
147 // (LEDC may briefly show wrong state after light sleep)
149 if (display && !display->isBacklightOn()) {
150 display->backlightOff();
151 }
152
153 // The charger wakeup callback refreshed the cached status during recovery,
154 // so USB presence is current here even though the main loop did not run.
155 bool usb = power_ && power_->isUsbConnected();
156
157 // Decide whether this wakeup fully wakes the device (exit the loop) or is a
158 // periodic refresh (timer, or a charger interrupt without USB such as the
159 // BQ25895 watchdog) that only updates the clock and re-enters sleep.
160 bool userWake;
161 if (usb) {
162 userWake = true; // USB plugged in during sleep -> wake fully
163 } else {
164 hal::WakeupSource source = sleep_->getWakeupSource();
165 if (source == hal::WakeupSource::TIMER) {
166 userWake = false;
167 } else if (source == hal::WakeupSource::GPIO) {
168 // Keypad and charger interrupts both report GPIO; only a key press
169 // wakes. A charger-only interrupt without USB re-enters sleep.
170 userWake = sleep_->wasKeypadWakeup();
171 } else {
172 userWake = true; // unknown source -> treat as user interaction
173 }
174 }
175
176 if (userWake) {
177 lockScreen_->removeStatusIcon(StatusIcon::LIGHT_SLEEP);
178 lockScreenEnteredMs_ = esp_timer_get_time() / 1000; // Reset timer
179 inLightSleep_ = false;
181 lockScreen_->markDirty();
182 return false;
183 }
184
185 // Refresh-only wakeup: update the clock, keep the sleep icon, re-enter sleep.
186 char hhmm[6] = {0};
187 time_t now = time(nullptr);
188 struct tm* tm = localtime(&now);
189 if (tm) {
190 char buf[40];
191 snprintf(hhmm, sizeof(hhmm), "%02d:%02d", tm->tm_hour, tm->tm_min);
192 lockScreen_->setClock(hhmm);
193 snprintf(buf, sizeof(buf), "%02d.%02d.%04d", tm->tm_mday, tm->tm_mon + 1, tm->tm_year + 1900);
194 lockScreen_->setDate(buf);
195 }
196
198
199 // Repaint only when the displayed minute changed, so the extra charger
200 // wakeups add no e-paper writes. The lock screen declares
201 // prefersLightRefresh(), so this stays a pure partial refresh.
202 if (strcmp(hhmm, lastRenderedHHMM_) != 0) {
203 strncpy(lastRenderedHHMM_, hhmm, sizeof(lastRenderedHHMM_) - 1);
204 lastRenderedHHMM_[sizeof(lastRenderedHHMM_) - 1] = '\0';
206 }
207 return true;
208}
209
213
219bool SleepManager::addSleepInhibitor(const char* reason) {
220 if (!reason) return false;
221
222 // Check if already exists
223 for (uint8_t i = 0; i < inhibitorCount_; i++) {
224 if (inhibitors_[i] && strcmp(inhibitors_[i], reason) == 0) {
225 return false; // Already exists
226 }
227 }
228
229 // Check if full
230 if (inhibitorCount_ >= MAX_SLEEP_INHIBITORS) {
231 LOG_W(TAG, "Sleep inhibitor list full, cannot add: %s", reason);
232 return false;
233 }
234
235 // Add inhibitor
236 inhibitors_[inhibitorCount_++] = reason;
237 LOG_I(TAG, "Sleep inhibitor added: %s (count=%d)", reason, inhibitorCount_);
238
239 // Update icon
240 updateCaffeinatedIcon();
241 return true;
242}
243
249bool SleepManager::removeSleepInhibitor(const char* reason) {
250 if (!reason) return false;
251
252 // Find and remove
253 for (uint8_t i = 0; i < inhibitorCount_; i++) {
254 if (inhibitors_[i] && strcmp(inhibitors_[i], reason) == 0) {
255 // Shift remaining entries
256 for (uint8_t j = i; j < inhibitorCount_ - 1; j++) {
257 inhibitors_[j] = inhibitors_[j + 1];
258 }
259 inhibitors_[--inhibitorCount_] = nullptr;
260
261 LOG_I(TAG, "Sleep inhibitor removed: %s (count=%d)", reason, inhibitorCount_);
262
263 // Update icon
264 updateCaffeinatedIcon();
265 return true;
266 }
267 }
268
269 return false; // Not found
270}
271
276void SleepManager::updateCaffeinatedIcon() {
277 if (!lockScreen_) return;
278
279 if (inhibitorCount_ > 0) {
281 } else {
283 }
284}
285
286} // namespace cdc::ui
static const char * TAG
CDC Log: logging over TinyUSB CDC and UART.
#define LOG_W(tag, fmt,...)
Definition cdc_log.h:146
#define LOG_I(tag, fmt,...)
Definition cdc_log.h:147
static EventBus & instance()
Returns singleton event-bus instance.
Definition EventBus.cpp:19
virtual bool isUsbConnected() const =0
virtual void enterLightSleep()=0
void removeStatusIcon(StatusIcon icon)
Removes one status icon flag.
void addStatusIcon(StatusIcon icon)
Adds one status icon flag.
bool isSleepInhibited() const
void resetTimer()
Resets lock-screen sleep timer using current system tick.
void checkLockScreenSleep(uint32_t nowMs)
Evaluates whether lock-screen light sleep should be entered.
bool addSleepInhibitor(const char *reason)
Sleep inhibitor API implementation.
static SleepManager & instance()
Returns singleton sleep manager instance.
bool removeSleepInhibitor(const char *reason)
Removes a sleep inhibitor reason.
void init(hal::ISleepController *sleep, hal::IPowerManager *power, LockScreenView *lockScreen)
Initializes sleep-manager dependencies and state.
void render(bool synchronous=false)
Render current view (and modal if present) and flush to display.
static ViewStack & instance()
Returns singleton view-stack instance.
Definition ViewStack.cpp:53
IDisplay * getDisplayInstance()
Returns lazily created singleton display instance.
Centralized key-code constants for cdc_views.
Definition IModule.h:8
static constexpr uint8_t MAX_SLEEP_INHIBITORS
static constexpr uint32_t LIGHT_SLEEP_TIMEOUT_MS
Gdey029T94 * display
void updatePowerStatusIcons()
Synchronizes lock-screen status icons with current hardware state.
Definition AppUi.cpp:296
static const char * TAG
void usb_cdc_reenumerate(void)
Forces a USB re-enumeration by toggling the bus connection.
Definition usb_cdc.cpp:193