Locked Screen and LVGL Best Pratic

I’m already a little experienced with LVGL and I’m suffering from a problem.

My screen is freezing.

After 20 minutes or 10 minutes (There is no rule, the screen stops updating.)

I’m going to share the code I’m using. Can anyone shed some light on whether I’m using a good practice?

void f_DisplayTempA(void *parameters){
                float tempReceivedA, tempReceivedB, tempReceivedC, tempReceivedD;
                int tempReceived_Int;
                float tempReceivedAntA, tempReceivedAntB, tempReceivedAntC, tempReceivedAntD;
                tempReceivedAntA = -200;tempReceivedAntB = -200;tempReceivedAntC = -200;tempReceivedAntD = -200;
                char tempC_char[10];
                datetime_t received_time;
                char dateReceived_char[64];
                char timeReceived_char[64];
                while(1){
                        if (xQueueReceive(qSensor_Display_A, &tempReceivedA, pdMS_TO_TICKS(200))){
                                if (tempReceivedA !=tempReceivedAntA){
                                        tempReceived_Int = (int)tempReceivedA;
                                        sprintf(tempC_char, "%.2fºC", tempReceivedA);
                                        tempReceivedAntA=tempReceivedA;
                                        lock();
                                                lv_meter_set_indicator_end_value(ui->Main_mt_Canal_A, ui->Main_mt_Canal_A_scale_0_arc_0, tempReceived_Int-1);
                                                lv_meter_set_indicator_start_value(ui->Main_mt_Canal_A, ui->Main_mt_Canal_A_scale_0_arc_1, tempReceived_Int);
                                                lv_meter_set_indicator_end_value(ui->Main_mt_Canal_A, ui->Main_mt_Canal_A_scale_0_arc_1, tempReceived_Int+1);
                                                lv_meter_set_indicator_start_value(ui->Main_mt_Canal_A, ui->Main_mt_Canal_A_scale_0_arc_2, tempReceived_Int+2);
                                                lv_label_set_text(ui->Main_lbl_Temp_A, tempC_char);
                                        unlock();
                                }
                        }
                        if (xQueueReceive(qSensor_Display_B, &tempReceivedB, pdMS_TO_TICKS(200))){
                                if (tempReceivedB !=tempReceivedAntB){
                                        tempReceived_Int = (int)tempReceivedB;
                                        sprintf(tempC_char, "%.2fºC", tempReceivedB);
                                        tempReceivedAntB=tempReceivedB;
                                        lock();
                                                lv_meter_set_indicator_end_value(ui->Main_mt_Canal_B, ui->Main_mt_Canal_B_scale_0_arc_0, tempReceived_Int-1);
                                                lv_meter_set_indicator_start_value(ui->Main_mt_Canal_B, ui->Main_mt_Canal_B_scale_0_arc_1, tempReceived_Int);
                                                lv_meter_set_indicator_end_value(ui->Main_mt_Canal_B, ui->Main_mt_Canal_B_scale_0_arc_1, tempReceived_Int+1);
                                                lv_meter_set_indicator_start_value(ui->Main_mt_Canal_B, ui->Main_mt_Canal_B_scale_0_arc_2, tempReceived_Int+2);
                                                lv_label_set_text(ui->Main_lbl_Temp_B, tempC_char);
                                        unlock();
                                }
                        }
                        if (xQueueReceive(qSensor_Display_C, &tempReceivedC, pdMS_TO_TICKS(200))){
                                if (tempReceivedC !=tempReceivedAntC){
                                        tempReceived_Int = (int)tempReceivedC;
                                        sprintf(tempC_char, "%.2fºC", tempReceivedC);
                                        tempReceivedAntC=tempReceivedC;
                                        lock();
                                                lv_meter_set_indicator_end_value(ui->Main_mt_Canal_C, ui->Main_mt_Canal_C_scale_0_arc_0, tempReceived_Int-1);
                                                lv_meter_set_indicator_start_value(ui->Main_mt_Canal_C, ui->Main_mt_Canal_C_scale_0_arc_1, tempReceived_Int);
                                                lv_meter_set_indicator_end_value(ui->Main_mt_Canal_C, ui->Main_mt_Canal_C_scale_0_arc_1, tempReceived_Int+1);
                                                lv_meter_set_indicator_start_value(ui->Main_mt_Canal_C, ui->Main_mt_Canal_C_scale_0_arc_2, tempReceived_Int+2);
                                                lv_label_set_text(ui->Main_lbl_Temp_C, tempC_char);
                                        unlock();
                                }
                        }
                        if (xQueueReceive(qSensor_Display_D, &tempReceivedD, pdMS_TO_TICKS(200))){
                                if (tempReceivedD !=tempReceivedAntD){
                                        tempReceived_Int = (int)tempReceivedD;
                                        //sprintf(tempC_char, "%.2fºC", tempReceived);
                                        sprintf(tempC_char, "%d%%", (int)tempReceivedD);
                                        tempReceivedAntD=tempReceivedD;
                                        lock();
                                                lv_meter_set_indicator_end_value(ui->Main_mt_Canal_D, ui->Main_mt_Canal_D_scale_0_arc_0, tempReceived_Int);
                                                lv_label_set_text(ui->Main_lbl_Hum, tempC_char);
                                        unlock();
                                }
                        }
                        if (xQueueReceive(qDataHora, &received_time, pdMS_TO_TICKS(200))) {
                                snprintf(dateReceived_char, sizeof(timeReceived_char),"%02d/%02d/%d",received_time.day, received_time.month, received_time.year);
                                snprintf(timeReceived_char, sizeof(timeReceived_char),"%02d:%02d:%02d", received_time.hour, received_time.minute, received_time.second);
                                lock();
                                        lv_label_set_text(ui->Main_lbl_Data, dateReceived_char);
                                        lv_label_set_text(ui->Main_lbl_Hora, timeReceived_char);
                                unlock();
                        }

                        vTaskDelay(400 / portTICK_PERIOD_MS);
                }
        }

