lvgl-micropython / lvgl_micropython

LVGL module for MicroPython
MIT License
73 stars 24 forks source link

ESP-IDF not installing using documented build command #128

Closed MitchBradley closed 1 week ago

MitchBradley commented 2 weeks ago

Build system: Ubuntu 22.04.3 LTS under WSL Version of lvgl_micropython: da4f795

I deleted ~/.espressif because I have had version problems before and I wanted to start from scratch. Then

$ git clone https://github.com/lvgl-micropython/lvgl_micropython.git
$ cd lvgl_micropython
$ python3 make.py esp32 submodules clean mpy_cross BOARD=ESP32_GENERIC_S3 BOARD_VARIANT=SPIRAM_OCT DISPLAY=st7796 INDEV=gt911
Cleaning build....
Getting ESP-IDF build Environment
ESP-IDF version 5.2.0 is needed to compile
Please rerun the build using the command below...
"/usr/bin/python3 make.py esp32 submodules clean BOARD=ESP32_GENERIC_S3 BOARD_VARIANT=SPIRAM_OCT DISPLAY=st7796 INDEV=gt911"

The "python3 .." command that I used was copied from the README.md I reran the "using command below" command, and got exactly the same response.

So I tried doing the submodules first, with

python3 make.py esp32 submodules

That seemed to work, but a subsequent build with the rest of the line failed with

MICROPY_PY_BTREE is enabled but the berkeley-db submodule is not initialised.

which has already been reported.

kdschlosser commented 2 weeks ago

give me a few minutes to fix the problem.

kdschlosser commented 2 weeks ago

OH...

simply deleting the espressif directory is not going to work. You also need to remove the environment variables that are set.

kdschlosser commented 2 weeks ago

if you type in printenv into your wsl you will see them.

MitchBradley commented 2 weeks ago

Yeah, I did that too. Sorry for not mentioning it. I removed everything having to do with espressif and esp-idf from PATH

kdschlosser commented 2 weeks ago

I just pushed a commit that should fix the issue. Delete your clone and then clone the repo again.

MitchBradley commented 2 weeks ago

Great, thanks. I will try it after my current build finishes. Just to put this in context, I am a developer on the FluidNC CNC software project. Some of our users have been asking for a UI that runs on an Elecrow 7" HMI display with ESP32-S3. I figured I would do it with MicroPython instead of C++. I found an old binary that supports the board and got my code working, but figured that I should update to a less-ancient version before deploying it. So I am valiantly trying to get a build working for this board.

MitchBradley commented 2 weeks ago

Do you have a way I can donate to support your work? If so I will pass on some of the money that has been donated to our project.

MitchBradley commented 2 weeks ago

So far it is working with your fix. I like that you did it by deleting code. That is best kind of fix.

kdschlosser commented 2 weeks ago

I had made some changes recently so that needing to setup the submodules was automatic so it would no longer need to be done at the build command. I forgot about that section of code and having it collect the submodules instead of what it was doing.

so now with the way it is set up if you have ESP-IDF cloned and setup if the version is correct it will use that. If it's not correct then it will clone it and set it up. it should work if there is an already existing environment. The nice thing is it doesn't change the environment that you ran the build script from so as soon as the build script exits all of the environment settings don't stay. They aren't persistent anything that is created inside of the /espressif folder does stay.

MitchBradley commented 2 weeks ago

The build is failing with

CMake Error at /mnt/c/Users/wmb/Documents/GitHub/lvgl_micropython/lib/micropython/extmod/extmod.cmake:114 (message):
MICROPY_PY_BTREE is enabled but the berkeley-db submodule is not initialised.

I tried several different ways of issuing the build commands, each time from a fresh clone of the repo.

  1. If "mpy_cross" is on the command line, it builds mpy_cross but then stops, saying " No rule to make target 'mpy_cross'" That is consistent with the "IMPORTANT" section that says that mpy_cross is now automatic, but inconsistent with the fact that the README's example build command includes mpy_cross.
  2. This case
    $ git clone https://github.com/lvgl-micropython/lvgl_micropython.git
    $ cd lvgl_micropython
    $ python3 make.py esp32 clean BOARD=ESP32_GENERIC_S3 BOARD_VARIANT=SPIRAM DISPLAY=st7796 INDEV=gt911

    results in the berkeley-db submodule error

  3. This case
