Standalone binary .mpy module for lvgl (so we can "just use it" in micropython)

I have had my head in the sand the last 3 weeks developing a driver and architecture that do most of these things, including compiling as a USER_C_MODULE, using DMA and non-blocking. I currently only have it implemented for ESP32 targets, but the C code for ESP32-specific calls is separated from the common code, so adding other targets should be simplified. Most of the plumbing is essentially the same as @kdschlosser’s implementation because they are both based on ESP_LCD in ESP-IDF. The difference is my approach is essentially a replacement for framebuf.Framebuffer like @andrewleech mentioned that will compile in bare Micropython yet have functions exposed for allocating DMA buffers, blitting to the display, and registering a callback function to be run when the blit is complete, so they will work with LVGL or any other graphics library that can call blit. The buffers can be full screen (resources permitting) or partial. They remove lv_micropython’s dependency on ESP-IDF! I haven’t tried compiling as an .mpy yet, but I don’t see why that wouldn’t work since they compile as a USER_C_MODULE now.

I wrote a helper Devices class in python that makes it easy to attach non-LVGL drivers for display, touch and rotary encoders to be used with LVGL. This is awesome because we can use ANY Micropython touchscreen driver that has a function to read the touch coordinates (which is all touchscreen drivers). Likewise with rotary encoders–we need to know the function that reads the position and the function that reads the button pin value and that’s it. Now we’ve got a lot bigger developer base writing drivers because they don’t have to be LVGL specific! I think the Devices class, or some variation of it, may help in standardizing an API and facilitate generic naming like @gitcnd mentioned. Here’s an example of a complete display_driver.py (or whatever you would name it, using that name for historical purposes and because most lvmp examples use that name to load the drivers):

from lvmp_devices import Devices
from mpdisplay import allocate_buffer
from display_config import display_drv
from i2c_config import touch_drv  # Add ', rtc'  or any other I2C devices on the same bus
from encoder_config import enc1_funcs, enc2_funcs

devices = Devices(
    display_drv = display_drv,
    bgr = True, # for LVGL 8 only.  LVGL 9 removed lv.COLOR_FORMAT.NATIVE_REVERSE
    factor = 10,
    blit_func = display_drv.blit,
    alloc_buf_func = allocate_buffer,
    reg_ready_cb_func = display_drv.register_cb,
    touch_read_func = touch_drv.get_positions,
    touch_rotation = 5,
    enc_funcs = [enc1_funcs, enc2_funcs],
    )

What’s more, if you have a second display and touchscreen, you just do the same thing, like:

devices2 = Devices(...

usage could be either

import display_driver

or better yet

from display_driver import devices
devices.list() # not required, lists all the devices and functions setup by Devices

Since Devices takes care of lv.init and starting the eventloop if that hasn’t already been done, the above display_driver.py is complete. Since the class is created in python rather than C, other hardware devices that aren’t LVGL specific can be tacked on to the devices object, which is what I do with the external RTC on one of my displays since the driver for it is loaded at the same time as the touchscreen driver in i2c_config.py.

devices.rtc = rtc

Please take a look at the Display Driver announcement.