and the code:

void f_updateScreen(void *arg){
        ESP_LOGI(TAG, "Starting LVGL task");
        while (1) {
                lock();
                        lv_timer_handler();
                unlock();        
            vTaskDelay(pdMS_TO_TICKS(10));
        }
}


//void lock(){if (xSemaphoreDisplay!=NULL){xSemaphoreTake(xSemaphoreDisplay, pdMS_TO_TICKS(200));}}
void lock(){if (xSemaphoreDisplay!=NULL){xSemaphoreTake(xSemaphoreDisplay, portMAX_DELAY);}}
void unlock(){if (xSemaphoreDisplay!=NULL){xSemaphoreGive(xSemaphoreDisplay);}}

In the case in question, I cannot call lv_timer_handler() within the f_DisplayTempA() loop because sometimes other visual elements are called such as email sending, error, successful sending, etc…

My problem is that the screen is freezing. After a few minutes of use, the screen freezes…although the freeRTOs tasks continue to work.

image

Where am I going wrong??

Please help me?

Have you tried running with the LVGL logs turned on? 90% of the time for me when this happens there is a log entry from LVGL saying it is unable to allocate memory.

How to enable LVGL logs with ESP IDF 5 ? Please help me.

I tried to look at the LVGL logs but I couldn’t get it to work at all.

Below is the code I am using to initialize LVGL:

#include <stdio.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "freertos/semphr.h"
#include "esp_timer.h"
#include "esp_lcd_panel_io.h"
#include "esp_lcd_panel_vendor.h"
#include "esp_lcd_panel_ops.h"
#include "driver/gpio.h"
#include "driver/spi_master.h"
#include "esp_err.h"
#include "esp_log.h"
#include "lvgl.h"
#include "nvs_flash.h"
#include "esp_lcd_ili9341.h"
#include "esp_lcd_touch_xpt2046.h"
#include "touch.h"
#include "hardware.h"
#include "lcd.h"
#include "sq/ui.h"
#include "f_display.h"
// #include "generated/gui_guider.h"
// lv_ui guider_ui;

static const char *TAG = "LCD:";
esp_lcd_touch_handle_t tp_Handle;
static SemaphoreHandle_t lvgl_mux = NULL;


// Tag para identificar os logs do LVGL


void lv_log_callback(const char *buf) {
    // Redireciona o log do LVGL para o sistema do ESP-IDF
    ESP_LOGI(TAG, "%s", buf);
}




static bool flush_ready_cb(esp_lcd_panel_io_handle_t panel_io, esp_lcd_panel_io_event_data_t *edata, void *user_ctx){
        lv_disp_drv_t *disp_driver = (lv_disp_drv_t *)user_ctx;
        lv_disp_flush_ready(disp_driver);
        return false;
}

