feat(ui): scene view + original fonts, 3MB apps

This commit is contained in:
Claude Worker claude2
2026-06-10 21:28:31 +02:00
parent 21b06665e8
commit 2de7b20cd8
6 changed files with 6618 additions and 8 deletions
@@ -1,5 +1,7 @@
idf_component_register(
SRCS "display_ui.cpp"
"fonts/lv_font_orbitron_40.c"
"fonts/lv_font_ibmplexmono_18.c"
INCLUDE_DIRS "include"
PRIV_REQUIRES driver local_puzzles esp_timer
)
+77 -1
View File
@@ -59,6 +59,10 @@
#include <LovyanGFX.hpp>
#include "lvgl.h"
// Original UI fonts (fonts/ — copied from ui_freenove_allinone/src/ui/fonts/).
LV_FONT_DECLARE(lv_font_orbitron_40);
LV_FONT_DECLARE(lv_font_ibmplexmono_18);
static const char *TAG = "display_ui";
// ─── LovyanGFX device (faithful port of FreenoveLgfxDevice) ──────────────────
@@ -171,10 +175,19 @@ typedef struct {
lv_obj_t *value; // value label of a row
} dui_row_t;
static lv_obj_t *s_scr_status; // status screen (no active step)
static lv_obj_t *s_lbl_title;
static dui_row_t s_row_ip, s_row_wake, s_row_step, s_row_armed, s_row_solved;
static lv_obj_t *s_lbl_code;
// Scene view (active step) — structure mirrors the original ui_manager scene
// frame: title (Orbitron), subtitle (IBM Plex Mono), symbol slot (the
// assembled code, pulse effect = SceneEffect::kPulse default).
static lv_obj_t *s_scr_scene;
static lv_obj_t *s_scene_title; // step id, Orbitron 40, top-center
static lv_obj_t *s_scene_subtitle; // armed puzzle, IBM Plex 18, bottom
static lv_obj_t *s_scene_code; // assembled code, Orbitron 40, center
static lv_obj_t *make_row(lv_obj_t *parent, int y, const char *label_txt,
dui_row_t *out) {
lv_obj_t *lbl = lv_label_create(parent);
@@ -195,8 +208,51 @@ static lv_obj_t *make_row(lv_obj_t *parent, int y, const char *label_txt,
return val;
}
// Pulse animation on the scene code — SceneEffect::kPulse equivalent.
static void pulse_anim_cb(void *obj, int32_t v) {
lv_obj_set_style_opa((lv_obj_t *) obj, (lv_opa_t) v, 0);
}
static void build_scene_screen(void) {
s_scr_scene = lv_obj_create(NULL);
lv_obj_set_style_bg_color(s_scr_scene, lv_color_hex(0x000000), 0);
lv_obj_set_style_bg_opa(s_scr_scene, LV_OPA_COVER, 0);
s_scene_title = lv_label_create(s_scr_scene);
lv_obj_set_style_text_color(s_scene_title, lv_color_hex(COL_VALUE), 0);
lv_obj_set_style_text_font(s_scene_title, &lv_font_orbitron_40, 0);
lv_label_set_text(s_scene_title, "");
lv_label_set_long_mode(s_scene_title, LV_LABEL_LONG_SCROLL_CIRCULAR);
lv_obj_set_width(s_scene_title, DUI_HOR_RES - 24);
lv_obj_set_style_text_align(s_scene_title, LV_TEXT_ALIGN_CENTER, 0);
lv_obj_align(s_scene_title, LV_ALIGN_TOP_MID, 0, 24);
s_scene_code = lv_label_create(s_scr_scene);
lv_obj_set_style_text_color(s_scene_code, lv_color_hex(COL_CODE), 0);
lv_obj_set_style_text_font(s_scene_code, &lv_font_orbitron_40, 0);
lv_label_set_text(s_scene_code, "");
lv_obj_align(s_scene_code, LV_ALIGN_CENTER, 0, 16);
s_scene_subtitle = lv_label_create(s_scr_scene);
lv_obj_set_style_text_color(s_scene_subtitle, lv_color_hex(COL_LABEL), 0);
lv_obj_set_style_text_font(s_scene_subtitle, &lv_font_ibmplexmono_18, 0);
lv_label_set_text(s_scene_subtitle, "");
lv_obj_align(s_scene_subtitle, LV_ALIGN_BOTTOM_LEFT, 8, -10);
// Pulse on the code (original default scene effect).
lv_anim_t a;
lv_anim_init(&a);
lv_anim_set_var(&a, s_scene_code);
lv_anim_set_exec_cb(&a, pulse_anim_cb);
lv_anim_set_values(&a, LV_OPA_COVER, LV_OPA_50);
lv_anim_set_time(&a, 600);
lv_anim_set_playback_time(&a, 600);
lv_anim_set_repeat_count(&a, LV_ANIM_REPEAT_INFINITE);
lv_anim_start(&a);
}
static void build_status_screen(void) {
lv_obj_t *scr = lv_scr_act();
lv_obj_t *scr = s_scr_status;
lv_obj_set_style_bg_color(scr, lv_color_hex(COL_BG), 0);
lv_obj_set_style_bg_opa(scr, LV_OPA_COVER, 0);
@@ -232,6 +288,7 @@ static void build_status_screen(void) {
}
static void apply_status(const display_status_t *s) {
// Status screen rows
lv_label_set_text(s_row_ip.value, s->ip[0] ? s->ip : "(aucune)");
lv_label_set_text(s_row_wake.value, s->wake_active ? "oui" : "non");
lv_label_set_text(s_row_step.value, s->step_id[0] ? s->step_id : "(aucune)");
@@ -244,6 +301,23 @@ static void apply_status(const display_status_t *s) {
char code[32];
snprintf(code, sizeof(code), "Code: %s", s->code[0] ? s->code : "---");
lv_label_set_text(s_lbl_code, code);
// Scene screen
lv_label_set_text(s_scene_title, s->step_id);
if (s->armed[0] && strcmp(s->armed, "none") != 0) {
snprintf(tmp, sizeof(tmp), "enigme: %s", s->armed);
} else {
snprintf(tmp, sizeof(tmp), "resolues: %u", (unsigned) s->solved_count);
}
lv_label_set_text(s_scene_subtitle, tmp);
lv_label_set_text(s_scene_code, s->code[0] ? s->code : "");
// Active step → scene view; idle → status view. Fade 240 ms, the
// original ui_manager default transition (SceneTransition::kFade, 240).
lv_obj_t *want = s->step_id[0] ? s_scr_scene : s_scr_status;
if (lv_scr_act() != want) {
lv_scr_load_anim(want, LV_SCR_LOAD_ANIM_FADE_ON, 240, 0, false);
}
}
// ─── Raw splash (pre-LVGL, visible during the rest of boot) ───────────────────
@@ -309,7 +383,9 @@ static void display_task(void *arg) {
(void) arg;
// LVGL lives entirely on this task.
s_scr_status = lv_scr_act();
build_status_screen();
build_scene_screen();
display_status_t local_status;
memset(&local_status, 0, sizeof(local_status));
@@ -0,0 +1,3 @@
# Fonts — copied from ui_freenove_allinone/src/ui/fonts/ (original Arduino UI).
# Generated LVGL 8 bitmap fonts (see each file header for lv_font_conv options).
# Orbitron 40: scene titles/symbol. IBM Plex Mono 18: scene body text.
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
+13 -7
View File
@@ -5,21 +5,27 @@
# esp-sr component CMake glue).
#
# Total target: 16 MB flash (Freenove ESP32-S3 N16R8).
# Layout (rough): 0x000000-0x020000 system, 0x020000-0x620000 apps (3×2 MB),
# 0x620000-0x720000 model (SPIFFS 1 MB, esp-sr models),
# 0x720000-0xC20000 storage (LittleFS 5 MB),
# 0xC20000-0x1000000 unused (~3.87 MB headroom).
# Layout (rough): 0x000000-0x020000 system, 0x020000-0x920000 apps (3×3 MB),
# 0x920000-0xA20000 model (SPIFFS 1 MB, esp-sr models),
# 0xA20000-0xF20000 storage (LittleFS 5 MB),
# 0xF20000-0x1000000 unused (~0.87 MB headroom).
#
# App partition bumped 1.5 MB → 2 MB (2026-05-03) to give voice/STT/hints
# slices and esp-sr integration ~25 % free space margin. OTA dual-bank
# preserved (factory + ota_0 + ota_1 all 2 MB → rollback symmetry intact).
#
# Bumped 2 MB → 3 MB (2026-06-10, display_ui phase 2b): LVGL + LovyanGFX +
# the original UI fonts left only 5 % app headroom; later UI phases (camera
# preview, more fonts, effects) need room. NVS/otadata/phy offsets unchanged
# (Wi-Fi creds survive); ota_0/ota_1/model/storage shift — LittleFS content
# is lost on reflash (auto-reformats; re-push scenario via POST /game/scenario).
#
# Name, Type, SubType, Offset, Size, Flags
nvs, data, nvs, 0x9000, 0x6000,
otadata, data, ota, 0xf000, 0x2000,
phy_init, data, phy, 0x11000, 0x1000,
factory, app, factory, 0x20000, 0x200000,
ota_0, app, ota_0, , 0x200000,
ota_1, app, ota_1, , 0x200000,
factory, app, factory, 0x20000, 0x300000,
ota_0, app, ota_0, , 0x300000,
ota_1, app, ota_1, , 0x300000,
model, data, spiffs, , 0x100000,
storage, data, littlefs,, 0x500000,
1 # Zacus master partition table — merges ota_server's OTA layout (factory +
5 # esp-sr component CMake glue).
6 #
7 # Total target: 16 MB flash (Freenove ESP32-S3 N16R8).
8 # Layout (rough): 0x000000-0x020000 system, 0x020000-0x620000 apps (3×2 MB), # Layout (rough): 0x000000-0x020000 system, 0x020000-0x920000 apps (3×3 MB),
9 # 0x620000-0x720000 model (SPIFFS 1 MB, esp-sr models), # 0x920000-0xA20000 model (SPIFFS 1 MB, esp-sr models),
10 # 0x720000-0xC20000 storage (LittleFS 5 MB), # 0xA20000-0xF20000 storage (LittleFS 5 MB),
11 # 0xC20000-0x1000000 unused (~3.87 MB headroom). # 0xF20000-0x1000000 unused (~0.87 MB headroom).
12 #
13 # App partition bumped 1.5 MB → 2 MB (2026-05-03) to give voice/STT/hints
14 # slices and esp-sr integration ~25 % free space margin. OTA dual-bank
15 # preserved (factory + ota_0 + ota_1 all 2 MB → rollback symmetry intact).
16 #
17 # Bumped 2 MB → 3 MB (2026-06-10, display_ui phase 2b): LVGL + LovyanGFX +
18 # the original UI fonts left only 5 % app headroom; later UI phases (camera
19 # preview, more fonts, effects) need room. NVS/otadata/phy offsets unchanged
20 # (Wi-Fi creds survive); ota_0/ota_1/model/storage shift — LittleFS content
21 # is lost on reflash (auto-reformats; re-push scenario via POST /game/scenario).
22 #
23 # Name, Type, SubType, Offset, Size, Flags
24 nvs, data, nvs, 0x9000, 0x6000,
25 otadata, data, ota, 0xf000, 0x2000,
26 phy_init, data, phy, 0x11000, 0x1000,
27 factory, app, factory, 0x20000, 0x200000, factory, app, factory, 0x20000, 0x300000,
28 ota_0, app, ota_0, , 0x200000, ota_0, app, ota_0, , 0x300000,
29 ota_1, app, ota_1, , 0x200000, ota_1, app, ota_1, , 0x300000,
30 model, data, spiffs, , 0x100000,
31 storage, data, littlefs,, 0x500000,