feat(firmware): honor server X-Interval-Ms instead of capping at 60s

The dev-only cap that forced every-1-min polling regardless of the app's
schedule is removed. The device now sleeps for whatever X-Interval-Ms
the server hands back (driven by rotationIntervalMinutes / wakeTimes),
clamped to [30s, 25h] as a safety net against malformed values.

Renamed FETCH_INTERVAL_MS to FETCH_INTERVAL_MS_FALLBACK — it's now
*only* used when the header is absent (rare; rolling deploy / hand-
crafted response). Added SLEEP_CLAMP_MIN/MAX for the bounds.

Tests FW-09 and FW-10 flipped to lock the new behavior; added FW-10b
covering sub-MIN clamping (battery protection if server sends 1000ms).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-05-07 15:34:20 -04:00
parent 8915a3d1f4
commit 988759f738
4 changed files with 61 additions and 44 deletions
+18 -14
View File
@@ -122,19 +122,23 @@
#define APP_BASE_URL "https://pictureframe.edholm.me"
#define AP_IP "192.168.4.1"
#define WIFI_TIMEOUT_MS 30000
#ifndef FETCH_INTERVAL_MS
// TODO(post-dev): drop the 60s cap. Today this value is used in
// operation.h as BOTH the no-header fallback AND the upper bound that
// clamps the server-provided X-Interval-Ms. While we're iterating on the
// firmware we want the frame to poll every minute so changes land fast,
// but in production we want to honor whatever the app sends (e.g.,
// rotationIntervalMinutes=60 → 1 hour, or wakeHour set → ~24 h sleep).
// When the firmware stabilizes, split this into two constants:
// - FETCH_INTERVAL_MS_FALLBACK (used when no X-Interval-Ms header)
// - SLEEP_CLAMP_MIN_MS / SLEEP_CLAMP_MAX_MS (sanity bounds, not the
// primary schedule)
// and let server values flow through. See operation.h:138 for the cap
// site, and tests FW-09/FW-10 for the assertions that will need updating.
#define FETCH_INTERVAL_MS 60000 // 1 min deep sleep between polls (DEV value)
// Server's X-Interval-Ms is the primary schedule — driven by the user's
// rotationIntervalMinutes / wakeTimes settings. The constants below are
// only safety nets:
// - FALLBACK is used when the server omits the header (shouldn't happen
// in normal operation, but guards against a rolling deploy or hand-
// crafted response).
// - CLAMP_MIN/MAX bound the server value to sane physical limits — no
// runaway polling on a malformed 0/negative, no week-long naps on a
// misconfigured 999 days. CLAMP_MAX is just past 24 h to give DST
// transitions and edge-case wake-time math a little slack.
#ifndef FETCH_INTERVAL_MS_FALLBACK
#define FETCH_INTERVAL_MS_FALLBACK 60000ULL // 1 min, used only when no X-Interval-Ms header
#endif
#ifndef SLEEP_CLAMP_MIN_MS
#define SLEEP_CLAMP_MIN_MS 30000ULL // 30 s — protect against runaway polls
#endif
#ifndef SLEEP_CLAMP_MAX_MS
#define SLEEP_CLAMP_MAX_MS (25ULL * 60ULL * 60ULL * 1000ULL) // 25 h — past 24h with DST slack
#endif
#define IMAGE_PATH "/img.bin"