Implement native ESP-IDF radar firmware
This commit is contained in:
504
main/ui.c
Normal file
504
main/ui.c
Normal file
@@ -0,0 +1,504 @@
|
||||
#include "ui.h"
|
||||
|
||||
#include <math.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "lvgl.h"
|
||||
|
||||
#define MAIN_WIDTH 610
|
||||
#define SIDEBAR_WIDTH 190
|
||||
#define RADAR_TOP 52
|
||||
#define RADAR_HEIGHT 428
|
||||
#define RADAR_CENTER_X 300
|
||||
#define RADAR_CENTER_Y 420
|
||||
#define RADAR_RADIUS 340
|
||||
#define RADAR_MAX_MM 8000.0f
|
||||
#define DEGREES_TO_RADIANS 0.01745329251994329577f
|
||||
|
||||
static const uint32_t COLOR_BACKGROUND = 0x071015;
|
||||
static const uint32_t COLOR_PANEL = 0x0D191F;
|
||||
static const uint32_t COLOR_CARD = 0x13232A;
|
||||
static const uint32_t COLOR_GRID = 0x1F4145;
|
||||
static const uint32_t COLOR_GRID_MAJOR = 0x2C6665;
|
||||
static const uint32_t COLOR_ACCENT = 0x4BE1C1;
|
||||
static const uint32_t COLOR_TEXT = 0xEAF3F2;
|
||||
static const uint32_t COLOR_MUTED = 0x70858A;
|
||||
static const uint32_t COLOR_DANGER = 0xFF6B6B;
|
||||
static const uint32_t TARGET_COLORS[3] = {0xFF6B6B, 0xFFB454, 0x52C9F3};
|
||||
|
||||
static ui_config_t s_config;
|
||||
static radar_snapshot_t s_snapshot;
|
||||
static lv_obj_t *s_radar;
|
||||
static lv_obj_t *s_status_dot;
|
||||
static lv_obj_t *s_status_label;
|
||||
static lv_obj_t *s_frame_age_label;
|
||||
static lv_obj_t *s_nearest_value;
|
||||
static lv_obj_t *s_empty_label;
|
||||
static lv_obj_t *s_sound_button;
|
||||
static lv_obj_t *s_sound_label;
|
||||
static lv_obj_t *s_brightness_slider;
|
||||
static lv_obj_t *s_target_rows[3];
|
||||
static lv_obj_t *s_target_row_dots[3];
|
||||
static lv_obj_t *s_target_row_labels[3];
|
||||
static lv_obj_t *s_target_tags[3];
|
||||
static bool s_sound_enabled;
|
||||
|
||||
static lv_color_t color(uint32_t hex)
|
||||
{
|
||||
return lv_color_hex(hex);
|
||||
}
|
||||
|
||||
static void remove_default_style(lv_obj_t *object)
|
||||
{
|
||||
lv_obj_remove_style_all(object);
|
||||
lv_obj_clear_flag(object, LV_OBJ_FLAG_SCROLLABLE);
|
||||
}
|
||||
|
||||
static lv_obj_t *make_label(lv_obj_t *parent, const char *text,
|
||||
const lv_font_t *font, uint32_t text_color)
|
||||
{
|
||||
lv_obj_t *label = lv_label_create(parent);
|
||||
lv_label_set_text(label, text);
|
||||
lv_obj_set_style_text_font(label, font, 0);
|
||||
lv_obj_set_style_text_color(label, color(text_color), 0);
|
||||
return label;
|
||||
}
|
||||
|
||||
static float distance_to_radius(float distance_mm)
|
||||
{
|
||||
if (distance_mm <= 0.0f) {
|
||||
return 0.0f;
|
||||
}
|
||||
if (distance_mm <= 2000.0f) {
|
||||
return (distance_mm / 2000.0f) * 0.50f * RADAR_RADIUS;
|
||||
}
|
||||
if (distance_mm <= 4000.0f) {
|
||||
return (0.50f + ((distance_mm - 2000.0f) / 2000.0f) * 0.25f) * RADAR_RADIUS;
|
||||
}
|
||||
if (distance_mm <= 6000.0f) {
|
||||
return (0.75f + ((distance_mm - 4000.0f) / 2000.0f) * 0.15f) * RADAR_RADIUS;
|
||||
}
|
||||
if (distance_mm >= RADAR_MAX_MM) {
|
||||
return RADAR_RADIUS;
|
||||
}
|
||||
return (0.90f + ((distance_mm - 6000.0f) / 2000.0f) * 0.10f) * RADAR_RADIUS;
|
||||
}
|
||||
|
||||
static uint8_t target_color_index(uint32_t id)
|
||||
{
|
||||
return id == 0U ? 0U : (uint8_t)((id - 1U) % 3U);
|
||||
}
|
||||
|
||||
static bool target_to_point(const radar_target_view_t *target,
|
||||
lv_coord_t origin_x, lv_coord_t origin_y,
|
||||
lv_point_t *point)
|
||||
{
|
||||
if (!target->valid || target->y_mm <= 0.0f) {
|
||||
return false;
|
||||
}
|
||||
const float distance = sqrtf(target->x_mm * target->x_mm +
|
||||
target->y_mm * target->y_mm);
|
||||
if (distance < 100.0f || distance > RADAR_MAX_MM) {
|
||||
return false;
|
||||
}
|
||||
const float angle = atan2f(target->x_mm, target->y_mm);
|
||||
const float radius = distance_to_radius(distance);
|
||||
point->x = origin_x + RADAR_CENTER_X + (lv_coord_t)lroundf(sinf(angle) * radius);
|
||||
point->y = origin_y + RADAR_CENTER_Y - (lv_coord_t)lroundf(cosf(angle) * radius);
|
||||
return true;
|
||||
}
|
||||
|
||||
static void draw_line(lv_draw_ctx_t *ctx, lv_color_t line_color,
|
||||
lv_coord_t width, lv_point_t start, lv_point_t end)
|
||||
{
|
||||
lv_draw_line_dsc_t descriptor;
|
||||
lv_draw_line_dsc_init(&descriptor);
|
||||
descriptor.color = line_color;
|
||||
descriptor.width = width;
|
||||
descriptor.opa = LV_OPA_COVER;
|
||||
lv_draw_line(ctx, &descriptor, &start, &end);
|
||||
}
|
||||
|
||||
static void draw_radar_event(lv_event_t *event)
|
||||
{
|
||||
if (lv_event_get_code(event) != LV_EVENT_DRAW_MAIN) {
|
||||
return;
|
||||
}
|
||||
|
||||
lv_obj_t *object = lv_event_get_target(event);
|
||||
lv_draw_ctx_t *ctx = lv_event_get_draw_ctx(event);
|
||||
lv_area_t coordinates;
|
||||
lv_obj_get_coords(object, &coordinates);
|
||||
const lv_point_t center = {
|
||||
.x = coordinates.x1 + RADAR_CENTER_X,
|
||||
.y = coordinates.y1 + RADAR_CENTER_Y,
|
||||
};
|
||||
|
||||
const uint16_t distances[] = {2000, 4000, 6000, 8000};
|
||||
for (size_t i = 0; i < 4; ++i) {
|
||||
lv_draw_arc_dsc_t arc;
|
||||
lv_draw_arc_dsc_init(&arc);
|
||||
arc.color = color(i == 3 ? COLOR_GRID_MAJOR : COLOR_GRID);
|
||||
arc.width = i == 3 ? 2 : 1;
|
||||
arc.opa = LV_OPA_COVER;
|
||||
lv_draw_arc(ctx, &arc, ¢er,
|
||||
(uint16_t)lroundf(distance_to_radius(distances[i])), 210, 330);
|
||||
}
|
||||
|
||||
const int16_t angles[] = {-60, -30, 0, 30, 60};
|
||||
for (size_t i = 0; i < 5; ++i) {
|
||||
const float radians = (float)angles[i] * DEGREES_TO_RADIANS;
|
||||
const lv_point_t edge = {
|
||||
.x = center.x + (lv_coord_t)lroundf(sinf(radians) * RADAR_RADIUS),
|
||||
.y = center.y - (lv_coord_t)lroundf(cosf(radians) * RADAR_RADIUS),
|
||||
};
|
||||
draw_line(ctx, color(angles[i] == 0 ? COLOR_GRID_MAJOR : COLOR_GRID),
|
||||
angles[i] == 0 ? 2 : 1, center, edge);
|
||||
}
|
||||
|
||||
lv_draw_rect_dsc_t sensor;
|
||||
lv_draw_rect_dsc_init(&sensor);
|
||||
sensor.radius = LV_RADIUS_CIRCLE;
|
||||
sensor.bg_color = color(s_snapshot.sensor_online ? COLOR_ACCENT : COLOR_MUTED);
|
||||
sensor.bg_opa = LV_OPA_COVER;
|
||||
const lv_area_t sensor_area = {
|
||||
.x1 = center.x - 6, .y1 = center.y - 6,
|
||||
.x2 = center.x + 6, .y2 = center.y + 6,
|
||||
};
|
||||
lv_draw_rect(ctx, &sensor, &sensor_area);
|
||||
|
||||
for (size_t i = 0; i < RADAR_SERVICE_MAX_TARGETS; ++i) {
|
||||
const radar_target_view_t *target = &s_snapshot.targets[i];
|
||||
lv_point_t point;
|
||||
if (!target_to_point(target, coordinates.x1, coordinates.y1, &point)) {
|
||||
continue;
|
||||
}
|
||||
const lv_color_t target_color = color(TARGET_COLORS[target_color_index(target->id)]);
|
||||
|
||||
lv_draw_rect_dsc_t halo;
|
||||
lv_draw_rect_dsc_init(&halo);
|
||||
halo.radius = LV_RADIUS_CIRCLE;
|
||||
halo.bg_opa = LV_OPA_TRANSP;
|
||||
halo.border_color = target_color;
|
||||
halo.border_opa = LV_OPA_COVER;
|
||||
halo.border_width = 2;
|
||||
const lv_area_t halo_area = {
|
||||
.x1 = point.x - 13, .y1 = point.y - 13,
|
||||
.x2 = point.x + 13, .y2 = point.y + 13,
|
||||
};
|
||||
lv_draw_rect(ctx, &halo, &halo_area);
|
||||
|
||||
lv_draw_rect_dsc_t dot;
|
||||
lv_draw_rect_dsc_init(&dot);
|
||||
dot.radius = LV_RADIUS_CIRCLE;
|
||||
dot.bg_color = target_color;
|
||||
dot.bg_opa = LV_OPA_COVER;
|
||||
const lv_area_t dot_area = {
|
||||
.x1 = point.x - 5, .y1 = point.y - 5,
|
||||
.x2 = point.x + 5, .y2 = point.y + 5,
|
||||
};
|
||||
lv_draw_rect(ctx, &dot, &dot_area);
|
||||
}
|
||||
}
|
||||
|
||||
static void update_sound_button(void)
|
||||
{
|
||||
lv_label_set_text(s_sound_label, s_sound_enabled ? "SOUND ON" : "SOUND OFF");
|
||||
lv_obj_set_style_bg_color(s_sound_button,
|
||||
color(s_sound_enabled ? COLOR_ACCENT : COLOR_CARD), 0);
|
||||
lv_obj_set_style_text_color(s_sound_label,
|
||||
color(s_sound_enabled ? COLOR_BACKGROUND : COLOR_MUTED), 0);
|
||||
}
|
||||
|
||||
static void sound_button_event(lv_event_t *event)
|
||||
{
|
||||
if (lv_event_get_code(event) != LV_EVENT_CLICKED) {
|
||||
return;
|
||||
}
|
||||
s_sound_enabled = !s_sound_enabled;
|
||||
update_sound_button();
|
||||
if (s_config.sound_changed != NULL) {
|
||||
s_config.sound_changed(s_sound_enabled);
|
||||
}
|
||||
}
|
||||
|
||||
static void brightness_event(lv_event_t *event)
|
||||
{
|
||||
const lv_event_code_t code = lv_event_get_code(event);
|
||||
if (code != LV_EVENT_VALUE_CHANGED && code != LV_EVENT_RELEASED) {
|
||||
return;
|
||||
}
|
||||
if (s_config.brightness_changed != NULL) {
|
||||
const uint8_t value = (uint8_t)lv_slider_get_value(s_brightness_slider);
|
||||
s_config.brightness_changed(value, code == LV_EVENT_RELEASED);
|
||||
}
|
||||
}
|
||||
|
||||
static void create_header(lv_obj_t *root)
|
||||
{
|
||||
lv_obj_t *title = make_label(root, "RADICK", &lv_font_montserrat_24, COLOR_TEXT);
|
||||
lv_obj_set_pos(title, 22, 13);
|
||||
|
||||
lv_obj_t *subtitle = make_label(root, "RD-03D / 24 GHz / 3 TARGET",
|
||||
&lv_font_montserrat_12, COLOR_MUTED);
|
||||
lv_obj_set_pos(subtitle, 140, 22);
|
||||
|
||||
lv_obj_t *line = lv_obj_create(root);
|
||||
remove_default_style(line);
|
||||
lv_obj_set_size(line, MAIN_WIDTH - 40, 1);
|
||||
lv_obj_set_pos(line, 20, 50);
|
||||
lv_obj_set_style_bg_color(line, color(COLOR_GRID), 0);
|
||||
lv_obj_set_style_bg_opa(line, LV_OPA_COVER, 0);
|
||||
}
|
||||
|
||||
static void create_radar(lv_obj_t *root)
|
||||
{
|
||||
s_radar = lv_obj_create(root);
|
||||
remove_default_style(s_radar);
|
||||
lv_obj_set_pos(s_radar, 0, RADAR_TOP);
|
||||
lv_obj_set_size(s_radar, MAIN_WIDTH, RADAR_HEIGHT);
|
||||
lv_obj_add_event_cb(s_radar, draw_radar_event, LV_EVENT_DRAW_MAIN, NULL);
|
||||
|
||||
const uint16_t distances[] = {2000, 4000, 6000, 8000};
|
||||
for (size_t i = 0; i < 4; ++i) {
|
||||
char text[8];
|
||||
snprintf(text, sizeof(text), "%um", (unsigned)(distances[i] / 1000U));
|
||||
lv_obj_t *label = make_label(s_radar, text, &lv_font_montserrat_12,
|
||||
i == 3 ? COLOR_GRID_MAJOR : COLOR_MUTED);
|
||||
lv_obj_set_pos(label, RADAR_CENTER_X + 7,
|
||||
RADAR_CENTER_Y - (lv_coord_t)distance_to_radius(distances[i]) - 8);
|
||||
}
|
||||
|
||||
s_empty_label = make_label(s_radar, "WAITING FOR RADAR",
|
||||
&lv_font_montserrat_16, COLOR_MUTED);
|
||||
lv_obj_align(s_empty_label, LV_ALIGN_TOP_MID, 0, 32);
|
||||
|
||||
for (size_t i = 0; i < RADAR_SERVICE_MAX_TARGETS; ++i) {
|
||||
s_target_tags[i] = make_label(s_radar, "", &lv_font_montserrat_12, COLOR_TEXT);
|
||||
lv_obj_set_style_bg_color(s_target_tags[i], color(COLOR_BACKGROUND), 0);
|
||||
lv_obj_set_style_bg_opa(s_target_tags[i], LV_OPA_70, 0);
|
||||
lv_obj_set_style_pad_hor(s_target_tags[i], 5, 0);
|
||||
lv_obj_set_style_pad_ver(s_target_tags[i], 3, 0);
|
||||
lv_obj_set_style_radius(s_target_tags[i], 4, 0);
|
||||
lv_obj_add_flag(s_target_tags[i], LV_OBJ_FLAG_HIDDEN);
|
||||
}
|
||||
}
|
||||
|
||||
static lv_obj_t *create_card(lv_obj_t *parent, lv_coord_t y, lv_coord_t height)
|
||||
{
|
||||
lv_obj_t *card = lv_obj_create(parent);
|
||||
lv_obj_clear_flag(card, LV_OBJ_FLAG_SCROLLABLE);
|
||||
lv_obj_set_pos(card, 12, y);
|
||||
lv_obj_set_size(card, SIDEBAR_WIDTH - 24, height);
|
||||
lv_obj_set_style_bg_color(card, color(COLOR_CARD), 0);
|
||||
lv_obj_set_style_bg_opa(card, LV_OPA_COVER, 0);
|
||||
lv_obj_set_style_border_width(card, 0, 0);
|
||||
lv_obj_set_style_radius(card, 10, 0);
|
||||
lv_obj_set_style_pad_all(card, 0, 0);
|
||||
return card;
|
||||
}
|
||||
|
||||
static void create_sidebar(lv_obj_t *root)
|
||||
{
|
||||
lv_obj_t *sidebar = lv_obj_create(root);
|
||||
remove_default_style(sidebar);
|
||||
lv_obj_set_pos(sidebar, MAIN_WIDTH, 0);
|
||||
lv_obj_set_size(sidebar, SIDEBAR_WIDTH, 480);
|
||||
lv_obj_set_style_bg_color(sidebar, color(COLOR_PANEL), 0);
|
||||
lv_obj_set_style_bg_opa(sidebar, LV_OPA_COVER, 0);
|
||||
|
||||
s_status_dot = lv_obj_create(sidebar);
|
||||
remove_default_style(s_status_dot);
|
||||
lv_obj_set_size(s_status_dot, 9, 9);
|
||||
lv_obj_set_pos(s_status_dot, 14, 19);
|
||||
lv_obj_set_style_radius(s_status_dot, LV_RADIUS_CIRCLE, 0);
|
||||
lv_obj_set_style_bg_opa(s_status_dot, LV_OPA_COVER, 0);
|
||||
|
||||
s_status_label = make_label(sidebar, "RADAR OFFLINE",
|
||||
&lv_font_montserrat_14, COLOR_MUTED);
|
||||
lv_obj_set_pos(s_status_label, 31, 14);
|
||||
s_frame_age_label = make_label(sidebar, "NO DATA",
|
||||
&lv_font_montserrat_12, COLOR_MUTED);
|
||||
lv_obj_set_pos(s_frame_age_label, 31, 31);
|
||||
|
||||
lv_obj_t *nearest_card = create_card(sidebar, 58, 94);
|
||||
lv_obj_t *nearest_title = make_label(nearest_card, "NEAREST",
|
||||
&lv_font_montserrat_12, COLOR_MUTED);
|
||||
lv_obj_set_pos(nearest_title, 12, 10);
|
||||
s_nearest_value = make_label(nearest_card, "--.- m",
|
||||
&lv_font_montserrat_40, COLOR_TEXT);
|
||||
lv_obj_set_pos(s_nearest_value, 10, 33);
|
||||
|
||||
lv_obj_t *targets_title = make_label(sidebar, "TARGETS",
|
||||
&lv_font_montserrat_12, COLOR_MUTED);
|
||||
lv_obj_set_pos(targets_title, 14, 167);
|
||||
|
||||
for (size_t i = 0; i < 3; ++i) {
|
||||
s_target_rows[i] = create_card(sidebar, 186 + (lv_coord_t)i * 53, 45);
|
||||
s_target_row_dots[i] = lv_obj_create(s_target_rows[i]);
|
||||
remove_default_style(s_target_row_dots[i]);
|
||||
lv_obj_set_size(s_target_row_dots[i], 8, 8);
|
||||
lv_obj_set_pos(s_target_row_dots[i], 11, 18);
|
||||
lv_obj_set_style_radius(s_target_row_dots[i], LV_RADIUS_CIRCLE, 0);
|
||||
lv_obj_set_style_bg_color(s_target_row_dots[i], color(TARGET_COLORS[i]), 0);
|
||||
lv_obj_set_style_bg_opa(s_target_row_dots[i], LV_OPA_COVER, 0);
|
||||
s_target_row_labels[i] = make_label(s_target_rows[i], "--",
|
||||
&lv_font_montserrat_12, COLOR_MUTED);
|
||||
lv_obj_set_pos(s_target_row_labels[i], 29, 8);
|
||||
}
|
||||
|
||||
s_sound_button = lv_btn_create(sidebar);
|
||||
lv_obj_set_pos(s_sound_button, 12, 351);
|
||||
lv_obj_set_size(s_sound_button, SIDEBAR_WIDTH - 24, 42);
|
||||
lv_obj_set_style_shadow_width(s_sound_button, 0, 0);
|
||||
lv_obj_set_style_border_width(s_sound_button, 0, 0);
|
||||
lv_obj_set_style_radius(s_sound_button, 9, 0);
|
||||
lv_obj_add_event_cb(s_sound_button, sound_button_event, LV_EVENT_CLICKED, NULL);
|
||||
s_sound_label = make_label(s_sound_button, "", &lv_font_montserrat_14, COLOR_TEXT);
|
||||
lv_obj_center(s_sound_label);
|
||||
update_sound_button();
|
||||
|
||||
lv_obj_t *brightness_title = make_label(sidebar, "BRIGHTNESS",
|
||||
&lv_font_montserrat_12, COLOR_MUTED);
|
||||
lv_obj_set_pos(brightness_title, 14, 411);
|
||||
s_brightness_slider = lv_slider_create(sidebar);
|
||||
lv_obj_set_pos(s_brightness_slider, 14, 442);
|
||||
lv_obj_set_size(s_brightness_slider, SIDEBAR_WIDTH - 28, 8);
|
||||
lv_slider_set_range(s_brightness_slider, 10, 100);
|
||||
lv_slider_set_value(s_brightness_slider, s_config.brightness_percent, LV_ANIM_OFF);
|
||||
lv_obj_set_style_bg_color(s_brightness_slider, color(COLOR_CARD), LV_PART_MAIN);
|
||||
lv_obj_set_style_bg_color(s_brightness_slider, color(COLOR_ACCENT), LV_PART_INDICATOR);
|
||||
lv_obj_set_style_bg_color(s_brightness_slider, color(COLOR_TEXT), LV_PART_KNOB);
|
||||
lv_obj_set_style_pad_all(s_brightness_slider, 6, LV_PART_KNOB);
|
||||
lv_obj_set_ext_click_area(s_brightness_slider, 12);
|
||||
lv_obj_add_event_cb(s_brightness_slider, brightness_event, LV_EVENT_VALUE_CHANGED, NULL);
|
||||
lv_obj_add_event_cb(s_brightness_slider, brightness_event, LV_EVENT_RELEASED, NULL);
|
||||
|
||||
if (!s_config.touch_available) {
|
||||
lv_obj_t *touch_warning = make_label(sidebar, "TOUCH OFFLINE",
|
||||
&lv_font_montserrat_12, COLOR_DANGER);
|
||||
lv_obj_align(touch_warning, LV_ALIGN_BOTTOM_MID, 0, -5);
|
||||
}
|
||||
}
|
||||
|
||||
void ui_init(const ui_config_t *config)
|
||||
{
|
||||
memset(&s_snapshot, 0, sizeof(s_snapshot));
|
||||
if (config != NULL) {
|
||||
s_config = *config;
|
||||
} else {
|
||||
memset(&s_config, 0, sizeof(s_config));
|
||||
s_config.brightness_percent = 75;
|
||||
}
|
||||
s_sound_enabled = s_config.sound_enabled;
|
||||
|
||||
lv_obj_t *root = lv_scr_act();
|
||||
lv_obj_clear_flag(root, LV_OBJ_FLAG_SCROLLABLE);
|
||||
lv_obj_set_style_bg_color(root, color(COLOR_BACKGROUND), 0);
|
||||
lv_obj_set_style_bg_opa(root, LV_OPA_COVER, 0);
|
||||
lv_obj_set_style_pad_all(root, 0, 0);
|
||||
|
||||
create_header(root);
|
||||
create_radar(root);
|
||||
create_sidebar(root);
|
||||
}
|
||||
|
||||
void ui_update(const radar_snapshot_t *snapshot)
|
||||
{
|
||||
if (snapshot == NULL || s_radar == NULL) {
|
||||
return;
|
||||
}
|
||||
const bool online = snapshot->sensor_online;
|
||||
const bool radar_changed =
|
||||
snapshot->frame_count != s_snapshot.frame_count ||
|
||||
snapshot->target_count != s_snapshot.target_count ||
|
||||
online != s_snapshot.sensor_online;
|
||||
s_snapshot = *snapshot;
|
||||
lv_obj_set_style_bg_color(s_status_dot,
|
||||
color(online ? COLOR_ACCENT : COLOR_DANGER), 0);
|
||||
lv_label_set_text(s_status_label, online ? "RADAR ONLINE" : "RADAR OFFLINE");
|
||||
lv_obj_set_style_text_color(s_status_label,
|
||||
color(online ? COLOR_TEXT : COLOR_DANGER), 0);
|
||||
|
||||
if (online) {
|
||||
char age_text[24];
|
||||
snprintf(age_text, sizeof(age_text), "%lu FR / %lums",
|
||||
(unsigned long)snapshot->frame_count,
|
||||
(unsigned long)snapshot->last_frame_age_ms);
|
||||
lv_label_set_text(s_frame_age_label, age_text);
|
||||
} else {
|
||||
lv_label_set_text(s_frame_age_label, "NO SENSOR DATA");
|
||||
}
|
||||
|
||||
float nearest = INFINITY;
|
||||
for (size_t i = 0; i < RADAR_SERVICE_MAX_TARGETS; ++i) {
|
||||
const radar_target_view_t *target = &snapshot->targets[i];
|
||||
if (target->valid) {
|
||||
const float distance = sqrtf(target->x_mm * target->x_mm +
|
||||
target->y_mm * target->y_mm);
|
||||
if (distance < nearest) {
|
||||
nearest = distance;
|
||||
}
|
||||
|
||||
char row_text[44];
|
||||
snprintf(row_text, sizeof(row_text), "T%lu %.2fm\n%+.0f cm/s",
|
||||
(unsigned long)target->id, distance / 1000.0f,
|
||||
target->speed_cm_s);
|
||||
lv_label_set_text(s_target_row_labels[i], row_text);
|
||||
const uint8_t color_index = target_color_index(target->id);
|
||||
lv_obj_set_style_text_color(s_target_row_labels[i], color(COLOR_TEXT), 0);
|
||||
lv_obj_set_style_bg_color(s_target_row_dots[i],
|
||||
color(TARGET_COLORS[color_index]), 0);
|
||||
lv_obj_clear_flag(s_target_row_dots[i], LV_OBJ_FLAG_HIDDEN);
|
||||
|
||||
lv_point_t point;
|
||||
if (target_to_point(target, 0, 0, &point)) {
|
||||
char tag[24];
|
||||
snprintf(tag, sizeof(tag), "T%lu %.1fm",
|
||||
(unsigned long)target->id, distance / 1000.0f);
|
||||
lv_label_set_text(s_target_tags[i], tag);
|
||||
lv_obj_set_style_text_color(s_target_tags[i],
|
||||
color(TARGET_COLORS[color_index]), 0);
|
||||
lv_coord_t tag_x = point.x + 15;
|
||||
if (tag_x > MAIN_WIDTH - 82) {
|
||||
tag_x = point.x - 78;
|
||||
}
|
||||
lv_coord_t tag_y = point.y - 12;
|
||||
if (tag_y < 0) {
|
||||
tag_y = 0;
|
||||
}
|
||||
lv_obj_set_pos(s_target_tags[i], tag_x, tag_y);
|
||||
lv_obj_clear_flag(s_target_tags[i], LV_OBJ_FLAG_HIDDEN);
|
||||
} else {
|
||||
lv_obj_add_flag(s_target_tags[i], LV_OBJ_FLAG_HIDDEN);
|
||||
}
|
||||
} else {
|
||||
lv_label_set_text(s_target_row_labels[i], "--");
|
||||
lv_obj_set_style_text_color(s_target_row_labels[i], color(COLOR_MUTED), 0);
|
||||
lv_obj_add_flag(s_target_row_dots[i], LV_OBJ_FLAG_HIDDEN);
|
||||
lv_obj_add_flag(s_target_tags[i], LV_OBJ_FLAG_HIDDEN);
|
||||
}
|
||||
}
|
||||
|
||||
if (isfinite(nearest)) {
|
||||
char nearest_text[16];
|
||||
snprintf(nearest_text, sizeof(nearest_text), "%.1f m", nearest / 1000.0f);
|
||||
lv_label_set_text(s_nearest_value, nearest_text);
|
||||
} else {
|
||||
lv_label_set_text(s_nearest_value, "--.- m");
|
||||
}
|
||||
|
||||
if (!online) {
|
||||
lv_label_set_text(s_empty_label, "WAITING FOR RADAR");
|
||||
lv_obj_clear_flag(s_empty_label, LV_OBJ_FLAG_HIDDEN);
|
||||
} else if (snapshot->target_count == 0U) {
|
||||
lv_label_set_text(s_empty_label, "AREA CLEAR");
|
||||
lv_obj_clear_flag(s_empty_label, LV_OBJ_FLAG_HIDDEN);
|
||||
} else {
|
||||
lv_obj_add_flag(s_empty_label, LV_OBJ_FLAG_HIDDEN);
|
||||
}
|
||||
if (radar_changed) {
|
||||
lv_obj_invalidate(s_radar);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user