diff --git a/10-OBD-Display-2024/display_gif_embedded/include/lv_conf.h b/10-OBD-Display-2024/display_gif_embedded/include/lv_conf.h index 3a63501..4939f5f 100644 --- a/10-OBD-Display-2024/display_gif_embedded/include/lv_conf.h +++ b/10-OBD-Display-2024/display_gif_embedded/include/lv_conf.h @@ -201,7 +201,7 @@ *-----------*/ /*Enable the log module*/ -#define LV_USE_LOG 1 +#define LV_USE_LOG 0 #if LV_USE_LOG /*How important log should be added: diff --git a/10-OBD-Display-2024/display_gif_embedded/include/scr_st77916.h b/10-OBD-Display-2024/display_gif_embedded/include/scr_st77916.h index 184a4a8..82ac61f 100644 --- a/10-OBD-Display-2024/display_gif_embedded/include/scr_st77916.h +++ b/10-OBD-Display-2024/display_gif_embedded/include/scr_st77916.h @@ -10,6 +10,7 @@ #define SCREEN_RES_VER 360 static lv_color_t *disp_draw_buf; +static lv_color_t *disp_draw_buf2; // second buffer: LVGL renders into one while the other is sent to the LCD static lv_disp_draw_buf_t draw_buf; static lv_disp_drv_t disp_drv; static lv_indev_t *indev_touchpad; @@ -418,8 +419,9 @@ void scr_lvgl_init() size_t lv_cache_rows = 72; disp_draw_buf = (lv_color_t *)heap_caps_malloc(lv_cache_rows * SCREEN_RES_HOR * 2, MALLOC_CAP_INTERNAL | MALLOC_CAP_8BIT); + disp_draw_buf2 = (lv_color_t *)heap_caps_malloc(lv_cache_rows * SCREEN_RES_HOR * 2, MALLOC_CAP_INTERNAL | MALLOC_CAP_8BIT); lv_init(); - lv_disp_draw_buf_init(&draw_buf, disp_draw_buf, NULL, SCREEN_RES_HOR * lv_cache_rows); + lv_disp_draw_buf_init(&draw_buf, disp_draw_buf, disp_draw_buf2, SCREEN_RES_HOR * lv_cache_rows); lv_disp_drv_init(&disp_drv); disp_drv.hor_res = SCREEN_RES_HOR; diff --git a/10-OBD-Display-2024/display_gif_embedded/platformio.ini b/10-OBD-Display-2024/display_gif_embedded/platformio.ini index a306284..1e006f4 100644 --- a/10-OBD-Display-2024/display_gif_embedded/platformio.ini +++ b/10-OBD-Display-2024/display_gif_embedded/platformio.ini @@ -1,29 +1,32 @@ -; JC3636W518 — ESP32-S3R8, 16 MB QIO flash, 8 MB OPI PSRAM, 360x360 ST77916 QSPI + CST816S -; Settings mirror the vendor Arduino IDE setup (2-Program Example/Demo_Arduino/docs/Arduino set up.png) - -[env:jc3636w518] -; pioarduino = Arduino core 3.0.5 (ESP32_Display_Panel 0.1.4 needs core 3.x) -platform = https://github.com/pioarduino/platform-espressif32/releases/download/51.03.05/platform-espressif32.zip -board = esp32-s3-devkitc-1 -framework = arduino - -board_build.arduino.memory_type = qio_opi -board_build.flash_mode = qio -board_upload.flash_size = 16MB -; GIF compiled into firmware -> 3.4 MB, too big for huge_app (3 MB); 6.25 MB app slots -board_build.partitions = default_16MB.csv - -build_flags = - -DBOARD_HAS_PSRAM - -DARDUINO_USB_CDC_ON_BOOT=1 - ; LVGL (also when compiled as a library) picks up include/lv_conf.h of this project - -DLV_CONF_INCLUDE_SIMPLE - -Iinclude - -; vendor-bundled libraries: ESP32_Display_Panel 0.1.4, ESP32_IO_Expander 0.0.2, lvgl 8.3.11 -lib_extra_dirs = ../../2-Program Example/Demo_Arduino/library - -upload_port = COM21 -monitor_port = COM21 -monitor_speed = 115200 -monitor_filters = esp32_exception_decoder +; JC3636W518 — ESP32-S3R8, 16 MB QIO flash, 8 MB OPI PSRAM, 360x360 ST77916 QSPI + CST816S +; Settings mirror the vendor Arduino IDE setup (2-Program Example/Demo_Arduino/docs/Arduino set up.png) + +[env:jc3636w518] +; pioarduino = Arduino core 3.0.5 (ESP32_Display_Panel 0.1.4 needs core 3.x) +platform = https://github.com/pioarduino/platform-espressif32/releases/download/51.03.05/platform-espressif32.zip +board = esp32-s3-devkitc-1 +framework = arduino + +board_build.arduino.memory_type = qio_opi +board_build.flash_mode = qio +board_upload.flash_size = 16MB +; GIF compiled into firmware -> 3.4 MB, too big for huge_app (3 MB); 6.25 MB app slots +board_build.partitions = default_16MB.csv + +; optimise for speed instead of size (faster GIF decoding) +build_unflags = -Os +build_flags = + -O2 + -DBOARD_HAS_PSRAM + -DARDUINO_USB_CDC_ON_BOOT=1 + ; LVGL (also when compiled as a library) picks up include/lv_conf.h of this project + -DLV_CONF_INCLUDE_SIMPLE + -Iinclude + +; vendor-bundled libraries: ESP32_Display_Panel 0.1.4, ESP32_IO_Expander 0.0.2, lvgl 8.3.11 +lib_extra_dirs = ../../2-Program Example/Demo_Arduino/library + +upload_port = COM21 +monitor_port = COM21 +monitor_speed = 115200 +monitor_filters = esp32_exception_decoder diff --git a/10-OBD-Display-2024/display_gif_embedded/src/main.cpp b/10-OBD-Display-2024/display_gif_embedded/src/main.cpp index 4eccda6..7afb460 100644 --- a/10-OBD-Display-2024/display_gif_embedded/src/main.cpp +++ b/10-OBD-Display-2024/display_gif_embedded/src/main.cpp @@ -12,6 +12,30 @@ const char* password = "123456789"; String httpGETRequest(const char* serverName); +// FPS measurement: LVGL calls this after every screen refresh (= one GIF frame here) +static volatile uint32_t refr_count = 0; +static volatile uint32_t refr_time_ms = 0; +static uint32_t busy_us = 0; // time spent inside lv_timer_handler() + +static void refr_monitor_cb(lv_disp_drv_t* drv, uint32_t time, uint32_t px) { + refr_count++; + refr_time_ms += time; +} + +void print_fps() { + static uint32_t last_ms = 0; + uint32_t now = millis(); + if (now - last_ms < 1000) return; + // decode = busy time not spent refreshing the screen (mostly GIF decoding) + Serial.printf("FPS: %.1f avg refresh: %lu ms avg decode: %lu ms\n", refr_count * 1000.0f / (now - last_ms), + refr_count ? refr_time_ms / refr_count : 0, + refr_count ? (busy_us / 1000 - refr_time_ms) / refr_count : 0); + refr_count = 0; + refr_time_ms = 0; + busy_us = 0; + last_ms = now; +} + void my_print(const char* buf) { Serial.println(buf); // Print logs to the serial monitor @@ -106,6 +130,7 @@ void setup() { delay(200); Serial.begin(115200); scr_lvgl_init(); + lv_disp_get_default()->driver->monitor_cb = refr_monitor_cb; lv_init(); @@ -143,15 +168,11 @@ void setup() { } void loop() { + uint32_t t0 = micros(); lv_timer_handler(); - vTaskDelay(5); - - lv_mem_monitor_t mem_mon; - lv_mem_monitor(&mem_mon); - printf("Free memory: %d bytes\n", mem_mon.free_size); - - Serial.print("Available Heap: "); - Serial.println(esp_get_free_heap_size()); + busy_us += micros() - t0; + vTaskDelay(1); + print_fps(); // if (WiFi.status() == WL_CONNECTED) { diff --git a/CLAUDE.md b/CLAUDE.md index 1530ba1..e500219 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -96,6 +96,11 @@ Cheap dual-mode clones typically expose BLE service `FFF0` (notify `FFF1`, write ## Gotchas / observations - **LVGL GIF bug (patched locally):** LVGL 8.3.11 `gifdec.c` (`gd_open_gif`) filled the canvas *opaque* with the GIF's background color. GIFs with a transparent background (e.g. `loligod.gif`, bg = RGB 28,29,49) showed that dark gray except where "restore to background" frames had cleared to transparent (→ screen black). **Patched** in the vendor copy `2-Program Example/Demo_Arduino/library/lvgl/src/extra/libs/gif/gifdec.c`: initial canvas alpha 0x00 (marked `LOCAL PATCH`). Re-apply if LVGL is ever replaced/upgraded. - Most GIFs in `assets/gifs` are optimized (transparent unchanged pixels, disposal 1/2); `loligod.gif` has a fully transparent background (~84 % of each frame). +- **GIF performance** (360×360 loligod, embedded, measured with a `monitor_cb` FPS counter in `display_gif_embedded`): + baseline 15.4 fps (decode 27 ms + refresh 31 ms) → no LVGL log / no per-loop prints: no change → `vTaskDelay(1)`: 16.5 → **double draw buffer** (2×72 rows internal RAM): 19.5 (refresh 21 ms) → **`-O2`** instead of `-Os`: **20.6 fps** (refresh 19 ms, decode ~24 ms). GIF's own rate is 23.7 fps. From SD card it was only ~1.4 fps. + Remaining limit is LVGL's single-threaded GIF decode; going further would need decoding on the other core or a lighter format (e.g. pre-converted frames/MJPEG). +- **SD card (user's 487 MB SDSC)** intermittently fails to init (`sdmmc_init_ocr ... 0x107`) after resets; once stuck only a power cycle helps (card VDD is hard-wired to 3V3). Recommend a newer SDHC card. +- Reading boot logs: USB-CDC — reset via DTR=0/RTS pulse, then reopen the port (it re-enumerates). - LVGL version: all JC3636W518 projects use the vendor-bundled **LVGL 8.3.11**. LVGL 9 would require porting `scr_st77916.h` (display/touch driver glue). - Display is round: corners of the 360×360 framebuffer are not visible — design UI within a 360 px circle. - Compiling a 13.9 MB GIF into the firmware will not fit "Huge APP" (3 MB); large media should live on SD (or in a custom partition).