ESP32 + Squareline Studio Performance Problems with TFT Display

Hello everyone. I’m new to this platform and need help with displaying a user interface created in Squareline Studio. I have developed a simple project containing text and a switch. After exporting it, I properly configured the lv_conf.h and User_Setup.h files from the TFT_eSPI library and uploaded the code to my ESP32-WROOM-32 without any compilation errors. However, I’m experiencing two issues:

  1. When starting up, vertical lines appear on the TFT LCD screen
  2. The interface takes 10 to 15 seconds to fully display, loading gradually

The monitor shows CPU usage at 97% with a frame rate of 33 FPS.

What do you want to achieve?
I need to display my interface fluidly on my 2.4″ RGB SPI 240X320 TFT LCD screen using the ESP32-WROOM-32

What have you tried so far?
To compare performance, I conducted an additional test: using the same configuration in the lv_conf.h and User_Setup.h files, I loaded a simple program displaying text and graphics, but this time without using Squareline Studio. The results were significantly different:

  • Without Squareline Studio: The program starts quickly and CPU usage remains at only 4% at 33 FPS
  • With Squareline Studio: Slow startup and CPU usage reaches 97% at 33 FPS

This notable difference in performance suggests that there must be some specific Squareline Studio configuration that isn’t properly optimized.

#include <lvgl.h>
#include <TFT_eSPI.h>

#define SCREEN_WIDTH 320
#define SCREEN_HEIGHT 240

// Define buffer size for LVGL
static lv_disp_draw_buf_t draw_buf;
static lv_color_t buf[SCREEN_WIDTH * SCREEN_HEIGHT / 13];  // Buffer for 10 lines

TFT_eSPI tft = TFT_eSPI(SCREEN_WIDTH, SCREEN_HEIGHT);  // Initialize TFT_eSPI with screen dimensions

// Function to write to the screen
void my_disp_flush(lv_disp_drv_t *disp, const lv_area_t *area, lv_color_t *color_p) {
    uint32_t w = ( area->x2 - area->x1 + 1 );
    uint32_t h = ( area->y2 - area->y1 + 1 );

    tft.startWrite();
    tft.setAddrWindow( area->x1, area->y1, w, h );
    tft.pushColors( ( uint16_t * )&color_p->full, w * h, true );
    tft.endWrite();

    lv_disp_flush_ready( disp );
}

void setup() {
    // Initialize TFT screen
    tft.begin();
    tft.setRotation(1);  // Adjust rotation as needed

    // Initialize LVGL
    lv_init();

    // Initialize LVGL drawing buffer
    lv_disp_draw_buf_init(&draw_buf, buf, NULL, SCREEN_WIDTH * SCREEN_HEIGHT  / 13);

    // Initialize LVGL display driver
    static lv_disp_drv_t disp_drv;
    lv_disp_drv_init(&disp_drv);
    disp_drv.hor_res = SCREEN_WIDTH;
    disp_drv.ver_res = SCREEN_HEIGHT;
    disp_drv.flush_cb = my_disp_flush;
    disp_drv.draw_buf = &draw_buf;
    lv_disp_drv_register(&disp_drv);

    // Create a simple text object
    lv_obj_t *label = lv_label_create(lv_scr_act());
    lv_label_set_text(label, "Pantalla LVGL con ESP32");
    lv_obj_align(label, LV_ALIGN_TOP_MID, 0, 10);  // Align text at the top

    // Create a simple chart
    lv_obj_t *chart = lv_chart_create(lv_scr_act());
    lv_obj_set_size(chart, 200, 150);  // Chart size
    lv_obj_align(chart, LV_ALIGN_CENTER, 0, 0);  // Center on screen

    lv_chart_set_type(chart, LV_CHART_TYPE_LINE);  // Chart type: lines

    // Add a data series
    lv_chart_series_t *ser = lv_chart_add_series(chart, lv_palette_main(LV_PALETTE_RED), LV_CHART_AXIS_PRIMARY_Y);
    
    // Insert values into the series
    lv_chart_set_next_value(chart, ser, 10);
    lv_chart_set_next_value(chart, ser, 20);
    lv_chart_set_next_value(chart, ser, 30);
    lv_chart_set_next_value(chart, ser, 20);
    lv_chart_set_next_value(chart, ser, 15);
    lv_chart_set_next_value(chart, ser, 25);
}