$ git clone https://github.com/lvgl-micropython/lvgl_micropython.git
$ cd lvgl_micropython
$ python3 make.py esp32 submodules clean BOARD=ESP32_GENERIC_S3 BOARD_VARIANT=SPIRAM_OCT DISPLAY=st7796 INDEV=gt911

also results in the aforementioned berkeley-db submodule error

kdschlosser commented 2 weeks ago

sorry about that, I fixed it in a branch and I forgot to fix that issue in the main branch.. It is fixed now.

MitchBradley commented 2 weeks ago

Thanks! I think I am very close to winning. I'm trying to make a display driver based on RGBBus. The examples that I see in this thread https://forum.lvgl.io/t/micropython-display-drivers-part-2/14131/52 are close, but there is a problem with display_bus.init() which appears to need more arguments than before. Could you point me to the source code for the RGBBus class so I can understand it in detail?

MitchBradley commented 2 weeks ago

Crap, I spoke too soon. Now I get

Including User C Module(s) from ../../../../../ext_mod/micropython.cmake
-- Configuring incomplete, errors occurred!
See also "/mnt/c/Users/wmb/Documents/GitHub/lvgl_micropython/lib/micropython/ports/esp32/build-ESP32_GENERIC_S3-SPIRAM_OCT/CMakeFiles/CMakeOutput.log".
Found User C Module(s): usermod_lcd_bus, usermod_lvgl, lvgl_interface, usermod_lcd_utils
CMake Error at /mnt/c/Users/wmb/Documents/GitHub/lvgl_micropython/lib/esp-idf/tools/cmake/component.cmake:313 (message):
Include directory
'/mnt/c/Users/wmb/Documents/GitHub/lvgl_micropython/lib/micropython/ports/esp32/managed_components/espressif__esp_io_expander/include'
is not a directory.
Call Stack (most recent call first):
/mnt/c/Users/wmb/Documents/GitHub/lvgl_micropython/lib/esp-idf/tools/cmake/component.cmake:481 (__component_add_include_dirs)
esp32_common.cmake:140 (idf_component_register)

The commands from a clean clone were:

$ python3 make.py esp32 submodules clean BOARD=ESP32_GENERIC_S3 BOARD_VARIANT=SPIRAM_OCT DISPLAY=st7796 INDEV=gt911 --flash-size=4
$ make.py esp32 clean BOARD=ESP32_GENERIC_S3 BOARD_VARIANT=SPIRAM_OCT DISPLAY=st7796 INDEV=gt911 --flash-size=4
MitchBradley commented 2 weeks ago

Oh, I think the RGBBus stuff must be in ext_mod/lcd_bus/esp32_src/rgb_bus.c

kdschlosser commented 2 weeks ago

OK I fixed the RGBDisplay driver so it should work like it used to.

Here is a code example.

from micropython import const  # NOQA
from i2c import I2C
import gt911
import lcd_bus
import task_handler
import lvgl as lv  # NOQA
import rgb_display

_WIDTH = const(800)
_HEIGHT = const(480)

# width * height * 2
_BUFFER_SIZE = const(76800)

_CTP_SCL = const(9)
_CTP_SDA = const(8)
_CTP_IRQ = const(4)

_SD_MOSI = const(11)
_SD_SCK = const(12)
_SD_MISO = const(13)

_LCD_FREQ = const(13000000)
_PCLK_ACTIVE_NEG = const(0)

_HSYNC_PULSE_WIDTH = const(10)
_HSYNC_BACK_PORCH = const(10)
_HSYNC_FRONT_PORCH = const(10)

_VSYNC_PULSE_WIDTH = const(10)
_VSYNC_BACK_PORCH = const(10)
_VSYNC_FRONT_PORCH = const(20)

_PCLK = const(7)
_HSYNC = const(46)
_VSYNC = const(3)
_DE = const(5)
_DISP = None
_BCKL = None
_DRST = None

I2C_BUS = I2C.Bus(
    host=1,
    scl=_CTP_SCL,
    sda=_CTP_SDA,
    freq=400000,
    use_locks=False
)

TOUCH_DEVICE = I2C.Device(
    I2C_BUS,
    dev_id=gt911.I2C_ADDR,
    reg_bits=gt911.BITS
)

