#include "prts.h" #include "lvgl.h" #include "prts_lvgl_port.h" #include #include #include #define PRTS_CARD_BG_COLOR 0x0E1B24 #define PRTS_VALUE_BG_COLOR 0x132530 #define PRTS_VALUE_PANEL_WIDTH 228 #define PRTS_VALUE_PANEL_HEIGHT 34 #define PRTS_VALUE_LABEL_WIDTH 210 #define PRTS_VALUE_LABEL_HEIGHT 28 #define PRTS_VALUE_LABEL_X 12 #define PRTS_VALUE_LABEL_Y 4 #define PRTS_VALUE_ACCENT_WIDTH 4 static bool prts_initialized = false; static bool prts_lvgl_initialized = false; static lv_obj_t *prts_temperature_panel = NULL; static lv_obj_t *prts_temperature_label = NULL; static lv_obj_t *prts_temperature_accent = NULL; static lv_obj_t *prts_mode_panel = NULL; static lv_obj_t *prts_mode_label = NULL; static lv_obj_t *prts_mode_accent = NULL; static lv_obj_t *prts_status_bar = NULL; static int16_t prts_last_temperature_tenths = INT16_MIN; static RobotMode_t prts_last_mode = (RobotMode_t) 0xFF; static int16_t prts_temperature_to_tenths(float temperature_c) { return (int16_t) ((temperature_c * 10.0f) + (temperature_c >= 0.0f ? 0.5f : -0.5f)); } static void prts_format_temperature(char *buf, size_t len, int16_t temperature_tenths) { uint16_t abs_temp; uint16_t integer; uint8_t decimal; if (buf == NULL || len == 0U) { return; } if (temperature_tenths < 0) { abs_temp = (uint16_t) (-temperature_tenths); integer = abs_temp / 10U; decimal = (uint8_t) (abs_temp % 10U); (void) snprintf(buf, len, "-%u.%u C", (unsigned int) integer, (unsigned int) decimal); } else { abs_temp = (uint16_t) temperature_tenths; integer = abs_temp / 10U; decimal = (uint8_t) (abs_temp % 10U); (void) snprintf(buf, len, "%u.%u C", (unsigned int) integer, (unsigned int) decimal); } } static const char *prts_robot_mode_text(RobotMode_t mode) { switch (mode) { case NORMAL_MODE: return "NORMAL"; case SYS_ERROR_OCCURRED: return "ERROR"; case REMOTE_NOT_CONNECTED: return "REMOTE OFF"; case REMOTE_CONNECTED: return "REMOTE ON"; case AUTO_SHOOTING_MODE: return "AUTO SHOOT"; case IMU_CALIBERATION_MODE: return "IMU CAL"; default: return "UNKNOWN"; } } static lv_color_t prts_robot_mode_color(RobotMode_t mode) { switch (mode) { case NORMAL_MODE: return lv_color_hex(0x25D366); case SYS_ERROR_OCCURRED: case REMOTE_NOT_CONNECTED: return lv_color_hex(0xFF4D4F); case REMOTE_CONNECTED: return lv_color_hex(0xFFD666); case AUTO_SHOOTING_MODE: return lv_color_hex(0x40C4FF); case IMU_CALIBERATION_MODE: return lv_color_hex(0xC678FF); default: return lv_color_hex(0xD9D9D9); } } static lv_color_t prts_robot_mode_text_color(RobotMode_t mode) { switch (mode) { case SYS_ERROR_OCCURRED: case REMOTE_NOT_CONNECTED: return lv_color_hex(0xFFE2E2); case REMOTE_CONNECTED: return lv_color_hex(0xFFF3C4); default: return lv_color_hex(0xFFFFFF); } } static void prts_style_label(lv_obj_t *label, lv_color_t color) { lv_obj_set_style_bg_opa(label, LV_OPA_TRANSP, 0); lv_obj_set_style_text_color(label, color, 0); } static lv_obj_t *prts_create_value_panel(lv_obj_t *parent, int32_t x, int32_t y) { lv_obj_t *panel = lv_obj_create(parent); lv_obj_remove_flag(panel, LV_OBJ_FLAG_SCROLLABLE); lv_obj_set_pos(panel, x, y); lv_obj_set_size(panel, PRTS_VALUE_PANEL_WIDTH, PRTS_VALUE_PANEL_HEIGHT); lv_obj_set_style_radius(panel, 0, 0); lv_obj_set_style_border_width(panel, 0, 0); lv_obj_set_style_pad_all(panel, 0, 0); lv_obj_set_style_bg_color(panel, lv_color_hex(PRTS_VALUE_BG_COLOR), 0); lv_obj_set_style_bg_opa(panel, LV_OPA_COVER, 0); return panel; } static lv_obj_t *prts_create_value_accent(lv_obj_t *parent, lv_color_t color) { lv_obj_t *accent = lv_obj_create(parent); lv_obj_remove_flag(accent, LV_OBJ_FLAG_SCROLLABLE); lv_obj_set_pos(accent, 0, 0); lv_obj_set_size(accent, PRTS_VALUE_ACCENT_WIDTH, PRTS_VALUE_PANEL_HEIGHT); lv_obj_set_style_radius(accent, 0, 0); lv_obj_set_style_border_width(accent, 0, 0); lv_obj_set_style_pad_all(accent, 0, 0); lv_obj_set_style_bg_color(accent, color, 0); lv_obj_set_style_bg_opa(accent, LV_OPA_COVER, 0); return accent; } static void prts_style_value_label(lv_obj_t *label, lv_color_t color) { lv_label_set_long_mode(label, LV_LABEL_LONG_MODE_CLIP); lv_obj_set_size(label, PRTS_VALUE_LABEL_WIDTH, PRTS_VALUE_LABEL_HEIGHT); lv_obj_set_style_radius(label, 0, 0); lv_obj_set_style_border_width(label, 0, 0); lv_obj_set_style_pad_all(label, 0, 0); lv_obj_set_style_bg_opa(label, LV_OPA_TRANSP, 0); lv_obj_set_style_text_align(label, LV_TEXT_ALIGN_LEFT, 0); lv_obj_set_style_text_color(label, color, 0); } static lv_obj_t *prts_create_card(lv_obj_t *parent, int32_t x, int32_t y, int32_t w, int32_t h) { lv_obj_t *card = lv_obj_create(parent); lv_obj_remove_flag(card, LV_OBJ_FLAG_SCROLLABLE); lv_obj_set_pos(card, x, y); lv_obj_set_size(card, w, h); lv_obj_set_style_radius(card, 4, 0); lv_obj_set_style_border_width(card, 1, 0); lv_obj_set_style_border_color(card, lv_color_hex(0x27475A), 0); lv_obj_set_style_bg_color(card, lv_color_hex(PRTS_CARD_BG_COLOR), 0); lv_obj_set_style_bg_opa(card, LV_OPA_COVER, 0); lv_obj_set_style_pad_all(card, 10, 0); return card; } static void prts_create_status_screen(void) { lv_obj_t *screen = lv_obj_create(NULL); lv_obj_t *title; lv_obj_t *subtitle; lv_obj_t *temp_card; lv_obj_t *mode_card; lv_obj_t *caption; lv_obj_remove_flag(screen, LV_OBJ_FLAG_SCROLLABLE); lv_obj_set_style_bg_color(screen, lv_color_hex(0x071016), 0); lv_obj_set_style_bg_opa(screen, LV_OPA_COVER, 0); lv_obj_set_style_pad_all(screen, 0, 0); title = lv_label_create(screen); lv_label_set_text(title, "PRTS"); prts_style_label(title, lv_color_hex(0x6EE7F9)); lv_obj_set_pos(title, 16, 12); subtitle = lv_label_create(screen); lv_label_set_text(subtitle, "PORTABLE RM TELEMETRY"); prts_style_label(subtitle, lv_color_hex(0xB5C8D5)); lv_obj_set_pos(subtitle, 16, 34); prts_status_bar = lv_obj_create(screen); lv_obj_remove_flag(prts_status_bar, LV_OBJ_FLAG_SCROLLABLE); lv_obj_set_pos(prts_status_bar, 190, 16); lv_obj_set_size(prts_status_bar, 74, 8); lv_obj_set_style_radius(prts_status_bar, 2, 0); lv_obj_set_style_border_width(prts_status_bar, 0, 0); lv_obj_set_style_bg_color(prts_status_bar, lv_color_hex(0xD9D9D9), 0); lv_obj_set_style_bg_opa(prts_status_bar, LV_OPA_COVER, 0); temp_card = prts_create_card(screen, 16, 62, 248, 68); caption = lv_label_create(temp_card); lv_label_set_text(caption, "TEMPERATURE"); prts_style_label(caption, lv_color_hex(0xFFD666)); lv_obj_set_pos(caption, 0, 0); prts_temperature_panel = prts_create_value_panel(temp_card, 0, 25); prts_temperature_accent = prts_create_value_accent(prts_temperature_panel, lv_color_hex(0x40C4FF)); prts_temperature_label = lv_label_create(prts_temperature_panel); lv_label_set_text(prts_temperature_label, "--.- C"); prts_style_value_label(prts_temperature_label, lv_color_hex(0xFFFFFF)); lv_obj_set_pos(prts_temperature_label, PRTS_VALUE_LABEL_X, PRTS_VALUE_LABEL_Y); mode_card = prts_create_card(screen, 16, 146, 248, 68); caption = lv_label_create(mode_card); lv_label_set_text(caption, "CURRENT STATE"); prts_style_label(caption, lv_color_hex(0xFFD666)); lv_obj_set_pos(caption, 0, 0); prts_mode_panel = prts_create_value_panel(mode_card, 0, 25); prts_mode_accent = prts_create_value_accent(prts_mode_panel, lv_color_hex(0xD9D9D9)); prts_mode_label = lv_label_create(prts_mode_panel); lv_label_set_text(prts_mode_label, "UNKNOWN"); prts_style_value_label(prts_mode_label, lv_color_hex(0xD9D9D9)); lv_obj_set_pos(prts_mode_label, PRTS_VALUE_LABEL_X, PRTS_VALUE_LABEL_Y); lv_screen_load(screen); } bool PRTS_Init(void) { if (prts_initialized) { return true; } if (!prts_lvgl_initialized) { lv_init(); prts_lvgl_initialized = true; } if (!PRTS_LVGLPortInit()) { return false; } prts_create_status_screen(); prts_initialized = true; return true; } void PRTS_UpdateStatus(float temperature_c, RobotMode_t mode) { int16_t temperature_tenths; lv_color_t mode_color; char temperature_text[16]; if (!prts_initialized) { return; } temperature_tenths = prts_temperature_to_tenths(temperature_c); if (temperature_tenths != prts_last_temperature_tenths) { bool temperature_warning = temperature_tenths >= 550; prts_format_temperature(temperature_text, sizeof(temperature_text), temperature_tenths); lv_obj_invalidate(prts_temperature_panel); lv_label_set_text(prts_temperature_label, temperature_text); lv_obj_set_style_bg_color(prts_temperature_accent, temperature_warning ? lv_color_hex(0xFF4D4F) : lv_color_hex(0x40C4FF), 0); lv_obj_set_style_text_color(prts_temperature_label, temperature_warning ? lv_color_hex(0xFFE2E2) : lv_color_hex(0xFFFFFF), 0); lv_obj_invalidate(prts_temperature_panel); prts_last_temperature_tenths = temperature_tenths; } if (mode != prts_last_mode) { mode_color = prts_robot_mode_color(mode); lv_obj_invalidate(prts_mode_panel); lv_label_set_text(prts_mode_label, prts_robot_mode_text(mode)); lv_obj_set_style_text_color(prts_mode_label, prts_robot_mode_text_color(mode), 0); lv_obj_set_style_bg_color(prts_mode_accent, mode_color, 0); lv_obj_set_style_bg_color(prts_status_bar, mode_color, 0); lv_obj_invalidate(prts_mode_panel); prts_last_mode = mode; } } uint32_t PRTS_Task(void) { uint32_t next_ms; if (!prts_initialized) { return 10U; } next_ms = lv_timer_handler(); if (next_ms == 0U || next_ms > 10U) { next_ms = 10U; } return next_ms; }