merged changes

This commit is contained in:
Mikkeli Matlock
2026-02-16 20:55:12 +09:00
parent b33c658885
commit 706c7ac21b
6 changed files with 73 additions and 28 deletions

View File

@@ -39,6 +39,8 @@ static lv_obj_t *lbl_cpu_temp;
/* Services table */
static lv_obj_t *tbl_services;
static int s_service_count;
static int s_dup_rows; /* duplicate rows appended for looping */
static lv_coord_t s_row_h; /* measured row height in px */
/* Local sensors (bottom bar) */
static lv_obj_t *lbl_local;
@@ -116,21 +118,52 @@ static lv_obj_t *create_label(lv_obj_t *parent, int x, int y, const lv_font_t *f
static void scroll_timer_cb(lv_timer_t *timer)
{
(void)timer;
if (s_service_count <= 4) {
if (s_dup_rows <= 0 || s_row_h <= 0) {
/* Too few services or not yet measured — no scrolling */
lv_obj_scroll_to_y(tbl_services, 0, LV_ANIM_OFF);
return;
}
lv_coord_t cur_y = lv_obj_get_scroll_y(tbl_services);
/* Each row is ~16px (12px font + 2px pad top + 2px pad bot) */
lv_coord_t row_h = 16;
lv_coord_t max_scroll = (s_service_count - 4) * row_h;
lv_coord_t wrap_y = s_service_count * s_row_h;
if (cur_y >= max_scroll) {
lv_obj_scroll_to_y(tbl_services, 0, LV_ANIM_ON);
} else {
lv_obj_scroll_to_y(tbl_services, cur_y + row_h, LV_ANIM_ON);
if (cur_y >= wrap_y) {
lv_obj_scroll_to_y(tbl_services, 0, LV_ANIM_OFF);
cur_y = 0;
}
lv_obj_scroll_to_y(tbl_services, cur_y + s_row_h, LV_ANIM_OFF);
}
/* Measure row height, compute visible rows, append duplicates for seamless loop */
static void fill_duplicate_rows(int count)
{
s_dup_rows = 0;
s_row_h = 0;
if (count <= 0) return;
/* Trim table to exactly count rows so measurement is clean */
lv_table_set_row_cnt(tbl_services, count);
lv_obj_update_layout(tbl_services);
lv_coord_t content_h = lv_obj_get_self_height(tbl_services);
lv_coord_t row_h = content_h / count;
if (row_h <= 0) return;
lv_coord_t visible_h = lv_obj_get_content_height(tbl_services);
int visible_rows = (visible_h + row_h - 1) / row_h; /* ceil */
if (count <= visible_rows) return; /* everything fits — no scrolling */
for (int i = 0; i < visible_rows; i++) {
lv_table_set_cell_value(tbl_services, count + i, 0,
lv_table_get_cell_value(tbl_services, i, 0));
lv_table_set_cell_value(tbl_services, count + i, 1,
lv_table_get_cell_value(tbl_services, i, 1));
}
s_dup_rows = visible_rows;
s_row_h = row_h;
}
/* ---------- Create UI sections ---------- */
@@ -221,7 +254,7 @@ static void create_time_bar(lv_obj_t *parent)
lbl_date = lv_label_create(bar_cont);
lv_obj_set_style_text_font(lbl_date, &InziuIosevka_Slab_CC_20px, 0);
lv_obj_set_style_text_color(lbl_date, lv_color_black(), 0);
lv_obj_align(lbl_date, LV_ALIGN_RIGHT_MID, -10, 0);
lv_obj_align(lbl_date, LV_ALIGN_RIGHT_MID, -20, 0);
lv_label_set_text(lbl_date, "----/--/-- ---");
}
@@ -232,7 +265,7 @@ static void create_main_section(lv_obj_t *parent)
tbl_services = lv_table_create(parent);
lv_obj_set_pos(tbl_services, 4, MAIN_Y + 16);
lv_obj_set_size(tbl_services, 190, 68);
lv_obj_set_size(tbl_services, 190, 82);
lv_table_set_col_cnt(tbl_services, 2);
lv_table_set_col_width(tbl_services, 0, 110);
lv_table_set_col_width(tbl_services, 1, 65);
@@ -248,8 +281,8 @@ static void create_main_section(lv_obj_t *parent)
lv_table_set_cell_value(tbl_services, i, 1, "---");
}
/* Auto-scroll timer: 3 second period */
s_scroll_timer = lv_timer_create(scroll_timer_cb, 3000, NULL);
/* Auto-scroll timer: 1 second period */
s_scroll_timer = lv_timer_create(scroll_timer_cb, 1000, NULL);
/* === Left column: Pi Vitals (below services) === */
int rx = 0;
@@ -258,12 +291,12 @@ static void create_main_section(lv_obj_t *parent)
int bar_w = 82;
int bar_h = 12;
int val_x = rx + 4 + lbl_w + bar_w + 4; /* value label after bar */
int temp_x = rx + 160; /* TEMP column, right of value labels */
int temp_x = rx + 155; /* TEMP column, right of value labels */
/* Pi Vitals header — Y=162 */
create_label(parent, rx + 4, 162, &InziuIosevka_Slab_CC_12px, "PI VITALS");
/* Pi Vitals header */
create_label(parent, rx + 4, 177, &InziuIosevka_Slab_CC_12px, "PI VITALS");
int ry = 176;
int ry = 192;
/* CPU [========] 12% TEMP */
create_label(parent, rx + 4, ry, &InziuIosevka_Slab_CC_12px, "CPU");
@@ -290,7 +323,7 @@ static void create_main_section(lv_obj_t *parent)
/* === Right column: Status image (200x200) === */
img_status = lv_img_create(parent);
lv_obj_set_pos(img_status, 200, MAIN_Y + 2);
lv_obj_set_pos(img_status, 200, MAIN_Y + 1);
lv_obj_set_size(img_status, 200, 200);
lv_obj_set_style_bg_color(img_status, lv_color_white(), 0);
lv_obj_set_style_bg_opa(img_status, LV_OPA_COVER, 0);
@@ -316,7 +349,7 @@ static void create_bottom_bar(lv_obj_t *parent)
lv_obj_set_style_text_font(lbl_net, &InziuIosevka_Slab_CC_12px, 0);
lv_obj_set_style_text_color(lbl_net, lv_color_black(), 0);
lv_obj_align(lbl_net, LV_ALIGN_LEFT_MID, 0, 0);
lv_label_set_text(lbl_net, "NETWORK RX: ---- kbps TX: ---- kbps");
lv_label_set_text(lbl_net, "NETWORK DOWN: ---- kBps / UP: ---- kBps");
/* Local sensor readings — right-aligned */
lbl_local = lv_label_create(bot_cont);
@@ -378,11 +411,8 @@ void dashboard_ui_update_stats(const pi_stats_t *stats)
lv_table_set_cell_value(tbl_services, i, 0, stats->services[i].name);
lv_table_set_cell_value(tbl_services, i, 1, tag);
}
/* Clear unused rows */
for (int i = stats->service_count; i < WS_MAX_SERVICES; i++) {
lv_table_set_cell_value(tbl_services, i, 0, "");
lv_table_set_cell_value(tbl_services, i, 1, "");
}
/* Measure row height, compute visible rows, append duplicates */
fill_duplicate_rows(stats->service_count);
/* Uptime */
snprintf(buf, sizeof(buf), "Uptime: %.0fh", stats->uptime_hrs);
@@ -390,7 +420,7 @@ void dashboard_ui_update_stats(const pi_stats_t *stats)
/* Network */
char net_buf[64];
snprintf(net_buf, sizeof(net_buf), "NETWORK RX: %.0f kbps TX: %.0f kbps",
snprintf(net_buf, sizeof(net_buf), "NETWORK DOWN: %.0f kBps / UP: %.0f kBps",
stats->net_rx_kbps, stats->net_tx_kbps);
lv_label_set_text(lbl_net, net_buf);
}