_DATA15 = const(10)  # B7
_DATA14 = const(17)  # B6
_DATA13 = const(18)  # B5
_DATA12 = const(38)  # B4
_DATA11 = const(14)  # B3
_DATA10 = const(21)  # G7
_DATA9 = const(47)  # G6
_DATA8 = const(48)  # G5
_DATA7 = const(45)  # G4
_DATA6 = const(0)  # G3
_DATA5 = const(39)  # G2
_DATA4 = const(40)  # R7
_DATA3 = const(41)  # R6
_DATA2 = const(42)  # R5
_DATA1 = const(2)  # R4
_DATA0 = const(1)  # R3

bus = lcd_bus.RGBBus(
    hsync=_HSYNC,
    vsync=_VSYNC,
    de=_DE,
    pclk=_PCLK,
    data_pins=(
        _DATA0,
        _DATA1,
        _DATA2,
        _DATA3,
        _DATA4,
        _DATA5,
        _DATA6,
        _DATA7,
        _DATA8,
        _DATA9,
        _DATA10,
        _DATA11,
        _DATA12,
        _DATA13,
        _DATA14,
        _DATA15
    ),
    freq=_LCD_FREQ,
    hsync_front_porch=_HSYNC_FRONT_PORCH,
    hsync_back_porch=_HSYNC_BACK_PORCH,
    hsync_pulse_width=_HSYNC_PULSE_WIDTH,
    hsync_idle_low=False,
    vsync_front_porch=_VSYNC_FRONT_PORCH,
    vsync_back_porch=_VSYNC_BACK_PORCH,
    vsync_pulse_width=_VSYNC_PULSE_WIDTH,
    vsync_idle_low=False,
    de_idle_high=False,
    pclk_idle_high=False,
    pclk_active_low=_PCLK_ACTIVE_NEG,
)

buf1 = bus.allocate_framebuffer(_BUFFER_SIZE, lcd_bus.MEMORY_SPIRAM)
buf2 = bus.allocate_framebuffer(_BUFFER_SIZE, lcd_bus.MEMORY_SPIRAM)

display = rgb_display.RGBDisplay(
    data_bus=bus,
    display_width=_WIDTH,
    display_height=_HEIGHT,
    frame_buffer1=buf1,
    frame_buffer2=buf2,
    color_space=lv.COLOR_FORMAT.RGB565,
    rgb565_byte_swap=True
)

display.set_power(True)
display.init()
display.set_backlight(100)
indev = gt911.GT911(TOUCH_DEVICE)

# display.set_rotation(lv.DISPLAY_ROTATION._90)  # NOQA

scrn = lv.screen_active()
scrn.set_style_bg_color(lv.color_hex(0x000000), 0)

slider = lv.slider(scrn)
# slider.set_size(300, 75)
slider.set_size(500, 75)
slider.center()

task_handler.TaskHandler()
kdschlosser commented 2 weeks ago

also you don't need to use the clean build command. This is done automatically each time you compile. The only time you want to use that command is if something happens during a build and for whatever reason you keep on getting an error. Using the clean command will remove the directory forcibly not using make at all.

you mentioned using an RGB display but you have st7796 set as the display. You need to set the display as rgb_display in order for it to load the driver. You can optionally specify more than one display driver.

Now remember you DO NOT need to supply submodules or clean when compiling. This is handled automatically so those commands only get passed onto make when compiling. No reason to do that and have it do extra work when it is not needed. you don't need to use the mpy_cross command either as that is all done automatically as well.

$ python3 make.py esp32 BOARD=ESP32_GENERIC_S3 BOARD_VARIANT=SPIRAM_OCT DISPLAY=rgb_display DISPLAY=st7796 INDEV=gt911 --flash-size=4

The same holds true for the indev drivers as well.

kdschlosser commented 2 weeks ago

As a suggestion. If you are using an RGB display I recommend getting an S3 that has octal ram and also octal flash. If you have a board those is octal for both of those I can tell you how to overclock the SPI for the rem and the flash. It will go from 80mhz to an effective data clock of 240mhz. It's a hell of a speed increase. I do recommend adding a heat sink to the ESP32 chip on the board when overclocking the ram and flash.

kdschlosser commented 2 weeks ago

and if you want to see the API for the RGBBus there is a file in the root of the repo named lcd_bus.pyi. This is a python stub file and it will allow code completion and intellisense to work properly in an IDE. But you are able to view the file as well if you are like me and you like to view the code. While it is not the actual code because the lcd_bus module is not written in python it is written in C and it a lot harder to understand. The stub file give you a complete picture of the API.

MitchBradley commented 2 weeks ago

