diff --git a/firmware/src/reporter.cpp b/firmware/src/reporter.cpp index a896e71..a980a13 100644 --- a/firmware/src/reporter.cpp +++ b/firmware/src/reporter.cpp @@ -1,6 +1,7 @@ // firmware/src/reporter.cpp #include "reporter.h" #include "hmac.h" +#include "event_log.h" #include #include #include @@ -21,25 +22,46 @@ static uint32_t now_ts() { return (uint32_t)time(nullptr); } -static bool post_json(const DeviceConfig& cfg, const char* path, const String& body) { +static bool post_json_once(const DeviceConfig& cfg, const char* path, const String& body) { uint32_t ts = now_ts(); - // Reject if NTP hasn't synced yet (timestamp would be near epoch 0) - if (ts < 1700000000UL) return false; // pre-2023 → clock not valid + if (ts < 1700000000UL) return false; String sig = hmac_sign(cfg.hmac_secret, "POST", path, ts, body); - if (sig.isEmpty()) return false; // HMAC failed + if (sig.isEmpty()) return false; HTTPClient http; String url = String(REPORTER_API_HOST) + path; http.begin(url); + http.setConnectTimeout(5000); // DNS + TCP connect + http.setTimeout(10000); // per-transaction response timeout http.addHeader("Content-Type", "application/json"); http.addHeader("X-Device-Id", cfg.device_id); http.addHeader("X-Timestamp", String(ts)); http.addHeader("X-Signature", sig); + uint32_t t0 = millis(); int code = http.POST(body); + uint32_t elapsed = millis() - t0; http.end(); - Serial.printf("[HTTP] POST %s → %d\n", url.c_str(), code); - return (code == 200); + uint16_t phash = event_log_path_hash(path); + Serial.printf("[HTTP] POST %s -> %d (%u ms)\n", url.c_str(), code, (unsigned)elapsed); + if (code == 200) { + event_log_write(EVT_HTTP_OK, phash, (uint16_t)((elapsed > 65535) ? 65535 : elapsed)); + return true; + } + event_log_write(EVT_HTTP_FAIL, phash, (uint16_t)code); + return false; +} + +static bool post_json(const DeviceConfig& cfg, const char* path, const String& body) { + // 3 attempts with 0/2s/5s delays. Total worst case ~ 2x(5+10)s + 7s = 37s; + // acceptable for an hourly task and within the 30s TWDT once Task 6 lands + // IF TWDT is fed between attempts (see Task 6). + static const uint16_t DELAYS_MS[] = { 0, 2000, 5000 }; + for (int i = 0; i < 3; i++) { + if (DELAYS_MS[i]) vTaskDelay(pdMS_TO_TICKS(DELAYS_MS[i])); + if (post_json_once(cfg, path, body)) return true; + } + return false; } static String build_camera_batch(const DeviceConfig& cfg, @@ -147,7 +169,7 @@ void reporter_submit_ble(const DeviceConfig& cfg, const BLEHourlyRecord& rec) { } } -void reporter_heartbeat(const DeviceConfig& cfg, uint32_t uptime_s, int wifi_rssi) { +bool reporter_heartbeat(const DeviceConfig& cfg, uint32_t uptime_s, int wifi_rssi) { JsonDocument doc; doc["device_id"] = cfg.device_id; doc["firmware_version"] = "1.0.0"; @@ -156,7 +178,7 @@ void reporter_heartbeat(const DeviceConfig& cfg, uint32_t uptime_s, int wifi_rss doc["pending_records"] = (int)(s_cam_buf.size() + s_ble_buf.size()); doc["uptime_seconds"] = uptime_s; String body; serializeJson(doc, body); - post_json(cfg, "/api/v1/heartbeat", body); + return post_json(cfg, "/api/v1/heartbeat", body); } void reporter_flush(const DeviceConfig& cfg) { diff --git a/firmware/src/reporter.h b/firmware/src/reporter.h index 96e796e..cd3f49e 100644 --- a/firmware/src/reporter.h +++ b/firmware/src/reporter.h @@ -17,5 +17,5 @@ static const char* REPORTER_API_HOST = "http://logs.research.bike"; void reporter_init(); void reporter_submit_camera(const DeviceConfig& cfg, const CameraHourlyRecord& rec); void reporter_submit_ble(const DeviceConfig& cfg, const BLEHourlyRecord& rec); -void reporter_heartbeat(const DeviceConfig& cfg, uint32_t uptime_s, int wifi_rssi); +bool reporter_heartbeat(const DeviceConfig& cfg, uint32_t uptime_s, int wifi_rssi); void reporter_flush(const DeviceConfig& cfg);