Creating a component with menus

Description

Need sugestions on creating a component that has a menu system.

This post has two parts. Component and Menus.

What MCU/Processor/Board and compiler are you using?

ESP32 with ESP-IDF (cmake)

What do you want to achieve?

I’m building a project that has a menu system, as in you click a button and are taken to another screen.
I’m using LVGL for the whole UI part of the project.
I need to turn my code into a component. This is the first time I’m doing this so I have lots of questions, mostly because of LVGL variable declarations and how to properly build a menu system with LVGL.

What have you tried so far?

First I will have to explain how my component is structured in order for the menu questions to make sense.

Component

Right now I have some working code and menus.
I had all the code in one my_component.c, on the same level as main.c.
As it was getting lengthy and I’ve since decided to partition it in several files, leaving only the component’s global functions in the my_component.c file.

The component structure is as follows:

- project/
         - CMakeLists.txt
         - main.c
         - components/ - lvgl/ - CMakeLists.txt
                               - ...
                       - my_component/ - CMakeLists.txt
                                       - my_component.c
                                       - my_component.h
                                       - menus/ - menu_main.h
                                                - menu_main.c
                                                - menu_options.h
                                                - menu_options.c

In my_component/CMakeLists.txt I have:

idf_component_register(SRCS "my_component.c"
							"menus/menu_main.c"
							"menus/menu_options.c"
						INCLUDE_DIRS "." "menus"
						REQUIRES lvgl
					   )

I have the following includes:

/* my_component.c */
#include “my_component.h”

/* my_component.h */
#include “lvgl/lvgl.h”

And

/* menu_main.c */
#include “menu_main.h”

/* menu_main.h */
#include “my_component.h”

The same is true for the menu_options.c and menu_options.h files.

I have some variables I need for multiple menus. For example, I need the same icon for several menus (back icon, home icon, etc).
Before turning this into a component I would simply declare them as static variables in the my_component.c file that was on the project’s root directory.

Menus

The way I’m doing the menu system is I have one create_menu(lv_obj_t * parent) functions for each menu which creates the objects for the whole screen.
I then have a global function in my_component.c to create the selected menu:

lv_obj_t * scr;

void my_component_select_menu(int menu_id){

	lv_obj_t * old_scr = lv_scr_act();
	scr = lv_obj_create(NULL, NULL);
	lv_obj_del(old_scr);

	switch(menu_id){
	case MENU_ID_MAIN:
		create_main_menu(scr);
		break;
	case MENU_ID_OPTIONS:
		create_options_menu(scr);
		break;
	default: return;
	}
	lv_scr_load(scr);
}

my_component_select_menu is called when a new menu is to be drawn (e.g.: a button is pressed).

Questions:

  1. Should I re-arrange the component structure?
  2. How should I declare the image variables so I can use them in multiple files? Should I declare them as static variables in each menu file?
  3. Is there a better way of doing the menu system?
  4. Should I create all the menus with different screen global variables and then just select them as the active screen when I need that specific menu instead of creating the whole screen on the same global screen variable?
  5. In some menus I have variables that need to be constantly updated (some by hardware input (sensors), some by lvgl button action, e.g: increasing a value when a button is pressed on screen). How should I go about this? Redraw the whole screen? Only redraw the label containing the value?
  6. I’ve had some memory issues (not enough memory) with the last question. I’m not sure what the best practices are in this case. Increase the LV_MEM_SIZE? Change the way I’m drawing the menus?

These might be some basic questions/problems, but being new to LVGL and ESP-IDF I’m somewhat lost.

Let me know if there are any more details or clarifications needed.
Thank you!

Hi,

I will try to help you as soon as I get my pc in front of me, typing on the phone is awful.

EDIT

The component structure looks fine to me, remember add the component into the main requirements.

If the icons you are using are the symbols LVGL provides you should be able to use them by including lvgl into your source files.

If they are custom images I think you can define them into a source file and “import” them into the source files where you use them by using the extern keyword. The linker will not be able to “see” them if you mark them as static.

Something like this:

images.c

const my_image = {...};

where_you_use_it.c

extern my_image;

/* Now you can use it on this file */

Update the label only.

Let me know if you have more issues or questions, we created the lv_port_esp32 repository by using ESP-IDF components.

Here are some relevant tips:

  • Avoid thinking about “redrawing” when working with LVGL. Think about updating the values of widgets. The whole purpose of LVGL is to save you from thinking about erasing and setting pixels. :slightly_smiling_face:
  • Only update the widgets you need to. If you need to increase the value displayed on a label when a button is pressed, change that label’s value. Don’t tear down and recreate the entire object hierarchy (I’m not sure if that’s what you meant or not). It’s very slow and more likely to fragment the heap.
    • Also, make sure that you aren’t recreating a label each time you want to set its value. To some people, this might seem like an obvious error, but it can happen quite easily if you’re not used to the widget-oriented GUI approach.
  • If you’re running out of RAM, you can try one of two things:
    • Try to restructure your code so you only create the objects that a user can actually see (e.g. instead of showing an already-created object when they click a button, create the object when they click the button). It slows down navigation (to some extent), but is much more efficient in terms of RAM usage. It looks like you’re doing this to some extent already.
    • Increase LV_MEM_SIZE. This is the fastest option if you have the RAM for it. It can hide memory leaks, however, so keep an eye on the data from lv_mem_monitor.
1 Like

@Carlos_Diaz @embeddedt thank you so much for your replies. Answered a lot of my doubts and I’ve made significant progress.
Do you have any comments on the menu structure I’m using?

One more question popped up meanwhile.

I have some styles that are global to the whole component (used in all menus - for buttons and text mostly).
How should I go about this?

Should I declare in my_component.c, initialize and define it, and then use extern like I used in images?
Should I declare, initialize and define the same style in all .c files?

What do you recommend?

I recommend either declaring the styles as global variables in one file or using a theme. Declaring the same style in multiple files would add duplicate code and also be a waste of system resources.