Thanks very much for all the info. I was hesitant to deviate from the commands specified in the README because nothing I tried was working. Being so new to this code base, I wanted to stick with the published example until I could get a clean build from that. Now I can start to explore based on your recommendations. It is great that clean is unnecessary. I hate wasting time with spurious recompiles.

Unfortunately, we are not out of the weeds yet. There is a typo in commit 211cc70 - there is a spurious line containing a single 'l' character that cause the build to stop with a cmake parse error. After removing that bad line, the build (with this revised line) gets farther but errors out later. Here is the command line:

python3 make.py esp32 BOARD=ESP32_GENERIC_S3 BOARD_VARIANT=SPIRAM_OCT DISPLAY=rgb_display INDEV=gt911 --flash-size=4

and here are the errors:

/mnt/c/Users/wmb/Documents/GitHub/lvgl_micropython/lib/micropython/ports/esp32/machine_hw_spi.c: In function 'machine_hw_spi_device_transfer':
/mnt/c/Users/wmb/Documents/GitHub/lvgl_micropython/lib/micropython/ports/esp32/machine_hw_spi.c:373:28: error: 'transaction' is a pointer; did you mean to use '->'?
373 |                 transaction.flags |= SPI_TRANS_MODE_DIO;
|                            ^
|                            ->
/mnt/c/Users/wmb/Documents/GitHub/lvgl_micropython/lib/micropython/ports/esp32/machine_hw_spi.c:375:28: error: 'transaction' is a pointer; did you mean to use '->'?
375 |                 transaction.flags |= SPI_TRANS_MODE_QIO;
|                            ^
|                            ->
/mnt/c/Users/wmb/Documents/GitHub/lvgl_micropython/lib/micropython/ports/esp32/machine_hw_spi.c:377:28: error: 'transaction' is a pointer; did you mean to use '->'?
377 |                 transaction.flags |= SPI_TRANS_MODE_OCT;
|                            ^
|                            ->
/mnt/c/Users/wmb/Documents/GitHub/lvgl_micropython/lib/micropython/ports/esp32/machine_hw_spi.c: In function 'machine_hw_spi_bus_make_new':
/mnt/c/Users/wmb/Documents/GitHub/lvgl_micropython/lib/micropython/ports/esp32/machine_hw_spi.c:441:9: error: unknown type name 'p_obj_tuple_t'; did you mean 'mp_obj_tuple_t'?
441 |         p_obj_tuple_t *quad_data_pins = MP_OBJ_TO_PTR(args[ARG_quad_pins].u_obj);
|         ^~~~~~~~~~~~~
|         mp_obj_tuple_t
/mnt/c/Users/wmb/Documents/GitHub/lvgl_micropython/lib/micropython/ports/esp32/machine_hw_spi.c:447:13: error: expected expression before ')' token
447 |             );
|             ^
/mnt/c/Users/wmb/Documents/GitHub/lvgl_micropython/lib/micropython/ports/esp32/machine_hw_spi.c:444:13: error: too many arguments to function 'mp_raise_msg'
444 |             mp_raise_msg(
|             ^~~~~~~~~~~~
In file included from /mnt/c/Users/wmb/Documents/GitHub/lvgl_micropython/lib/micropython/ports/esp32/../../../../micropy_updates/common/mp_spi_common.h:4,
from /mnt/c/Users/wmb/Documents/GitHub/lvgl_micropython/lib/micropython/ports/esp32/machine_hw_spi.c:31:
/mnt/c/Users/wmb/Documents/GitHub/lvgl_micropython/lib/micropython/py/runtime.h:260:15: note: declared here
260 | NORETURN void mp_raise_msg(const mp_obj_type_t *exc_type, mp_rom_error_text_t msg);
|               ^~~~~~~~~~~~
/mnt/c/Users/wmb/Documents/GitHub/lvgl_micropython/lib/micropython/ports/esp32/machine_hw_spi.c:450:27: error: request for member 'len' in something not a structure or union
450 |         if (quad_data_pins->len != 2) {
|                           ^~
/mnt/c/Users/wmb/Documents/GitHub/lvgl_micropython/lib/micropython/ports/esp32/machine_hw_spi.c:454:31: error: request for member 'len' in something not a structure or union
454 |                 quad_data_pins->len
|                               ^~
/mnt/c/Users/wmb/Documents/GitHub/lvgl_micropython/lib/micropython/ports/esp32/machine_hw_spi.c:459:51: error: request for member 'items' in something not a structure or union
459 |         data2 = (int)mp_obj_get_int(quad_data_pins->items[0])
|                                                   ^~
/mnt/c/Users/wmb/Documents/GitHub/lvgl_micropython/lib/micropython/ports/esp32/machine_hw_spi.c:459:62: error: expected ';' before 'data3'
459 |         data2 = (int)mp_obj_get_int(quad_data_pins->items[0])
|                                                              ^
|                                                              ;
460 |         data3 = (int)mp_obj_get_int(quad_data_pins->items[1])
|         ~~~~~
/mnt/c/Users/wmb/Documents/GitHub/lvgl_micropython/lib/micropython/ports/esp32/machine_hw_spi.c:465:21: error: 'ARG_data_pins' undeclared (first use in this function); did you mean 'ARG_octal_pins'?
465 |     } else if (args[ARG_data_pins].u_obj != mp_const_none) {
|                     ^~~~~~~~~~~~~
|                     ARG_octal_pins
/mnt/c/Users/wmb/Documents/GitHub/lvgl_micropython/lib/micropython/ports/esp32/machine_hw_spi.c:465:21: note: each undeclared identifier is reported only once for each function it appears in
/mnt/c/Users/wmb/Documents/GitHub/lvgl_micropython/lib/micropython/ports/esp32/machine_hw_spi.c:466:9: error: unknown type name 'p_obj_tuple_t'; did you mean 'mp_obj_tuple_t'?
466 |         p_obj_tuple_t *octal_data_pins = MP_OBJ_TO_PTR(args[ARG_octal_pins].u_obj);
|         ^~~~~~~~~~~~~
|         mp_obj_tuple_t
/mnt/c/Users/wmb/Documents/GitHub/lvgl_micropython/lib/micropython/ports/esp32/machine_hw_spi.c:472:13: error: expected expression before ')' token
472 |             );
|             ^
/mnt/c/Users/wmb/Documents/GitHub/lvgl_micropython/lib/micropython/ports/esp32/machine_hw_spi.c:469:13: error: too many arguments to function 'mp_raise_msg'
469 |             mp_raise_msg(
|             ^~~~~~~~~~~~
/mnt/c/Users/wmb/Documents/GitHub/lvgl_micropython/lib/micropython/py/runtime.h:260:15: note: declared here
260 | NORETURN void mp_raise_msg(const mp_obj_type_t *exc_type, mp_rom_error_text_t msg);
|               ^~~~~~~~~~~~
/mnt/c/Users/wmb/Documents/GitHub/lvgl_micropython/lib/micropython/ports/esp32/machine_hw_spi.c:476:28: error: request for member 'len' in something not a structure or union
476 |         if (octal_data_pins->len != 6) {
|                            ^~
/mnt/c/Users/wmb/Documents/GitHub/lvgl_micropython/lib/micropython/ports/esp32/machine_hw_spi.c:480:32: error: request for member 'len' in something not a structure or union
480 |                 octal_data_pins->len
|                                ^~
/mnt/c/Users/wmb/Documents/GitHub/lvgl_micropython/lib/micropython/ports/esp32/machine_hw_spi.c:485:52: error: request for member 'items' in something not a structure or union
485 |         data2 = (int)mp_obj_get_int(octal_data_pins->items[0])
|                                                    ^~
/mnt/c/Users/wmb/Documents/GitHub/lvgl_micropython/lib/micropython/ports/esp32/machine_hw_spi.c:485:63: error: expected ';' before 'data3'
485 |         data2 = (int)mp_obj_get_int(octal_data_pins->items[0])
|                                                               ^
|                                                               ;
486 |         data3 = (int)mp_obj_get_int(octal_data_pins->items[1])
|         ~~~~~
/mnt/c/Users/wmb/Documents/GitHub/lvgl_micropython/lib/micropython/ports/esp32/machine_hw_spi.c: In function 'machine_hw_spi_bus_initilize':
/mnt/c/Users/wmb/Documents/GitHub/lvgl_micropython/lib/micropython/ports/esp32/machine_hw_spi.c:554:9: error: 'self' undeclared (first use in this function)
554 |     if (self->dual) flags |= SPICOMMON_BUSFLAG_DUAL;
|         ^~~~
[1360/1956] Building C object esp-idf/main_esp32s3/CMakeFiles/__idf_main_esp32s3.dir/__/modespnow.c.obj        
ninja: build stopped: subcommand failed.
MitchBradley commented 2 weeks ago

It seems like there is some sort of hot-patching going on because my attempts to fix the compile problem by changing "p_obj_tuple_t" to "mp_object_tuple_t" seem to be overwritten during the build. Furthermore there are several files in that directory that show diffs from the checked-out version of the submodule.

kdschlosser commented 2 weeks ago

ok sir, give it a try.

kdschlosser commented 2 weeks ago

yes there is hot patching that is taking place. There are a lot of thing the build script modifies in MicroPython to allow everything to work as it does.

kdschlosser commented 2 weeks ago

I literally just added/removed some features and I have not yet had the chance to update the README file. I am sorry for that.

I have not made a release yet because I am still trying to hammer out the kinks in some places and also add more functionality, correcting some of the shortfalls in MicroPython and also in LVGL.

MitchBradley commented 2 weeks ago

You have done an amazing job wrangling an almost-intractable problem. I see that the patches are in micropy_updates . I am slowly learning where to find things in this large and impressive code base.

MitchBradley commented 2 weeks ago

We have a winner:

ESP-ROM:esp32s3-20210327
Build:Mar 27 2021
rst:0x1 (POWERON),boot:0x8 (SPI_FAST_FLASH_BOOT)
SPIWP:0xee
mode:DIO, clock div:1
load:0x3fce3820,len:0x105c
load:0x403c9700,len:0x4
load:0x403c9704,len:0xbd8
load:0x403cc700,len:0x2e30
entry 0x403c989c
LVGL MicroPython 1.23.0 on 2024-09-21; Generic ESP32S3 module with Octal-SPIRAM with ESP32S3
Type "help()" for more information.
>>>

Thanks very much for the fixes.

kdschlosser commented 2 weeks ago

The code base is as large as it is so any of the requirements can easily be updated. It is also written to provide flexibility for the advanced user while still being a single command build system. I also wrote it so there is a common API across the drivers. providing a driver framework to make writing drivers really easy to do without sacrificing the ability to add onto or change the built in drivers. lots of display board will have different init commands depending on the actual screen that is attached to the driver IC. I wanted to provide the mechanics for a user to be able to add their own init command or to extend the ones that already exist. I did it in a manner that uses as little memory as possible by placing the initialization sequences on the stack instead of in heap.

some day I am going to get into the binding code generator and clean that up and make it more memory efficient by flattening the API so it is more like the C API. The only thing I wish was different about MicroPython is how it handles storing names for things like function parameters. I wish it would deal with them as strings instead of having them stored in a memory resident database of sorts.

MitchBradley commented 2 weeks ago

I understand. It is large because it is doing a lot of things.

Right now I am adding rgb_display_framework via FROZEN_MANIFEST=... . I wonder if it should be included by default, since rgb_display depends on it?

MitchBradley commented 1 week ago

rgb_display.py has

    color_space=lv.COLOR_FORMAT.RGB888,  # NOQA

but lv is not defined in that context. I changed it to color_space=15, # lv.COLOR_FORMAT.RGB888, # NOQA

kdschlosser commented 1 week ago

both of those are now fixed.

MitchBradley commented 1 week ago

Great, thanks. I am currently having trouble getting the screen to turn on, but I am confident that I can solve it. Thanks to your assistance, I now know my way around the code base well enough to trace through the relevant driver files, and I have the requisite hardware and software debugging skills to track it down. The ability to compile an up-to-date version and customize it is a game-changer for my project.

MitchBradley commented 1 week ago

Too easy. The only problem was that the backlight pin wasn't defined. Adding to the RGBDisplay constructor made the display light up.

MitchBradley commented 1 week ago

My collaborator on this project just cloned your repo and did the build from MacOS. Worked perfectly. I'm going to close this ticket with many thanks. Please tell me how I can support your efforts.

MitchBradley commented 1 week ago

More kudos - my collaborator just said this:

I built a macOS version (and geez was it simple). I had to make it executable and export a Dynamic Library path
$ python3 make.py macOS
...
$ chmod +x /Users/mark_hoy/git/lvgl_micropython/build/lvgl_micropy_macos
$ export DYLD_LIBRARY_PATH=/opt/homebrew/lib
$ /Users/mark_hoy/git/lvgl_micropython/build/lvgl_micropy_macos
LVGL MicroPython 1.23.0 on 2024-09-22; darwin [GCC 4.2.1] version
Use Ctrl-D to exit, Ctrl-E for paste mode
MitchBradley commented 1 week ago

For reference, here is crowpanel7_init.py - display and touch initialization for an Elecrow "Crowpanel 7 HMI Display": crowpanel7_init.txt

kdschlosser commented 1 week ago

I am guessing that you are liking the simplicity of using this library. Sorry about the couple of hurdles we had to jump with the ESP32. You just happen to pick the day to use it which was right after I made a bunch of updates that still needed to have the glitches ironed out.

Thank you for reporting the issues and helping with the testing to get those glitches taken care of.

I did want to mention that I have been working on ma pretty wild change to the RGB bus driver. I am trying to eliminate the need to have a stall in the program to wait for the buffer to finish transmitting. The change should also allow sor small updates to the UI to not need a full redraw and LVGL is not going to have to copy buffer data from one to another. This would be handled by the bus driver using the second core on the ESP32 to handle the copying.. To make this all work would require additional memory use, how much additional would be determined by the user.

There would be 2 frame buffers that are the size of the display. These buffers do not get handed off to LVGL. They stay internal to the bus driver. There is a 3rd frame buffer that is a partial buffer. This is what the user is able to decide on how large they want to make it. Rotation is handled when the partial buffer gets copied to the idle frame buffer. When the idle frame buffer changes to being the active one the data gets sent from it to the display using DMA. While that buffer is being transmitted to the display the second core is copying the data from the buffer to the now idle frame buffer. The user code is able to continue to run on core 0. The partial buffer should be small enough so that it is able to reside in SRAM so writing to that buffer is not going to effect the transfer speed.

kdschlosser commented 1 week ago

How are the display updates using a display that is 800 x 480 resolution? I have been curious about how the performance is...

Another thing is if you want to improve scrolling performance set the indev driver to high priority. This is done by using...

indev = gt911,GT911(...)
indev.enable_input_priority()
MitchBradley commented 1 week ago

The process of working through the glitches was actually helpful for me, forcing me to look at a lot of places in the code. I feel like I know the basic landscape now so I can find stuff.

The display performance is okay for my application which is mostly static with a few overlays. You can almost see the drawing speed when the overlays go up and down, but it is not objectionable. Your buffering ideas sound nice. For a previous similar project I used LovyanGFX with off-screen drawing canvases that were splatted to the screen with a reasonably fast SPI interface. It worked pretty well on a tiny screen.

The indev priority thing seems to improve the touch responsiveness, which is a big plus. Here is what my main screen looks like, with an overlay. It is control surface for CNC with big dumb buttons that are easy to hit when you are in the workshop. After I get the functionality nailed down, I'll let some stylists have at it to make the palette more tasteful.

image

kdschlosser commented 1 week ago

The indev priority thing seems to improve the touch responsiveness, which is a big plus.

This was the intention of adding that specific feature. You would really notice a difference if scrolling something on the display. I personally hate it when a UI feels laggy when using it. This is even more compounded if there is nothing that is being done that should cause the UI to be laggy. I consider user input to be of the highest priority. This passes the feeling of something being made to a higher quality and the software is written better with resources being managed better.

I have been updating the code and adding little things like this to improve the users interactions. Python is a high level programming language which by default makes it run slow. However it's ease of use and it's ability to allow fast prototyping is what makes it a fantastic language to use. With respect to LVGL I have taken it one step further and provided the ability to prototype on a desktop and that exact same code is able to be moved onto an MCU and it will run..

Here is an example of that..

from micropython import const  # NOQA

import sys
import lcd_bus

_WIDTH = const(800)
_HEIGHT = const(480)

_BUFFER_SIZE = const(768000)  # width * height * 2

if sys.platform in ('linux', 'darwin'):
    backlight_pin = None

    import sdl_display
    import sdl_pointer

    STATE_PWM = sdl_display.STATE_PWM

    TOUCH_DEVICE = None
    indev_driver = sdl_pointer.SDLPointer
    display_driver = sdl_display.SDLDisplay
    bus = lcd_bus.SDLBus(flags=lcd_bus.SDLBus.WINDOW_BORDERLESS)

else:
    backlight_pin = 2

    import rgb_display
    import gt911

    from i2c import I2C

    display_driver = rgb_display.RGBDisplay
    STATE_PWM = rgb_display.STATE_PWM

    # For reference, the I2C IRQ is on pin 38
    I2C_BUS = I2C.Bus(
        host=1,
        scl=20,
        sda=19,
        freq=400000,
        use_locks=False
    )

    TOUCH_DEVICE = I2C.Device(
        I2C_BUS,
        dev_id=gt911.I2C_ADDR,
        reg_bits=gt911.BITS
    )

    indev_driver = gt911.GT911

    bus = lcd_bus.RGBBus(
        hsync=39,
        vsync=40,
        de=41,
        pclk=0,
        data0=15,
        data1=7,
        data2=6,
        data3=5,
        data4=4,
        data5=9,
        data6=46,
        data7=3,
        data8=8,
        data9=16,
        data10=1,
        data11=14,
        data12=21,
        data13=47,
        data14=48,
        data15=45,
        freq=12000000,  # The display creeps if this is too fast
        hsync_idle_low=False,
        hsync_front_porch=40,
        hsync_pulse_width=48,
        hsync_back_porch=40,
        vsync_idle_low=False,
        vsync_front_porch=1,
        vsync_pulse_width=31,
        vsync_back_porch=13,
        de_idle_high=False,
        pclk_idle_high=False,
        pclk_active_low=1,
        disp=-1,
    )

import lvgl as lv  # NOQA

display = display_driver(
    data_bus=bus,
    display_width=_WIDTH,
    display_height=_HEIGHT,
    backlight_pin=backlight_pin,
    backlight_on_state=STATE_PWM,
    color_space=lv.COLOR_FORMAT.RGB565,
    rgb565_byte_swap=False
)

display.set_power(True)
display.init()
display.set_backlight(100)
# display.set_rotation(lv.DISPLAY_ROTATION._90)

indev = indev_driver(TOUCH_DEVICE)

import task_handler

task_handler.TaskHandler()

Now you would have to write your own classes that would handle accessing hardware specific things like IO pins and have it read a file that you have created that would mimic the data that is expecting to be seen on specific pins. Not terribly hard to do to put together a desktop prototype environment. You can set the amount of memory that is available to MicroPython so it matches what is available on the MCU. This allows the desktop environment to be as close as possible to be on par with what the MCU environment is like.

One thing I am wanting to add is the ability to throttle the speed to be in closer alignment with what an MCU is, That's going to be a while before I add it.

MitchBradley commented 1 week ago

I have been doing it exactly like you say - prototyping on the host OS then moving to the MCU. My code has this at the beginning:

try:
    import sdl_init
except:
    import crowpanel7_init

The filesystem on the MCU doesn't have the sdl_init.py file. Detailed emulation of the MCU environment is often unnecessary, since it is easy enough to just use the MCU when you need to "get real". The time difference between testing on the host and testing on the MCU is only a few seconds - the time it takes to upload a file to the MCU's file system with Thonny. As a result, there isn't a huge motivation to emulate the MCU too accurately. The host simulation is very convenient for working out the UI details, though.

On another topic, I am trying to use LVGL themes to make it easier to play with stylistic variants. The sparseness of the theme documentation, the shortage of examples, and the clunkiness of the interface makes me think that the themes feature is rarely used and barely maintained. Is that correct?

kdschlosser commented 1 week ago

I have never used themes. I create a style and that style gets shared across the different UI elements I create.

MitchBradley commented 1 week ago

Yeah, I pretty much decided that LVGL themes are not worth the complexity, so I just did it the straightforward way and moved on to the next task.

MitchBradley commented 1 week ago

my collaborator has a Mac and is having problems getting lvgl micropython to display anything. Is that expected, or is he just doing something wrong?

kdschlosser commented 1 week ago

on the Mac you need to have the SDL2 dynamic library in the same folder as the micropython binary.

kdschlosser commented 1 week ago

I did want to mention that you can subclass the different objects. This allows you to apply styles in an easier manner. as an example...

import lvgl as lv

class BorderMixin:

    def __init__(self, _):
        self.set_style_border_color(lv.color_hex(0xFF0000), 0)
        self.set_style_border_opa(255, 0)

class BackgroundMixin:

    def __init__(self, _):
        self.set_style_bg_color(lv.color_hex(0x00FF00), 0)
        self.set_style_bg_opa(255, 0)

class SliderWidget(lv.slider, BackgroundMixin, BorderMixin):

    def __init__(self, parent=None):
        super().__init__(parent)