From d900083398e32eba6c7518e9779a5b9007fd2d5d Mon Sep 17 00:00:00 2001 From: Matt Edholm Date: Fri, 15 May 2026 14:15:37 -0400 Subject: [PATCH] chore(13e6): TEMP power-monitor telemetry headers MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit To validate the PIN_PWR rail-cut change (e2c9d8f) without a bench multimeter, have the device report its previous cycle's awake time and panel-init time on each poll: X-Prev-Awake-Ms — millis() at the moment esp_deep_sleep_start armed, last cycle. Total awake duration since reset, ~5–10 s steady-state. X-Prev-Panel-Init-Ms — duration of epd_init() last cycle. Spikes here would suggest the rail isn't coming back up cleanly after the GPIO-hold release. Headers are sent only when the cached NVS values are non-zero (skips the first boot under this firmware). All call sites marked `// TEMP: power-monitor` for clean removal once the change is validated. Two new NVS keys (tm_awk, tm_pin) sit alongside the existing ones; mock Preferences extended with getUInt/putUInt to match. Server side logs the headers via `device.poll.power_telemetry` (separate commit in pictureFrame-webApp). Co-Authored-By: Claude Opus 4.7 --- src/config.h | 7 +++++++ src/operation.h | 31 +++++++++++++++++++++++++++++++ test/mocks/Preferences.h | 9 ++++++++- 3 files changed, 46 insertions(+), 1 deletion(-) diff --git a/src/config.h b/src/config.h index f92ba99..82fb7b0 100644 --- a/src/config.h +++ b/src/config.h @@ -117,6 +117,13 @@ // would silently keep displaying the prior owner's photos until the new // owner happens to navigate to /setup/{mac}. #define NVS_KEY_JUST_PROVISIONED "just_prov" +// TEMP: power-monitor — stores previous cycle's awake duration and +// epd_init() duration so the next boot can report them server-side as +// X-Prev-Awake-Ms / X-Prev-Panel-Init-Ms. Lets us verify PIN_PWR rail +// cut doesn't slow panel re-init or extend the awake window. Remove +// these keys + their reads/writes once the change is validated. +#define NVS_KEY_PREV_AWAKE_MS "tm_awk" +#define NVS_KEY_PREV_PANEL_INIT_MS "tm_pin" // Bump when introducing a schema migration. Each new value can force a one-shot // recovery action on first boot of the new firmware. diff --git a/src/operation.h b/src/operation.h index e193760..ea568c2 100644 --- a/src/operation.h +++ b/src/operation.h @@ -116,6 +116,12 @@ void normal_operation_impl(const String& mac, HTTP& http, const String& url, Pre bool errBorder = prefs.getInt(NVS_KEY_ERR_BORDER, 0) != 0; int schemaV = prefs.getInt(NVS_KEY_SCHEMA_V, 0); bool justProvisioned = prefs.getInt(NVS_KEY_JUST_PROVISIONED, 0) != 0; + // TEMP: power-monitor — previous cycle's awake + panel-init durations. + // Reported as X-Prev-Awake-Ms / X-Prev-Panel-Init-Ms below. Remove + // along with the corresponding writes near deep_sleep_start + the + // epd_init() timing block once the PIN_PWR cut is validated. + uint32_t prevAwakeMs = prefs.getUInt(NVS_KEY_PREV_AWAKE_MS, 0); + uint32_t prevPanelInitMs = prefs.getUInt(NVS_KEY_PREV_PANEL_INIT_MS, 0); prefs.end(); // Schema migration: on first boot under err-border-aware firmware, the @@ -162,6 +168,17 @@ void normal_operation_impl(const String& mac, HTTP& http, const String& url, Pre http.addHeader("X-Just-Provisioned", "1"); } + // TEMP: power-monitor — last cycle's awake + panel-init times. + // Lets us see, server-side, whether the PIN_PWR rail cut affects + // either. Send only if non-zero (skips the first boot after a + // firmware that didn't store them). + if (prevAwakeMs > 0) { + http.addHeader("X-Prev-Awake-Ms", String((unsigned long)prevAwakeMs)); + } + if (prevPanelInitMs > 0) { + http.addHeader("X-Prev-Panel-Init-Ms", String((unsigned long)prevPanelInitMs)); + } + const char* collectHeaders[] = { "X-Interval-Ms", "X-Image-Id", "X-Image-Sha256", "X-Claimed" }; http.collectHeaders(collectHeaders, 4); int code = http.GET(); @@ -244,7 +261,12 @@ void normal_operation_impl(const String& mac, HTTP& http, const String& url, Pre } displayInitialized = true; + // TEMP: power-monitor — time the panel init, stored in NVS at + // end of cycle for next boot to report. Remove the timing + // wrapper when the PIN_PWR cut is validated. + uint32_t panelInitStart = millis(); epd_init(); + uint32_t panelInitMs = millis() - panelInitStart; File r = LittleFS.open(IMAGE_PATH, "r"); if (r) { epd_draw_image_from_file(r); r.close(); } @@ -253,6 +275,7 @@ void normal_operation_impl(const String& mac, HTTP& http, const String& url, Pre prefs.begin(NVS_NAMESPACE, false); prefs.putInt(NVS_KEY_DRAW_NEEDED, 0); prefs.putInt(NVS_KEY_ERR_BORDER, 0); + prefs.putUInt(NVS_KEY_PREV_PANEL_INIT_MS, panelInitMs); // TEMP prefs.end(); } @@ -362,6 +385,14 @@ void normal_operation_impl(const String& mac, HTTP& http, const String& url, Pre // up, losing the saving. Released per-pin in epd_setup_pins() on // wake via gpio_hold_dis(). gpio_deep_sleep_hold_en(); + + // TEMP: power-monitor — millis() at this point is the total awake + // duration since boot. Next boot reads it back and reports as + // X-Prev-Awake-Ms. Remove when PIN_PWR cut is validated. + prefs.begin(NVS_NAMESPACE, false); + prefs.putUInt(NVS_KEY_PREV_AWAKE_MS, millis()); + prefs.end(); + esp_deep_sleep_start(); } diff --git a/test/mocks/Preferences.h b/test/mocks/Preferences.h index 65c1694..ee9976d 100644 --- a/test/mocks/Preferences.h +++ b/test/mocks/Preferences.h @@ -8,6 +8,7 @@ extern int g_prefs_putint_seq; // sequence position of last putInt call struct Preferences { std::map ints; + std::map uints; std::map strings; bool _open = false; @@ -26,10 +27,16 @@ struct Preferences { g_call_seq++; } + uint32_t getUInt(const char* key, uint32_t def = 0) { + auto it = uints.find(key); + return it != uints.end() ? it->second : def; + } + void putUInt(const char* key, uint32_t val) { uints[key] = val; } + String getString(const char* key, const char* def = "") { auto it = strings.find(key); return it != strings.end() ? String(it->second) : String(def); } void putString(const char* key, const String& val) { strings[key] = val._s; } - void clear() { ints.clear(); strings.clear(); } + void clear() { ints.clear(); uints.clear(); strings.clear(); } };