From 5c9f5df0ceace3deb7a53af96083d7970e390d41 Mon Sep 17 00:00:00 2001 From: Peter Woolery Date: Thu, 23 Apr 2026 13:54:55 -0700 Subject: [PATCH] feat(firmware): include diagnostics in heartbeat payload Heartbeat v1.1.0 now carries heap stats (free + min_free since boot), esp_reset_reason(), last WiFi disconnect code, and the last 8 persisted event-log entries. Makes field failures diagnosable server-side without retrieving the device: the post-reboot heartbeat will include EVT_BOOT with reset reason and whatever EVT_WIFI_DOWN or EVT_HTTP_FAIL entries preceded it. --- firmware/src/reporter.cpp | 25 ++++++++++++++++++++++++- 1 file changed, 24 insertions(+), 1 deletion(-) diff --git a/firmware/src/reporter.cpp b/firmware/src/reporter.cpp index 8880f4c..b3ed72b 100644 --- a/firmware/src/reporter.cpp +++ b/firmware/src/reporter.cpp @@ -2,6 +2,7 @@ #include "reporter.h" #include "hmac.h" #include "event_log.h" +#include "net_guard.h" #include #include #include @@ -9,6 +10,8 @@ #include #include #include +#include +#include static std::vector s_cam_buf; static std::vector s_ble_buf; @@ -175,11 +178,31 @@ void reporter_submit_ble(const DeviceConfig& cfg, const BLEHourlyRecord& rec) { 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"; + doc["firmware_version"] = "1.1.0"; doc["free_storage_pct"] = 100; doc["wifi_rssi"] = wifi_rssi; doc["pending_records"] = (int)(s_cam_buf.size() + s_ble_buf.size()); doc["uptime_seconds"] = uptime_s; + + // Diagnostics (new in 1.1.0) + doc["reset_reason"] = (int)esp_reset_reason(); + doc["heap_free"] = (int)esp_get_free_heap_size(); + doc["heap_min_free"] = (int)esp_get_minimum_free_heap_size(); + doc["last_disconnect_code"] = (int)net_guard_last_disconnect_reason(); + + // Last 8 event-log entries, newest first + EventLogEntry recent[8]; + size_t n = event_log_read_recent(recent, 8); + JsonArray evs = doc["recent_events"].to(); + for (size_t i = 0; i < n; i++) { + JsonObject e = evs.add(); + e["t"] = recent[i].tag; + e["d0"] = recent[i].data0; + e["d1"] = recent[i].data1; + e["ts"] = recent[i].ts_unix; + e["up"] = recent[i].uptime_s; + } + String body; serializeJson(doc, body); return post_json(cfg, "/api/v1/heartbeat", body); }