|
@@ -183,14 +183,45 @@ pulse, no init/window/plane-write, just that one command table then `DONE`.
|
|
|
Not run automatically by `request_full_update()`: this engine does a full
|
|
Not run automatically by `request_full_update()`: this engine does a full
|
|
|
hardware reset every cycle regardless of prior power state, so correctness
|
|
hardware reset every cycle regardless of prior power state, so correctness
|
|
|
never depends on calling either - they exist purely for panel longevity
|
|
never depends on calling either - they exist purely for panel longevity
|
|
|
-(reduced fading between infrequent updates) and, eventually, MCU-side
|
|
|
|
|
-deep-sleep pairing. `request_hibernate()` runs `power_off_sequence` then
|
|
|
|
|
|
|
+(reduced fading between infrequent updates) and MCU-side deep-sleep pairing
|
|
|
|
|
+(see below). `request_hibernate()` runs `power_off_sequence` then
|
|
|
`hibernate_sequence` (hibernate()'s *tail* only, past whatever it shares with
|
|
`hibernate_sequence` (hibernate()'s *tail* only, past whatever it shares with
|
|
|
powerOff() - see below); no paired "wake" call exists or is needed, since
|
|
powerOff() - see below); no paired "wake" call exists or is needed, since
|
|
|
`request_full_update()`'s `RESET_LOW` is an unconditional hardware reset pulse
|
|
`request_full_update()`'s `RESET_LOW` is an unconditional hardware reset pulse
|
|
|
every cycle, which is exactly how GxEPD2 wakes a hibernating (RST-wakeable)
|
|
every cycle, which is exactly how GxEPD2 wakes a hibernating (RST-wakeable)
|
|
|
panel.
|
|
panel.
|
|
|
|
|
|
|
|
|
|
+**Deep-sleep pairing.** `GxEPD2Display::on_powerdown()` overrides
|
|
|
|
|
+`Component::on_powerdown()`, which ESPHome calls on every component right
|
|
|
|
|
+before `deep_sleep` actually suspends the MCU, and on `safe_reboot()` (see
|
|
|
|
|
+`Application::run_powerdown_hooks()`; `deep_sleep`'s `should_teardown_()` is
|
|
|
|
|
+unconditional on every supported platform, so this always fires before a
|
|
|
|
|
+sleep). It calls `hibernate()` if a reset pin is configured, else
|
|
|
|
|
+`power_off()` - no YAML/lambda needed for the common case, and no engine
|
|
|
|
|
+change, since `hibernate()`'s "no paired wake call needed" property already
|
|
|
|
|
+makes this free.
|
|
|
|
|
+
|
|
|
|
|
+This is deliberately NOT paired with a `Component::teardown()` override to
|
|
|
|
|
+wait out a mid-flight refresh. ESPHome's teardown budget is short and fixed
|
|
|
|
|
+by the *caller*, not by us - 1s on `safe_reboot()`
|
|
|
|
|
+(`Application::TEARDOWN_TIMEOUT_REBOOT_MS`), 5s on `deep_sleep`
|
|
|
|
|
+(`TEARDOWN_TIMEOUT_DEEP_SLEEP_MS`) - and it's a hard cap: `teardown_components()`
|
|
|
|
|
+busy-polls every component's `teardown()` until all return `true` or the
|
|
|
|
|
+budget expires, then logs one warning per straggler and proceeds regardless.
|
|
|
|
|
+That budget is sized for a network flush (see `api_server::teardown()`,
|
|
|
|
|
+one of only two overrides in ESPHome core, both networking), not a hardware
|
|
|
|
|
+operation - it's far shorter than this engine's slower `busy_timeout_ms`
|
|
|
|
|
+classes (up to 45-60s for 4bpp/7-color panels), so stalling `teardown()`
|
|
|
|
|
+here would just lose that race every time and get force-advanced anyway.
|
|
|
|
|
+If a refresh is still running when `on_powerdown()` fires,
|
|
|
|
|
+`power_off()`/`hibernate()`'s own idle-check skips it with a warning instead.
|
|
|
|
|
+`GxEPD2Display::idle()` (wraps `CycleEngine::idle()`) is exposed publicly for
|
|
|
|
|
+exactly this: a caller who cares about the race - or a more complex
|
|
|
|
|
+application tracking its own multi-step update sequence - can check it and
|
|
|
|
|
+decide for themselves whether to hold off triggering deep sleep, rather than
|
|
|
|
|
+the component trying to enforce a resolution the framework itself won't
|
|
|
|
|
+support.
|
|
|
|
|
+
|
|
|
Verifying `power_off()`/`hibernate()` actually took effect needs the panel on
|
|
Verifying `power_off()`/`hibernate()` actually took effect needs the panel on
|
|
|
a bench, since the real effect is electrical (driving voltage off, or deep
|
|
a bench, since the real effect is electrical (driving voltage off, or deep
|
|
|
sleep) - nothing the MCU can see directly. `hibernate()`'s documented
|
|
sleep) - nothing the MCU can see directly. `hibernate()`'s documented
|