wifi-densepose/firmware/esp32-csi-node/main/display_ui_st7789.c

135 lines
5.2 KiB
C

/**
* @file display_ui_st7789.c
* @brief ADR-045: compact LVGL UI for the 240x280 ST7789 panel.
*
* The 4-view AMOLED UI (display_ui.c) is laid out for 368x448 and overflows a
* 1.69" LCD. This variant is a single legible screen sized for 240x280: node
* identity, a big ACTIVITY bar driven by the CSI motion/presence metrics (so
* movement is visible across a room), and a live CSI packet-rate footer.
* Selected at build time via CONFIG_DISPLAY_PANEL_ST7789 (CMakeLists compiles
* this instead of display_ui.c). Same display_ui.h contract.
*/
#include "display_ui.h"
#include "csi_collector.h" /* node id + live CSI packet rate */
#include "sdkconfig.h"
#if CONFIG_DISPLAY_ENABLE && CONFIG_DISPLAY_PANEL_ST7789
#include <stdio.h>
#include "esp_log.h"
#include "esp_timer.h"
#include "esp_system.h"
#include "edge_processing.h"
static const char *TAG = "disp_ui_st7789";
#define COLOR_BG lv_color_make(0x0A, 0x0A, 0x0F)
#define COLOR_CYAN lv_color_make(0x00, 0xD4, 0xFF)
#define COLOR_TEXT lv_color_make(0xCC, 0xCC, 0xDD)
#define COLOR_DIM lv_color_make(0x66, 0x66, 0x77)
#define COLOR_GREEN lv_color_make(0x00, 0xFF, 0x80)
#define COLOR_TRACK lv_color_make(0x1C, 0x1C, 0x26)
/* Activity = max(motion_energy, presence_score); ~0 idle, climbs past 6 on
* movement near the link. Scale x10 to a 0-100 bar (clamped). */
#define ACT_SCALE 10.0f
static lv_obj_t *s_node = NULL;
static lv_obj_t *s_act_val = NULL; /* big activity number */
static lv_obj_t *s_bar = NULL; /* activity bar */
static lv_obj_t *s_foot = NULL; /* CSI rate + RSSI */
static lv_obj_t *s_foot2 = NULL; /* uptime + heap */
static lv_obj_t *s_hb = NULL; /* heartbeat dot */
static lv_obj_t *label(lv_obj_t *p, const lv_font_t *font, lv_color_t color,
lv_align_t align, int x, int y, const char *text)
{
lv_obj_t *l = lv_label_create(p);
lv_label_set_text(l, text);
lv_obj_set_style_text_font(l, font, 0);
lv_obj_set_style_text_color(l, color, 0);
lv_obj_align(l, align, x, y);
return l;
}
void display_ui_create(lv_obj_t *parent)
{
lv_obj_set_style_bg_color(parent, COLOR_BG, 0);
lv_obj_set_style_bg_opa(parent, LV_OPA_COVER, 0);
label(parent, &lv_font_montserrat_20, COLOR_DIM, LV_ALIGN_TOP_MID, 0, 6, "RuView CSI");
s_node = label(parent, &lv_font_montserrat_36, COLOR_CYAN, LV_ALIGN_TOP_MID, 0, 30, "NODE -");
s_hb = label(parent, &lv_font_montserrat_20, COLOR_CYAN, LV_ALIGN_TOP_RIGHT, -10, 8, "*");
label(parent, &lv_font_montserrat_20, COLOR_TEXT, LV_ALIGN_TOP_MID, 0, 92, "ACTIVITY");
s_act_val = label(parent, &lv_font_montserrat_36, COLOR_GREEN, LV_ALIGN_TOP_MID, 0, 116, "0");
s_bar = lv_bar_create(parent);
lv_obj_set_size(s_bar, 200, 24);
lv_obj_align(s_bar, LV_ALIGN_TOP_MID, 0, 172);
lv_bar_set_range(s_bar, 0, 100);
lv_bar_set_value(s_bar, 0, LV_ANIM_OFF);
lv_obj_set_style_bg_color(s_bar, COLOR_TRACK, LV_PART_MAIN);
lv_obj_set_style_bg_color(s_bar, COLOR_GREEN, LV_PART_INDICATOR);
lv_obj_set_style_radius(s_bar, 4, LV_PART_MAIN);
s_foot = label(parent, &lv_font_montserrat_14, COLOR_TEXT, LV_ALIGN_BOTTOM_MID, 0, -26, "CSI --/s RSSI --");
s_foot2 = label(parent, &lv_font_montserrat_14, COLOR_DIM, LV_ALIGN_BOTTOM_MID, 0, -6, "up 0h00m heap -- KB");
ESP_LOGI(TAG, "Compact ST7789 UI created (240x280, activity bar)");
}
void display_ui_update(void)
{
char buf[48];
/* Heartbeat — blink ~2 Hz so the UI is visibly alive regardless of data. */
static uint32_t s_hb_last = 0;
static bool s_hb_on = false;
uint32_t now_ms = (uint32_t)(esp_timer_get_time() / 1000);
if (now_ms - s_hb_last >= 500) {
s_hb_last = now_ms;
s_hb_on = !s_hb_on;
lv_label_set_text(s_hb, s_hb_on ? "*" : " ");
}
snprintf(buf, sizeof(buf), "NODE %u", (unsigned)csi_collector_get_node_id());
lv_label_set_text(s_node, buf);
uint16_t pps = csi_collector_get_pkt_yield_per_sec();
edge_vitals_pkt_t v;
int rssi = 0;
float activity = 0.0f;
if (edge_get_vitals(&v)) {
rssi = v.rssi;
activity = v.motion_energy > v.presence_score ? v.motion_energy : v.presence_score;
}
int act100 = (int)(activity * ACT_SCALE);
if (act100 > 100) act100 = 100;
if (act100 < 0) act100 = 0;
lv_bar_set_value(s_bar, act100, LV_ANIM_OFF);
snprintf(buf, sizeof(buf), "%d", act100);
lv_label_set_text(s_act_val, buf);
lv_obj_set_style_text_color(s_act_val, act100 > 10 ? COLOR_GREEN : COLOR_DIM, 0);
snprintf(buf, sizeof(buf), "CSI %u/s RSSI %d", (unsigned)pps, rssi);
lv_label_set_text(s_foot, buf);
uint32_t up = (uint32_t)(esp_timer_get_time() / 1000000);
snprintf(buf, sizeof(buf), "up %luh%02lum heap %luK",
(unsigned long)(up / 3600), (unsigned long)((up % 3600) / 60),
(unsigned long)(esp_get_free_heap_size() / 1024));
lv_label_set_text(s_foot2, buf);
/* NOTE: no per-loop ESP_LOG here. On the USB-Serial-JTAG console, logging
* with no host attached (e.g. running off a wall charger) blocks once the
* TX buffer fills — which would hang the display task. Keep this loop
* log-free so the panel keeps refreshing headless. */
}
#endif /* CONFIG_DISPLAY_ENABLE && CONFIG_DISPLAY_PANEL_ST7789 */