Support for Python3

Re lv_binding_micropython Github issue #13: Support for regular python

I’m interested in an lvgl Python3 module for use with the Onion Omega2. It has a Linux OS and supports framebuffers as well as full Python3.

Is it feasible to modify the existing lv_binding_micropython generator to produce Python3 bindings? How different would the Python3 bindings be?

I’ve taken a look at https://github.com/rreilink/pylvgl but it looks like there’s been no development since May.

@amirgon would you be interested in collaborating?

Hi @Lazar_Demin!

Under the hoods, Python and Micropython have several significant differences.
The mechanism for adding a native C modules is very different.
The memory management is different. Python relies on reference counting while Micropython uses Garbage Collection.
So, while the front end (python syntax) is very similar, the backend is very different.

My point is - I don’t think you can “convert” the Micropython binding to Python binding without re-designing and re-writing significant parts of it.

My focus is Micropython and it aligns well with LittlevGL, both were designed to work well on resource constrained devices such as microcontrollers.

As far as I know, @rreilink’s work is the most advanced attempt to create Python binding for LittlevGL. His bakcend is different but there was a time when he and I tried to collaborate and create a unified front-end. Unfortunately, it looks like @rreilink is busy with other things and didn’t make much progress with the Python binding for many months.
There are several open questions regarding the python binding, for example, how to manage memory and how to handle access to structs and fields. While on Micropython these are implemented and are working well, I’m not sure what is the best way to implement those for Python.

Did you consider using Micropython?

The Omega2 is a powerful device, but still constrained by RAM and Flash.
You could try the unix (Linux) port of Micropython on the Omega2, it could probably be an interesting project with a lot of potential!

Oh, and one last thing I forgot to mention, if you are worried about Micropython API to other modules with C API (other than LittlevGL) - you can also use the micropython bindings to interact with any other C library! (under certain assumptions).

More details here:


https://blog.littlevgl.com/2019-08-05/micropython-pure-display-driver#micropython-api-to-any-c-library

Thanks for the detailed response! I’ve tried out the linux micropython port on the Omega2, it looks like it’s stable and very usable. Especially with the micropython-lib package that adds a whole suite of standard libraries.

Based on the work you’ve done, how much additional work would be involved in creating a standalone micropython littlev module? Would you be interested in working on this with the Onion team?

Btw, we have hardware prototypes ready for testing. We can send you a beta unit if you’re interested.
demo-lvgl-0

Yes… but take into account that many of these libraries have limited functionality and some of them are even just placeholders to prevent an error when you try to import them (but not when you try to use them).
It doesn’t get close to the “real” Python standard library.

Don’t expect a random python script which has some standard Python imports to “just run” on Micropython.

If I understand correctly, you already successfully ran both lvgl and micropython on Omega2. Is that correct?

If that’s the case, it should be pretty simple to create a micropython littlevgl module.
Did you try building lv_micropython? (try branch dev-6.1, that’s the closest to micropython’s upstream)
Did you have any problem building/running lv_micropython?

How “open” are your hardware and software? What license do you use?
When I contribute on my own free time, I limit myself to open projects such as lvgl (which has “MIT” license)

If your long term policy is all-embracing open hardware and software - I’ll be happy to help and collaborate!

That’s right, we’ve been able to run C lvgl and micropython on the Omega2.

From my understanding it looks like lv_micropython builds a full micropython runtime that has an lvgl module built-in.

Our preference would be to stick with the already available micropython package for OpenWRT and load lvgl as an external module. That’s where we could use your help since we’re new to micropython.

The intention of this project is to provide a reference design for display applications with the Omega2. So the schematics and layout of the hardware will be open source, as will any work done with drivers and application software.

Generally, we use GPLv3 but are open to other licenses.

I don’t think you could load lvgl binding as an external module without rebuilding micropython.
I had to do some small but important changes in micropython itself.

For example, lvgl uses micropython’s Garbage Collector for memory management, so I had to add lvgl gc-root pointers to micropython (see MICROPY_PORT_ROOT_POINTERS).
External modules don’t support Micropython gc.

Another example is a change I tried to push to micropython but unfortunately didn’t succeed yet, so I had to do it on my micropython fork.

btw, in micropython External C Modules feature don’t mean you can load the module on runtime like you load a shared library. What it means is that you can add your C files at some location and build micropython without manually writing the Makefile. So you would have to build your OpenWRT micropython project anyway if you want to add anything.

Remember that micropython was primarily designed for embedded platforms where you usually don’t load code dynamically, like you do in Linux.

What you can do, is build a fork of your OpenWRT micropython package with lvgl binding included.

What about other projects? What about the entire Onion policy?
I took a look at your website and couldn’t find any information about hardware/software licensing, or about what is supposed to be open and what is not.
I found, however, a few troubling posts on the forum.

I think that what’s missing is clarity.
What is the company’s policy and intentions regarding licensing? What parts are GPL? What parts are MIT? What parts are proprietary?

Thanks for the advice, I forked lv_micropython and lv_binding_micropython, made some adjustments, and compiled it for the Omega2.

Displaying lvgl objects works great, but I haven’t been able to get touch input detection to work at all. I press on a button and the style doesn’t change nor does the registered callback fire.

A few points worth mentioning:

  • Confirmed that the correct x and y coordinates were being read using a print statement
  • Confirmed that the mp_lv_task_handler function runs on an interval
  • We know the touch hardware and software work
    • We had previously tested lvgl in C and wrote a driver for the XPT7603 touch input device used on the Omega2 Dash (see file demo/littlev-demo/lv_drivers/indev/XPT2046.c in that repo)

