LV_INDEV_TYPE_ENCODER working but not LV_INDEV_TYPE_KEYPAD (with Duppa NavKey input device)

First of all, congratulations for your amazing library !

I’m using LVGL v8.4 with a Makerfabs ESP32-S3 Parallel TFT with Touch 3.5’’ ILI9488 board
(https://www.makerfabs.com/esp32-s3-parallel-tft-with-touch-ili9488.html).

Display is working great, but I can’t make LVGL to work using LV_INDEV_TYPE_KEYPAD input device type with a Duppa I2C NavKey v2.1 controller (with neither the directional part of the controller, nor the rotary encoder part of the controller). Rotary encoder part of the NavKey work well, just by changing the input device type to LV_INDEV_TYPE_ENCODER (not changing anything else to the code), but the directional part still doesn’t work.

I’ve read the LVGL documentation over and over again, but I can’t find the issue.

Here is the input device read callback function :

void read_navkey(lv_indev_drv_t *indev, lv_indev_data_t *data)
{

    navkey.updateStatus();

    if (navkey.readStatus(i2cNavKey::CTRP))
    {
        data->state = LV_INDEV_STATE_PR;
        printf("Push\n");
    }
    else
    {
        data->state = LV_INDEV_STATE_REL;
        printf("Release\n");
    }

    int CounterValue = navkey.readCounterInt();
    if(CounterValue != 0)
    {
        data->enc_diff = CounterValue;
        printf("Diff: %d\n", data->enc_diff);

        // Reset the navkey rotary encoder to zero
        navkey.writeCounter(0);
    }

    if (navkey.readStatus(i2cNavKey::UPP))
    {
        // data->key = LV_KEY_UP;
        data->key = LV_KEY_PREV;

        printf("UP\n");
    }

    if (navkey.readStatus(i2cNavKey::DNP))
    {
        // data->key = LV_KEY_DOWN;
        data->key = LV_KEY_NEXT;
        printf("DOWN\n");
    }

    if (navkey.readStatus(i2cNavKey::RTP))
    {
        data->key = LV_KEY_RIGHT;
        printf("RIGHT\n");
    }

    if (navkey.readStatus(i2cNavKey::LTP))
    {
        data->key = LV_KEY_LEFT;
        printf("LEFT\n");
    }
}

I also have some questions :

  1. When using (for example) data->key = LV_KEY_PREV; as I do in the callback function, should it make the focus change to the previous object in the input device group (that I previously set) on the screen ?

  2. Should data->enc_diff = CounterValue; also work with LV_INDEV_TYPE_KEYPAD input device type set ?

  3. Should all data->key still work with LV_INDEV_TYPE_ENCODER input device type set ?

Thank you very much in advance for your help.

Anyone ? @kisvegabor maybe ? :pray:

Hi,

At first look read_navkey is fine.

Do you use groups in the UI? (It’s required for navigation with keys and encoder)

These are twp separete things:

  • data->enc_diff is for ENCODER only
  • data->key is for KEYPAD only

You can create 2 input devices for the 2 types without any issues.

I believe that you will need to create two separate Input devices @maventech: One being an Encoder to handle the Left/Right/Press and a second one being a KeyPad to handle LV_KEY_LEFT/RIGHT/UP/DOWN.

Once you have the two input devices you can then link both to the active group and the group will handle the input from both.

Hi.

@kisvegabor : Yes, I use the groups :

In the app_main :

...
InputDevice_Group = lv_group_create();
...
lv_indev_set_group(lv_navkey_indev, InputDevice_Group);
...

Then, in the Menu Loaded CallBack functions :

void MainMenuLoaded_CallBack(lv_event_t *event)
{
    printf("MainMenuLoaded_CallBack\n");
    lv_group_remove_all_objs(InputDevice_Group);
    lv_group_add_obj(InputDevice_Group, ui_TurnOff);
    lv_group_add_obj(InputDevice_Group, ui_Action);
    lv_group_add_obj(InputDevice_Group, ui_AddToList);
    lv_group_add_obj(InputDevice_Group, ui_RemoveFromList);
}

void ItemInfoMenuLoaded_CallBack(lv_event_t *event)
{
    printf("ItemInfoMenuLoaded_CallBack\n");
    lv_group_remove_all_objs(InputDevice_Group);
    lv_group_add_obj(InputDevice_Group, ui_Back);
    lv_group_add_obj(InputDevice_Group, ui_ActionMode);
}

I understand that data->enc_diff is for ENCODER only and data->key is for KEYPAD only. But shouldn’t the code related to KEYPAD work if I change the input device type to LV_INDEV_TYPE_KEYPAD (supporting only the keypad part of the navkey) ? Because it is not working (see details in my previous post).

Also, because there are input devices supporting both input types, wouldn’t be a great idea to support a “combined type” input device ?

@Gildons : I understand, but as I mentioned, nothing is working if I only use the keypad part of the navkey, defining it as a LV_INDEV_TYPE_KEYPAD device type.

I think I got it!

Assuming that i2cNavKey::CTRP means “click” of the encoder down, when you change your driver to act as a “KEYPAD” you are not setting the state of each key.

I think that, if you set it to KEYPAD, hold one of the keys down, and click the encode, you will probably get the key to be read (you will be feeding the state of the key with state of the encode).

If that is the case, and you want to be able to read all keys with multiple presses you will probably need to read one at time, set data->key and data->state for each key and tell the read callback that data->continue_reading until you have read all 4 keys. Check Input device interface — LVGL documentation

@Gildons You effectively got it !!! :slight_smile: :pray: :clap: :muscle: :hugs: :+1:

I don’t understand all the logic behind this for now, but you are right !!! Thank you so much !

I will adjust the code accordingly (I just did what you suggested by pressing the buttons for now).

I think that managing it as this (in read_navkey function) would work, right ?

        if (navkey.readStatus(i2cNavKey::DNP))
        {
            // data->key = LV_KEY_DOWN;
            data->key = LV_KEY_NEXT;
            data->state = LV_INDEV_STATE_PR;            
            printf("DOWN PRESSED\n");
        }

        if (navkey.readStatus(i2cNavKey::DNR))
        {
            // data->key = LV_KEY_DOWN;
            data->key = LV_KEY_NEXT;
            data->state = LV_INDEV_STATE_REL;            
            printf("DOWN RELEASE\n");
        }

I’m not sure about what you mentioned regarding data->continue_reading. But I will read more about this.

Do you think a “combined mode” (see my previous post) would be a feasible feature in LVGL ? (Why creating 2 input devices, when it can be managed with only 1 ?)

Glad it worked!

I think you still need two lv_indev_drv_t because changing the driver TYPE on the fly might not work, or at least not be ideal, but you can definitely try it.

The TL;DR is the read callback needs to know if each individual key is pressed or not, however it will call the read callback only once per read cycle

So if you have code like this:

        if (navkey.readStatus(i2cNavKey::DNP))
        {
            data->key = LV_KEY_NEXT;
            data->state = LV_INDEV_STATE_PR;            
        }
        else
        { 
            data->key = LV_KEY_NEXT;
            data->state = LV_INDEV_STATE_REL;
        }

        if (navkey.readStatus(i2cNavKey::UPP))
        {
            data->key = LV_KEY_PREV;
            data->state = LV_INDEV_STATE_REL;            
        }
        else
        { 
            data->key = LV_KEY_PREV;
            data->state = LV_INDEV_STATE_REL;
        }

and you have both Down and Up pressed, it will only register the status of UP, because the first if/else will be true, but the second one will also be true, overriding the values of key and state set by the first if/else.

I believe you can fix this issue by sending one pair of key/state at time, and set continue_reading until you are done sending the status of all keys (you can do that by having a counter for each time read_cb is called for example.