Crown behaviour not working from Micropython 1.27.0 with LVGL 9.3.0

I am using LVGL 9.3.0 with Micropython 1.27.0

I have a rotary encoder configured using lv.INDEV_TYPE.ENCODER and this works fine.

I cannot get crown behaviour working using lv.INDEV_TYPE.POINTER.
I am testing with a slider control that has focus.

Documentation for 9.3.0 says set data->diff to the encoder delta, but inspection of the c code shows that there is only data->enc_diff, so I am using this field. In micropython, dir(data) from within read_cb shows only the enc_diff field is present. Is this the problem?

9.3 code seems to be mismatched with docs here: Input Device (lv_indev) - LVGL 9.3 documentation

I have tried many things to get this working:

  • I have set indev rotary sensitivity to 256.
  • I have read back the slider rotary sensitivity and it is 256.
  • I have debug in my code to prove encoder deltas are being read in the pointer read_cb. Anyway, they work fine for encoder mode, just not pointer mode.
  • I have an event cb setup for ALL events on the slider. I see many events for prev, next, left, right and enter etc. I see no events when rotating my crown/pointer configured encoder.
  • I have tried using two encoders with one configured as encoder and one configured as crown. Clicking the the encoder button allows me to edit. In this state, rotating the crown still does not produce any events or change in the slider position. Again, I see many events on the slider for the encoder that is configured with lv.INDEV_TYPE.ENCODER.
  • I am setting the group correctly. Other controls set to the same group are working just fine. In fact this encoder when set to lv.INDEV_TYPE.ENCODER instead of lv.INDEV_TYPE.POINTER also works just fine.

Fundamentally:

  • Does 9.3.0 support crown behaviour?
  • Does a slider receive ROTARY events?
  • Can a slider widget be controlled by a crown wheel?

Code attached. I’m out of ideas. If anyone has a working example of crown wheel behaviour in either c or micropython (that works in 9.3.0), I am sure I could get my code working. 9.3.0 is the newest I can go to at the moment, as this is currently the latest lv_binding_micropython.

test.py (7.4 KB)

In LVGL 9.3, this is mostly a docs vs. reality issue, not your code.

In short, the way the docs say crown behavior should work in Micropython 1.27 / LVGL 9.3 bindings is not how it actually works.

longer version after looking into this more:

In C, internal pointer scroll logic handles lv.INDEV_TYPE.POINTER + crown.

that path depends on fields and flags that the Micropython binding doesn’t show or fully map

The documentation says that data->diff is no longer correct. Core uses enc_diff, but that only works for encoder-style navigation.

Sliders don’t get rotary or crown events directly. They only respond when they are driven by group focus and encoder indev.

Pointer indevs don’t make the same group navigation events (like LV_EVENT_KEY).

That’s why:

encoder mode is working

Pointer (crown) mode makes no noise at all.

No events, no slider movement, no matter how sensitive it is.

So, to answer your questions directly:

Does 9.3.0 support crown behavior? → yes in C, but not completely

Does Micropython binding work with it? → no, not completely

Can the crown control a slider? → only through ENCODER indev

a simple solution that everyone ends up using:

keep the crown as lv.INDEV_TYPE.ENCODER

Scale enc_diff yourself to make the crown feel smooth.

For now, don’t pay any attention to POINTER in Micropython.

Until lv_binding_micropython catches up with the 9.x input refactor, crown == encoder with better math. It’s not pretty, but it’s the only thing that works right now.

Thanks for the reply, and for looking into what is going on.

I have been experimenting with different user input methods. I have navigation (up, down, left, right, select) buttons and was hoping to use these in conjunction with an encoder. The reason I went down the crownwheel path was because I wanted to be able to alter the value of a control without hitting the encoder pushbutton both before and after using the encoder to make the value change. In my situation, this should be unnecessary, since I have other (button/keypad) controls that can perform the next/prev functions. So I’m guessing that there’s no simple way to use an encoder in this way. Am I correct?

At the moment, I have created a keypad indev for the encoder, where the callback returns key up and key down values, rather than encoder or crown wheel type data. See below:

            ...
            self.delta = 0
            self.indev_dial.set_read_cb(self.enc_keypad_read)
            self.indev_dial.set_type(lv.INDEV_TYPE.KEYPAD)    
            ...

    def enc_keypad_read(self, drv, data):
        if self.delta == 0:
            self.delta = self.enc_board.encoder.encoder_delta()

        if self.delta:
            if self.delta > 0:
                data.key = lv.KEY.UP
                data.state = lv.INDEV_STATE.PRESSED
                self.delta -= 1
            elif self.delta < 0:
                data.key = lv.KEY.DOWN
                data.state = lv.INDEV_STATE.PRESSED
                self.delta += 1
            data.continue_reading = True

Do you think the above is the closest I can get to crown wheel functionality with the current code version available? To be honest, it seems to be equivalent for what I have tested with so far. But perhaps I am settings myself up for later problems - I’m not sure, as I am new to lvgl.

Thanks again

Steve