View File

@@ -245,10 +245,10 @@ static void sensor_task(void *arg)
static void button_task(void *arg)
{
for (;;) {
/* Wait for GP18 button event (single click = bit 0) */
/* Wait for GP18 button event (single click = bit 0, long press = bit 2) */
EventBits_t bits = xEventGroupWaitBits(
GP18ButtonGroups,
set_bit_button(0), /* single press bit */
set_bit_button(0) | set_bit_button(2),
pdTRUE, /* clear on exit */
pdFALSE, /* any bit */
pdMS_TO_TICKS(500)
@@ -257,7 +257,12 @@ static void button_task(void *arg)
if (bits & set_bit_button(0)) {
bool muted = !alert_is_muted();
alert_mute(muted);
ESP_LOGI(TAG, "GP18 pressed: alerts %s", muted ? "muted" : "unmuted");
ESP_LOGI(TAG, "GP18 single press: alerts %s", muted ? "muted" : "unmuted");
}
if (bits & set_bit_button(2)) {
ESP_LOGI(TAG, "GP18 long press: forcing WS reconnect");
ws_client_reconnect();
}
}
}

View File

@@ -210,6 +210,14 @@ void ws_client_stop(void)
}
}
void ws_client_reconnect(void)
{
if (!s_client) return;
ESP_LOGI(TAG, "Manual WS reconnect triggered");
esp_websocket_client_close(s_client, pdMS_TO_TICKS(2000));
s_last_data_tick = xTaskGetTickCount();
}
ws_state_t ws_client_get_state(void)
{
return s_state;

View File

@@ -59,6 +59,7 @@ typedef void (*ws_state_callback_t)(ws_state_t state);
void ws_client_init(const char *uri);
void ws_client_start(void);
void ws_client_stop(void);
void ws_client_reconnect(void);
ws_state_t ws_client_get_state(void);
void ws_client_get_stats(pi_stats_t *out);
void ws_client_set_data_callback(ws_data_callback_t cb);