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