feat: production-ready firmware with BLE memory management, device_id fixes, and docs

- Reduce debug level to 1 (errors only) for production builds
- Replace BLE pause/resume with full deinit/reinit during HTTP uploads (~25KB freed)
- Add 60s boot report delay for fast post-deploy connectivity verification
- Add device_id to BLE batch and heartbeat request bodies
- Correct API host to http:// (plain HTTP, not HTTPS)
- Add HTTP response logging and CV entry/exit serial logging
- Create root README.md with operator setup and architecture overview
- Update design spec: HMAC format, BLE memory approach, request body shapes, reporting intervals

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-04-16 11:13:50 -07:00
parent 4b671843b3
commit 9d5b588231
8 changed files with 190 additions and 26 deletions

View File

@@ -18,6 +18,7 @@
#define CAM_FPS 5
#define CAM_INTERVAL_MS (1000 / CAM_FPS)
#define REPORT_INTERVAL_S 3600
#define BOOT_REPORT_DELAY_S 60 // first report fires 60s after NTP sync
static DeviceConfig g_cfg;
static CVState g_cv;
@@ -44,7 +45,9 @@ static void task_camera(void*) {
while (true) {
if (camera_capture_96(frame)) {
if (xSemaphoreTake(s_cv_mutex, pdMS_TO_TICKS(100)) == pdTRUE) {
cv_process(g_cv, frame, g_cfg.line_offset);
CVResult r = cv_process(g_cv, frame, g_cfg.line_offset);
if (r.entries_delta) Serial.printf("[CV] entry +%d (total %d)\n", r.entries_delta, g_cv.entries);
if (r.exits_delta) Serial.printf("[CV] exit +%d (total %d)\n", r.exits_delta, g_cv.exits);
xSemaphoreGive(s_cv_mutex);
}
}
@@ -62,8 +65,11 @@ static void task_reporter(void*) {
uint32_t now = (uint32_t)(time(nullptr));
if (now < 1700000000UL) continue; // NTP not synced
// First valid timestamp — initialize without reporting
if (last_report_ts == 0) { last_report_ts = now; continue; }
// First valid timestamp — schedule boot report 60s from now
if (last_report_ts == 0) {
last_report_ts = now - (REPORT_INTERVAL_S - BOOT_REPORT_DELAY_S);
continue;
}
if ((now - last_report_ts) < REPORT_INTERVAL_S) continue;
@@ -71,9 +77,9 @@ static void task_reporter(void*) {
uint32_t period_end = now;
last_report_ts = now;
// Pause BLE during upload
ble_scanner_pause();
led_set(true); // yellow indicator (single LED: on = uploading)
// Deinit BLE to free ~25KB heap for SSL handshakes
ble_scanner_deinit();
led_set(true); // on = uploading
CameraHourlyRecord cam_rec;
if (xSemaphoreTake(s_cv_mutex, pdMS_TO_TICKS(500)) == pdTRUE) {
@@ -82,7 +88,7 @@ static void task_reporter(void*) {
xSemaphoreGive(s_cv_mutex);
} else {
// Failed to acquire — skip this cycle, will report next hour
ble_scanner_resume();
ble_scanner_reinit();
led_set(false);
continue;
}
@@ -93,7 +99,7 @@ static void task_reporter(void*) {
reporter_submit_ble(g_cfg, ble_rec);
reporter_heartbeat(g_cfg, millis() / 1000, WiFi.RSSI());
ble_scanner_resume();
ble_scanner_reinit();
led_set(false);
}
}