static void flush_cb(lv_disp_drv_t *drv, const lv_area_t *area, lv_color_t *color_map){
        esp_lcd_panel_handle_t panel_handle = (esp_lcd_panel_handle_t) drv->user_data;
        int offsetx1 = area->x1;
        int offsetx2 = area->x2;
        int offsety1 = area->y1;
        int offsety2 = area->y2;
        esp_lcd_panel_draw_bitmap(panel_handle, offsetx1, offsety1, offsetx2 + 1, offsety2 + 1, color_map);
}

static void touch_cb(lv_indev_drv_t * drv, lv_indev_data_t * data){
        uint16_t touchpad_x[1] = {0};
        uint16_t touchpad_y[1] = {0};
        uint8_t touchpad_cnt = 0;
        esp_lcd_touch_read_data(drv->user_data);
        bool touchpad_pressed = esp_lcd_touch_get_coordinates(drv->user_data, touchpad_x, touchpad_y, NULL, &touchpad_cnt, 1);
        if (touchpad_pressed && touchpad_cnt > 0) {
            data->point.x = touchpad_x[0];
            data->point.y = touchpad_y[0];
            data->state = LV_INDEV_STATE_PRESSED;
            //ESP_LOGI(TAG, "Touch x(%d), y(%d)", data->point.x, data->point.y);
        } else {
            data->state = LV_INDEV_STATE_RELEASED;
        }
}
static void example_increase_lvgl_tick(void *arg){
        lv_tick_inc(EXAMPLE_LVGL_TICK_PERIOD_MS);
}

