Are there any sample projects with source code based on LittlevGL?

I have ported littlevGL and created some screens but I am having trouble using it. I don’t have much knowledge about embedded development.

My workflow is like this -


The screen 1, 2 and 3 are created but I am having trouble achieving this flow. Where should I call the next function ? I cannot do it in main as lv_task_handler() is being called in a while(1) loop.

If someone can share some already written code for their project maybe I can take notes from it.

1 Like

You should transition between screens (and populate widgets) within the button event handlers.

Can you please explain it a little more ?

Thank You

I would also like to see some sample code of how to code the question asked. Thanks

This is more of an architectural question than one that I can provide a direct sample to.

Like I said before, one way of approaching it (but this is not the only one) is that you have functions for creating screens 1, 2, and 3. When LittlevGL starts, you manually create screen 1 (and all its child objects), and set it as the root object using lv_scr_load. You then delete whatever the previous screen was.

lv_obj_t * next_scr = create_screen2();
lv_obj_t * prev_scr = lv_scr_act();
lv_scr_load(next_scr);
lv_obj_del_async(prev_scr); /*not sure if async is needed here or not */

Inside LV_EVENT_CLICKED handlers registered on the buttons, you basically do the same thing, but obviously you would create different screens.

There are several ways you can approach the problem. I can’t provide a more specific code example without some more information about what result you’re looking to get. Do you need a transition between screens, or is an instant flip like this acceptable? How much memory do you have? Can you afford to store three screens in RAM (to avoid needing to recreate them each time) or are you constrained to only having one in RAM at a time?

Can he use the tab control but hide its top?

Yes; that would be another approach. Then you could transition between tabs, and animation would be provided for you.

Of course, this comes with the downside that you need all the tabs in memory at once. That’s why I asked for more information.

An instant flip is totally acceptable.

I am using Nordic’s nRF52840.

In future there will be more screens. Tab control is thus not possible.

You are saying I should do something like this.

This is a scenario in my application is this.

The above scenario in code.

main.c

void main()
{
    create_screen_1();
    while(1)
    {
        lv_task_handler()
    }
}

create_screen_1.c

static void ok_event_handler(lv_obj_t * obj, lv_event_t event)
{
    if(event == LV_EVENT_CLICKED)
    {
        lv_obj_t * next_scr = create_screen2();
        lv_obj_t * prev_scr = lv_scr_act();
        lv_scr_load(next_scr);
        lv_obj_del_async(prev_scr);
    }
}

void create_screen_1()
{
    //Some code to create objects on current active screen
    lv_obj_t *ok_btn = lv_btn_create(lv_scr_act(), NULL);
    lv_obj_set_event_cb(ok_btn, ok_event_handler);
    
    //Something same to create a cancel button but with a separate event handler

    //Create a label showing this string
    lv_obj_t *label;
    label = lv_label_create(lv_scr_act(), NULL);
    lv_label_set_text(label, "Put a NFC card below the device to read data from it" );
}

create_screen_2.c

static void ok_event_handler(lv_obj_t * obj, lv_event_t event)
{
    if(event == LV_EVENT_CLICKED)
    {
        lv_obj_t * next_scr = create_screen3();
        lv_obj_t * prev_scr = lv_scr_act();
        lv_scr_load(next_scr);
        lv_obj_del_async(prev_scr);
    }
}

static void cancel_event_handler(lv_obj_t * obj, lv_event_t event)
{
    if(event == LV_EVENT_CLICKED)
    {
        lv_obj_t * next_scr = create_screen1();
        lv_obj_t * prev_scr = lv_scr_act();
        lv_scr_load(next_scr);
        lv_obj_del_async(prev_scr);
    }
}

lv_obj_t *  create_screen_2()
{
    lv_obj_t *scr;

    //Some code to create objects on current active screen
    lv_obj_t *ok_btn = lv_btn_create(lv_scr_act(), NULL);
    lv_obj_set_event_cb(ok_btn, ok_event_handler);
    
    //Creating cancel button
    lv_obj_t *cancel_btn = lv_btn_create(lv_scr_act(), NULL);
    lv_obj_set_event_cb(canel_btn, cancel_event_handler);

    //Do some computations 

    //Suppose I have a nfc reader and I want to display data read from it
    char string[1024];
    string = read_data_from_nfc();
    
    //Create a label showing this string
    lv_obj_t *label;
    label = lv_label_create(scr, NULL);
    lv_label_set_text(label, string);

    return scr;
}

Is this how you are suggesting to code ?

Thank you for your help. You have been very helpful.

Yes. That should work.

1 Like

