Encoder (Potentiometer) Use With LVGL Menu

I am trying to make an autonomous selector which uses a potentiometer in order to “save” the auton selection when the robot is turned off. I am using LVGL to make a graphical interface so that the selection is visible when turned on. I have the menu with buttons set up, but I cannot figure out how to bridge the gap between the menu and the potentiometer highlighting the selected button.


Competition_initialize {

While (true) {

if the potentiometer is in a certain section of its range one of the four buttons will be
lit up. My plan was to use the pressed style for this and the released style for the default. What I am not sure of is how to trigger the pressed style without actually pressing the button (and using the potentiometer instead).



I looked into using the Encoder Input Device in the LVGL documentation, but it is SO beyond confusing to me that I have no hope of making it work, so I am going back to a second try at a simpler solution.

Ok, I found a command called

lv_event_send(Auto4,LV_BTN_ACTION_CLICK, &btn_id);

which appears to have the ability to manually trigger a button click event. However, the issue is that I get a “use of undeclared identifier ‘lv_event_send’” error.

Could someone help me learn to fix this??

(I would have put my code in the preformatted text thing but it was acting up and I could not get it to work well. Last time I used it it involved indenting every line individually 4 spaces)

Here is my code:

#include “main.h”
//#if LV_USE_IMG

pros::ADIAnalogIn potentiometer (POTENTIOMETER_PORT);

lv_obj_t * Auto1;
lv_obj_t * Auto2;
lv_obj_t * Auto3;
lv_obj_t * Auto4;
lv_obj_t * AutoLBL1;
lv_obj_t * AutoLBL2;
lv_obj_t * AutoLBL3;
lv_obj_t * AutoLBL4;
lv_obj_t * WarningMSG1;
lv_obj_t * WarningMSG2;

uint32_t btn_id;

lv_style_t myButtonStyleREL; //released style
lv_style_t myButtonStylePR; //pressed style

static lv_res_t btn_click_action(lv_obj_t * btn)
uint8_t id = lv_obj_get_free_num(btn); //id useful when there are multiple buttons

	return LV_RES_OK;


void initialize()


void competition_initialize() {
while (true) {
// auton selector HERE
lv_style_copy(&myButtonStyleREL, &lv_style_plain);
myButtonStyleREL.body.main_color = lv_color_hex(0x804000); //%25
myButtonStyleREL.body.grad_color = lv_color_hex(0x003d66); //%25
myButtonStyleREL.body.radius = 10;
myButtonStyleREL.text.color = lv_color_hex(0x999999);

lv_style_copy(&myButtonStylePR, &lv_style_plain);
myButtonStylePR.body.main_color = lv_color_hex(0xff9933); //%60
myButtonStylePR.body.grad_color = lv_color_hex(0x006bb3); //%60
myButtonStylePR.body.radius = 10;
myButtonStylePR.text.color = LV_COLOR_MAKE(255, 255, 255);

Auto1 = lv_btn_create(lv_scr_act(), NULL); //create button, lv_scr_act() is deafult screen object
lv_obj_set_free_num(Auto1, 0); //set button is to 0
Auto2 = lv_btn_create(lv_scr_act(), NULL); //create button, lv_scr_act() is deafult screen object
lv_obj_set_free_num(Auto2, 1); //set button is to 0
Auto3 = lv_btn_create(lv_scr_act(), NULL); //create button, lv_scr_act() is deafult screen object
lv_obj_set_free_num(Auto3, 2); //set button is to 0
Auto4 = lv_btn_create(lv_scr_act(), NULL); //create button, lv_scr_act() is deafult screen object
lv_obj_set_free_num(Auto4, 3); //set button is to 0

lv_btn_set_action(Auto1, LV_BTN_ACTION_CLICK, btn_click_action); //set function to be called on button click
lv_btn_set_style(Auto1, LV_BTN_STYLE_REL, &myButtonStyleREL); //set the released style
lv_btn_set_style(Auto1, LV_BTN_STYLE_PR, &myButtonStylePR); //set the pressed style
lv_obj_set_size(Auto1, 250, 30); //set the button size (acceptable default: 200,50)
lv_obj_align(Auto1, NULL, LV_ALIGN_CENTER, 0, -85); //set the position to CENTER
lv_btn_set_action(Auto2, LV_BTN_ACTION_CLICK, btn_click_action); //set function to be called on button click
lv_btn_set_style(Auto2, LV_BTN_STYLE_REL, &myButtonStyleREL); //set the released style
lv_btn_set_style(Auto2, LV_BTN_STYLE_PR, &myButtonStylePR); //set the pressed style
lv_obj_set_size(Auto2, 250, 30); //set the button size (acceptable default: 200,50)
lv_obj_align(Auto2, NULL, LV_ALIGN_CENTER, 0, -40); //set the position to CENTER
lv_btn_set_action(Auto3, LV_BTN_ACTION_CLICK, btn_click_action); //set function to be called on button click
lv_btn_set_style(Auto3, LV_BTN_STYLE_REL, &myButtonStyleREL); //set the released style
lv_btn_set_style(Auto3, LV_BTN_STYLE_PR, &myButtonStylePR); //set the pressed style
lv_obj_set_size(Auto3, 250, 30); //set the button size (acceptable default: 200,50)
lv_obj_align(Auto3, NULL, LV_ALIGN_CENTER, 0, 5); //set the position to CENTER
lv_btn_set_action(Auto4, LV_BTN_ACTION_CLICK, btn_click_action); //set function to be called on button click
lv_btn_set_style(Auto4, LV_BTN_STYLE_REL, &myButtonStyleREL); //set the released style
lv_btn_set_style(Auto4, LV_BTN_STYLE_PR, &myButtonStylePR); //set the pressed style
lv_obj_set_size(Auto4, 250, 30); //set the button size (acceptable default: 200,50)
lv_obj_align(Auto4, NULL, LV_ALIGN_CENTER, 0, 50); //set the position to CENTER

AutoLBL1 = lv_label_create(Auto1, NULL); //create label and puts it inside of the button
lv_label_set_text(AutoLBL1, “Routine 1”); //sets label text
AutoLBL2 = lv_label_create(Auto2, NULL); //create label and puts it inside of the button
lv_label_set_text(AutoLBL2, “Routine 2”); //sets label text
AutoLBL3 = lv_label_create(Auto3, NULL); //create label and puts it inside of the button
lv_label_set_text(AutoLBL3, “Routine 3”); //sets label text
AutoLBL4 = lv_label_create(Auto4, NULL); //create label and puts it inside of the button
lv_label_set_text(AutoLBL4, “Routine 4”); //sets label text

WarningMSG1 = lv_label_create(lv_scr_act(), NULL); //create label and puts it on the screen
lv_label_set_text(WarningMSG1, “Please check that the selected”); //sets label text
lv_obj_align(WarningMSG1, NULL, LV_ALIGN_CENTER, 0, 80);
WarningMSG2 = lv_label_create(lv_scr_act(), NULL); //create label and puts it on the screen
lv_label_set_text(WarningMSG2, “autonomous routine is correct!!”); //sets label text
lv_obj_align(WarningMSG2, NULL, LV_ALIGN_CENTER, 00, 100);

if (potentiometer.get_value()<1000)
lv_event_send(Auto1,LV_BTN_ACTION_CLICK, &btn_id);
if (1000<potentiometer.get_value() && potentiometer.get_value()<2000)
lv_event_send(Auto2,LV_BTN_ACTION_CLICK, &btn_id);
if (2000<potentiometer.get_value() && potentiometer.get_value()<3000)
lv_event_send(Auto3,LV_BTN_ACTION_CLICK, &btn_id);
if (3000<potentiometer.get_value())
lv_event_send(Auto4,LV_BTN_ACTION_CLICK, &btn_id);


Error locations are in bold.

1 Like

As far as I’m aware PROS uses lvgl 5.1 and the official docs don’t have that versions documentation available, so don’t be surprised if some features are missing.

You could try the toggle feature for buttons.
I believe it is lv_btn_set_toggle()

Thank you for helping. I will test it out. I didn’t know that it wasn’t documented, that really kinda screws me over I guess. I was reading up in the documentation available and there is no further documentation for that function. I was also reading about the undeclared identifier thing and it really does look like it simply isn’t defined anywhere in the header files. I don’t think I’m doing anything wrong because I followed the example code extremely closely.

1 Like

My bad, I meant 5.3. the same issue with the docs still applies. But if you need the docs here is the link for the files, however do be aware you either need to host it or have some missing images and interactivity with the demos. https://docs.lvgl.io/v6/en/html/_downloads/8115770449e5a7cb76ab80178d2858df/docs_v5_3.zip
I have no idea why they don’t host it anymore.

Do you happen to know how to insert the code into the preformatted thing correctly? I would like to try to fix that also :joy:

1 Like

Just put a backtick before the start of the code block and then another at the end. Otherwise I think there is a button you can just press which creates the block

code block

Also this is a backtick `
(It’s next to the 1 key, and above tab)

Thanks for the resource. I have the program partially working now that I switched to the toggle. Also those docs are somewhat confusing but I will try to take advantage of them still. I literally started using PROS on a V5 brain for the first time yesterday, I’d never had an opportunity previously. What does it mean to host them?
Thank you for the advice about formatting.
Edit: for some reason the backtick doesn’t do what it should, evidenced above.

1 Like

I have another question. The potentiometer seems to be able to change which button is “highlighted” or “pressed” now. The problem is that it will only do this for a short period of time before the program acts as if it has frozen. Do you have any idea why this could be happening? I added a delay of 10 ms and it seems to have drastically improved the behavior, but it is still not perfect.

Ok, testing again with a 30 ms delay it is MUCH smoother when it allows me to change it but then it still “freezes” after ~75 seconds and doesn’t allow me to do anything anymore.

It seems like the brain over time is using up resources somehow and then slowing itself to a crawl and finally stops due to lag of some sort.

Fixed it, I think it was creating thousands of buttons and running out of memory. Once I moved the creation code outside of the loop it fixed it.

1 Like

You could also only change the state if the potentiometer values changed.
if(potentiometer != potentiometer 30ms ago)
Execute logic to see what button should toggle
Do nothing

1 Like

Thanks. I figured out a way to use another command called set_state, which is actually more powerful I think because it allows more customization (choose any state). Then if the pot is in any one of the 4 given ranges, it just sets the right button to pressed and the other three to released.

Happy to help :grinning:

1 Like

Do you happen to know how to make images work? I cannot for the life of me put a background down and I really want to one to complete the look of my menu.
btw I recently created another thread on this but it hasn’t helped yet.

You have to load the image onto a micro SD card then put it in the v5 brain.

I don’t know too well as I haven’t tried but refer to the docs or look up ‘lvgl image’ on the forums.


1 Like

Oh ok that’s fine. Apparently most people haven’t been able to make that approach work, there is an alternate which I was attempting. Thanks for trying to help.

1 Like

To properly format multiline code blocks:

    // code here

Note that the LVGL header files in your project kinda provides better documentation than the 5.3 online docs.

For your purpose, I’d recommend using a button matrix, and then in a loop, setting the toggled button to whatever your potentiometer is set to.

jpearman got SD card to work for images, but using the c-array method (with the old convertor) is better.

You could also use this which works with LVGL:


Can you show me how to get to the old converter? I think that is my problem. I used the new one.

It’s not trivial, it’s removed from the website but the source still exists.

Your reasonable options:

  • get the SD card working with lvgl, as per jpearman’s instructions somewhere on the forum
  • use gif-pros with a gif that is just an image with framerate of 0
  • wait until PROS upgrades LVGL