feat(reporter): apply server-pushed CV tuning from heartbeat response

Heartbeat POST now captures the response body (up to 2048 bytes) and
looks for a "config" object. If cfg_version advances past the stored
value and all tunable fields pass range validation, the new tuning is
applied to g_cv and persisted to NVS.

- cv_tuning_validate: pure range checker (cv.cpp)
- cv_apply_tuning / cv_get_tuning: mutex-guarded helpers in main.cpp
  exposed via cv_apply.h; 500 ms timeout, drop on contention
- post_json now returns int (HTTP status) and optionally captures the
  response body; existing callers check == 200
- heartbeat: parse → cfg_version check → override present fields →
  validate → apply → save. Silent no-op when server returns no config.
- 3 new native tests (15/15 pass). timercam flash 1,423,897 bytes
  (+9,828 vs baseline).
This commit is contained in:
2026-04-16 17:34:34 -07:00
parent 94d74e425c
commit 21f3bc77d1
6 changed files with 169 additions and 9 deletions

View File

@@ -36,6 +36,16 @@ void cv_reset_counts(CVState& state) {
state.exits = 0;
}
bool cv_tuning_validate(const CVTuning& t) {
if (t.cfg_version == 0) return false;
if (t.diff_thresh < 5 || t.diff_thresh > 120) return false;
if (t.min_blob_px < 16 || t.min_blob_px > 4096) return false;
if (t.max_move < 2.0f || t.max_move > 50.0f) return false;
if (t.max_missed < 1 || t.max_missed > 60) return false;
if (t.line_offset > 100) return false; // uint8, min 0
return true;
}
struct Point { int x, y; };
// Note: queue may grow to CV_PIXELS entries (~72KB) on large blobs.