It just seems like there’s some disconnect between the input and the displayed components.
Do you have any advice for where to look or what to investigate? It would be much appreciated.

Agreed, we need to be more clear about our licensing. This is something we’re slowly working towards.

The post on the forum that you mentioned was from early 2017. Back then the entire team was focused on completing fulfillment for the Omega2 crowd-funding campaigns - our logistics and shipping weren’t as built-out as they are now.
When we moved the technical team back to the product side, the build system was been open sourced and remains that way: https://github.com/OnionIoT/source

A brief overview of our licensing:

  • The build system is based on OpenWRT and is open source under the GPLv2 license
  • The only proprietary software in the firmware is the wifi driver
  • On the hardware side, the Omega2 modules are proprietary, all other hardware is open source under the MIT license

Which touch driver do you use? How did you register it?
I have an example of a pure micropython XPT2046 touch driver that works well for me, you can use it as a reference if you want.

Basically, you register a touch driver like this:

        indev_drv = lv.indev_drv_t()
        lv.indev_drv_init(indev_drv)
        indev_drv.type = lv.INDEV_TYPE.POINTER
        indev_drv.read_cb = self.read
        lv.indev_drv_register(indev_drv)

You can do it in C or in Micropython.

Try to put some printing in the your read callback in your driver implementation. It’s supposed to be called periodically by lvgl. Is it called? What values does it set for data.point and data.state?


Thanks for the clarification.

I added a C driver for the xpt7603 device to our fork of lv_binding_micropython. See the driver file here, it’s based it on the existing driver/esp32/modxpt2046.c in the same repo, just modified to work with the I2C based XPT7603.

I had tested the xpt7603 code with a C lvgl program and it worked as expected.

For registering, I followed the available examples and register the touch driver in the micropython program:

import xpt7603
touch = xpt7603.xpt7603()
touch.init()

indev_drv = lv.indev_drv_t()
lv.indev_drv_init(indev_drv) 
indev_drv.type = lv.INDEV_TYPE.POINTER;
indev_drv.read_cb = touch.read;
lv.indev_drv_register(indev_drv);

The full test script is on Github.

Good idea. I do see valid touch coordinates being read on a consistent interval.
Here is an example of when I first press:

xpt7603_read data: x = 65, y = 49, valid = 1, state = 1

And when I release:

xpt7603_read data: x = 65, y = 49, valid = 0, state = 0

Where state is enum { LV_INDEV_STATE_REL = 0, LV_INDEV_STATE_PR };

So it looks like it should be ok. But I have a button in the script at coordinates 50, 50, and the callback attached to it never fires.

I’ll try out a pure micropython xpt7603 implementation like you suggested. Do you have any pointers or advice in the meantime?

I don’t know how big your button is right now, but if it’s pretty small, you could try making it bigger and see if you can click it at all.

If it’s at least as large as your finger, though, that probably won’t help.

That is strange.
You are saying that your driver’s read callback is called by lvgl, updates data with valid coordinates and state - but still, nothing happens on the GUI.

Whatever problem it is, I don’t think it’s related directly to Micropython. Perhaps @kisvegabor or @embeddedt would have an idea what that could be.

Maybe it’s not the touch, but the display driver? Or the combination of both?
Did you connect both the display and the touch driver to the same SPI bus? This is possible, (for example, my pure micropython ILI9341 driver can work with pure micropython XPT2046 on the same SPI bus), but if I remember correctly, the original C code of the XPT and ILI drivers was not designed to work on the same bus on the ESP32.

Maybe lvgl changes the display after the touch, but the display driver is stuck or malfunctions and cannot show the change?
It’s very easy to check - just put some animation and see if it keeps moving while you are touching the screen.

According to @Lazar_Demin, the same driver worked fine with a C application. That makes me think it is an issue between C and Python somewhere.

I think you should always be returning false from the xpt7603_read callback, and thus you should rename valid to something more accurate like pressed.

@Lazar_Demin Where do you call lv_task_handler periodically? Maybe it’s not called?

You can verify it if you display an animation and make sure if it keeps moving when you are on the infinite loop at the bottom of your script.

You can also try to call it explicitly on that loop

print('starting loop')
while True:
    lv.task_handler()

I’m using the linux framebuffer (with the existing modfb from lv_binding_micropython) for display and the touch input is I2C-based. So can’t be clashing.

lv_task_handler is being called by modfb using mp_sched_schedule. I confirmed that it’s running with a print statement in the C code.

Tried calling it explicitly, didn’t make a difference.

Great suggestion! The animation freezes while I’m touching the screen:

Any idea why this could be happening even though I’ve confirmed the lv_task_handler function is being run periodically?

Is there something that could be making the xpt7603_read callback blocking?
Should the xpt7603_read callback always return false, like @embeddedt suggested?

Yes it can be related to @embeddedt comment.
When read returns “true” it means that there is more data to be sent, false means no more data, so in general you almost always want to return false from read.

1 Like

I’m 99% sure you should always return false from that function. Otherwise, LittlevGL will freeze (as you can see from the first and last lines of this code snippet).

Happy new year everyone! I had some time to revisit this and changed the xpt7603_read callback to always return false

Good news: it resolved the blocking issue I was seeing before. I can touch the screen and animations will still do their thing:

Bad news: touch input still isn’t properly propagating to the rest of lvgl.
My test script has a button with a callback attached and the callback never gets called no matter what I do.

Appreciative of any ideas and pointers

If you uncomment this printf, what coordinates do you see?

Could you try reading coordinates from your script as well?
Something like:

p = lv.point_t()
lv.indev_get_point(lv.indev_get_act(), p)
print("x=%d, y=%d" % (p.x, p.y))