How to use hardware buttons to change screen state

I am trying to implement a simple slider type UI, using ssd1306 OLED and 3 tactile switches on ESP32.

I am trying to achieve the following:

  1. When I press button_1, screen changes to time display
  2. when I press button_2, screen changes to date display
  3. when I press button_3, screen changes to month display

I cannot figure out how to map button press to hide/change specific lv_labels. I think this can be achieved by saying when button_1 pressed hide label2 and label3, and show label1. I found only one outdated example, which achieved something similar but using a button object.

Can this be achieved simply by setting a callback function to input_drv ?

I don’t have any significant code to show, sorry for the inconvenience. If I could be pointed to a example, or topic which will help me, it will be great.

For something that simple, I would honestly just skip the built-in input system and call lv_obj_set_hidden manually on the labels. If you’re using interrupts, this is a tad more complicated as you can’t call LVGL functions from inside them. If you’re polling, that isn’t an issue.

Hi,
thanks for the reply. I want to have a kind of scrollable menu thing, do you think it will be easier to do it the manual way or does lvgl have any built in system to do it ?

Inshort, I just want to assign those three buttons to lvgl events (UP, DOWN and SELECT). something like this (docs . lvgl . io/latest/en/html/porting/indev.html#using-buttons-with-encoder-logic) sorry it said can’t include links

Couldn’t find a working example using buttons as encoder

There are very few examples of how to get encoders and buttons working, since most of our example projects are for touchscreens.

At first glance, something like this could work with LV_INDEV_TYPE_ENCODER, however I have not tested it:

bool keys_read(lv_indev_drv_t * drv, lv_indev_data_t*data){
  data->key = <currently pressed keycode>;
  data->enc_diff = 0;
  if(key_pressed()) data->state = LV_INDEV_STATE_PR;
  else {
      data->state = LV_INDEV_STATE_REL;
  }
  return false; /*No buffering now so no more data read*/
}

Select would map to LV_KEY_ENTER, up would map to LV_KEY_LEFT, and down would map to LV_KEY_RIGHT.

This worked with a slight change.

Now, the issue is how to receive the event posted by the encoder ? I have added the pseudo code.

int read_key()
{
    if (read_switch(SWITCH_UP))
    {
        return LV_KEY_UP;
    }
    else if (read_switch(SWITCH_DOWN))
    {
        return LV_KEY_DOWN;
    }
    else if (read_switch(SWITCH_SELECT))
    {
        return LV_KEY_ENTER;
    }

    return 0;
}

bool encoder_with_buttons(lv_indev_drv_t *drv, lv_indev_data_t *data)
{
    int key_id = read_key();

    if (key_id > 0)
    {
        // ESP_LOGI("button_handler", "pressed key %d", key_id);

        data->key = key_id;
        data->state = LV_INDEV_STATE_PR;
    }
    else
    {
        data->state = LV_INDEV_STATE_REL;
    }

    return false;
}

I believe your objects need to be in a group.

No, I didn’t mean this.

For example, if a Text area is focused and you press some letter on a keyboard, the keys will be sent and inserted into the text area. Similarly, if a Slider is focused and you press the left or right arrows, the slider’s value will be changed.

I don’t want to associate the event to any object, rather it should be handled by a free standing event handler, is this possible ?

Ohk, got it working with groups, but then I am unable to read encoder left/right events

Because LVGL works at an object level I’m not sure that that’s possible; you would probably need to make an event handler yourself.

Adding objects to group worked, earlier events were not being sent to the label object, without the group, now they do.

Because LVGL works at an object level I’m not sure that that’s possible; you would probably need to make an event handler yourself.

So, is it possible to create a dummy lv_obj_t and make it handle events ?

Thanks for the help :slight_smile:

Technically if you put one object in a group by itself, it should receive all the encoder events. I don’t have an encoder so I haven’t tried this.

1 Like