void f_setupDisplay(){
            static lv_disp_draw_buf_t disp_buf; // contains internal graphic buffer(s) called draw buffer(s)
            static lv_disp_drv_t disp_drv;      // contains callback functions
            ESP_LOGI(TAG, "Turn off LCD backlight");
            gpio_config_t bk_gpio_config = {.mode = GPIO_MODE_OUTPUT,.pin_bit_mask = 1ULL << EXAMPLE_PIN_NUM_BK_LIGHT};
            ESP_ERROR_CHECK(gpio_config(&bk_gpio_config));
            ESP_LOGI(TAG, "Initialize SPI bus");
            spi_bus_config_t buscfg = {
                    .sclk_io_num = EXAMPLE_PIN_NUM_SCLK,
                    .mosi_io_num = EXAMPLE_PIN_NUM_MOSI,
                    .miso_io_num = EXAMPLE_PIN_NUM_MISO,
                    .quadwp_io_num = -1,
                    .quadhd_io_num = -1,
                    .max_transfer_sz = CONFIG_LCD_HRES * 80 * sizeof(uint16_t),
            };
            ESP_ERROR_CHECK(spi_bus_initialize(LCD_HOST, &buscfg, SPI_DMA_CH_AUTO));
            ESP_LOGI(TAG, "Install panel IO");
            esp_lcd_panel_io_handle_t io_handle = NULL;
            esp_lcd_panel_io_spi_config_t io_config = {
                    .dc_gpio_num = EXAMPLE_PIN_NUM_LCD_DC,
                    .cs_gpio_num = EXAMPLE_PIN_NUM_LCD_CS,
                    .pclk_hz = EXAMPLE_LCD_PIXEL_CLOCK_HZ,
                    .lcd_cmd_bits = EXAMPLE_LCD_CMD_BITS,
                    .lcd_param_bits = EXAMPLE_LCD_PARAM_BITS,
                    .spi_mode = 0,
                    .trans_queue_depth = 10,
                    .on_color_trans_done = flush_ready_cb,
                    .user_ctx = &disp_drv,
            };
            ESP_ERROR_CHECK(esp_lcd_new_panel_io_spi((esp_lcd_spi_bus_handle_t)LCD_HOST, &io_config, &io_handle));
            esp_lcd_panel_handle_t panel_handle = NULL;
            esp_lcd_panel_dev_config_t panel_config = {
                    .reset_gpio_num = EXAMPLE_PIN_NUM_LCD_RST,
                    .rgb_ele_order = LCD_RGB_ELEMENT_ORDER_BGR,
                    .bits_per_pixel = 16,
            };
            ESP_LOGI(TAG, "Install ILI9341 panel driver");
            ESP_ERROR_CHECK(esp_lcd_new_panel_ili9341(io_handle, &panel_config, &panel_handle));
            ESP_ERROR_CHECK(esp_lcd_panel_reset(panel_handle));
            ESP_ERROR_CHECK(esp_lcd_panel_init(panel_handle));

            ESP_ERROR_CHECK(esp_lcd_panel_swap_xy(panel_handle, true));

            //ESP_ERROR_CHECK(esp_lcd_panel_mirror(panel_handle, true, false));
            ESP_ERROR_CHECK(esp_lcd_panel_disp_on_off(panel_handle, true));
            
            //tp_Handle = InitTouchPanel();
            
            ESP_LOGI(TAG, "Turn on LCD backlight");
            gpio_set_level(EXAMPLE_PIN_NUM_BK_LIGHT, EXAMPLE_LCD_BK_LIGHT_ON_LEVEL);
            ESP_LOGI(TAG, "Initialize LVGL library");
            lv_init();
            
            lv_log_register_print_cb(lv_log_callback);  

            lv_color_t *buf1 = heap_caps_malloc(CONFIG_LCD_HRES * 25 * sizeof(lv_color_t), MALLOC_CAP_DMA);
            assert(buf1);// alloc draw buffers used by LVGL
            lv_color_t *buf2 = heap_caps_malloc(CONFIG_LCD_HRES * 25 * sizeof(lv_color_t), MALLOC_CAP_DMA);
            assert(buf2);// it's recommended to choose the size of the draw buffer(s) to be at least 1/10 screen sized
           
        //    lv_color_t *buf1 = heap_caps_malloc(CONFIG_LCD_HRES * (CONFIG_LCD_VRES / 8) * sizeof(lv_color_t), MALLOC_CAP_DMA);
        //    assert(buf1);
        //    lv_color_t *buf2 = heap_caps_malloc(CONFIG_LCD_HRES * (CONFIG_LCD_VRES / 8) * sizeof(lv_color_t), MALLOC_CAP_DMA);
        //    assert(buf2);

            lv_disp_draw_buf_init(&disp_buf, buf1, buf2, CONFIG_LCD_HRES * 20);
            ESP_LOGI(TAG, "Register display driver to LVGL");
            lv_disp_drv_init(&disp_drv);
            disp_drv.hor_res = CONFIG_LCD_HRES;
            disp_drv.ver_res = CONFIG_LCD_VRES;
            disp_drv.flush_cb = flush_cb;
            disp_drv.draw_buf = &disp_buf;
            disp_drv.user_data = panel_handle;
            lv_disp_t *disp = lv_disp_drv_register(&disp_drv);

            tp_Handle = InitTouchPanel(panel_handle);

            ESP_LOGI(TAG, "Install LVGL tick timer");
            const esp_timer_create_args_t lvgl_tick_timer_args = {.callback = &example_increase_lvgl_tick,.name = "lvgl_tick"};
            esp_timer_handle_t lvgl_tick_timer = NULL;
            ESP_ERROR_CHECK(esp_timer_create(&lvgl_tick_timer_args, &lvgl_tick_timer));
            ESP_ERROR_CHECK(esp_timer_start_periodic(lvgl_tick_timer, EXAMPLE_LVGL_TICK_PERIOD_MS * 1000));
            static lv_indev_drv_t indev_drv;    // Input device driver (Touch)
            lv_indev_drv_init(&indev_drv);
            indev_drv.type = LV_INDEV_TYPE_POINTER;
            indev_drv.disp = disp;
            indev_drv.read_cb = touch_cb;
            indev_drv.user_data = tp_Handle;
            lv_indev_drv_register(&indev_drv);
            lvgl_mux = xSemaphoreCreateRecursiveMutex();
            assert(lvgl_mux);
            ESP_LOGI(TAG, "Create LVGL task");

            xTaskCreatePinnedToCore(f_updateScreen, "f_updateScreen", 2700, NULL, tskIDLE_PRIORITY + 2, NULL, APP_CPU_NUM);

            ui_init();    
                 

}

My CMAKE to main:

file(GLOB_RECURSE SOURCES_C "*.c")


idf_component_register(
    SRCS ${SOURCES_C}
    INCLUDE_DIRS "." "gui_guider/custom" "gui_guider/generated"
)


add_compile_definitions(LV_LVGL_H_INCLUDE_SIMPLE)

add_compile_definitions(
    LV_USE_LOG=1
    LV_LOG_LEVEL=LV_LOG_LEVEL_TRACE
    LV_LOG_PRINTF=1
)


spiffs_create_partition_image(storage ../partition FLASH_IN_PROJECT)  

and enable the lvgl log in MENUCONFIG