dashboard: add battery detail screen
This commit is contained in:
@@ -1,6 +1,6 @@
|
||||
#pragma once
|
||||
|
||||
static const char *DASHBOARD_VERSION = "0.1.77-remove-wifi-bars";
|
||||
static const char *DASHBOARD_VERSION = "0.1.78-battery-detail-screen";
|
||||
|
||||
// Update these if your Cargo ESP AP credentials are different.
|
||||
static const char *CARGO_WIFI_SSID = "OverlandController";
|
||||
|
||||
@@ -30,6 +30,33 @@ static lv_obj_t *system_status_label = nullptr;
|
||||
static lv_obj_t *cargo_status_dot = nullptr;
|
||||
static lv_obj_t *outside_status_temp_label = nullptr;
|
||||
|
||||
enum DashboardScreen {
|
||||
SCREEN_BATTERY_DETAIL = 0,
|
||||
SCREEN_OVERVIEW = 1,
|
||||
};
|
||||
|
||||
static DashboardScreen current_dashboard_screen = SCREEN_OVERVIEW;
|
||||
static lv_obj_t *battery_detail_soc_label = nullptr;
|
||||
static lv_obj_t *battery_detail_status_label = nullptr;
|
||||
static lv_obj_t *battery_detail_voltage_label = nullptr;
|
||||
static lv_obj_t *battery_detail_current_label = nullptr;
|
||||
static lv_obj_t *battery_detail_power_label = nullptr;
|
||||
static lv_obj_t *battery_detail_remaining_label = nullptr;
|
||||
static lv_obj_t *battery_detail_capacity_label = nullptr;
|
||||
static lv_obj_t *battery_detail_temp_label = nullptr;
|
||||
static lv_obj_t *battery_detail_delta_label = nullptr;
|
||||
static lv_obj_t *battery_detail_estimate_label = nullptr;
|
||||
static String last_battery_detail_soc_text;
|
||||
static String last_battery_detail_status_text;
|
||||
static String last_battery_detail_voltage_text;
|
||||
static String last_battery_detail_current_text;
|
||||
static String last_battery_detail_power_text;
|
||||
static String last_battery_detail_remaining_text;
|
||||
static String last_battery_detail_capacity_text;
|
||||
static String last_battery_detail_temp_text;
|
||||
static String last_battery_detail_delta_text;
|
||||
static String last_battery_detail_estimate_text;
|
||||
|
||||
static lv_obj_t *vehicle_coolant_meter = nullptr;
|
||||
static lv_meter_indicator_t *vehicle_coolant_needle = nullptr;
|
||||
static lv_obj_t *vehicle_coolant_value_label = nullptr;
|
||||
@@ -510,11 +537,164 @@ static void system_card_event_cb(lv_event_t *event)
|
||||
|
||||
|
||||
static void create_overland_overview_screen();
|
||||
static void create_battery_detail_screen();
|
||||
static void show_dashboard_screen(DashboardScreen screen);
|
||||
static void update_battery_detail_widgets();
|
||||
static void show_boot_screen();
|
||||
static void show_diagnostics_overlay();
|
||||
static void close_diagnostics_overlay();
|
||||
static void update_diagnostics_overlay();
|
||||
|
||||
|
||||
static String signed_float_text(float value, int decimals, const char *suffix)
|
||||
{
|
||||
String text;
|
||||
if (value > 0.0) text += "+";
|
||||
text += String(value, decimals);
|
||||
text += suffix;
|
||||
return text;
|
||||
}
|
||||
|
||||
static void dashboard_root_event_cb(lv_event_t *event)
|
||||
{
|
||||
if (lv_event_get_code(event) != LV_EVENT_GESTURE) return;
|
||||
lv_indev_t *indev = lv_indev_get_act();
|
||||
if (indev == nullptr) return;
|
||||
|
||||
lv_dir_t dir = lv_indev_get_gesture_dir(indev);
|
||||
if (current_dashboard_screen == SCREEN_OVERVIEW && dir == LV_DIR_RIGHT) {
|
||||
show_dashboard_screen(SCREEN_BATTERY_DETAIL);
|
||||
} else if (current_dashboard_screen == SCREEN_BATTERY_DETAIL && dir == LV_DIR_LEFT) {
|
||||
show_dashboard_screen(SCREEN_OVERVIEW);
|
||||
}
|
||||
}
|
||||
|
||||
static void make_detail_label(lv_obj_t *parent, lv_obj_t **label, const char *text, const lv_font_t *font, lv_color_t color, int width, lv_align_t align, int x, int y, lv_text_align_t text_align = LV_TEXT_ALIGN_LEFT)
|
||||
{
|
||||
*label = lv_label_create(parent);
|
||||
lv_label_set_text(*label, text);
|
||||
lv_obj_set_style_text_color(*label, color, 0);
|
||||
lv_obj_set_style_text_font(*label, font, 0);
|
||||
lv_obj_set_width(*label, width);
|
||||
lv_obj_set_style_text_align(*label, text_align, 0);
|
||||
lv_label_set_long_mode(*label, LV_LABEL_LONG_WRAP);
|
||||
lv_obj_align(*label, align, x, y);
|
||||
}
|
||||
|
||||
static void update_battery_detail_widgets()
|
||||
{
|
||||
if (battery_detail_soc_label == nullptr) return;
|
||||
|
||||
String soc_text = status_model.soc >= 0 ? String(status_model.soc) + "%" : "--%";
|
||||
String status_text = status_model.bms_connected ? "BMS CONNECTED" : "BMS OFFLINE";
|
||||
String voltage_text = String(status_model.voltage, 2) + " V";
|
||||
String current_text = signed_float_text(status_model.current, 2, " A");
|
||||
String power_text = signed_float_text(status_model.voltage * status_model.current, 0, " W");
|
||||
String remaining_text = String(status_model.remaining_ah, 1) + " Ah";
|
||||
String capacity_text = String(status_model.capacity_ah, 1) + " Ah";
|
||||
String temp_text = String(status_model.battery_temp_f, 1) + "°F";
|
||||
String delta_text = status_model.cell_delta_mv >= 0 ? String(status_model.cell_delta_mv) + " mV" : "-- mV";
|
||||
|
||||
String estimate_text = "Idle";
|
||||
if (status_model.current > 0.05 && status_model.capacity_ah > 0.0) {
|
||||
estimate_text = "Full in " + format_hours((status_model.capacity_ah - status_model.remaining_ah) / status_model.current);
|
||||
} else if (status_model.current < -0.05 && status_model.remaining_ah > 0.0) {
|
||||
estimate_text = "ETR " + format_hours(status_model.remaining_ah / abs(status_model.current));
|
||||
}
|
||||
|
||||
set_label_text_if_changed(battery_detail_soc_label, last_battery_detail_soc_text, soc_text);
|
||||
set_label_text_if_changed(battery_detail_status_label, last_battery_detail_status_text, status_text);
|
||||
set_label_text_if_changed(battery_detail_voltage_label, last_battery_detail_voltage_text, voltage_text);
|
||||
set_label_text_if_changed(battery_detail_current_label, last_battery_detail_current_text, current_text);
|
||||
set_label_text_if_changed(battery_detail_power_label, last_battery_detail_power_text, power_text);
|
||||
set_label_text_if_changed(battery_detail_remaining_label, last_battery_detail_remaining_text, remaining_text);
|
||||
set_label_text_if_changed(battery_detail_capacity_label, last_battery_detail_capacity_text, capacity_text);
|
||||
set_label_text_if_changed(battery_detail_temp_label, last_battery_detail_temp_text, temp_text);
|
||||
set_label_text_if_changed(battery_detail_delta_label, last_battery_detail_delta_text, delta_text);
|
||||
set_label_text_if_changed(battery_detail_estimate_label, last_battery_detail_estimate_text, estimate_text);
|
||||
}
|
||||
|
||||
static lv_obj_t *create_battery_metric_tile(lv_obj_t *parent, const char *title, lv_obj_t **value_label, int x, int y)
|
||||
{
|
||||
lv_obj_t *tile = lv_obj_create(parent);
|
||||
lv_obj_set_size(tile, 292, 132);
|
||||
lv_obj_align(tile, LV_ALIGN_TOP_LEFT, x, y);
|
||||
lv_obj_set_style_radius(tile, 18, 0);
|
||||
lv_obj_set_style_bg_color(tile, lv_color_hex(0x17202A), 0);
|
||||
lv_obj_set_style_border_color(tile, lv_color_hex(0x2F3A46), 0);
|
||||
lv_obj_set_style_border_width(tile, 2, 0);
|
||||
lv_obj_set_style_pad_all(tile, 0, 0);
|
||||
lv_obj_clear_flag(tile, LV_OBJ_FLAG_SCROLLABLE);
|
||||
|
||||
lv_obj_t *title_label = lv_label_create(tile);
|
||||
lv_label_set_text(title_label, title);
|
||||
lv_obj_set_style_text_color(title_label, lv_color_hex(0xB8C0C8), 0);
|
||||
lv_obj_set_style_text_font(title_label, &lv_font_montserrat_22, 0);
|
||||
lv_obj_set_width(title_label, 260);
|
||||
lv_obj_align(title_label, LV_ALIGN_TOP_LEFT, 16, 14);
|
||||
|
||||
make_detail_label(tile, value_label, "--", &lv_font_montserrat_30, lv_color_hex(0xFFFFFF), 260, LV_ALIGN_TOP_LEFT, 16, 58);
|
||||
return tile;
|
||||
}
|
||||
|
||||
static void create_battery_detail_screen()
|
||||
{
|
||||
lv_obj_t *screen = lv_scr_act();
|
||||
lv_obj_clean(screen);
|
||||
current_dashboard_screen = SCREEN_BATTERY_DETAIL;
|
||||
|
||||
lv_obj_set_style_bg_color(screen, lv_color_hex(0x101418), 0);
|
||||
lv_obj_add_event_cb(screen, dashboard_root_event_cb, LV_EVENT_GESTURE, nullptr);
|
||||
|
||||
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 *title_label = nullptr;
|
||||
make_detail_label(system_card, &title_label, "BATTERY DETAIL", &lv_font_montserrat_26, lv_color_hex(0xDDE3EA), 360, LV_ALIGN_LEFT_MID, 22, 0);
|
||||
|
||||
lv_obj_t *page_dots = lv_label_create(system_card);
|
||||
lv_label_set_text(page_dots, "● ○");
|
||||
lv_obj_set_style_text_color(page_dots, lv_color_hex(0xB8C0C8), 0);
|
||||
lv_obj_set_width(page_dots, 120);
|
||||
lv_obj_set_style_text_align(page_dots, LV_TEXT_ALIGN_RIGHT, 0);
|
||||
lv_obj_align(page_dots, LV_ALIGN_RIGHT_MID, -10, 0);
|
||||
|
||||
lv_obj_t *hero_card = create_card(screen, "", 300, 500, LV_ALIGN_TOP_LEFT, 32, 78);
|
||||
make_detail_label(hero_card, &battery_detail_status_label, "BMS --", &lv_font_montserrat_22, lv_color_hex(0xB8C0C8), 260, LV_ALIGN_TOP_MID, 0, 30, LV_TEXT_ALIGN_CENTER);
|
||||
make_detail_label(hero_card, &battery_detail_soc_label, "--%", &lv_font_montserrat_48, lv_color_hex(0xFFFFFF), 260, LV_ALIGN_TOP_MID, 0, 126, LV_TEXT_ALIGN_CENTER);
|
||||
make_detail_label(hero_card, &battery_detail_estimate_label, "--", &lv_font_montserrat_30, lv_color_hex(0xDDE3EA), 260, LV_ALIGN_TOP_MID, 0, 236, LV_TEXT_ALIGN_CENTER);
|
||||
|
||||
lv_obj_t *hint_label = nullptr;
|
||||
make_detail_label(hero_card, &hint_label, "Swipe left for main dashboard", &lv_font_montserrat_20, lv_color_hex(0x7D8996), 260, LV_ALIGN_BOTTOM_MID, 0, -28, LV_TEXT_ALIGN_CENTER);
|
||||
|
||||
create_battery_metric_tile(screen, "Voltage", &battery_detail_voltage_label, 356, 78);
|
||||
create_battery_metric_tile(screen, "Current", &battery_detail_current_label, 670, 78);
|
||||
create_battery_metric_tile(screen, "Power", &battery_detail_power_label, 356, 230);
|
||||
create_battery_metric_tile(screen, "Remaining", &battery_detail_remaining_label, 670, 230);
|
||||
create_battery_metric_tile(screen, "Capacity", &battery_detail_capacity_label, 356, 382);
|
||||
create_battery_metric_tile(screen, "Battery Temp", &battery_detail_temp_label, 670, 382);
|
||||
|
||||
lv_obj_t *delta_card = create_card(screen, "", 606, 70, LV_ALIGN_BOTTOM_MID, 177, -22);
|
||||
lv_obj_t *delta_title = nullptr;
|
||||
make_detail_label(delta_card, &delta_title, "Cell delta", &lv_font_montserrat_22, lv_color_hex(0xB8C0C8), 180, LV_ALIGN_LEFT_MID, 20, 0);
|
||||
make_detail_label(delta_card, &battery_detail_delta_label, "-- mV", &lv_font_montserrat_30, lv_color_hex(0xFFFFFF), 220, LV_ALIGN_RIGHT_MID, -20, 0, LV_TEXT_ALIGN_RIGHT);
|
||||
|
||||
update_battery_detail_widgets();
|
||||
}
|
||||
|
||||
static void show_dashboard_screen(DashboardScreen screen)
|
||||
{
|
||||
lvgl_port_lock(-1);
|
||||
if (screen == SCREEN_BATTERY_DETAIL) {
|
||||
create_battery_detail_screen();
|
||||
} else {
|
||||
create_overland_overview_screen();
|
||||
}
|
||||
lvgl_port_unlock();
|
||||
}
|
||||
|
||||
|
||||
static void layout_relay_buttons()
|
||||
{
|
||||
int visible_count = relay_count;
|
||||
@@ -1218,6 +1398,7 @@ static void parse_status_json(const String &body)
|
||||
|
||||
update_overview_widgets();
|
||||
update_system_status_label();
|
||||
update_battery_detail_widgets();
|
||||
}
|
||||
|
||||
static bool fetch_status_fields(const char *url, const char *label)
|
||||
@@ -1350,6 +1531,7 @@ static void create_overland_overview_screen()
|
||||
{
|
||||
lv_obj_t *screen = lv_scr_act();
|
||||
lv_obj_clean(screen);
|
||||
current_dashboard_screen = SCREEN_OVERVIEW;
|
||||
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);
|
||||
@@ -1580,9 +1762,9 @@ cargo_status_dot = lv_obj_create(system_card);
|
||||
lv_obj_align(outside_status_temp_label, LV_ALIGN_CENTER, 0, 0);
|
||||
|
||||
lv_obj_t *page_dots = lv_label_create(system_card);
|
||||
lv_label_set_text(page_dots, "● ○ ○ ○");
|
||||
lv_label_set_text(page_dots, "○ ●");
|
||||
lv_obj_set_style_text_color(page_dots, lv_color_hex(0xB8C0C8), 0);
|
||||
lv_obj_set_width(page_dots, 140);
|
||||
lv_obj_set_width(page_dots, 120);
|
||||
lv_obj_set_style_text_align(page_dots, LV_TEXT_ALIGN_RIGHT, 0);
|
||||
lv_obj_align(page_dots, LV_ALIGN_RIGHT_MID, -10, 0);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user