void loop() {
    // Run LVGL tasks
    lv_timer_handler();
    delay(5);  // Small delay to free up CPU
}

Screenshot or video

Others

  • **SquareLine Studio version: 1.4.2
  • **Operating system: Windows 11
  • **Target hardware: ESP32-WROOM-32, LCD TFT 2.4″ RGB SPI 240X320 ILI9431
#include <lvgl.h>
#include <TFT_eSPI.h>
#include <ui.h>

/*Change to your screen resolution*/
#define SCREEN_WIDTH 320
#define SCREEN_HEIGHT 240

static lv_disp_draw_buf_t draw_buf;
static lv_color_t buf[SCREEN_WIDTH * SCREEN_HEIGHT / 13];

TFT_eSPI tft = TFT_eSPI(SCREEN_WIDTH, SCREEN_HEIGHT); /* TFT instance */

#if LV_USE_LOG != 0
/* Serial debugging */
void my_print(const char * buf)
{
    Serial.printf(buf);
    Serial.flush();
}
#endif

/* Display flushing */
void my_disp_flush( lv_disp_drv_t *disp, const lv_area_t *area, lv_color_t *color_p )
{
    uint32_t w = ( area->x2 - area->x1 + 1 );
    uint32_t h = ( area->y2 - area->y1 + 1 );

    tft.startWrite();
    tft.setAddrWindow( area->x1, area->y1, w, h );
    tft.pushColors( ( uint16_t * )&color_p->full, w * h, true );
    tft.endWrite();

    lv_disp_flush_ready( disp );
}

void setup()
{
    Serial.begin( 115200 ); /* prepare for possible serial debug */

    String LVGL_Arduino = "Hello Arduino! ";
    LVGL_Arduino += String('V') + lv_version_major() + "." + lv_version_minor() + "." + lv_version_patch();

    Serial.println( LVGL_Arduino );
    Serial.println( "I am LVGL_Arduino" );

#if LV_USE_LOG != 0
    lv_log_register_print_cb( my_print ); /* register print function for debugging */
#endif

    tft.begin();          /* TFT init */
    tft.setRotation( 1 ); /* Landscape orientation, flipped */
    lv_init();
    lv_disp_draw_buf_init(&draw_buf, buf, NULL, SCREEN_WIDTH * SCREEN_HEIGHT  / 13);

    /*Initialize the display*/
    static lv_disp_drv_t disp_drv;
    lv_disp_drv_init( &disp_drv );
    /*Change the following line to your display resolution*/
    disp_drv.hor_res = SCREEN_WIDTH;
    disp_drv.ver_res = SCREEN_HEIGHT;
    disp_drv.flush_cb = my_disp_flush;
    disp_drv.draw_buf = &draw_buf;
    lv_disp_drv_register( &disp_drv );
    ui_init();

    Serial.println( "Setup done" );
}

void loop()
{
    lv_task_handler();
    delay(5);
}

I couldn’t spot a difference between your plain LVGL and SquareLine Studio exported codes that could cause that much CPU-usage and slowdown, draw-buffer sizes seem to be enough. If the GUI is simple (several widgets, no full-screen opacity/fading animations, no custom themes, etc.) and there’s no change on the screen the CPU utilization should be near zero during the main-loop, and much more during startup temporarily. Maybe other task on the ESP hogs the CPU or something causes continuous redrawing, or the delay(5) doesn’t work and makes the loop tight. Maybe it’s worth a check in differences between lv_conf.h and driver-config files (like for example display+touch SPI bus-frequency). Vertical lines at startup are probably due to turning on the backlight well before the initialization of the framebuffer. If the display is loading gradually (row by row? or widget-by-widget?) it really means there is something during drawing the pixels, but needs to be checked somehow, whether it comes from LVGL drawing routines or a background process. (Probably from LVGL according to the CPU-monitor display, commenting out lv_task_handler would show decrease in CPU usage if it is.)

Thank you for your response. The issue was resolved by configuring the lv_conf.h file and disabling the option #define LV_USE_LOG 1, changing it to 0. Additionally, I had LV_LOG_LEVEL_TRACE enabled, which generated many detailed logs and consumed a significant amount of CPU. Disabling both configurations greatly improved performance.

1 Like