Boot Screen Animation

What do you want to achieve?

I designed a boot screen animation in Square Line Studio, after flashing the same to ESP32-S3-Touch-LCD-4.3B I’m not getting a smooth animation like in square line studio. The boot screen animation is laggy.

What have you tried so far?

Tried some changes to the autogenerated code

Code to reproduce

///////////////////// ANIMATIONS ////////////////////
lv_anim_t * logo_Animation(lv_obj_t * TargetObject, int delay)
{
lv_anim_t * out_anim;
ui_anim_user_data_t * PropertyAnimation_0_user_data = lv_mem_alloc(sizeof(ui_anim_user_data_t));
PropertyAnimation_0_user_data->target = TargetObject;
PropertyAnimation_0_user_data->val = -1;
lv_anim_t PropertyAnimation_0;
lv_anim_init(&PropertyAnimation_0);
lv_anim_set_time(&PropertyAnimation_0, 1000);
lv_anim_set_user_data(&PropertyAnimation_0, PropertyAnimation_0_user_data);
lv_anim_set_custom_exec_cb(&PropertyAnimation_0, _ui_anim_callback_set_x);
lv_anim_set_values(&PropertyAnimation_0, -515, 500);
lv_anim_set_path_cb(&PropertyAnimation_0, lv_anim_path_linear);
lv_anim_set_delay(&PropertyAnimation_0, delay + 0);
lv_anim_set_deleted_cb(&PropertyAnimation_0, _ui_anim_callback_free_user_data);
lv_anim_set_playback_time(&PropertyAnimation_0, 0);
lv_anim_set_playback_delay(&PropertyAnimation_0, 0);
lv_anim_set_repeat_count(&PropertyAnimation_0, 0);
lv_anim_set_repeat_delay(&PropertyAnimation_0, 0);
lv_anim_set_early_apply(&PropertyAnimation_0, false);
lv_anim_set_get_value_cb(&PropertyAnimation_0, &_ui_anim_callback_get_x);
out_anim = lv_anim_start(&PropertyAnimation_0);
ui_anim_user_data_t * PropertyAnimation_1_user_data = lv_mem_alloc(sizeof(ui_anim_user_data_t));
PropertyAnimation_1_user_data->target = TargetObject;
PropertyAnimation_1_user_data->val = -1;
lv_anim_t PropertyAnimation_1;
lv_anim_init(&PropertyAnimation_1);
lv_anim_set_time(&PropertyAnimation_1, 2000);
lv_anim_set_user_data(&PropertyAnimation_1, PropertyAnimation_1_user_data);
lv_anim_set_custom_exec_cb(&PropertyAnimation_1, _ui_anim_callback_set_image_zoom);
lv_anim_set_values(&PropertyAnimation_1, 0, 800);
lv_anim_set_path_cb(&PropertyAnimation_1, lv_anim_path_ease_in);
lv_anim_set_delay(&PropertyAnimation_1, delay + 1000);
lv_anim_set_deleted_cb(&PropertyAnimation_1, _ui_anim_callback_free_user_data);
lv_anim_set_playback_time(&PropertyAnimation_1, 0);
lv_anim_set_playback_delay(&PropertyAnimation_1, 0);
lv_anim_set_repeat_count(&PropertyAnimation_1, 0);
lv_anim_set_repeat_delay(&PropertyAnimation_1, 0);
lv_anim_set_early_apply(&PropertyAnimation_1, false);
lv_anim_set_get_value_cb(&PropertyAnimation_1, &_ui_anim_callback_get_image_zoom);
out_anim = lv_anim_start(&PropertyAnimation_1);

return out_anim;

}
lv_anim_t * BootScreen_Animation(lv_obj_t * TargetObject, int delay)
{
lv_anim_t * out_anim;
ui_anim_user_data_t * PropertyAnimation_0_user_data = lv_mem_alloc(sizeof(ui_anim_user_data_t));
PropertyAnimation_0_user_data->target = TargetObject;
PropertyAnimation_0_user_data->val = -1;
lv_anim_t PropertyAnimation_0;
lv_anim_init(&PropertyAnimation_0);
lv_anim_set_time(&PropertyAnimation_0, 1500);
lv_anim_set_user_data(&PropertyAnimation_0, PropertyAnimation_0_user_data);
lv_anim_set_custom_exec_cb(&PropertyAnimation_0, _ui_anim_callback_set_opacity);
lv_anim_set_values(&PropertyAnimation_0, 255, 0);
lv_anim_set_path_cb(&PropertyAnimation_0, lv_anim_path_linear);
lv_anim_set_delay(&PropertyAnimation_0, delay + 250);
lv_anim_set_deleted_cb(&PropertyAnimation_0, _ui_anim_callback_free_user_data);
lv_anim_set_playback_time(&PropertyAnimation_0, 0);
lv_anim_set_playback_delay(&PropertyAnimation_0, 0);
lv_anim_set_repeat_count(&PropertyAnimation_0, 0);
lv_anim_set_repeat_delay(&PropertyAnimation_0, 0);
lv_anim_set_early_apply(&PropertyAnimation_0, false);
lv_anim_set_get_value_cb(&PropertyAnimation_0, &_ui_anim_callback_get_opacity);
out_anim = lv_anim_start(&PropertyAnimation_0);

return out_anim;

}
lv_anim_t * TopPart_Animation(lv_obj_t * TargetObject, int delay)
{
lv_anim_t * out_anim;
ui_anim_user_data_t * PropertyAnimation_0_user_data = lv_mem_alloc(sizeof(ui_anim_user_data_t));
PropertyAnimation_0_user_data->target = TargetObject;
PropertyAnimation_0_user_data->val = -1;
lv_anim_t PropertyAnimation_0;
lv_anim_init(&PropertyAnimation_0);
lv_anim_set_time(&PropertyAnimation_0, 1000);
lv_anim_set_user_data(&PropertyAnimation_0, PropertyAnimation_0_user_data);
lv_anim_set_custom_exec_cb(&PropertyAnimation_0, _ui_anim_callback_set_y);
lv_anim_set_values(&PropertyAnimation_0, 0, -450);
lv_anim_set_path_cb(&PropertyAnimation_0, lv_anim_path_linear);
lv_anim_set_delay(&PropertyAnimation_0, delay + 0);
lv_anim_set_deleted_cb(&PropertyAnimation_0, _ui_anim_callback_free_user_data);
lv_anim_set_playback_time(&PropertyAnimation_0, 0);
lv_anim_set_playback_delay(&PropertyAnimation_0, 0);
lv_anim_set_repeat_count(&PropertyAnimation_0, 0);
lv_anim_set_repeat_delay(&PropertyAnimation_0, 0);
lv_anim_set_early_apply(&PropertyAnimation_0, false);
lv_anim_set_get_value_cb(&PropertyAnimation_0, &_ui_anim_callback_get_y);
out_anim = lv_anim_start(&PropertyAnimation_0);

return out_anim;

}
lv_anim_t * BottomPart_Animation(lv_obj_t * TargetObject, int delay)
{
lv_anim_t * out_anim;
ui_anim_user_data_t * PropertyAnimation_0_user_data = lv_mem_alloc(sizeof(ui_anim_user_data_t));
PropertyAnimation_0_user_data->target = TargetObject;
PropertyAnimation_0_user_data->val = -1;
lv_anim_t PropertyAnimation_0;
lv_anim_init(&PropertyAnimation_0);
lv_anim_set_time(&PropertyAnimation_0, 1000);
lv_anim_set_user_data(&PropertyAnimation_0, PropertyAnimation_0_user_data);
lv_anim_set_custom_exec_cb(&PropertyAnimation_0, _ui_anim_callback_set_y);
lv_anim_set_values(&PropertyAnimation_0, 0, 450);
lv_anim_set_path_cb(&PropertyAnimation_0, lv_anim_path_linear);
lv_anim_set_delay(&PropertyAnimation_0, delay + 0);
lv_anim_set_deleted_cb(&PropertyAnimation_0, _ui_anim_callback_free_user_data);
lv_anim_set_playback_time(&PropertyAnimation_0, 0);
lv_anim_set_playback_delay(&PropertyAnimation_0, 0);
lv_anim_set_repeat_count(&PropertyAnimation_0, 0);
lv_anim_set_repeat_delay(&PropertyAnimation_0, 0);
lv_anim_set_early_apply(&PropertyAnimation_0, false);
lv_anim_set_get_value_cb(&PropertyAnimation_0, &_ui_anim_callback_get_y);
out_anim = lv_anim_start(&PropertyAnimation_0);

return out_anim;

}

