dashboard: add hidden diagnostics overlay

This commit is contained in:
2026-06-15 21:20:55 -06:00
parent 44128f83be
commit 33062fb0ba
7 changed files with 239 additions and 1 deletions
@@ -1,6 +1,6 @@
#pragma once
static const char *DASHBOARD_VERSION = "0.1.74-relay-latest-state-queue";
static const char *DASHBOARD_VERSION = "0.1.75-hidden-diagnostics";
// Update these if your Cargo ESP AP credentials are different.
static const char *CARGO_WIFI_SSID = "OverlandController";
@@ -24,4 +24,10 @@ struct DashboardStatus {
unsigned long uptime_seconds = 0;
String cargo_ap_ssid = "unknown";
String cargo_sta_ip = "unknown";
int cargo_ap_clients = 0;
int cargo_sta_rssi_dbm = 0;
unsigned long cargo_free_heap = 0;
unsigned long cargo_min_free_heap = 0;
String cargo_chip_model = "unknown";
int cargo_cpu_mhz = 0;
};
@@ -40,6 +40,13 @@ static lv_obj_t *vehicle_fuel_value_label = nullptr;
static lv_obj_t *relay_buttons[6] = {nullptr};
static lv_obj_t *relay_button_labels[6] = {nullptr};
static lv_obj_t *diagnostics_overlay = nullptr;
static lv_obj_t *diagnostics_label = nullptr;
static unsigned long diagnostics_last_update_ms = 0;
static unsigned long diagnostics_last_tap_ms = 0;
static unsigned long system_card_last_tap_ms = 0;
static int system_card_tap_count = 0;
static String relay_ids[6];
static String relay_names[6];
static bool relay_states[6] = {false};
@@ -353,8 +360,200 @@ static void request_fast_status_refresh()
fast_status_refresh_requested = false;
}
static String format_diag_uptime(unsigned long seconds)
{
unsigned long days = seconds / 86400;
seconds %= 86400;
unsigned long hours = seconds / 3600;
seconds %= 3600;
unsigned long minutes = seconds / 60;
String out;
if (days > 0) {
out += String(days);
out += "d ";
}
out += String(hours);
out += "h ";
out += String(minutes);
out += "m";
return out;
}
static void update_diagnostics_overlay()
{
if (diagnostics_overlay == nullptr || diagnostics_label == nullptr) {
return;
}
if (millis() - diagnostics_last_update_ms < 500) {
return;
}
diagnostics_last_update_ms = millis();
String text;
text += "DIAGNOSTICS\n\n";
text += "Dashboard ESP32-S3\n";
text += " Version: ";
text += DASHBOARD_VERSION;
text += "\n Uptime: ";
text += format_diag_uptime(millis() / 1000);
text += "\n Free heap: ";
text += String(ESP.getFreeHeap());
text += "\n Min heap: ";
text += String(ESP.getMinFreeHeap());
text += "\n WiFi: ";
text += WiFi.status() == WL_CONNECTED ? "connected" : "offline";
text += "\n RSSI: ";
text += WiFi.status() == WL_CONNECTED ? String(WiFi.RSSI()) : String("--");
text += " dBm\n IP: ";
text += WiFi.status() == WL_CONNECTED ? WiFi.localIP().toString() : String("--");
text += "\n Last HTTP: ";
text += String(status_model.http_code);
text += "\n Cargo API: ";
text += status_model.api_ok ? "OK" : "FAIL";
text += "\n\nCargo ESP\n";
text += " Version: ";
text += status_model.firmware_version;
text += "\n Uptime: ";
text += format_diag_uptime(status_model.uptime_seconds);
text += "\n Chip: ";
text += status_model.cargo_chip_model;
text += "\n CPU: ";
text += String(status_model.cargo_cpu_mhz);
text += " MHz";
text += "\n Free heap: ";
text += String(status_model.cargo_free_heap);
text += "\n Min heap: ";
text += String(status_model.cargo_min_free_heap);
text += "\n\nCargo Network\n";
text += " AP SSID: ";
text += status_model.cargo_ap_ssid;
text += "\n AP clients: ";
text += String(status_model.cargo_ap_clients);
text += "\n STA IP: ";
text += status_model.cargo_sta_ip.length() ? status_model.cargo_sta_ip : String("--");
text += "\n STA RSSI: ";
text += status_model.cargo_sta_rssi_dbm == 0 ? String("--") : String(status_model.cargo_sta_rssi_dbm);
text += " dBm";
text += "\n\nPower / Sensors\n";
text += " BMS: ";
text += status_model.bms_connected ? "connected" : "offline";
text += "\n SOC: ";
text += status_model.soc >= 0 ? String(status_model.soc) + "%" : String("--");
text += "\n Current: ";
text += String(status_model.current, 2);
text += " A";
text += "\n Cell delta: ";
text += String(status_model.cell_delta_mv);
text += " mV";
text += "\n\nControls\n";
text += " Relays visible: ";
text += String(relay_count);
text += "\n API busy: ";
text += api_request_in_progress ? "yes" : "no";
text += "\n\nDouble tap anywhere to return.";
lvgl_port_lock(-1);
lv_label_set_text(diagnostics_label, text.c_str());
lvgl_port_unlock();
}
static void close_diagnostics_overlay()
{
if (diagnostics_overlay == nullptr) {
return;
}
lvgl_port_lock(-1);
lv_obj_del(diagnostics_overlay);
diagnostics_overlay = nullptr;
diagnostics_label = nullptr;
lvgl_port_unlock();
}
static void diagnostics_overlay_event_cb(lv_event_t *event)
{
if (lv_event_get_code(event) != LV_EVENT_CLICKED) {
return;
}
unsigned long now = millis();
if (now - diagnostics_last_tap_ms < 650) {
close_diagnostics_overlay();
diagnostics_last_tap_ms = 0;
return;
}
diagnostics_last_tap_ms = now;
}
static void show_diagnostics_overlay()
{
if (diagnostics_overlay != nullptr) {
return;
}
lvgl_port_lock(-1);
diagnostics_overlay = lv_obj_create(lv_scr_act());
lv_obj_set_size(diagnostics_overlay, 1024, 600);
lv_obj_align(diagnostics_overlay, LV_ALIGN_CENTER, 0, 0);
lv_obj_set_style_bg_color(diagnostics_overlay, lv_color_hex(0x101418), 0);
lv_obj_set_style_bg_opa(diagnostics_overlay, LV_OPA_COVER, 0);
lv_obj_set_style_border_width(diagnostics_overlay, 0, 0);
lv_obj_set_style_pad_all(diagnostics_overlay, 22, 0);
lv_obj_add_flag(diagnostics_overlay, LV_OBJ_FLAG_CLICKABLE);
lv_obj_add_event_cb(diagnostics_overlay, diagnostics_overlay_event_cb, LV_EVENT_CLICKED, nullptr);
diagnostics_label = lv_label_create(diagnostics_overlay);
lv_label_set_text(diagnostics_label, "DIAGNOSTICS\nLoading...");
lv_obj_set_style_text_color(diagnostics_label, lv_color_hex(0xDDE3EA), 0);
lv_obj_set_style_text_font(diagnostics_label, &lv_font_montserrat_26, 0);
lv_obj_set_width(diagnostics_label, 960);
lv_label_set_long_mode(diagnostics_label, LV_LABEL_LONG_WRAP);
lv_obj_align(diagnostics_label, LV_ALIGN_TOP_LEFT, 8, 8);
lvgl_port_unlock();
diagnostics_last_update_ms = 0;
update_diagnostics_overlay();
}
static void system_card_event_cb(lv_event_t *event)
{
if (lv_event_get_code(event) != LV_EVENT_CLICKED) {
return;
}
unsigned long now = millis();
if (now - system_card_last_tap_ms > 3000) {
system_card_tap_count = 0;
}
system_card_last_tap_ms = now;
system_card_tap_count++;
if (system_card_tap_count >= 5) {
system_card_tap_count = 0;
show_diagnostics_overlay();
}
}
static void create_overland_overview_screen();
static void show_boot_screen();
static void show_diagnostics_overlay();
static void close_diagnostics_overlay();
static void update_diagnostics_overlay();
static void layout_relay_buttons()
{
@@ -903,12 +1102,18 @@ static void parse_status_json(const String &body)
JsonObject system = doc["system"];
status_model.firmware_version = String((const char *)(system["firmware_version"] | status_model.firmware_version.c_str()));
status_model.uptime_seconds = system["uptime_seconds"] | status_model.uptime_seconds;
status_model.cargo_free_heap = system["free_heap"] | status_model.cargo_free_heap;
status_model.cargo_min_free_heap = system["min_free_heap"] | status_model.cargo_min_free_heap;
status_model.cargo_chip_model = String((const char *)(system["chip_model"] | status_model.cargo_chip_model.c_str()));
status_model.cargo_cpu_mhz = system["cpu_mhz"] | status_model.cargo_cpu_mhz;
}
if (doc["network"].is<JsonObject>()) {
JsonObject network = doc["network"];
status_model.cargo_ap_ssid = String((const char *)(network["ap_ssid"] | status_model.cargo_ap_ssid.c_str()));
status_model.cargo_sta_ip = String((const char *)(network["sta_ip"] | status_model.cargo_sta_ip.c_str()));
status_model.cargo_ap_clients = network["ap_clients"] | status_model.cargo_ap_clients;
status_model.cargo_sta_rssi_dbm = network["sta_rssi_dbm"] | status_model.cargo_sta_rssi_dbm;
}
if (doc["temps"].is<JsonArray>()) {
@@ -1188,6 +1393,8 @@ static void create_overland_overview_screen()
lv_obj_set_style_bg_color(screen, lv_color_hex(0x101418), 0);
lv_obj_t *system_card = create_card(screen, "", 960, 48, LV_ALIGN_TOP_MID, 0, 12);
lv_obj_add_flag(system_card, LV_OBJ_FLAG_CLICKABLE);
lv_obj_add_event_cb(system_card, system_card_event_cb, LV_EVENT_CLICKED, nullptr);
lv_obj_t *battery_card = create_card(screen, "", 380, 310, LV_ALIGN_TOP_LEFT, 32, 68);
battery_card_obj = battery_card;
lv_obj_t *temp_card = create_card(screen, "", 260, 310, LV_ALIGN_TOP_LEFT, 426, 68);