My first lvgl attempt ever. It does switch screens with no effects.

    static void ok1_event_handler (lv_obj_t* obj, lv_event_t event) {
    	if (event == LV_EVENT_CLICKED) {
    		printf("Clicked\n");
    		lv_obj_t* next_scr = create_screen_2();
    		lv_obj_t* prev_scr = lv_scr_act();
    		lv_scr_load(next_scr);
    		lv_obj_del_async(prev_scr);
    	}
    }

    static void cancel_event_handler (lv_obj_t* obj, lv_event_t event) {
    	if (event == LV_EVENT_CLICKED) {
    		printf("Clicked\n");
    		lv_obj_t* next_scr = create_screen_1();
    		lv_obj_t* prev_scr = lv_scr_act();
    		lv_scr_load(next_scr);
    		lv_obj_del_async(prev_scr);
    	}
    }

    lv_obj_t* create_screen_1 (void) {
    	static lv_obj_t* scr;

    	lv_obj_t* label;
    	lv_obj_t* ok_btn;
    	lv_obj_t* cancel_btn;

    	scr = lv_obj_create(NULL, NULL); // create blank screen
    	ok_btn = lv_btn_create(scr, NULL);
    	lv_obj_set_event_cb(ok_btn, ok1_event_handler);
    	lv_obj_align(ok_btn, NULL, LV_ALIGN_IN_BOTTOM_RIGHT, 0, 0);
    	label = lv_label_create(ok_btn, NULL);
    	lv_label_set_text(label, "1 - Next");

    	return (scr);
    }

    lv_obj_t* create_screen_2 (void) {
    	static lv_obj_t* scr;

    	lv_obj_t* label;
    	lv_obj_t* ok_btn;
    	lv_obj_t* cancel_btn;

    	scr = lv_obj_create(NULL, NULL); // create blank screen
    	ok_btn = lv_btn_create(scr, NULL);
    	lv_obj_set_event_cb(ok_btn, ok1_event_handler);
    	lv_obj_align(ok_btn, NULL, LV_ALIGN_IN_BOTTOM_RIGHT, 0, 0);
    	label = lv_label_create(ok_btn, NULL);
    	lv_label_set_text(label, "2 - Next");

    	cancel_btn = lv_btn_create(scr, NULL);
    	lv_obj_set_event_cb(cancel_btn, cancel_event_handler);
    	lv_obj_align(cancel_btn, NULL, LV_ALIGN_IN_BOTTOM_LEFT, 0, 0);
    	label = lv_label_create(cancel_btn, NULL);
    	lv_label_set_text(label, "2 - Previous");

    	return (scr);
    }

    int main (int argc, char** argv) {
    	lv_init();
    	hal_init();
    	lv_scr_load(create_screen_1());

    	while (1) {
    		lv_task_handler();
    		Sleep(10);
    	}

    	return (0);
    }

Thank you for your help. I will try this approach soon and share the final work.

Hi, I need an animation transition effect when screen 1 switch to screen 2, what should I do?
Thank you.

Again, this type of question depends on the exact transition you’re looking for. Do you want to fade in/out? Do you want a sliding effect? You’ll need to provide more details.

Thank you for your attention, a sliding effect is what I want. I’m still evaluating this GUI, maybe I can do other effects myself if a sliding effect is possible.

The easiest way to get a sliding effect for free is to make use of the tabview but hide the buttons. You can then flip between screens with lv_tabview_set_tab_act. If you needed to save memory you could dynamically populate each tab as you switch to it.

lv_obj_t * tabview;
void btn_event_cb(lv_obj_t * btn, lv_event_t event)
{
    if(event == LV_EVENT_CLICKED) {
        lv_tabview_set_tab_act(tabview, (uint16_t)lv_obj_get_user_data(btn), LV_ANIM_ON);
    }
}

void test_screens(void)
{
    tabview = lv_tabview_create(lv_scr_act(), NULL);
    lv_tabview_set_btns_hidden(tabview, true);
    lv_tabview_set_sliding(tabview, false);

    /* Screen 1 */
    lv_obj_t * scr1 = lv_tabview_add_tab(tabview, "scr1");
    lv_obj_t * btn = lv_btn_create(scr1, NULL);
    lv_obj_set_user_data(btn, (lv_obj_user_data_t)1); /* Store the next screen ID in user data */
    lv_obj_set_event_cb(btn, btn_event_cb);
    lv_obj_t * label = lv_label_create(btn, NULL);
    lv_label_set_text(label, "Flip to Screen 2");

    /* Screen 2 */
    lv_obj_t * scr2 = lv_tabview_add_tab(tabview, "scr2");
    btn = lv_btn_create(scr2, NULL);
    lv_obj_set_user_data(btn, (lv_obj_user_data_t)0); /* Store the next screen ID in user data */
    lv_obj_set_event_cb(btn, btn_event_cb);
    label = lv_label_create(btn, NULL);
    lv_label_set_text(label, "Flip to Screen 1");
}

Thank you for your support, I found that “tabview” can only slide horizontally, even after setting LV_TABVIEW_BTNS_POS_LEFT. Is there any APIs can change the sliding direction? Or should I create a new “page” object to show the screen of another app?
Is that to say the object “screen” does not designed for showing screens of different applications, but for multi LCD?

I don’t believe you can change the sliding direction of the tabview at the moment, but it would definitely be a reasonable feature.

It is designed to show different screens of applications, but there is no built-in transition support.

Another option, if you want more flexibility over the sliding, is to manually create a container and put two full screen objects inside, and use animations to move them up and down as required. The object hierarchy would look like this:

scr
 | - container
      | - screen1
      | - screen2

Then you could move “container” up and down with an animation to make screen1 and screen2 visible.

But I have noticed there is a reminder “Note that the coordinates of screens can’t be changed. Attempting to use these functions on screens will result in undefined behavior” in document “https://docs.littlevgl.com/zh-CN/html/object-types/obj.html#coordinates”.

The container would not be set as the screen, but would be a child of the screen, which would never change.

You would never move the screen. You would only move the container.