///////////////////// FUNCTIONS ////////////////////

///////////////////// SCREENS ////////////////////

void ui_init(void)
{
lv_disp_t * dispp = lv_disp_get_default();
lv_theme_t * theme = lv_theme_default_init(dispp, lv_palette_main(LV_PALETTE_BLUE), lv_palette_main(LV_PALETTE_RED),
false, LV_FONT_DEFAULT);
lv_disp_set_theme(dispp, theme);
ui_Screen1_screen_init();
ui_Screen2_screen_init();
ui____initial_actions0 = lv_obj_create(NULL);
lv_disp_load_scr(ui_Screen1);
}

/*You code here*/

Screenshot and/or video

Environment

  • MCU/MPU/Board: ESP32-S3-Touch-LCD-4.3B
  • LVGL version: See lv_version.h 8.3.8

Hi

The PC performance is always quite different from the board, and it will depend greatly on what effort is necessary to render the widgets, not to mention that will depend on a lot of hardware configuration (MCU Clock, SPIRAM Clock, etc…)

If you run the lv_demo_benchmark() (enable performance monitor) on that board with your current code, what is the average FPS you get? This can give you an ideia off the board performance during some more heavy use cases (like moving widgets and so on).

According to Waveshare using the configuration they have (don’t really recommend putting SPIRAM at 120MHz) they get an average of 26FPS which means that in heavy processing scenarios is quite likely around 15 or bellow, which can give the look of sluggish screen updates.

For the current project I’m getting around 100 FPS and 12% CPU for the boot screen and 100 FPS 91% CPU in the main screen.
In the main screen I have a background, 3 text box which I use to display data.

