Problems Switching screens using dropdown List

What do you want to achieve?

I’m trying to use a dropdown list to switch through screens

What I have righ now

There doesn’t seem to be any pattern to when the display crashes. While switching through screens, at some point when I press the dropdown list, the display will crash with the following LVGL warning.

[Warn] (39.344, +38976) lv_obj_get_display: No screen found lv_obj_tree.c:323

Main Question

Is the code that using to switch screens causing my crashing or is there something wrong with the way im creating my screens?

Could it be a memory issue?
I currently have the LV_MEM_SIZE set to (128* 1024)

Code that I’m using to switch between screens

#include "ui_manager.h"
#include "screens/screen_home.h"
#include "screens/screen_sensors.h"
#include "screens/screen_warnings.h"
#include "screens/screen_manual.h"
#include "screens/screen_history.h"
#include "screens/screen_settings.h"
#include <Arduino.h>
#include <string.h>

extern void global_input_event_cb(lv_event_t * e);

static lv_style_t dropdown_list_style;
static bool dropdown_style_initialized = false;

static int       selected_index=-1;  // default to Sensor Overview
static lv_obj_t *current_screen = nullptr;

/*
Function: Drop down selection even handler
*/
static void dropdown_event_handler(lv_event_t * e) {
    lv_event_code_t code = lv_event_get_code(e);
    lv_obj_t * obj = lv_event_get_target_obj(e);

    // Open drop down menu and Add styling
    if (code == LV_EVENT_CLICKED) {
        lv_dropdown_open(obj);
        Serial.println("[GDL] opening dropdown"); // Debug
        lv_obj_t *list = lv_dropdown_get_list(obj);
        if (list) {
            // Apply custom style only when the list is available
            lv_obj_add_style(list, &dropdown_list_style, LV_PART_MAIN | LV_STATE_DEFAULT);
            lv_obj_set_style_max_height(list, LV_SIZE_CONTENT, 0);
            lv_obj_set_scroll_dir(list, LV_DIR_NONE);
            lv_obj_set_height(list, 71 * 4);
        }
    }

    // Change screen
    if(code == LV_EVENT_VALUE_CHANGED) {
        char buf[32];
        lv_dropdown_get_selected_str(obj, buf, sizeof(buf));
        handle_screen_selection(buf);
        // lv_dropdown_set_selected(obj, selected_index);
        // lv_dropdown_close(obj);
    }
}

/*
Function: Create the drop down menu button with its items
*/
void create_global_dropdown(lv_obj_t *parent) {

    ensure_dropdown_style();

    Serial.println("Create global dropdown"); // Debug

    lv_obj_t *dropdown = lv_dropdown_create(parent);

    lv_dropdown_set_selected(dropdown, selected_index >= 0 ? selected_index : 0);

    lv_obj_set_size(dropdown, 80, 76);  // width: 150px, height: 40px
    lv_obj_align(dropdown, LV_ALIGN_TOP_LEFT, 2, 2);
    lv_dropdown_set_symbol(dropdown, LV_SYMBOL_LIST);
    //lv_dropdown_set_max_height(dropdown, 400);
    lv_dropdown_set_options_static(dropdown,
    "Sensor Overview\n"
    "Manual Control\n"
    "Warnings\n"
    "Settings"
    );

    lv_obj_set_style_bg_color(dropdown, lv_color_hex(0x42649f), 0);
    lv_obj_set_style_bg_opa(dropdown, LV_OPA_COVER, 0);
    lv_obj_set_style_text_color(dropdown, lv_color_white(), 0);
    lv_obj_set_style_border_width(dropdown, 0, 0);  // No border

    // Hide the selected option text 
    
    lv_dropdown_set_text(dropdown, "");

    lv_obj_set_style_text_font(dropdown, &lv_font_montserrat_48, LV_PART_MAIN | LV_STATE_DEFAULT);
    
    // // drop down listed items
    // lv_style_init(&dropdown_list_style);
    // lv_style_set_text_font(&dropdown_list_style, &lv_font_montserrat_48);
    // lv_style_set_bg_color(&dropdown_list_style, lv_color_hex(0xc0c9d9));
    // // Set horizontal gradient from dark blue to light blue
    // lv_style_set_bg_color(&dropdown_list_style, lv_color_hex(0x42649F));
    // lv_style_set_bg_grad_color(&dropdown_list_style, lv_color_hex(0xA3B7E4));
    // lv_style_set_bg_grad_dir(&dropdown_list_style, LV_GRAD_DIR_HOR);
    
    // Click on menu button handler
    //lv_obj_add_event_cb(dropdown, dropdown_event_handler, LV_EVENT_VALUE_CHANGED, NULL);
    lv_obj_add_event_cb(dropdown, dropdown_event_handler, LV_EVENT_ALL, NULL);
}

