Files
serverinfotest/src/ui.cpp
DESKTOP-PUHFO60\dsx2016 f724796359 Add CYD host PC info panel with serial SI protocol and LVGL bars.
ESP32 shows CPU/GPU/RAM/disk from host/send_sysinfo.ps1 over USB; firmware packages for 2.8 and 3.5 included.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-10-27 20:13:09 +08:00

224 lines
6.9 KiB
C++

#include "ui.h"
#include "host_info.h"
#include "pins.h"
#include <Arduino.h>
#include <cstdio>
#include <lvgl.h>
static constexpr uint32_t COL_BG = 0x0B121A;
static constexpr uint32_t COL_INK = 0xE8EEF4;
static constexpr uint32_t COL_MUTED = 0x7A8B9C;
static constexpr uint32_t COL_LINE = 0x1A2734;
static constexpr uint32_t COL_OK = 0x3DDC97;
static constexpr uint32_t COL_WARN = 0xE8B84A;
static constexpr uint32_t COL_BAD = 0xE85D4C;
static constexpr uint32_t COL_ACCENT = 0x4FC3F7;
static constexpr uint32_t COL_BAR_BG = 0x162230;
struct MetricRow {
lv_obj_t *lbl_tag;
lv_obj_t *lbl_name;
lv_obj_t *lbl_pct;
lv_obj_t *bar;
};
static lv_obj_t *scr = nullptr;
static lv_obj_t *lbl_title = nullptr;
static lv_obj_t *lbl_host = nullptr;
static lv_obj_t *lbl_status = nullptr;
static MetricRow row_cpu = {};
static MetricRow row_gpu = {};
static MetricRow row_ram = {};
static MetricRow row_disk = {};
static uint32_t last_paint_ms = 0;
static int pct_of(float used, float total) {
if (total <= 0.01f) {
return 0;
}
int p = (int)((used * 100.0f) / total + 0.5f);
if (p < 0) {
p = 0;
}
if (p > 100) {
p = 100;
}
return p;
}
static uint32_t color_for_pct(int pct) {
if (pct >= 85) {
return COL_BAD;
}
if (pct >= 65) {
return COL_WARN;
}
return COL_OK;
}
static void style_bar(lv_obj_t *bar) {
lv_obj_set_style_bg_color(bar, lv_color_hex(COL_BAR_BG), LV_PART_MAIN);
lv_obj_set_style_bg_opa(bar, LV_OPA_COVER, LV_PART_MAIN);
lv_obj_set_style_radius(bar, 4, LV_PART_MAIN);
lv_obj_set_style_pad_all(bar, 0, LV_PART_MAIN);
lv_obj_set_style_bg_color(bar, lv_color_hex(COL_OK), LV_PART_INDICATOR);
lv_obj_set_style_bg_opa(bar, LV_OPA_COVER, LV_PART_INDICATOR);
lv_obj_set_style_radius(bar, 4, LV_PART_INDICATOR);
lv_bar_set_range(bar, 0, 100);
lv_bar_set_value(bar, 0, LV_ANIM_OFF);
}
static MetricRow make_row(lv_obj_t *parent, const char *tag, int y) {
MetricRow r = {};
r.lbl_tag = lv_label_create(parent);
lv_label_set_text(r.lbl_tag, tag);
lv_obj_set_style_text_color(r.lbl_tag, lv_color_hex(COL_ACCENT), 0);
lv_obj_set_style_text_font(r.lbl_tag, &lv_font_montserrat_14, 0);
lv_obj_set_pos(r.lbl_tag, UX(8), UY(y));
r.lbl_name = lv_label_create(parent);
lv_label_set_text(r.lbl_name, "—");
lv_label_set_long_mode(r.lbl_name, LV_LABEL_LONG_DOT);
lv_obj_set_width(r.lbl_name, UW(190));
lv_obj_set_style_text_color(r.lbl_name, lv_color_hex(COL_INK), 0);
lv_obj_set_style_text_font(r.lbl_name, &lv_font_montserrat_14, 0);
lv_obj_set_pos(r.lbl_name, UX(52), UY(y));
r.lbl_pct = lv_label_create(parent);
lv_label_set_text(r.lbl_pct, "--%");
lv_obj_set_style_text_color(r.lbl_pct, lv_color_hex(COL_MUTED), 0);
lv_obj_set_style_text_font(r.lbl_pct, &lv_font_montserrat_14, 0);
lv_obj_align(r.lbl_pct, LV_ALIGN_TOP_RIGHT, UX(-8), UY(y));
r.bar = lv_bar_create(parent);
lv_obj_set_size(r.bar, UI_HOR_RES - UW(16), UH(10));
lv_obj_set_pos(r.bar, UX(8), UY(y + 18));
style_bar(r.bar);
return r;
}
static void set_row_pct(MetricRow &r, int pct, const char *name, const char *right) {
if (r.lbl_name && name) {
lv_label_set_text(r.lbl_name, name);
}
if (r.lbl_pct && right) {
lv_label_set_text(r.lbl_pct, right);
lv_obj_set_style_text_color(r.lbl_pct, lv_color_hex(color_for_pct(pct)), 0);
}
if (r.bar) {
lv_bar_set_value(r.bar, pct, LV_ANIM_OFF);
lv_obj_set_style_bg_color(r.bar, lv_color_hex(color_for_pct(pct)),
LV_PART_INDICATOR);
}
}
void ui_create() {
scr = lv_obj_create(nullptr);
lv_obj_set_style_bg_color(scr, lv_color_hex(COL_BG), 0);
lv_obj_set_style_bg_opa(scr, LV_OPA_COVER, 0);
lv_obj_clear_flag(scr, LV_OBJ_FLAG_SCROLLABLE);
lv_screen_load(scr);
lbl_title = lv_label_create(scr);
lv_label_set_text(lbl_title, "PC Monitor");
lv_obj_set_style_text_color(lbl_title, lv_color_hex(COL_INK), 0);
lv_obj_set_style_text_font(lbl_title, &lv_font_montserrat_18, 0);
lv_obj_set_pos(lbl_title, UX(8), UY(6));
lbl_status = lv_label_create(scr);
lv_label_set_text(lbl_status, "waiting");
lv_obj_set_style_text_color(lbl_status, lv_color_hex(COL_WARN), 0);
lv_obj_set_style_text_font(lbl_status, &lv_font_montserrat_14, 0);
lv_obj_align(lbl_status, LV_ALIGN_TOP_RIGHT, UX(-8), UY(8));
lbl_host = lv_label_create(scr);
lv_label_set_text(lbl_host, "Connect USB + run host script");
lv_label_set_long_mode(lbl_host, LV_LABEL_LONG_DOT);
lv_obj_set_width(lbl_host, UI_HOR_RES - UW(16));
lv_obj_set_style_text_color(lbl_host, lv_color_hex(COL_MUTED), 0);
lv_obj_set_style_text_font(lbl_host, &lv_font_montserrat_14, 0);
lv_obj_set_pos(lbl_host, UX(8), UY(28));
lv_obj_t *line = lv_obj_create(scr);
lv_obj_set_size(line, UI_HOR_RES - UW(16), 1);
lv_obj_set_pos(line, UX(8), UY(48));
lv_obj_set_style_bg_color(line, lv_color_hex(COL_LINE), 0);
lv_obj_set_style_bg_opa(line, LV_OPA_COVER, 0);
lv_obj_set_style_border_width(line, 0, 0);
lv_obj_set_style_pad_all(line, 0, 0);
lv_obj_clear_flag(line, LV_OBJ_FLAG_SCROLLABLE);
row_cpu = make_row(scr, "CPU", 56);
row_gpu = make_row(scr, "GPU", 96);
row_ram = make_row(scr, "RAM", 136);
row_disk = make_row(scr, "DISK", 176);
last_paint_ms = 0;
}
static void paint() {
const HostInfo &h = host_info_get();
const bool live = host_info_is_live(5000);
if (lbl_status) {
if (!h.valid) {
lv_label_set_text(lbl_status, "waiting");
lv_obj_set_style_text_color(lbl_status, lv_color_hex(COL_WARN), 0);
} else if (live) {
lv_label_set_text(lbl_status, "live");
lv_obj_set_style_text_color(lbl_status, lv_color_hex(COL_OK), 0);
} else {
lv_label_set_text(lbl_status, "stale");
lv_obj_set_style_text_color(lbl_status, lv_color_hex(COL_BAD), 0);
}
}
if (!h.valid) {
return;
}
if (lbl_host) {
lv_label_set_text(lbl_host, h.host[0] ? h.host : "(unknown host)");
}
char buf[48];
int cpu_pct = (int)(h.cpu_pct + 0.5f);
snprintf(buf, sizeof(buf), "%d%%", cpu_pct);
set_row_pct(row_cpu, cpu_pct, h.cpu[0] ? h.cpu : "CPU", buf);
if (h.gpu_pct < 0) {
set_row_pct(row_gpu, 0, h.gpu[0] ? h.gpu : "GPU", "n/a");
if (row_gpu.lbl_pct) {
lv_obj_set_style_text_color(row_gpu.lbl_pct, lv_color_hex(COL_MUTED), 0);
}
} else {
int gpu_pct = (int)(h.gpu_pct + 0.5f);
snprintf(buf, sizeof(buf), "%d%%", gpu_pct);
set_row_pct(row_gpu, gpu_pct, h.gpu[0] ? h.gpu : "GPU", buf);
}
int ram_pct = pct_of(h.ram_used_gb, h.ram_total_gb);
snprintf(buf, sizeof(buf), "%.1f/%.0fG", (double)h.ram_used_gb,
(double)h.ram_total_gb);
set_row_pct(row_ram, ram_pct, "Memory", buf);
int disk_pct = pct_of(h.disk_used_gb, h.disk_total_gb);
snprintf(buf, sizeof(buf), "%.0f/%.0fG", (double)h.disk_used_gb,
(double)h.disk_total_gb);
set_row_pct(row_disk, disk_pct, "System drive", buf);
}
void ui_tick() {
const uint32_t now = millis();
if (now - last_paint_ms < 200) {
return;
}
last_paint_ms = now;
paint();
}