So, you are getting 100FPS during the boot screen? Is that where the animation are? and its not smooth?

Can you share a video?

Yes, I’m getting 100 FPS in boot screen.
Yes, the animation is in the boot screen.
So, I have a logo, it has 3 parts to it, a background with 2 colors that is split in the middle horizontally and a logo in the center. So, the animation is like the color on top should go to top and disappear and the color in the bottom should go to bottom and disappear like it goes out of the screen. then the logo in the center will fade and the next screen will load.

https://youtube.com/shorts/DhY1YH9xStQ?feature=share this video shows the UI in the display

https://youtube.com/shorts/AoRvanjku5E?feature=share this video shows the UI design in square line studio

Sorry, no concrete ideia.

From the video it seems there is some issue with updating the display (there is “garbage”) on screen, maybe some issue with synchronization with vsync from the Display, or framebuffer update internally in ESP PSRAM. Never used the RGB Driver from ESP-IDF to know some typical pitfalls…

Would try to change the CONFIG_LV_DISP_DEF_REFR_PERIOD back to 33 just to see if with less LGVL draw attempts the situation could improve or not.

Thanks for trying,
This is the current setup
#define LV_DISP_DEF_REFR_PERIOD 10
#define LV_INDEV_DEF_READ_PERIOD 30

Is it normal to get 12% CPU for a boot screen that does so many things and 91% CPU for the main that has a background image and 3 text boxes that display data that is received in CAN.

Well, the FPS and CPU usage “meter” inside LVGL is a bit tricky (and subject to a lot of discussions), and have some major modifications between releases. The values for v8 are not quite comparable to v9 since the benchmark process (screens / widgets) is different (very little hands on experience with V8).

I would say that during the animation, is not expected the CPU usage to be so low compared to when in the Main Screen, but this could be for example using partial buffers and just single buffer. Or if when in the main screen you are constantly updating the labels information (even if always with the same information) LVGL will redraw the label again spending CPU cycles drawing and copying the buffer for example.

What i would try:

  • Run the lv_demo_benchmark() to check how the system behaves with a known code example. If CPU usage too high and/or FPS low and/or display shows artifacts…, re-check display driver / LVGL configuration.
    • Eventually adding some console logs in the flush_cb and so on, to try to understand where time is being spent and what is happening.
  • Add console output on the animation callback, just to make sure they are being called at the expected interval with the expected values
  • Start by doing this with LV_DISP_DEF_REFR_PERIOD 30 just to make sure that everything works with standard refresh rate, and then eventually increase the refresh rate

Actually, I’m using double buffer here. I’m new here, just figuring it out and learning.

ok I will try running a lv_demo_benchmark.

I have changed LV_DISP_DEF_REFR_PERIOD to 33, I’m waiting for the code to build.
EDIT: After changing LV_DISP_DEF_REFR_PERIOD to 33, I’m getting FPS 30 in both boot screen and main screen as well.

What to be done in this case “if when in the main screen you are constantly updating the labels information (even if always with the same information) LVGL will redraw the label again spending CPU cycles drawing and copying the buffer for example.

Well, you need to manage that in your code.
You can:

  • Store the previous value received via CAN, and only update the label if it’s different
  • Directly compare the new text to be updated with the text the label currently has (using lv_label_get_text(test_label) and a strcmp or memcmp) and only update the text if different.
const char *cur_val = lv_label_get_text(test_label);
if (strcmp(new_val, cur_val) != 0) {
    lv_label_set_text(test_label, new_val);
}

If you want to know what areas of the screen are being “updated”, (for testing/debugging) you can enable LV_USE_REFR_DEBUG in lv_conf.h it will draw random colored rectangles over the refreshed areas.

For CAN data, I’m actually what you mentioned. I’m comparing the current data with new data and if it’s different then I will send it to the text.

What if LVGL is always redrawing the full background image?
Does that happen? If yes, then will it increase CPU usage?

Don’t know, i think it would only redraw the parts necessary if in background of another widget, it may depend on what rendering mode (for v8.4) you have (LV_DISPLAY_RENDER_MODE_PARTIAL, LV_DISPLAY_RENDER_MODE_DIRECT or LV_DISPLAY_RENDER_MODE_FULL), in LV_DISPLAY_RENDER_MODE_FULL → Always redraw the whole screen even if only one pixel has been changed.

It may also depend on other processes handled by the firmware, and LV_USE_REFR_DEBUG can be helpful for providing a visual clue in this case.

If that happens, the CPU usage will indeed increase. Even using a double buffer raises CPU usage due to the data copying between buffers, but it usually pays off since the copy is typically much faster than the flush_cb. However, this can vary depending on hardware constraints, and there isn’t a single solution that fits all scenarios. For instance, using a double buffer without the flush_cb transferring data via DMA to allow the CPU to keep processing LVGL code isn’t worthwhile.

If you are using ESP-IDF, and want to get a “maybe more correct” value for CPU usage, check this ESP-IDF example that gives the number of clock cycles spent for each RTOS Task during a certain time period