How to maintain dynamic UI under RTOS

Description

LVGL is not threadsafe, and one should use mutex to wrap LVGL function calls.
But what if one needs a dynamically created GUI?

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

Esp32 with default freeRTOS

What do you want to achieve?

For example i have a button callback, that should dynamically create a popup.

What have you tried so far?

I have lv_task_handler in separate task running, wrapped in mutex.
I have a function that creates main GUI and attaches callback and stuff.
I have another task, that updates some info on the UI once a second, that is also wrapped in mutex, because it uses LVGL functions.
The problem is that if i try to take mutex in the callback, i get a deadlock. And without using mutex, everything crashes. How to properly structure the code if i need to dynamically create UI elements? And even more, furter i plan to include a js interpreter, that will also create UI elements dynamically from linked functions.

Code to reproduce

Add a code snippet which can run in the simulator. It should contain only the relevant code that compiles without errors when separated from your main code base.

The code block(s) should be formatted like:

/*You code here*/

Screenshot and/or video

If possible, add screenshots and/or videos about the current state.

You never need to take the mutex inside an event callback, as they are either run from lv_task_handler, or (in rare cases like lv_obj_del) from a thread which already took the mutex.

Mutexes only need to be used when you are running outside of a LittlevGL context (i.e. in RTOS threads).

I’d suggest doing as much with LittlevGL tasks and event callbacks as possible. That way you can minimize the number of mutexes you need.

1 Like

thanks, but if i create popup inside a callback without mutex, my mcu freezes. seems like something goes wrong.

How are you creating the popup? Can you show a code snippet?

yeah, but not sure if it will be of any help
EDIT: found the problem, it was my fault with static strings. now popup works fine.

void Topbar::TimeClicked(lv_obj_t* obj, lv_event_t event) {
    if(event == LV_EVENT_PRESSED) {
        static char hours[72] = "";
        static char minutes[182] = "";
        static char tmp[3] = "";
        static int i = 0;
        if (i==0){
          for (i=0; i<24; i++) {
            sprintf(tmp, "%02d\n", i);
            strcat(hours, tmp);
          }
          hours[71] = 0;
          for (i=0; i<60; i++) {
            sprintf(tmp, "%02d\n", i);
            strcat(minutes, tmp);
          }
          minutes[179] = 0;
        }
        static lv_obj_t* clock_popup;
        static bool timeEditorVisible = false;

        if (timeEditorVisible) {
            lv_obj_del_async(clock_popup);
        } else {
            clock_popup = lv_obj_create(lv_scr_act(), NULL);
            lv_obj_set_size(clock_popup, 200, 100);
            lv_obj_set_pos(clock_popup, (LV_HOR_RES_MAX/2)-100, 32);
            
            hour_roller = lv_roller_create(clock_popup, NULL);
            lv_roller_set_options(hour_roller, hours, LV_ROLLER_MODE_INIFINITE);
            lv_roller_set_visible_row_count(hour_roller, 3);
            lv_roller_set_fix_width(hour_roller, 50);
            lv_obj_align(hour_roller, NULL, LV_ALIGN_CENTER, -30, 0);
            lv_obj_set_event_cb(hour_roller, TimeRollerChanged);
            
            minute_roller = lv_roller_create(clock_popup, NULL);
            lv_roller_set_options(minute_roller, minutes, LV_ROLLER_MODE_INIFINITE);
            lv_roller_set_visible_row_count(minute_roller, 3);
            lv_roller_set_fix_width(minute_roller, 50);
            lv_obj_align(minute_roller, NULL, LV_ALIGN_CENTER, 30, 0);
            lv_obj_set_event_cb(minute_roller, TimeRollerChanged);
        }

        timeEditorVisible = !timeEditorVisible;
    }
}

I ran your code on a single thread and it worked fine. I’ll try it with two threads now just to be sure, but I’m suspicious that the issue may be unrelated to the fact that you’re using an RTOS. Can you disable all your tasks except for the one running lv_task_handler, and see if the crash still happens?

1 Like

it was not working because of bad use of static variables.
what can you suggest for second part of my question about creating UI from an interpreter?
it won’t be an LVGL callback anymore. duktape translates js function calls to a C function calls, so i will need to use LVGL functions from C functions that will be called inside separate task thread. is it ok to do that with mutex?

Yes. Just make sure that in the binding functions you lock the LittlevGL mutex before you call any LittlevGL functions, and it should work properly.

1 Like