/*
Function: Handles the selection of each item in the drop down menu
*/
void handle_screen_selection(const char *selected_label) {
    Serial.println("[GDL] changing Screens..."); // Debug
    int new_index = -1;
    

    if      (!strcmp(selected_label, "Sensor Overview"))   new_index = 0;
    else if (!strcmp(selected_label, "Manual Control"))    new_index = 1;
    else if (!strcmp(selected_label, "Warnings"))          new_index = 2;
    else if (!strcmp(selected_label, "Settings"))          new_index = 3;
    else if (!strcmp(selected_label, "Home"))              new_index = 4;
    else new_index = 0;
    Serial.print("[LVGL] ");
    Serial.println(selected_label);

    if(new_index == selected_index) return; // no change

    // if it’s changed, actually switch
    if (new_index >= 0) {
       // create screen function returns lv_obj_t* of screen it is creating
        switch (new_index) {
            case 0: current_screen = create_sensor_screen();         break;
            case 1: current_screen = create_manual_control_screen(); break;
            case 2: current_screen = create_warnings_screen();       break;
            case 3: current_screen = create_settings_screen();       break;
            case 4: current_screen = create_home_screen();           break;
            default: current_screen = create_sensor_screen();        break;
        }

        if(current_screen) {
            lv_screen_load_anim(current_screen, LV_SCR_LOAD_ANIM_NONE, 0, 0, true);
            selected_index = new_index;
        }
    }

    // if (dropdown) {
    //     // Only close if dropdown is still valid and on the current screen
    //     if (lv_obj_is_valid(dropdown)) {
    //         lv_dropdown_set_selected(dropdown, selected_index);
    //         lv_dropdown_close(dropdown);
    //     }
    //     dropdown = nullptr; // Invalidate pointer after screen switch
    //     LV_ASSERT_MEM_INTEGRITY();
    // }
    // lv_mem_monitor_t mon;
    // lv_mem_monitor(&mon);

    // Serial.print(F("[LVGL] Used: "));
    // Serial.print(mon.total_size - mon.free_size);
    // Serial.print(F(" bytes | Free: "));
    // Serial.print(mon.free_size);
    // Serial.print(F(" bytes | Largest Free: "));
    // Serial.print(mon.free_biggest_size);
    // Serial.print(F(" bytes | Fragmentation: "));
    // Serial.print(mon.frag_pct);
    // Serial.println(F("%"));
}

void ensure_dropdown_style() {
    if (!dropdown_style_initialized) {
        lv_style_init(&dropdown_list_style);
        lv_style_set_text_font(&dropdown_list_style, &lv_font_montserrat_48);
        lv_style_set_bg_color(&dropdown_list_style, lv_color_hex(0x42649F));
        lv_style_set_bg_grad_color(&dropdown_list_style, lv_color_hex(0xA3B7E4));
        lv_style_set_bg_grad_dir(&dropdown_list_style, LV_GRAD_DIR_HOR);
        dropdown_style_initialized = true;
    }
}

