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
+13 -11
View File
@@ -131,20 +131,22 @@ void normal_operation_impl(const String& mac, HTTP& http, const String& url, Pre
http.collectHeaders(collectHeaders, 3);
int code = http.GET();
// TODO(post-dev): trust the server's X-Interval-Ms instead of capping
// it at FETCH_INTERVAL_MS. The cap is here so the dev unit polls
// every minute regardless of what the app's rotationIntervalMinutes /
// wakeHour settings say — fast iteration. Once the firmware is stable
// and we want real battery life on V2, the line below should become
// simply `sleepMs = v;` plus a sanity clamp (e.g. min 30 s, max 25 h).
// Tests FW-09 and FW-10 in test_normal_operation/test_main.cpp lock
// the current behavior — update them when removing the cap. See the
// matching note in config.h on FETCH_INTERVAL_MS.
uint64_t sleepMs = FETCH_INTERVAL_MS;
// Honor the server's X-Interval-Ms — that's the user's configured
// rotationIntervalMinutes / wakeTimes schedule, computed in
// DeviceImageController::computeIntervalMs. Clamp to sane physical
// limits so a malformed 0/garbage value doesn't burn the battery
// (CLAMP_MIN) and a misconfigured "every 999 days" doesn't strand the
// device for a week (CLAMP_MAX). When no header is present (server
// bug, mid-deploy), fall back to FETCH_INTERVAL_MS_FALLBACK.
uint64_t sleepMs = FETCH_INTERVAL_MS_FALLBACK;
String intervalHdr = http.header("X-Interval-Ms");
if (intervalHdr.length() > 0) {
uint64_t v = strtoull(intervalHdr.c_str(), nullptr, 10);
if (v > 0) sleepMs = std::min<uint64_t>(v, (uint64_t)FETCH_INTERVAL_MS);
if (v > 0) {
sleepMs = v;
if (sleepMs < SLEEP_CLAMP_MIN_MS) sleepMs = SLEEP_CLAMP_MIN_MS;
if (sleepMs > SLEEP_CLAMP_MAX_MS) sleepMs = SLEEP_CLAMP_MAX_MS;
}
}
bool displayInitialized = false;