From 37c40c3420c5d4353c2591ec98aa4eb658b89333 Mon Sep 17 00:00:00 2001 From: Zach Whitehead Date: Thu, 26 Mar 2026 18:07:15 -0400 Subject: [PATCH 1/3] Upgrade LVGL to v9 and adapt APIs Bump LVGL to v9 (platformio.ini) and update project code and config to the v9 API and data structures. Changes include: lv_conf.h rewritten for LVGL v9 config macros and memory/OS settings; switch from legacy lv_disp/lv_draw_buf APIs to lv_display-based APIs and byte-based buffers (LVGL_BUF_BYTES); update flush callback signature and usage (lv_display_flush_ready, px_map), use lv_timer_handler; convert image assets and descriptors to lv_image_dsc_t with new header/.data/.data_size fields; rename image and spinner APIs (lv_img_* -> lv_image_*, lv_spinner_set_anim_params); replace deprecated functions/types (lv_obj_clear_flag -> lv_obj_remove_flag, lv_obj_set_style_img_recolor -> lv_obj_set_style_image_recolor, lv_scr_load -> lv_screen_load, lv_refr_now/lv_disp_get_default -> lv_refr_now/lv_display_get_default, lv_screen_active()); switch point arrays to lv_point_precise_t; adjust various visibility/flag and style calls across UI files to match v9. Also add small .claude settings entries for extra PlatformIO tasks. These updates align the codebase with LVGL v9 API changes and improve rendering/flush handling for the display. --- .claude/settings.local.json | 4 +- inc/sp140/lvgl/lv_conf.h | 181 ++++++---------------- inc/sp140/lvgl/lvgl_core.h | 13 +- platformio.ini | 2 +- src/assets/img/cruise-control-340255-30.c | 21 +-- src/assets/img/energy-539741-26.c | 22 +-- src/assets/img/warning_2135850_30.c | 22 +-- src/sp140/lvgl/lvgl_alerts.cpp | 16 +- src/sp140/lvgl/lvgl_core.cpp | 43 ++--- src/sp140/lvgl/lvgl_main_screen.cpp | 53 +++---- src/sp140/lvgl/lvgl_updates.cpp | 52 +++---- src/sp140/main.cpp | 2 +- 12 files changed, 171 insertions(+), 260 deletions(-) diff --git a/.claude/settings.local.json b/.claude/settings.local.json index cc4d5e74..87bf95aa 100644 --- a/.claude/settings.local.json +++ b/.claude/settings.local.json @@ -4,7 +4,9 @@ "Bash(pio)", "Bash(pio run)", "Bash(cpplint:*)", - "Bash(grep:*)" + "Bash(grep:*)", + "Bash(pio test:*)", + "Bash(pio run:*)" ], "deny": [] } diff --git a/inc/sp140/lvgl/lv_conf.h b/inc/sp140/lvgl/lv_conf.h index e913cdc1..76423aa7 100644 --- a/inc/sp140/lvgl/lv_conf.h +++ b/inc/sp140/lvgl/lv_conf.h @@ -1,88 +1,46 @@ /** * @file lv_conf.h - * Configuration file for LVGL + * Configuration file for LVGL v9 */ #ifndef LV_CONF_H // NOLINT(build/header_guard) #define LV_CONF_H // NOLINT(build/header_guard) -#include - /*==================== COLOR SETTINGS *====================*/ -/*Color depth: 1 (1 byte per pixel), 8 (RGB332), 16 (RGB565), 32 (ARGB8888)*/ +/*Color depth: 8 (A8), 16 (RGB565), 24 (RGB888), 32 (XRGB8888)*/ #define LV_COLOR_DEPTH 16 -/*Swap the 2 bytes of RGB565 color. Useful if the display has an 8-bit interface*/ -#define LV_COLOR_16_SWAP 0 - -/*Enable features to draw on transparent background*/ -#define LV_COLOR_SCREEN_TRANSP 0 - -/*Images pixels with this color will not be drawn if they are chroma keyed)*/ -#define LV_COLOR_CHROMA_KEY lv_color_hex(0x00ff00) /*pure green*/ - /*========================= - MEMORY SETTINGS + STDLIB SETTINGS *=========================*/ -/*1: use custom malloc/free, 0: use the built-in `lv_mem_alloc()` and `lv_mem_free()`*/ -#define LV_MEM_CUSTOM 0 -#if LV_MEM_CUSTOM == 0 - /*Size of the memory available for `lv_mem_alloc()` in bytes (>= 2kB)*/ - #define LV_MEM_SIZE (48U * 1024U) /*[bytes]*/ - - /*Set an address for the memory pool instead of allocating it as a normal array. Can be in external SRAM too.*/ - #define LV_MEM_ADR 0 /*0: unused*/ - /*Instead of an address give a memory allocator that will be called to get a memory pool for LVGL. E.g. my_malloc*/ - #if LV_MEM_ADR == 0 +/*Use LVGL's built-in memory manager*/ +#define LV_USE_STDLIB_MALLOC LV_STDLIB_BUILTIN - #endif +/*Size of the memory available for `lv_malloc()` in bytes (>= 2kB)*/ +#define LV_MEM_SIZE (48U * 1024U) -#else /*LV_MEM_CUSTOM*/ - #define LV_MEM_CUSTOM_INCLUDE /*Header for the dynamic memory function*/ - #define LV_MEM_CUSTOM_ALLOC malloc - #define LV_MEM_CUSTOM_FREE free - #define LV_MEM_CUSTOM_REALLOC realloc -#endif /*LV_MEM_CUSTOM*/ +#define LV_USE_STDLIB_STRING LV_STDLIB_BUILTIN +#define LV_USE_STDLIB_SPRINTF LV_STDLIB_BUILTIN -/*Number of the intermediate memory buffer used during rendering and other internal processing mechanisms. - *You will see an error log message if there wasn't enough buffers. */ -#define LV_MEM_BUF_MAX_NUM 16 +/*========================= + OS SETTINGS + *=========================*/ -/*Use the standard `memcpy` and `memset` instead of LVGL's own functions. (Might or might not be faster).*/ -#define LV_MEMCPY_MEMSET_STD 0 +/*Use no OS - keep existing lvglMutex for now*/ +#define LV_USE_OS LV_OS_NONE /*==================== HAL SETTINGS *====================*/ -/*Default display refresh period. LVG will redraw changed areas with this period time*/ -#define LV_DISP_DEF_REFR_PERIOD 30 /*[ms]*/ - -/*Input device read period in milliseconds*/ -#define LV_INDEV_DEF_READ_PERIOD 30 /*[ms]*/ - -/*Disable input devices to save resources*/ -#define LV_USE_INDEV_TOUCHSCREEN 0 /*Disable touchscreen support*/ -#define LV_USE_INDEV_TOUCHPAD 0 /*Disable touchpad support*/ -#define LV_USE_INDEV_MOUSE 0 /*Disable mouse support*/ -#define LV_USE_INDEV_KEYPAD 0 /*Disable keypad support*/ -#define LV_USE_INDEV_ENCODER 0 /*Disable encoder support*/ -#define LV_USE_INDEV_BUTTON 0 /*Disable button input device emulation*/ - -/*Use a custom tick source that tells the elapsed time in milliseconds. - *It removes the need to manually update the tick with `lv_tick_inc()`)*/ -#define LV_TICK_CUSTOM 0 -#if LV_TICK_CUSTOM - #define LV_TICK_CUSTOM_INCLUDE "Arduino.h" /*Header for the system time function*/ - #define LV_TICK_CUSTOM_SYS_TIME_EXPR (millis()) /*Expression evaluating to current system time in ms*/ -#endif /*LV_TICK_CUSTOM*/ - -/*Default Dot Per Inch. Used to initialize default sizes such as widgets sized, style paddings. - *(Not so important, you can adjust it to modify default sizes and spaces)*/ +/*Default display refresh, period. LVGL will redraw changed areas with this period time*/ +#define LV_DEF_REFR_PERIOD 30 /*[ms]*/ + +/*Default Dot Per Inch. Used to initialize default sizes such as widgets sized, style paddings.*/ #define LV_DPI_DEF 130 /*[px/inch]*/ /*======================= @@ -93,67 +51,27 @@ * Drawing *-----------*/ -/*Enable complex draw engine. - *Required to draw shadow, gradient, rounded corners, circles, arc, skew lines, image transformations or any masks*/ -#define LV_DRAW_COMPLEX 1 -#if LV_DRAW_COMPLEX != 0 +/*Enable complex draw engine*/ +#define LV_DRAW_SW_COMPLEX 1 - /*Allow buffering some shadow calculation. - *LV_SHADOW_CACHE_SIZE is the max. shadow size to buffer, where shadow size is `shadow_width + radius` - *Caching has LV_SHADOW_CACHE_SIZE^2 RAM cost*/ - #define LV_SHADOW_CACHE_SIZE 0 +/*Allow buffering some shadow calculation. + *LV_DRAW_SW_SHADOW_CACHE_SIZE is the max. shadow size to buffer*/ +#define LV_DRAW_SW_SHADOW_CACHE_SIZE 0 - /* Set number of maximally cached circle data. - * The circumference of 1/4 circle are saved for anti-aliasing - * radius * 4 bytes are used per circle (the most often used radiuses are saved) - * 0: to disable caching */ - #define LV_CIRCLE_CACHE_SIZE 4 -#endif /*LV_DRAW_COMPLEX*/ +/*Set number of maximally cached circle data*/ +#define LV_DRAW_SW_CIRCLE_CACHE_SIZE 4 -/** - * "Simple layers" are used when a widget has `style_opa < 255` to buffer the widget into a layer - * and blend it as an image with the given opacity. - * Note that `bg_opa`, `text_opa` etc don't require buffering into layer) - * The widget can be buffered in smaller chunks to avoid using large buffers. - * - * - LV_LAYER_SIMPLE_BUF_SIZE: [bytes] the optimal target buffer size. LVGL will try to allocate it - * - LV_LAYER_SIMPLE_FALLBACK_BUF_SIZE: [bytes] used if `LV_LAYER_SIMPLE_BUF_SIZE` couldn't be allocated. - * - * Both buffer sizes are in bytes. - * "Transformed layers" (where transform_angle/zoom properties are used) use larger buffers - * and can't be drawn in chunks. So these settings affects only widgets with opacity. - */ -#define LV_LAYER_SIMPLE_BUF_SIZE (24 * 1024) -#define LV_LAYER_SIMPLE_FALLBACK_BUF_SIZE (3 * 1024) - -/*Default image cache size. Image caching keeps the images opened. - *If only the built-in image formats are used there is no real advantage of caching. (I.e. if no new image decoder is added) - *With complex image decoders (e.g. PNG or JPG) caching can save the continuous open/decode of images. - *However the opened images might consume additional RAM. - *0: to disable caching*/ -#define LV_IMG_CACHE_DEF_SIZE 0 - -/*Number of stops allowed per gradient. Increase this to allow more stops. - *This adds (sizeof(lv_color_t) + 1) bytes per additional stop*/ +/*Buffer size for simple layers*/ +#define LV_DRAW_LAYER_SIMPLE_BUF_SIZE (24 * 1024) + +/*Default image cache size*/ +#define LV_IMAGE_CACHE_DEF_SIZE 0 + +/*Number of stops allowed per gradient*/ #define LV_GRADIENT_MAX_STOPS 2 -/*Default gradient buffer size. - *When LVGL calculates the gradient "maps" it can save them into a cache to avoid calculating them again. - *LV_GRAD_CACHE_DEF_SIZE sets the size of this cache in bytes. - *If the cache is too small the map will be allocated only while it's required for the drawing. - *0 mean no caching.*/ -#define LV_GRAD_CACHE_DEF_SIZE 0 - -/*Allow dithering the gradients (to achieve visual smooth color gradients on limited color depth display) - *LV_DITHER_GRADIENT implies allocating one or two more lines of the object's rendering surface - *The increase in memory consumption is (32 bits * object width) plus 24 bits * object width if using error diffusion */ -#define LV_DITHER_GRADIENT 0 -#if LV_DITHER_GRADIENT - /*Add support for error diffusion dithering. - *Error diffusion dithering gets a much better visual result, but implies more CPU consumption and memory when drawing. - *The increase in memory consumption is (24 bits * object's width)*/ - #define LV_DITHER_ERROR_DIFFUSION 0 -#endif +/*Number of parallel draw units (1 = single threaded)*/ +#define LV_DRAW_THREAD_COUNT 1 /*------------- * Fonts @@ -191,8 +109,6 @@ /** * Enable handling large font and/or fonts with a lot of characters. - * The limit depends on the font size, font face and bpp. - * Compiler error will be triggered if a font needs it. */ #define LV_FONT_FMT_TXT_LARGE 0 @@ -208,9 +124,6 @@ /** * Select a character encoding for strings. - * Your IDE or editor should have the same character encoding - * - LV_TXT_ENC_UTF8 - * - LV_TXT_ENC_ASCII */ #define LV_TXT_ENC LV_TXT_ENC_UTF8 @@ -221,19 +134,15 @@ * WIDGET USAGE *=================*/ -/* -Documentation of the widgets: https://docs.lvgl.io/latest/en/html/widgets/index.html -*/ - -#define LV_USE_ANIMIMG 0 +#define LV_USE_ANIMIMAGE 0 #define LV_USE_ARC 1 #define LV_USE_BAR 1 -#define LV_USE_BTN 1 +#define LV_USE_BUTTON 1 -#define LV_USE_BTNMATRIX 1 /* Required by other components */ +#define LV_USE_BUTTONMATRIX 1 /* Required by other components */ #define LV_USE_CANVAS 0 /* Don't need canvas */ @@ -243,7 +152,7 @@ Documentation of the widgets: https://docs.lvgl.io/latest/en/html/widgets/index. #define LV_USE_DROPDOWN 0 /* Don't need dropdown */ -#define LV_USE_IMG 1 /*Requires: lv_label*/ +#define LV_USE_IMAGE 1 /*Requires: lv_label*/ #define LV_USE_LABEL 1 #if LV_USE_LABEL @@ -272,6 +181,9 @@ Documentation of the widgets: https://docs.lvgl.io/latest/en/html/widgets/index. /* keyboard widget (set to 0 since we don't use it) */ #define LV_USE_KEYBOARD 0 +/*Extra widgets*/ +#define LV_USE_LED 1 + /*================== * THEMES *==================*/ @@ -305,10 +217,11 @@ Documentation of the widgets: https://docs.lvgl.io/latest/en/html/widgets/index. *====================*/ #define LV_USE_FS_STDIO 0 -#if LV_USE_FS_STDIO - #define LV_FS_STDIO_LETTER 'A' /*Set an upper cased letter on which the drive will accessible (e.g. 'A')*/ - #define LV_FS_STDIO_PATH "" /*Set the working directory. File/directory paths will be appended to it.*/ - #define LV_FS_STDIO_CACHE_SIZE 0 /*>0 to cache this number of bytes in lv_fs_read()*/ -#endif + +/*==================== + * DEMOS & EXAMPLES + *====================*/ + +#define LV_BUILD_EXAMPLES 0 #endif /*LV_CONF_H*/ diff --git a/inc/sp140/lvgl/lvgl_core.h b/inc/sp140/lvgl/lvgl_core.h index 23cbe605..0b3be14a 100644 --- a/inc/sp140/lvgl/lvgl_core.h +++ b/inc/sp140/lvgl/lvgl_core.h @@ -12,19 +12,16 @@ #define SCREEN_WIDTH 160 #define SCREEN_HEIGHT 128 -// LVGL buffer size - optimize for our display -// Use half screen size for single flush to balance performance and memory usage -#define LVGL_BUFFER_SIZE (SCREEN_WIDTH * SCREEN_HEIGHT / 2) +// LVGL buffer size in bytes - half screen for partial rendering +// v9 uses byte-based buffers: width * height * bytes_per_pixel / 2 +#define LVGL_BUF_BYTES (SCREEN_WIDTH * SCREEN_HEIGHT * 2 / 2) // LVGL refresh time in ms - match the config file setting #define LVGL_REFRESH_TIME 40 // Core LVGL globals extern int8_t displayCS; // Display chip select pin -extern lv_disp_drv_t disp_drv; -extern lv_disp_draw_buf_t draw_buf; -extern lv_color_t buf[LVGL_BUFFER_SIZE]; -extern lv_color_t buf2[LVGL_BUFFER_SIZE]; // Second buffer for double buffering +extern lv_display_t* main_display; extern Adafruit_ST7735* tft_driver; extern uint32_t lvgl_last_update; // Shared SPI bus mutex (guards TFT + MCP2515 access) @@ -33,7 +30,7 @@ extern SemaphoreHandle_t spiBusMutex; // Core function declarations void setupLvglBuffer(); void setupLvglDisplay(const STR_DEVICE_DATA_140_V1& deviceData, int8_t dc_pin, int8_t rst_pin, SPIClass* spi); -void lvgl_flush_cb(lv_disp_drv_t* disp, const lv_area_t* area, lv_color_t* color_p); +void lvgl_flush_cb(lv_display_t* disp, const lv_area_t* area, uint8_t* px_map); void lv_tick_handler(); void updateLvgl(); void displayLvglSplash(const STR_DEVICE_DATA_140_V1& deviceData, int duration); diff --git a/platformio.ini b/platformio.ini index e526f348..4dafee48 100644 --- a/platformio.ini +++ b/platformio.ini @@ -59,7 +59,7 @@ lib_deps = https://github.com/rlogiacco/CircularBuffer@1.4.0 https://github.com/openppg/SINE-ESC-CAN#8caa93996b5d000fe10ca5265bd1c472dfdf885b https://github.com/openppg/ANT-BMS-CAN#fd54852bc6f1c9608e37af9ca7c13ea4135c095b - lvgl/lvgl@^8.4.0 + lvgl/lvgl@^9.2.2 h2zero/NimBLE-Arduino@^2.3.9 lib_ignore = Adafruit SleepyDog Library diff --git a/src/assets/img/cruise-control-340255-30.c b/src/assets/img/cruise-control-340255-30.c index a01aed14..5e45845b 100644 --- a/src/assets/img/cruise-control-340255-30.c +++ b/src/assets/img/cruise-control-340255-30.c @@ -54,14 +54,17 @@ const LV_ATTRIBUTE_MEM_ALIGN LV_ATTRIBUTE_LARGE_CONST LV_ATTRIBUTE_IMG_CRUISE_CO 0x00, 0x00, 0x00, 0x00, }; -const lv_img_dsc_t cruise_control_340255_30 = { - { - LV_IMG_CF_ALPHA_1BIT, - 0, - 0, - 30, - 30, +const lv_image_dsc_t cruise_control_340255_30 = { + .header = { + .magic = LV_IMAGE_HEADER_MAGIC, + .cf = LV_COLOR_FORMAT_A1, + .flags = 0, + .w = 30, + .h = 30, + .stride = 4, /* ceil(30/8) = 4 bytes per row */ + .reserved_2 = 0, }, - 120, - cruise_control_340255_30_map, + .data_size = 120, + .data = cruise_control_340255_30_map, + .reserved = NULL, }; diff --git a/src/assets/img/energy-539741-26.c b/src/assets/img/energy-539741-26.c index 5cb8531c..3c99eb7f 100644 --- a/src/assets/img/energy-539741-26.c +++ b/src/assets/img/energy-539741-26.c @@ -50,15 +50,17 @@ const LV_ATTRIBUTE_MEM_ALIGN LV_ATTRIBUTE_LARGE_CONST LV_ATTRIBUTE_IMG_ENERGY_53 0x00, 0x00, 0x00, }; -const lv_img_dsc_t energy_539741_26 = { - // Header struct (lv_img_header_t) - { - LV_IMG_CF_ALPHA_1BIT, // Color format - 0, // Always zero - 0, // Reserved - 24, // Width - 26, // Height +const lv_image_dsc_t energy_539741_26 = { + .header = { + .magic = LV_IMAGE_HEADER_MAGIC, + .cf = LV_COLOR_FORMAT_A1, + .flags = 0, + .w = 24, + .h = 26, + .stride = 3, /* ceil(24/8) = 3 bytes per row */ + .reserved_2 = 0, }, - 78, // Data size - energy_539741_26_map, // Pointer to image data + .data_size = 78, + .data = energy_539741_26_map, + .reserved = NULL, }; diff --git a/src/assets/img/warning_2135850_30.c b/src/assets/img/warning_2135850_30.c index 8cce6f75..95ec877a 100644 --- a/src/assets/img/warning_2135850_30.c +++ b/src/assets/img/warning_2135850_30.c @@ -54,15 +54,17 @@ const LV_ATTRIBUTE_MEM_ALIGN LV_ATTRIBUTE_LARGE_CONST LV_ATTRIBUTE_IMG_WARNING_2 0x00, 0x00, 0x00, 0x00, }; -const lv_img_dsc_t warning_2135850_30 = { - // Header struct (lv_img_header_t) - { - LV_IMG_CF_ALPHA_1BIT, // Color format - 0, // Always zero - 0, // Reserved - 30, // Width - 30, // Height +const lv_image_dsc_t warning_2135850_30 = { + .header = { + .magic = LV_IMAGE_HEADER_MAGIC, + .cf = LV_COLOR_FORMAT_A1, + .flags = 0, + .w = 30, + .h = 30, + .stride = 4, /* ceil(30/8) = 4 bytes per row */ + .reserved_2 = 0, }, - 120, // Data size - warning_2135850_30_map, // Pointer to image data + .data_size = 120, + .data = warning_2135850_30_map, + .reserved = NULL, }; diff --git a/src/sp140/lvgl/lvgl_alerts.cpp b/src/sp140/lvgl/lvgl_alerts.cpp index 83996971..a0d7d8e9 100644 --- a/src/sp140/lvgl/lvgl_alerts.cpp +++ b/src/sp140/lvgl/lvgl_alerts.cpp @@ -32,12 +32,12 @@ void loadAlertSnapshot(const AlertSnapshot& snap) { if (critical_text_label == NULL) return; lv_label_set_text(critical_text_label, txt); lv_obj_set_style_text_color(critical_text_label, lv_color_make(255, 0, 0), 0); - lv_obj_clear_flag(critical_text_label, LV_OBJ_FLAG_HIDDEN); + lv_obj_remove_flag(critical_text_label, LV_OBJ_FLAG_HIDDEN); } else { if (alert_text_label == NULL) return; lv_label_set_text(alert_text_label, txt); lv_obj_set_style_text_color(alert_text_label, lv_color_make(255, 165, 0), 0); - lv_obj_clear_flag(alert_text_label, LV_OBJ_FLAG_HIDDEN); + lv_obj_remove_flag(alert_text_label, LV_OBJ_FLAG_HIDDEN); } } @@ -137,7 +137,7 @@ void updateAlertCounterDisplay(const AlertCounts& counts) { snprintf(buf, sizeof(buf), "%u", counts.warningCount); } lv_label_set_text(warning_counter_label, buf); - lv_obj_clear_flag(warning_counter_circle, LV_OBJ_FLAG_HIDDEN); + lv_obj_remove_flag(warning_counter_circle, LV_OBJ_FLAG_HIDDEN); } else { lv_obj_add_flag(warning_counter_circle, LV_OBJ_FLAG_HIDDEN); } @@ -151,7 +151,7 @@ void updateAlertCounterDisplay(const AlertCounts& counts) { snprintf(buf, sizeof(buf), "%u", counts.criticalCount); } lv_label_set_text(critical_counter_label, buf); - lv_obj_clear_flag(critical_counter_circle, LV_OBJ_FLAG_HIDDEN); + lv_obj_remove_flag(critical_counter_circle, LV_OBJ_FLAG_HIDDEN); // Simple positioning: align with alert text if no warnings, otherwise right of warning circle if (counts.warningCount == 0) { @@ -195,7 +195,7 @@ void lv_showAlertTextWithLevel(SensorID id, AlertLevel level, bool critical) { // Place critical text to the right of the red bubble lv_obj_align_to(critical_text_label, critical_counter_circle, LV_ALIGN_OUT_RIGHT_MID, 4, 0); } - lv_obj_clear_flag(critical_text_label, LV_OBJ_FLAG_HIDDEN); + lv_obj_remove_flag(critical_text_label, LV_OBJ_FLAG_HIDDEN); } else { // Use alert_text_label for warning alerts if (alert_text_label == NULL) return; @@ -213,9 +213,9 @@ void lv_showAlertTextWithLevel(SensorID id, AlertLevel level, bool critical) { // Hide altitude since warning takes that space setAltitudeVisibility(false); if (warning_counter_circle && critical_counter_circle && altitude_char_labels[0]) { - lv_coord_t altY = lv_obj_get_y(altitude_char_labels[0]); + int32_t altY = lv_obj_get_y(altitude_char_labels[0]); // Align warning bubble X with critical bubble X so texts line up - lv_coord_t critX = lv_obj_get_x(critical_counter_circle); + int32_t critX = lv_obj_get_x(critical_counter_circle); // Place orange bubble aligned with critical bubble lv_obj_set_pos(warning_counter_circle, critX, altY + 2); // Place warning text to the right of the orange bubble @@ -231,7 +231,7 @@ void lv_showAlertTextWithLevel(SensorID id, AlertLevel level, bool critical) { lv_obj_align_to(alert_text_label, warning_counter_circle, LV_ALIGN_OUT_RIGHT_MID, 4, 0); } } - lv_obj_clear_flag(alert_text_label, LV_OBJ_FLAG_HIDDEN); + lv_obj_remove_flag(alert_text_label, LV_OBJ_FLAG_HIDDEN); } } diff --git a/src/sp140/lvgl/lvgl_core.cpp b/src/sp140/lvgl/lvgl_core.cpp index 24f927fd..b4df9d35 100644 --- a/src/sp140/lvgl/lvgl_core.cpp +++ b/src/sp140/lvgl/lvgl_core.cpp @@ -4,10 +4,9 @@ #include "../../../inc/sp140/esp32s3-config.h" // Global variables for core LVGL functionality -lv_disp_drv_t disp_drv; -lv_disp_draw_buf_t draw_buf; -lv_color_t buf[LVGL_BUFFER_SIZE]; -lv_color_t buf2[LVGL_BUFFER_SIZE]; // Second buffer for double buffering +lv_display_t* main_display = nullptr; +static uint8_t buf[LVGL_BUF_BYTES]; +static uint8_t buf2[LVGL_BUF_BYTES]; Adafruit_ST7735* tft_driver = nullptr; uint32_t lvgl_last_update = 0; // Define the shared SPI bus mutex @@ -16,9 +15,6 @@ SemaphoreHandle_t spiBusMutex = NULL; void setupLvglBuffer() { // Initialize LVGL library lv_init(); - - // Setup double buffer for LVGL to reduce tearing - lv_disp_draw_buf_init(&draw_buf, buf, buf2, LVGL_BUFFER_SIZE); } void setupLvglDisplay( @@ -50,33 +46,28 @@ void setupLvglDisplay( // Initialize LVGL buffer setupLvglBuffer(); - // Initialize display driver (use global disp_drv) - lv_disp_drv_init(&disp_drv); - - // Set display driver properties - disp_drv.hor_res = SCREEN_WIDTH; - disp_drv.ver_res = SCREEN_HEIGHT; - disp_drv.flush_cb = lvgl_flush_cb; - disp_drv.draw_buf = &draw_buf; + // Create display and configure it + main_display = lv_display_create(SCREEN_WIDTH, SCREEN_HEIGHT); + lv_display_set_flush_cb(main_display, lvgl_flush_cb); + lv_display_set_buffers(main_display, buf, buf2, sizeof(buf), LV_DISPLAY_RENDER_MODE_PARTIAL); + lv_display_set_color_format(main_display, LV_COLOR_FORMAT_RGB565); - // Register the display driver - lv_disp_drv_register(&disp_drv); USBSerial.println("Display driver registered"); // Set LVGL default theme - using default font lv_theme_t* theme = lv_theme_default_init( - lv_disp_get_default(), // Display + main_display, // Display lv_palette_main(LV_PALETTE_BLUE), // Primary color lv_palette_main(LV_PALETTE_AMBER), // Secondary color deviceData.theme == 1, // Dark mode LV_FONT_DEFAULT); // Default font - lv_disp_set_theme(lv_disp_get_default(), theme); + lv_display_set_theme(main_display, theme); } // Optimize the flush callback to minimize SPI transfers // CS pin management is handled here where actual SPI communication occurs -void lvgl_flush_cb(lv_disp_drv_t* disp, const lv_area_t* area, lv_color_t* color_p) { +void lvgl_flush_cb(lv_display_t* disp, const lv_area_t* area, uint8_t* px_map) { uint32_t w = (area->x2 - area->x1 + 1); uint32_t h = (area->y2 - area->y1 + 1); @@ -87,7 +78,7 @@ void lvgl_flush_cb(lv_disp_drv_t* disp, const lv_area_t* area, lv_color_t* color // SPI bus timeout - BMS might be doing long operation, skip display flush USBSerial.println("[DISPLAY] SPI bus timeout - skipping display flush"); // Must still signal LVGL that flush is done to avoid deadlock - lv_disp_flush_ready(disp); + lv_display_flush_ready(disp); return; } } @@ -99,7 +90,7 @@ void lvgl_flush_cb(lv_disp_drv_t* disp, const lv_area_t* area, lv_color_t* color // Push colors - using DMA if available uint32_t len = w * h; - tft_driver->writePixels((uint16_t*)color_p, len); // NOLINT(readability/casting) + tft_driver->writePixels((uint16_t*)px_map, len); // NOLINT(readability/casting) tft_driver->endWrite(); // Deselect display CS when done @@ -109,7 +100,7 @@ void lvgl_flush_cb(lv_disp_drv_t* disp, const lv_area_t* area, lv_color_t* color } // Indicate to LVGL that flush is done - lv_disp_flush_ready(disp); + lv_display_flush_ready(disp); } // LVGL tick handler - to be called from timer or in main loop @@ -130,7 +121,7 @@ void updateLvgl() { // Update LVGL at the defined refresh rate if (current_ms - lvgl_last_update > LVGL_REFRESH_TIME) { lv_tick_handler(); - lv_task_handler(); + lv_timer_handler(); lvgl_last_update = current_ms; } } @@ -140,10 +131,10 @@ void displayLvglSplash(const STR_DEVICE_DATA_140_V1& deviceData, int duration) { // Create a new screen for the splash lv_obj_t* splash_screen = lv_obj_create(NULL); - lv_scr_load(splash_screen); + lv_screen_load(splash_screen); // Disable scrollbars - lv_obj_clear_flag(splash_screen, LV_OBJ_FLAG_SCROLLABLE); + lv_obj_remove_flag(splash_screen, LV_OBJ_FLAG_SCROLLABLE); // Set background color based on theme lv_obj_set_style_bg_color(splash_screen, diff --git a/src/sp140/lvgl/lvgl_main_screen.cpp b/src/sp140/lvgl/lvgl_main_screen.cpp index a9e52236..85bf4239 100644 --- a/src/sp140/lvgl/lvgl_main_screen.cpp +++ b/src/sp140/lvgl/lvgl_main_screen.cpp @@ -58,7 +58,7 @@ void setAltitudeVisibility(bool visible) { for (int i = 0; i < 7; i++) { if (altitude_char_labels[i]) { if (visible) { - lv_obj_clear_flag(altitude_char_labels[i], LV_OBJ_FLAG_HIDDEN); + lv_obj_remove_flag(altitude_char_labels[i], LV_OBJ_FLAG_HIDDEN); } else { lv_obj_add_flag(altitude_char_labels[i], LV_OBJ_FLAG_HIDDEN); } @@ -104,7 +104,7 @@ void init_temp_styles(bool darkMode) { // Function to show the loading overlay void showLoadingOverlay() { if (spinner_overlay != NULL) { - lv_obj_clear_flag(spinner_overlay, LV_OBJ_FLAG_HIDDEN); + lv_obj_remove_flag(spinner_overlay, LV_OBJ_FLAG_HIDDEN); } } @@ -118,14 +118,14 @@ void hideLoadingOverlay() { // Setup the main screen layout once void setupMainScreen(bool darkMode) { if (main_screen != NULL) { - lv_obj_del(main_screen); + lv_obj_delete(main_screen); } // Create main screen main_screen = lv_obj_create(NULL); // Disable scrollbars - lv_obj_clear_flag(main_screen, LV_OBJ_FLAG_SCROLLABLE); + lv_obj_remove_flag(main_screen, LV_OBJ_FLAG_SCROLLABLE); // Set theme based on dark/light mode lv_obj_set_style_bg_color(main_screen, @@ -425,7 +425,7 @@ void setupMainScreen(bool darkMode) { // Draw divider lines // Create horizontal line between top and middle sections lv_obj_t* h_line1 = lv_line_create(main_screen); - static lv_point_t h_line1_points[] = {{0, 37}, {SCREEN_WIDTH, 37}}; + static lv_point_precise_t h_line1_points[] = {{0, 37}, {SCREEN_WIDTH, 37}}; lv_line_set_points(h_line1, h_line1_points, 2); lv_obj_set_style_line_color(h_line1, LVGL_GRAY, @@ -434,7 +434,7 @@ void setupMainScreen(bool darkMode) { // Create horizontal line between middle and bottom sections (stop at new section boundary) lv_obj_t* h_line2 = lv_line_create(main_screen); - static lv_point_t h_line2_points[] = {{0, 70}, {148, 70}}; + static lv_point_precise_t h_line2_points[] = {{0, 70}, {148, 70}}; lv_line_set_points(h_line2, h_line2_points, 2); lv_obj_set_style_line_color(h_line2, LVGL_GRAY, @@ -443,7 +443,7 @@ void setupMainScreen(bool darkMode) { // Create vertical line in middle section lv_obj_t* v_line1 = lv_line_create(main_screen); - static lv_point_t v_line1_points[] = {{102, 37}, {102, 70}}; + static lv_point_precise_t v_line1_points[] = {{102, 37}, {102, 70}}; lv_line_set_points(v_line1, v_line1_points, 2); lv_obj_set_style_line_color(v_line1, LVGL_GRAY, @@ -452,7 +452,7 @@ void setupMainScreen(bool darkMode) { // Create vertical line in bottom section lv_obj_t* v_line2 = lv_line_create(main_screen); - static lv_point_t v_line2_points[] = {{117, 70}, {117, 128}}; + static lv_point_precise_t v_line2_points[] = {{117, 70}, {117, 128}}; lv_line_set_points(v_line2, v_line2_points, 2); lv_obj_set_style_line_color(v_line2, LVGL_GRAY, @@ -461,7 +461,7 @@ void setupMainScreen(bool darkMode) { // Create vertical line for new far-right section lv_obj_t* v_line3 = lv_line_create(main_screen); - static lv_point_t v_line3_points[] = {{148, 37}, {148, 128}}; + static lv_point_precise_t v_line3_points[] = {{148, 37}, {148, 128}}; lv_line_set_points(v_line3, v_line3_points, 2); lv_obj_set_style_line_color(v_line3, LVGL_GRAY, @@ -471,7 +471,7 @@ void setupMainScreen(bool darkMode) { // Create horizontal dividers for temperature section // Line between B and E at Y=89 lv_obj_t* h_line3 = lv_line_create(main_screen); - static lv_point_t h_line3_points[] = {{117, 89}, {148, 89}}; + static lv_point_precise_t h_line3_points[] = {{117, 89}, {148, 89}}; lv_line_set_points(h_line3, h_line3_points, 2); lv_obj_set_style_line_color(h_line3, LVGL_GRAY, @@ -480,7 +480,7 @@ void setupMainScreen(bool darkMode) { // Line between E and M at Y=109 lv_obj_t* h_line4 = lv_line_create(main_screen); - static lv_point_t h_line4_points[] = {{117, 109}, {148, 109}}; + static lv_point_precise_t h_line4_points[] = {{117, 109}, {148, 109}}; lv_line_set_points(h_line4, h_line4_points, 2); lv_obj_set_style_line_color(h_line4, LVGL_GRAY, @@ -498,8 +498,8 @@ void setupMainScreen(bool darkMode) { lv_obj_add_flag(arm_indicator, LV_OBJ_FLAG_HIDDEN); // Create cruise control icon (initially hidden) - cruise_icon_img = lv_img_create(main_screen); - lv_img_set_src(cruise_icon_img, &cruise_control_340255_30); // Use the new 30x30 image descriptor + cruise_icon_img = lv_image_create(main_screen); + lv_image_set_src(cruise_icon_img, &cruise_control_340255_30); // Use the new 30x30 image descriptor // Set icon color using recoloring based on theme lv_color_t icon_color; @@ -508,34 +508,34 @@ void setupMainScreen(bool darkMode) { } else { icon_color = lv_color_black(); // Black icon on light background } - lv_obj_set_style_img_recolor(cruise_icon_img, icon_color, LV_PART_MAIN); - lv_obj_set_style_img_recolor_opa(cruise_icon_img, LV_OPA_COVER, LV_PART_MAIN); // Make icon fully opaque + lv_obj_set_style_image_recolor(cruise_icon_img, icon_color, LV_PART_MAIN); + lv_obj_set_style_image_recolor_opa(cruise_icon_img, LV_OPA_COVER, LV_PART_MAIN); // Make icon fully opaque lv_obj_align(cruise_icon_img, LV_ALIGN_CENTER, 5, -11); lv_obj_move_foreground(cruise_icon_img); // Ensure icon is on the top layer lv_obj_add_flag(cruise_icon_img, LV_OBJ_FLAG_HIDDEN); // Hide initially // Create charging icon (initially hidden) - charging_icon_img = lv_img_create(main_screen); - lv_img_set_src(charging_icon_img, &energy_539741_26); + charging_icon_img = lv_image_create(main_screen); + lv_image_set_src(charging_icon_img, &energy_539741_26); lv_obj_align_to(charging_icon_img, battery_label, LV_ALIGN_OUT_RIGHT_MID, 3, 0); // Align to right of battery label // Set charging icon color based on theme - lv_obj_set_style_img_recolor(charging_icon_img, icon_color, LV_PART_MAIN); // Use same icon_color as cruise - lv_obj_set_style_img_recolor_opa(charging_icon_img, LV_OPA_COVER, LV_PART_MAIN); + lv_obj_set_style_image_recolor(charging_icon_img, icon_color, LV_PART_MAIN); // Use same icon_color as cruise + lv_obj_set_style_image_recolor_opa(charging_icon_img, LV_OPA_COVER, LV_PART_MAIN); lv_obj_move_foreground(charging_icon_img); // Ensure icon is on top lv_obj_add_flag(charging_icon_img, LV_OBJ_FLAG_HIDDEN); // Hide initially // Create arm fail warning icon (initially hidden) - arm_fail_warning_icon_img = lv_img_create(main_screen); - lv_img_set_src(arm_fail_warning_icon_img, &warning_2135850_30); + arm_fail_warning_icon_img = lv_image_create(main_screen); + lv_image_set_src(arm_fail_warning_icon_img, &warning_2135850_30); // Align in the same position as the cruise icon lv_obj_align(arm_fail_warning_icon_img, LV_ALIGN_CENTER, 12, -9); // Set icon color based on theme (using the same logic as cruise icon) - lv_obj_set_style_img_recolor(arm_fail_warning_icon_img, icon_color, LV_PART_MAIN); - lv_obj_set_style_img_recolor_opa(arm_fail_warning_icon_img, LV_OPA_COVER, LV_PART_MAIN); + lv_obj_set_style_image_recolor(arm_fail_warning_icon_img, icon_color, LV_PART_MAIN); + lv_obj_set_style_image_recolor_opa(arm_fail_warning_icon_img, LV_OPA_COVER, LV_PART_MAIN); lv_obj_move_foreground(arm_fail_warning_icon_img); // Ensure icon is on top lv_obj_add_flag(arm_fail_warning_icon_img, LV_OBJ_FLAG_HIDDEN); // Hide initially @@ -549,7 +549,8 @@ void setupMainScreen(bool darkMode) { lv_obj_set_style_border_width(spinner_overlay, 0, LV_PART_MAIN); // Create spinning animation at the top center - now place on top of overlay - spinner = lv_spinner_create(spinner_overlay, 1000, 60); // 1000ms period, 60 arcade width + spinner = lv_spinner_create(spinner_overlay); + lv_spinner_set_anim_params(spinner, 1000, 60); // 1000ms period, 60 arc width lv_obj_set_size(spinner, 80, 80); // Even larger spinner for visibility lv_obj_align(spinner, LV_ALIGN_CENTER, 0, 0); // Position at center of screen @@ -578,7 +579,7 @@ void setupMainScreen(bool darkMode) { // Create horizontal divider line running from section start to screen edge climb_rate_divider_lines[i] = lv_line_create(main_screen); - static lv_point_t line_points[13][2]; // Static array for all line points + static lv_point_precise_t line_points[13][2]; // Static array for all line points line_points[i][0].x = 148; // Start at section boundary line_points[i][0].y = y_pos; line_points[i][1].x = 160; // End at screen edge @@ -637,5 +638,5 @@ void setupMainScreen(bool darkMode) { init_temp_styles(darkMode); // Load the screen - lv_scr_load(main_screen); + lv_screen_load(main_screen); } diff --git a/src/sp140/lvgl/lvgl_updates.cpp b/src/sp140/lvgl/lvgl_updates.cpp index 765ea99d..8fe6ec22 100644 --- a/src/sp140/lvgl/lvgl_updates.cpp +++ b/src/sp140/lvgl/lvgl_updates.cpp @@ -42,7 +42,7 @@ static void cruise_flash_timer_cb(lv_timer_t* timer) { // Toggle visibility if (lv_obj_has_flag(cruise_icon_img, LV_OBJ_FLAG_HIDDEN)) { - lv_obj_clear_flag(cruise_icon_img, LV_OBJ_FLAG_HIDDEN); + lv_obj_remove_flag(cruise_icon_img, LV_OBJ_FLAG_HIDDEN); } else { lv_obj_add_flag(cruise_icon_img, LV_OBJ_FLAG_HIDDEN); } @@ -79,7 +79,7 @@ void startCruiseIconFlash() { isFlashingCruiseIcon = true; // Start with the icon visible - lv_obj_clear_flag(cruise_icon_img, LV_OBJ_FLAG_HIDDEN); + lv_obj_remove_flag(cruise_icon_img, LV_OBJ_FLAG_HIDDEN); // Create the timer (250ms interval for on/off cycle) cruise_flash_timer = lv_timer_create(cruise_flash_timer_cb, 250, NULL); @@ -87,11 +87,11 @@ void startCruiseIconFlash() { // Failed to create timer, reset state isFlashingCruiseIcon = false; lv_obj_add_flag(cruise_icon_img, LV_OBJ_FLAG_HIDDEN); // Hide it again - lv_obj_set_style_img_recolor(cruise_icon_img, original_cruise_icon_color, LV_PART_MAIN); // Restore color on failure + lv_obj_set_style_image_recolor(cruise_icon_img, original_cruise_icon_color, LV_PART_MAIN); // Restore color on failure USBSerial.println("Error: Failed to create cruise flash timer!"); } else { // Set icon to red for flashing - lv_obj_set_style_img_recolor(cruise_icon_img, LVGL_RED, LV_PART_MAIN); + lv_obj_set_style_image_recolor(cruise_icon_img, LVGL_RED, LV_PART_MAIN); } xSemaphoreGive(lvglMutex); @@ -117,7 +117,7 @@ static void arm_fail_flash_timer_cb(lv_timer_t* timer) { // Toggle visibility if (lv_obj_has_flag(arm_fail_warning_icon_img, LV_OBJ_FLAG_HIDDEN)) { - lv_obj_clear_flag(arm_fail_warning_icon_img, LV_OBJ_FLAG_HIDDEN); + lv_obj_remove_flag(arm_fail_warning_icon_img, LV_OBJ_FLAG_HIDDEN); } else { lv_obj_add_flag(arm_fail_warning_icon_img, LV_OBJ_FLAG_HIDDEN); } @@ -133,7 +133,7 @@ static void arm_fail_flash_timer_cb(lv_timer_t* timer) { // Ensure icon is hidden after flashing lv_obj_add_flag(arm_fail_warning_icon_img, LV_OBJ_FLAG_HIDDEN); // Restore original color after flashing is done - lv_obj_set_style_img_recolor(arm_fail_warning_icon_img, original_arm_fail_icon_color, LV_PART_MAIN); + lv_obj_set_style_image_recolor(arm_fail_warning_icon_img, original_arm_fail_icon_color, LV_PART_MAIN); } } @@ -156,10 +156,10 @@ void startArmFailIconFlash() { isFlashingArmFailIcon = true; // Start with the icon visible - lv_obj_clear_flag(arm_fail_warning_icon_img, LV_OBJ_FLAG_HIDDEN); + lv_obj_remove_flag(arm_fail_warning_icon_img, LV_OBJ_FLAG_HIDDEN); // Set icon to red for flashing - lv_obj_set_style_img_recolor(arm_fail_warning_icon_img, LVGL_RED, LV_PART_MAIN); + lv_obj_set_style_image_recolor(arm_fail_warning_icon_img, LVGL_RED, LV_PART_MAIN); // Create the timer (250ms interval for on/off cycle) arm_fail_flash_timer = lv_timer_create(arm_fail_flash_timer_cb, 250, NULL); @@ -167,7 +167,7 @@ void startArmFailIconFlash() { // Failed to create timer, reset state isFlashingArmFailIcon = false; lv_obj_add_flag(arm_fail_warning_icon_img, LV_OBJ_FLAG_HIDDEN); // Hide it again - lv_obj_set_style_img_recolor(arm_fail_warning_icon_img, original_arm_fail_icon_color, LV_PART_MAIN); // Restore color + lv_obj_set_style_image_recolor(arm_fail_warning_icon_img, original_arm_fail_icon_color, LV_PART_MAIN); // Restore color USBSerial.println("Error: Failed to create arm fail flash timer!"); } @@ -187,7 +187,7 @@ static void critical_border_flash_timer_cb(lv_timer_t* timer) { lv_obj_set_style_border_opa(critical_border, LV_OPA_0, LV_PART_MAIN); lv_timer_set_period(timer, 700); // Off duration // Invalidate entire screen when hiding to ensure clean removal of border pixels - lv_obj_invalidate(lv_scr_act()); + lv_obj_invalidate(lv_screen_active()); } else { lv_obj_set_style_border_opa(critical_border, LV_OPA_100, LV_PART_MAIN); lv_timer_set_period(timer, 300); // On duration @@ -200,7 +200,7 @@ static void critical_border_flash_timer_cb(lv_timer_t* timer) { lv_obj_invalidate(critical_border); } // Force immediate refresh to minimize tearing - lv_refr_now(lv_disp_get_default()); + lv_refr_now(lv_display_get_default()); } } @@ -224,7 +224,7 @@ void stopCriticalBorderFlash() { if (critical_border != NULL) { lv_obj_set_style_border_opa(critical_border, LV_OPA_0, LV_PART_MAIN); // Invalidate entire screen to ensure clean removal - lv_obj_invalidate(lv_scr_act()); + lv_obj_invalidate(lv_screen_active()); } isFlashingCriticalBorder = false; xSemaphoreGive(lvglMutex); @@ -243,7 +243,7 @@ void startCriticalBorderFlashDirect() { lv_obj_invalidate(critical_border); // Ensure clean initial draw critical_border_flash_timer = lv_timer_create(critical_border_flash_timer_cb, 300, NULL); // Force immediate refresh for clean start - lv_refr_now(lv_disp_get_default()); + lv_refr_now(lv_display_get_default()); } } @@ -254,9 +254,9 @@ void stopCriticalBorderFlashDirect() { } if (critical_border != NULL) { lv_obj_set_style_border_opa(critical_border, LV_OPA_0, LV_PART_MAIN); - lv_obj_invalidate(lv_scr_act()); // Ensure clean removal of border + lv_obj_invalidate(lv_screen_active()); // Ensure clean removal of border // Force immediate refresh for clean stop - lv_refr_now(lv_disp_get_default()); + lv_refr_now(lv_display_get_default()); } isFlashingCriticalBorder = false; } @@ -678,10 +678,10 @@ void updateLvglMainScreen( lv_label_set_text_fmt(batt_temp_label, "%d", static_cast(batteryTemp)); if (batteryTemp >= bmsCellTempThresholds.critHigh) { lv_obj_add_style(batt_temp_bg, &style_critical, 0); - lv_obj_clear_flag(batt_temp_bg, LV_OBJ_FLAG_HIDDEN); + lv_obj_remove_flag(batt_temp_bg, LV_OBJ_FLAG_HIDDEN); } else if (batteryTemp >= bmsCellTempThresholds.warnHigh) { lv_obj_add_style(batt_temp_bg, &style_warning, 0); - lv_obj_clear_flag(batt_temp_bg, LV_OBJ_FLAG_HIDDEN); + lv_obj_remove_flag(batt_temp_bg, LV_OBJ_FLAG_HIDDEN); } else { lv_obj_add_flag(batt_temp_bg, LV_OBJ_FLAG_HIDDEN); } @@ -702,10 +702,10 @@ void updateLvglMainScreen( if (escTemp >= escMosTempThresholds.critHigh) { lv_obj_add_style(esc_temp_bg, &style_critical, 0); - lv_obj_clear_flag(esc_temp_bg, LV_OBJ_FLAG_HIDDEN); + lv_obj_remove_flag(esc_temp_bg, LV_OBJ_FLAG_HIDDEN); } else if (escTemp >= escMosTempThresholds.warnHigh) { lv_obj_add_style(esc_temp_bg, &style_warning, 0); - lv_obj_clear_flag(esc_temp_bg, LV_OBJ_FLAG_HIDDEN); + lv_obj_remove_flag(esc_temp_bg, LV_OBJ_FLAG_HIDDEN); } else { lv_obj_add_flag(esc_temp_bg, LV_OBJ_FLAG_HIDDEN); } @@ -726,10 +726,10 @@ void updateLvglMainScreen( if (motorTemp >= motorTempThresholds.critHigh) { lv_obj_add_style(motor_temp_bg, &style_critical, 0); - lv_obj_clear_flag(motor_temp_bg, LV_OBJ_FLAG_HIDDEN); + lv_obj_remove_flag(motor_temp_bg, LV_OBJ_FLAG_HIDDEN); } else if (motorTemp >= motorTempThresholds.warnHigh) { lv_obj_add_style(motor_temp_bg, &style_warning, 0); - lv_obj_clear_flag(motor_temp_bg, LV_OBJ_FLAG_HIDDEN); + lv_obj_remove_flag(motor_temp_bg, LV_OBJ_FLAG_HIDDEN); } else { lv_obj_add_flag(motor_temp_bg, LV_OBJ_FLAG_HIDDEN); } @@ -743,7 +743,7 @@ void updateLvglMainScreen( if (armed) { // Set background to CYAN when armed, regardless of cruise state lv_obj_set_style_bg_color(arm_indicator, LVGL_CYAN, LV_PART_MAIN); - lv_obj_clear_flag(arm_indicator, LV_OBJ_FLAG_HIDDEN); + lv_obj_remove_flag(arm_indicator, LV_OBJ_FLAG_HIDDEN); } else { lv_obj_add_flag(arm_indicator, LV_OBJ_FLAG_HIDDEN); } @@ -752,19 +752,19 @@ void updateLvglMainScreen( // Only update based on `cruising` state if not currently flashing if (!isFlashingCruiseIcon) { if (cruising) { - lv_obj_clear_flag(cruise_icon_img, LV_OBJ_FLAG_HIDDEN); + lv_obj_remove_flag(cruise_icon_img, LV_OBJ_FLAG_HIDDEN); } else { lv_obj_add_flag(cruise_icon_img, LV_OBJ_FLAG_HIDDEN); } // Restore original color after flashing is done - lv_obj_set_style_img_recolor(cruise_icon_img, original_cruise_icon_color, LV_PART_MAIN); + lv_obj_set_style_image_recolor(cruise_icon_img, original_cruise_icon_color, LV_PART_MAIN); } // Update Charging Icon Visibility - only when BMS is connected and reports charging if (charging_icon_img != NULL) { // Check object exists bool showChargingIcon = (bmsTelemetry.bmsState == TelemetryState::CONNECTED) && bmsTelemetry.is_charging; if (showChargingIcon) { - lv_obj_clear_flag(charging_icon_img, LV_OBJ_FLAG_HIDDEN); + lv_obj_remove_flag(charging_icon_img, LV_OBJ_FLAG_HIDDEN); } else { lv_obj_add_flag(charging_icon_img, LV_OBJ_FLAG_HIDDEN); } @@ -775,7 +775,7 @@ void updateLvglMainScreen( if (!isFlashingArmFailIcon && arm_fail_warning_icon_img != NULL) { lv_obj_add_flag(arm_fail_warning_icon_img, LV_OBJ_FLAG_HIDDEN); // Ensure color is reset if flashing ended abruptly elsewhere (though cb should handle it) - lv_obj_set_style_img_recolor(arm_fail_warning_icon_img, original_arm_fail_icon_color, LV_PART_MAIN); + lv_obj_set_style_image_recolor(arm_fail_warning_icon_img, original_arm_fail_icon_color, LV_PART_MAIN); } // Update climb rate indicator diff --git a/src/sp140/main.cpp b/src/sp140/main.cpp index d9fdd005..c84a863c 100644 --- a/src/sp140/main.cpp +++ b/src/sp140/main.cpp @@ -783,7 +783,7 @@ void setup() { // Load main screen if (main_screen != NULL) { - lv_scr_load(main_screen); + lv_screen_load(main_screen); USBSerial.println("Main screen loaded"); } else { USBSerial.println("Error: Main screen object is NULL after setup attempt"); From a31a5ad581be916f21c718994d0009eb4fe09c33 Mon Sep 17 00:00:00 2001 From: Zach Whitehead Date: Thu, 26 Mar 2026 18:10:42 -0400 Subject: [PATCH 2/3] Update platformio.ini --- platformio.ini | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/platformio.ini b/platformio.ini index 4dafee48..19cecb91 100644 --- a/platformio.ini +++ b/platformio.ini @@ -59,7 +59,7 @@ lib_deps = https://github.com/rlogiacco/CircularBuffer@1.4.0 https://github.com/openppg/SINE-ESC-CAN#8caa93996b5d000fe10ca5265bd1c472dfdf885b https://github.com/openppg/ANT-BMS-CAN#fd54852bc6f1c9608e37af9ca7c13ea4135c095b - lvgl/lvgl@^9.2.2 + lvgl/lvgl@^9.5.0 h2zero/NimBLE-Arduino@^2.3.9 lib_ignore = Adafruit SleepyDog Library From e710fe7405f05628a9b53797ee44cd640bf02127 Mon Sep 17 00:00:00 2001 From: Zach Whitehead Date: Thu, 26 Mar 2026 19:28:30 -0400 Subject: [PATCH 3/3] Match character height to font line height Increase character height from 24 to 30 to match lv_font_montserrat_28 line_height (v9: 30px). Update char_height in lvgl_main_screen.cpp and adjust corresponding lv_obj_set_size calls in lvgl_updates.cpp (decimal and feet positions) to use height 30 so the main screen layout matches the font metrics. --- src/sp140/lvgl/lvgl_main_screen.cpp | 2 +- src/sp140/lvgl/lvgl_updates.cpp | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/sp140/lvgl/lvgl_main_screen.cpp b/src/sp140/lvgl/lvgl_main_screen.cpp index 85bf4239..225c2d30 100644 --- a/src/sp140/lvgl/lvgl_main_screen.cpp +++ b/src/sp140/lvgl/lvgl_main_screen.cpp @@ -233,7 +233,7 @@ void setupMainScreen(bool darkMode) { // Layout: [thousands][hundreds][tens][ones][.][tenths][m] (7 positions total) int char_width = 19; // Slightly tighter character width spacing int decimal_width = 8; // Slightly wider decimal point for proportional spacing - int char_height = 24; // Larger character height + int char_height = 30; // Match lv_font_montserrat_28 line_height (v9: 30px) int unit_width = 12; // Narrower width for unit character to reduce buffer // Calculate total width needed and position from the right diff --git a/src/sp140/lvgl/lvgl_updates.cpp b/src/sp140/lvgl/lvgl_updates.cpp index 8fe6ec22..d0c806b2 100644 --- a/src/sp140/lvgl/lvgl_updates.cpp +++ b/src/sp140/lvgl/lvgl_updates.cpp @@ -596,7 +596,7 @@ void updateLvglMainScreen( lv_label_set_text(altitude_char_labels[5], digit_buffers[5]); // Adjust width of position 4 back to narrow for meters (decimal point) - lv_obj_set_size(altitude_char_labels[4], 8, 24); // decimal_width=8, char_height=24 + lv_obj_set_size(altitude_char_labels[4], 8, 30); // decimal_width=8, char_height=30 // Unit lv_label_set_text(altitude_char_labels[6], "m"); @@ -616,7 +616,7 @@ void updateLvglMainScreen( static char digit_buffers_ft[7][12]; // Static buffers for feet // Adjust width of position 4 for feet (should be normal width, not narrow) - lv_obj_set_size(altitude_char_labels[4], 17, 24); // char_width=19, char_height=24 + lv_obj_set_size(altitude_char_labels[4], 17, 30); // char_width=19, char_height=30 // Clear all positions first for (int i = 0; i < 7; i++) {