void create_header(lv_obj_t *parent, const char *title_txt) {
    Serial.println("[gH] creating header"); // Debug
    // Header Bar
    lv_obj_t *header = lv_obj_create(parent);
    lv_obj_set_size(header, lv_pct(100), 80);
    lv_obj_align(header, LV_ALIGN_TOP_MID, 0, 0);
    lv_obj_set_style_bg_color(header, lv_color_hex(0x42649f), LV_PART_MAIN);
    lv_obj_set_style_bg_opa(header, LV_OPA_COVER,    LV_PART_MAIN);
    lv_obj_clear_flag(header, LV_OBJ_FLAG_SCROLLABLE);
    lv_obj_set_scroll_dir(header, LV_DIR_NONE);

    // Title Label
    lv_obj_t *title = lv_label_create(header);
    lv_label_set_text(title, title_txt);
    lv_obj_center(title);
    lv_obj_set_style_text_color(title, lv_color_hex(0xc0c9d9), 0);
    lv_obj_set_style_text_font(title, &lv_font_montserrat_48,  0);

    // Global navigation dropdown (icon-only)
    Serial.println("[gH] creating GDL"); // Debug
    create_global_dropdown(parent);
}
//EOF

Code for one of my screens

lv_obj_t* create_sensor_screen(void) {

    sensor_screen = lv_obj_create(NULL);
    
    // Register the global input event callback
    //lv_obj_add_event_cb(sensor_screen, global_input_event_cb, LV_EVENT_ALL, NULL);

    // Background
    lv_obj_set_style_bg_color(sensor_screen, lv_color_hex(0xc0c9d9), LV_PART_MAIN);
    lv_obj_set_style_bg_opa(sensor_screen, LV_OPA_COVER, LV_PART_MAIN);
    lv_obj_clear_flag(sensor_screen, LV_OBJ_FLAG_SCROLLABLE);
    lv_obj_set_scroll_dir(sensor_screen, LV_DIR_NONE);

    // create header
    create_header(sensor_screen, "Sensor Overview");

    // ===== Sensor Data Grid =====
    lv_obj_t *grid = lv_obj_create(sensor_screen);
    lv_obj_set_size(grid, lv_pct(100), 300);
    lv_obj_align(grid, LV_ALIGN_TOP_MID, 0, 65);

    static lv_coord_t col_dsc[] = { 200, 200, 180, LV_GRID_TEMPLATE_LAST };
    static lv_coord_t row_dsc[] = { 60, 60, 60, 60, LV_GRID_TEMPLATE_LAST };

    lv_obj_set_grid_dsc_array(grid, col_dsc, row_dsc);
    lv_obj_set_layout(grid, LV_LAYOUT_GRID);
    lv_obj_set_style_bg_opa(grid, LV_OPA_TRANSP, 0);
    lv_obj_set_style_border_width(grid, 0, 0);

    // Create a horizontal line between header row and sensor rows
    static lv_point_precise_t line_points[] = { {0, 400}, {0, 0} }; // Line from x=0 to x=320

    lv_obj_t *line = lv_line_create(sensor_screen);
    lv_line_set_points(line, line_points, 2);
    lv_obj_set_style_line_color(line, lv_color_black(), 0);
    lv_obj_set_style_line_width(line, 4, 0);
    lv_obj_align(line, LV_ALIGN_TOP_LEFT, 620, 80);  // Align just under row 0 of the grid
    
    // ----- Row Labels + Sensor Value Labels -----
    for (int i = 0; i < 3; i++) {
        // Row titles (Sensor 1, 2, 3)
        lv_obj_t *label_sensor = lv_label_create(grid);
        lv_label_set_text_fmt(label_sensor, "Sensor %d", i + 1);
        lv_obj_set_grid_cell(label_sensor, LV_GRID_ALIGN_START, 0, 1, LV_GRID_ALIGN_CENTER, i, 1);
        lv_obj_set_style_text_font(label_sensor, &lv_font_montserrat_48, 0);
        lv_obj_set_style_text_color(label_sensor, lv_color_black(), 0);

        // Temp
        label_temp[i] = lv_label_create(grid);
        lv_obj_set_grid_cell(label_temp[i], LV_GRID_ALIGN_CENTER, 1, 1, LV_GRID_ALIGN_CENTER, i, 1);
        lv_obj_set_style_text_font(label_temp[i], &lv_font_montserrat_48, 0);
        lv_obj_set_style_text_color(label_temp[i], lv_color_black(), 0);

        // Humidity
        label_hum[i] = lv_label_create(grid);
        lv_obj_set_grid_cell(label_hum[i], LV_GRID_ALIGN_CENTER, 2, 1, LV_GRID_ALIGN_CENTER, i, 1);
        lv_obj_set_style_text_font(label_hum[i], &lv_font_montserrat_48, 0);
        lv_obj_set_style_text_color(label_hum[i], lv_color_black(), 0);
    }
    
    // Line 
    lv_obj_t *line1 = lv_line_create(sensor_screen);
    lv_line_set_points(line1, line_points, 2);
    lv_obj_set_style_line_color(line1, lv_color_black(), 0);
    lv_obj_set_style_line_width(line1, 4, 0);
    lv_obj_align(line1, LV_ALIGN_TOP_LEFT, 0, 400-37+60);  // Align just under row 0 of the grid

    // O2 Data
    lv_obj_t *label_o2_title = lv_label_create(grid);
    lv_label_set_text(label_o2_title, "O2 %");
    lv_obj_set_grid_cell(label_o2_title, LV_GRID_ALIGN_CENTER, 0, 1, LV_GRID_ALIGN_CENTER, 3, 1);
    lv_obj_set_style_text_font(label_o2_title, &lv_font_montserrat_48, 0);
    lv_obj_set_style_text_color(label_o2_title, lv_color_black(), 0);

    label_o2 = lv_label_create(grid);
    lv_obj_set_grid_cell(label_o2, LV_GRID_ALIGN_CENTER, 1, 1, LV_GRID_ALIGN_CENTER, 3, 1);
    lv_obj_set_style_text_font(label_o2, &lv_font_montserrat_48, 0);
    lv_obj_set_style_text_color(label_o2, lv_color_black(), 0);

   // Compost level bar on right
    bar_level = lv_bar_create(sensor_screen);
    lv_obj_set_size(bar_level,40,310);
    lv_obj_align(bar_level,LV_ALIGN_RIGHT_MID,-30, 5);
    lv_bar_set_range(bar_level,0,100);
    lv_bar_set_value(bar_level,0,LV_ANIM_OFF);
    lv_obj_set_style_radius(bar_level,0,LV_PART_MAIN);

    static lv_style_t si; lv_style_init(&si);
    lv_style_set_bg_opa(&si,LV_OPA_COVER);
    lv_style_set_bg_color(&si,lv_palette_main(LV_PALETTE_RED));
    lv_style_set_bg_grad_color(&si,lv_palette_main(LV_PALETTE_GREEN));
    lv_style_set_bg_grad_dir(&si,LV_GRAD_DIR_VER);
    lv_style_set_radius(&si,0);
    lv_obj_add_style(bar_level,&si,LV_PART_INDICATOR);

    // Dynamic percentage label
    label_bar_pct = lv_label_create(sensor_screen);
    lv_label_set_text(label_bar_pct, "0%%");
    lv_obj_set_style_text_font(label_bar_pct, &lv_font_montserrat_40, 0);
    
    
    
    // Diagnostics button bottom-left
    lv_obj_t *btn_diag = lv_btn_create(sensor_screen);
    lv_obj_set_size(btn_diag, 140, 70);
    lv_obj_align(btn_diag, LV_ALIGN_BOTTOM_RIGHT, -200, -60);
    lv_obj_add_event_cb(btn_diag, [](lv_event_t* e) {
        LV_UNUSED(e);
        lv_scr_load(create_diagnostics_screen());
    }, LV_EVENT_CLICKED, NULL);
    lv_obj_t *lbl_diag = lv_label_create(btn_diag);
    lv_label_set_text(lbl_diag, "Diagnostics");
    lv_obj_center(lbl_diag);
    
    update_sensor_values(); // Initial values
    // Create footer
    create_footer(sensor_screen);

    return sensor_screen;
}

Environment

  • MCU/MPU/Board: Arduinio Giga R1 wifi
  • LVGL version: 9.3

Hello,

First off

I think you are missing an lv_obj_t* before sensor_screen.

Secondly, I may have missed this, but where do you delete the created screens?

Kind regards