ESP-IDF 5.5 + LVGL 9 firmware that plays random public cameras from insecam.org on the T-Embed's 320x170 screen, live MJPEG where available, a new camera every 20 seconds. Knob steps through cameras, LED ring shows the countdown. Auto-detects the original vs CC1101 board, reuses Wi-Fi credentials from NVS and falls back to a captive-portal setup hotspot.
185 lines
5.6 KiB
C
185 lines
5.6 KiB
C
#include "ui.h"
|
|
#include "display.h"
|
|
#include "esp_lvgl_port.h"
|
|
#include "lvgl.h"
|
|
#include <string.h>
|
|
|
|
static lv_obj_t *scr_status, *lbl_title, *lbl_msg;
|
|
static lv_obj_t *scr_cam, *canvas, *overlay, *lbl_place, *lbl_meta, *badge, *bar, *toast;
|
|
static lv_timer_t *toast_timer;
|
|
static bool overlay_on = true;
|
|
|
|
static lv_obj_t *label(lv_obj_t *parent, const lv_font_t *font, uint32_t rgb)
|
|
{
|
|
lv_obj_t *l = lv_label_create(parent);
|
|
lv_obj_set_style_text_font(l, font, 0);
|
|
lv_obj_set_style_text_color(l, lv_color_hex(rgb), 0);
|
|
return l;
|
|
}
|
|
|
|
static lv_obj_t *plain_screen(void)
|
|
{
|
|
lv_obj_t *s = lv_obj_create(NULL);
|
|
lv_obj_remove_style_all(s);
|
|
lv_obj_set_size(s, LCD_H_RES, LCD_V_RES);
|
|
lv_obj_set_style_bg_color(s, lv_color_hex(0x000000), 0);
|
|
lv_obj_set_style_bg_opa(s, LV_OPA_COVER, 0);
|
|
return s;
|
|
}
|
|
|
|
static void toast_hide_cb(lv_timer_t *t)
|
|
{
|
|
lv_obj_set_hidden(toast, true);
|
|
toast_timer = NULL;
|
|
}
|
|
|
|
void ui_init(void)
|
|
{
|
|
lvgl_port_lock(0);
|
|
|
|
// --- status page ---
|
|
scr_status = plain_screen();
|
|
lbl_title = label(scr_status, &lv_font_montserrat_24, 0xffffff);
|
|
lv_obj_set_width(lbl_title, LCD_H_RES - 24);
|
|
lv_obj_set_style_text_align(lbl_title, LV_TEXT_ALIGN_CENTER, 0);
|
|
lv_obj_align(lbl_title, LV_ALIGN_TOP_MID, 0, 34);
|
|
lbl_msg = label(scr_status, &lv_font_montserrat_14, 0xb8c0cc);
|
|
lv_obj_set_width(lbl_msg, LCD_H_RES - 24);
|
|
lv_obj_set_style_text_align(lbl_msg, LV_TEXT_ALIGN_CENTER, 0);
|
|
lv_label_set_long_mode(lbl_msg, LV_LABEL_LONG_WRAP);
|
|
lv_obj_align(lbl_msg, LV_ALIGN_TOP_MID, 0, 80);
|
|
|
|
// --- camera page ---
|
|
scr_cam = plain_screen();
|
|
canvas = lv_canvas_create(scr_cam);
|
|
lv_obj_set_pos(canvas, 0, 0);
|
|
lv_obj_set_size(canvas, LCD_H_RES, LCD_V_RES);
|
|
|
|
overlay = lv_obj_create(scr_cam);
|
|
lv_obj_remove_style_all(overlay);
|
|
lv_obj_set_size(overlay, LCD_H_RES, 44);
|
|
lv_obj_align(overlay, LV_ALIGN_BOTTOM_MID, 0, 0);
|
|
lv_obj_set_style_bg_color(overlay, lv_color_hex(0x000000), 0);
|
|
lv_obj_set_style_bg_opa(overlay, LV_OPA_60, 0);
|
|
|
|
lbl_place = label(overlay, &lv_font_montserrat_18, 0xffffff);
|
|
lv_obj_set_width(lbl_place, LCD_H_RES - 16);
|
|
lv_label_set_long_mode(lbl_place, LV_LABEL_LONG_DOT);
|
|
lv_obj_set_pos(lbl_place, 8, 3);
|
|
lbl_meta = label(overlay, &lv_font_montserrat_12, 0xc4ccd8);
|
|
lv_obj_set_width(lbl_meta, LCD_H_RES - 16);
|
|
lv_label_set_long_mode(lbl_meta, LV_LABEL_LONG_DOT);
|
|
lv_obj_set_pos(lbl_meta, 8, 26);
|
|
|
|
bar = lv_bar_create(scr_cam);
|
|
lv_obj_remove_style_all(bar);
|
|
lv_obj_set_size(bar, LCD_H_RES, 3);
|
|
lv_obj_align(bar, LV_ALIGN_BOTTOM_MID, 0, 0);
|
|
lv_bar_set_range(bar, 0, 1000);
|
|
lv_obj_set_style_bg_color(bar, lv_color_hex(0xffffff), LV_PART_INDICATOR);
|
|
lv_obj_set_style_bg_opa(bar, LV_OPA_80, LV_PART_INDICATOR);
|
|
|
|
badge = label(scr_cam, &lv_font_montserrat_12, 0xffffff);
|
|
lv_obj_set_style_bg_opa(badge, LV_OPA_COVER, 0);
|
|
lv_obj_set_style_radius(badge, 3, 0);
|
|
lv_obj_set_style_pad_hor(badge, 5, 0);
|
|
lv_obj_set_style_pad_ver(badge, 2, 0);
|
|
lv_obj_align(badge, LV_ALIGN_TOP_RIGHT, -6, 6);
|
|
lv_obj_set_hidden(badge, true);
|
|
|
|
toast = label(scr_cam, &lv_font_montserrat_14, 0xffffff);
|
|
lv_obj_set_style_bg_color(toast, lv_color_hex(0x202830), 0);
|
|
lv_obj_set_style_bg_opa(toast, LV_OPA_90, 0);
|
|
lv_obj_set_style_radius(toast, 6, 0);
|
|
lv_obj_set_style_pad_all(toast, 8, 0);
|
|
lv_obj_align(toast, LV_ALIGN_CENTER, 0, -10);
|
|
lv_obj_set_hidden(toast, true);
|
|
|
|
lv_screen_load(scr_status);
|
|
lvgl_port_unlock();
|
|
}
|
|
|
|
void ui_status(const char *title, const char *msg)
|
|
{
|
|
lvgl_port_lock(0);
|
|
lv_label_set_text(lbl_title, title ? title : "");
|
|
lv_label_set_text(lbl_msg, msg ? msg : "");
|
|
if (lv_screen_active() != scr_status) lv_screen_load(scr_status);
|
|
lvgl_port_unlock();
|
|
}
|
|
|
|
void ui_show_camera_page(void)
|
|
{
|
|
lvgl_port_lock(0);
|
|
if (lv_screen_active() != scr_cam) lv_screen_load(scr_cam);
|
|
lvgl_port_unlock();
|
|
}
|
|
|
|
void ui_present_frame(uint16_t *rgb565)
|
|
{
|
|
lvgl_port_lock(0);
|
|
lv_canvas_set_buffer(canvas, rgb565, LCD_H_RES, LCD_V_RES, LV_COLOR_FORMAT_RGB565);
|
|
lv_obj_invalidate(canvas);
|
|
lvgl_port_unlock();
|
|
}
|
|
|
|
void ui_set_caption(const char *place, const char *meta)
|
|
{
|
|
lvgl_port_lock(0);
|
|
lv_label_set_text(lbl_place, place ? place : "");
|
|
lv_label_set_text(lbl_meta, meta ? meta : "");
|
|
lvgl_port_unlock();
|
|
}
|
|
|
|
void ui_set_badge(const char *text, uint32_t rgb)
|
|
{
|
|
lvgl_port_lock(0);
|
|
if (!text || !overlay_on) {
|
|
lv_obj_set_hidden(badge, true);
|
|
} else {
|
|
lv_label_set_text(badge, text);
|
|
lv_obj_set_style_bg_color(badge, lv_color_hex(rgb), 0);
|
|
lv_obj_set_hidden(badge, false);
|
|
}
|
|
lvgl_port_unlock();
|
|
}
|
|
|
|
void ui_set_progress(int permille)
|
|
{
|
|
lvgl_port_lock(0);
|
|
if (permille < 0 || !overlay_on) {
|
|
lv_obj_set_hidden(bar, true);
|
|
} else {
|
|
lv_bar_set_value(bar, permille > 1000 ? 1000 : permille, LV_ANIM_OFF);
|
|
lv_obj_set_hidden(bar, false);
|
|
}
|
|
lvgl_port_unlock();
|
|
}
|
|
|
|
void ui_toast(const char *msg, uint32_t ms)
|
|
{
|
|
lvgl_port_lock(0);
|
|
lv_label_set_text(toast, msg);
|
|
lv_obj_set_hidden(toast, false);
|
|
if (toast_timer) lv_timer_delete(toast_timer);
|
|
toast_timer = lv_timer_create(toast_hide_cb, ms, NULL);
|
|
lv_timer_set_repeat_count(toast_timer, 1);
|
|
lvgl_port_unlock();
|
|
}
|
|
|
|
void ui_set_overlay_visible(bool visible)
|
|
{
|
|
lvgl_port_lock(0);
|
|
overlay_on = visible;
|
|
if (visible) {
|
|
lv_obj_set_hidden(overlay, false);
|
|
} else {
|
|
lv_obj_set_hidden(overlay, true);
|
|
lv_obj_set_hidden(badge, true);
|
|
lv_obj_set_hidden(bar, true);
|
|
}
|
|
lvgl_port_unlock();
|
|
}
|
|
|
|
bool ui_overlay_visible(void) { return overlay_on; }
|