Open kdschlosser opened 2 months ago
I just pulled the latest version (commit ee1bdbcbbb86ecb1d627f347d2032857d214b3d6 ) from github. For non SPIRAM it comples fine, but when using SPIRAM I get the IRAM error:
python3 make.py esp32 submodules clean mpy_cross BOARD=ESP32_GENERIC BOARD_VARIANT=SPIRAM DISPLAY=ili9341 INDEV=xpt2046 LV_CFLAGS="-DLV_COLOR_DEPTH=16"
with this error:
cat /home/stefan/projects/esp32/lvgl_micropython/lib/micropython/ports/esp32/build-ESP32_GENERIC-SPIRAM/log/idf_py_stderr_output_14003
make[1]: warning: -j6 forced in submake: resetting jobserver mode.
/home/stefan/.espressif/tools/xtensa-esp-elf/esp-13.2.0_20230928/xtensa-esp-elf/bin/../lib/gcc/xtensa-esp-elf/13.2.0/../../../../xtensa-esp-elf/bin/ld: micropython.elf section `.iram0.text' will not fit in region `iram0_0_seg'
/home/stefan/.espressif/tools/xtensa-esp-elf/esp-13.2.0_20230928/xtensa-esp-elf/bin/../lib/gcc/xtensa-esp-elf/13.2.0/../../../../xtensa-esp-elf/bin/ld: IRAM0 segment data does not fit.
/home/stefan/.espressif/tools/xtensa-esp-elf/esp-13.2.0_20230928/xtensa-esp-elf/bin/../lib/gcc/xtensa-esp-elf/13.2.0/../../../../xtensa-esp-elf/bin/ld: region `iram0_0_seg' overflowed by 3128 bytes
collect2: error: ld returned 1 exit status
make[3]: *** [CMakeFiles/micropython.elf.dir/build.make:5218: micropython.elf] Error 1
make[2]: *** [CMakeFiles/Makefile2:2065: CMakeFiles/micropython.elf.dir/all] Error 2
make[1]: *** [Makefile:136: all] Error 2
Is there a simple fix for this IRAM overflow error?
do you get this error if you compile vanilla MicroPython 1.23 as well?? I think this issue might be upstream and if it is I will open a PR there.
I will clone MicroPython and see if there is an issue. Not a big deal to do,
That would be great. I just compiled vanilla python 1.24. Works fine with native installed esp-idf 5.0.4
OK so it does compile for vanilla MicroPython. I wonder if there is not enough IRAM for the esp_lcd component to run. I will ask the folks over at MicroPython about squeezing more space. I am pretty sure that is what the issue is. It may end up being a case of the ESP32 not being supported or I am going to have to write custom drivers possibly. we will see what they say over there.
Have you tried with SPIRAM support. Without SPIRAM support your code compiles fine.
with SPIRAM the code size is larger and there is more that is placed into IRAM.
I just pushed a commit which takes the keyboard exception and scheduling exceptions and places them into flash instead of IRAM. See if that does the trick for ya.
3k in space is not a whole lot of space to try and free up. I don't know how much IRAM a function uses up and I am hoping that between the 2 things I moved it solves the issue.
Basically what I am doing is this...
This code block is in mpconfigport.h
#if !(CONFIG_IDF_TARGET_ESP32 && CONFIG_SPIRAM && CONFIG_SPIRAM_CACHE_WORKAROUND)
#define MICROPY_WRAP_MP_BINARY_OP(f) IRAM_ATTR f
#endif
#define MICROPY_WRAP_MP_EXECUTE_BYTECODE(f) IRAM_ATTR f
#define MICROPY_WRAP_MP_LOAD_GLOBAL(f) IRAM_ATTR f
#define MICROPY_WRAP_MP_LOAD_NAME(f) IRAM_ATTR f
#define MICROPY_WRAP_MP_MAP_LOOKUP(f) IRAM_ATTR f
#define MICROPY_WRAP_MP_OBJ_GET_TYPE(f) IRAM_ATTR f
#define MICROPY_WRAP_MP_SCHED_EXCEPTION(f) IRAM_ATTR f
#define MICROPY_WRAP_MP_SCHED_KEYBOARD_INTERRUPT(f) IRAM_ATTR f
I am changing it to read..
#if !(CONFIG_IDF_TARGET_ESP32 && CONFIG_SPIRAM && CONFIG_SPIRAM_CACHE_WORKAROUND)
#define MICROPY_WRAP_MP_SCHED_KEYBOARD_INTERRUPT(f) IRAM_ATTR f
#define MICROPY_WRAP_MP_SCHED_EXCEPTION(f) IRAM_ATTR f
#define MICROPY_WRAP_MP_BINARY_OP(f) IRAM_ATTR f
#endif
#define MICROPY_WRAP_MP_EXECUTE_BYTECODE(f) IRAM_ATTR f
#define MICROPY_WRAP_MP_LOAD_GLOBAL(f) IRAM_ATTR f
#define MICROPY_WRAP_MP_LOAD_NAME(f) IRAM_ATTR f
#define MICROPY_WRAP_MP_MAP_LOOKUP(f) IRAM_ATTR f
#define MICROPY_WRAP_MP_OBJ_GET_TYPE(f) IRAM_ATTR f
The other things I really want to leave in IRAM because if they are taken out of IRAM it could end up causing a pretty large performance hit.
You can also change the following in the same file
#ifndef MICROPY_PY_MACHINE_I2S
#define MICROPY_PY_MACHINE_I2S (SOC_I2S_SUPPORTED)
#endif
to read
#ifndef MICROPY_PY_MACHINE_I2S
#define MICROPY_PY_MACHINE_I2S (0)
#endif
If you are not using I2S. There are a couple of function that MicroPython has defined using IRAM when using I2S.
You can also change the following code in the same file
#define MICROPY_PY_MACHINE_BITSTREAM (1)
to
#define MICROPY_PY_MACHINE_BITSTREAM (0)
if you are not using the cycle counter in MicroPython (machine.bitstream
)
There is a typo in /lvgl_micropython/builder/esp32.py. Line 856
with open(mpconfigport, 'rb') as f:
should be
with open(mpconfigport, 'wb') as f:
I toke the branch with the changed mpconfigport.h (lines moved for IRM), disabled I2S, but I need bitstream as it is needed for NeoPixels. Still runs out of IRAM. What would be the reason the the former lvgl_micropython version did not have this problem?
Hi Kevin...
Using ESP32 SPIRAM.. Built using 6/20/2024 repo command...
python3 make.py esp32 submodules clean mpy_cross BOARD=ESP32_GENERIC BOARD_VARIANT=SPIRAM DISPLAY=ili9341 INDEV=xpt2046 --flash-size=4 --optimize-size
Build went fine !!!
Flashed to ESP32 Got REPL !
Ran the following code with no issues Screen turns on (command to driver) but have no text 'HELLO WORLD!'
import lvgl as lv # NOQA
import ili9341 # NOQA
import lcd_bus
from machine import SPI, Pin # NOQA
from micropython import const # NOQA
# Display settings
WIDTH = const(240)
HEIGHT = const(320)
# Display SPI bus settings
LCD_HOST = const(2) # I use vspi which is
LCD_FREQ = const(400000)
LCD_MISO = const(23)
LCD_MOSI = const(19)
LCD_SCK = const(18)
LCD_CS = const(15)
LCD_DC = const(13)
LCD_BKL = const(27)
# create the SPI bus for the display
spi_bus = SPI(
LCD_HOST,
LCD_FREQ,
mosi=LCD_MOSI,
miso=LCD_MISO,
sck=LCD_SCK
)
# create the SPI device on the bus for the display
display_bus = lcd_bus.SPIBus(
spi_bus=spi_bus,
dc=LCD_DC,
freq=LCD_FREQ,
cs=LCD_CS
)
# create the display driver
display = ili9341.ILI9341(
data_bus=display_bus,
display_width=WIDTH,
display_height=HEIGHT,
reset_pin=None,
power_pin=None,
backlight_pin=LCD_BKL,
color_space=lv.COLOR_FORMAT.RGB565,
rgb565_byte_swap=True
)
# display.set_power(True)
display.set_backlight(True)
display.init()
# display.set_backlight(100)
# lv.init()
# scr = lv.obj()
# scr = lv.scr_act()
scrn = lv.screen_active()
label = lv.label(scrn)
label.set_text('HELLO WORLD!')
label.align(lv.ALIGN.CENTER, 0, 50)
# lv.scr_load(scr)
lv.refr_now(None)
Doubble checked my pins... I know, I am using VSPI Also, leading underscores are not working with const . I must have over looked something very simple.
by the way... I'm the Galileo thermometer guy.
OH Hey what's going on?
I will get you up and running as fast as I am able to.
Having the underscore prefix to a constant means it is not a variable. IE if you have a module named some_module1
and in that module you have this code
from micropython import const
_TEST = const(1)
In a second module named some_module2
you have the following code
import some_module1
print(some_module1._TEST)
You will get an attribute error.
This is because _TEST is not actually a variable so it is not accessible. Using const works in a similar manner to using the c preprocessor macro define (#define
) in C code. except that it's access is limited to only the module it is declared in.
VSPI pins are defined as
MISO: 19 MOSI: 23 CLK: 18 CS: 5 WP: 22 HD: 21
It looks like you have MISO and MOSI flip flopped. Your CS pin is also wrong as well. Pin 5 is the standard pin for the CS line.
This is from the ESP-IDF
#define VSPI_IOMUX_PIN_NUM_MISO 19
#define VSPI_IOMUX_PIN_NUM_MOSI 23
#define VSPI_IOMUX_PIN_NUM_CLK 18
#define VSPI_IOMUX_PIN_NUM_CS 5
#define VSPI_IOMUX_PIN_NUM_WP 22
#define VSPI_IOMUX_PIN_NUM_HD 21
You can use display.set_backlight(100)
to turn the backlight on. This is actually a better way and makes your code more portable in the event you are using a display that you can dim the backlight on.
also replace lv.refr_now(None)
with the following code.
import task_handler
th = task_handler.TaskHandler()
@ste7anste7an
You are correct. good catch!!!
That was it... I swapped MO and MI I have done this before... couldn't they picked easier names. display.set_backlight(100) works but not with proportions with my screen. If understand correctly you're working on touch and SD card to all coexist on one SPI bus. My cs is 15 due to pcb layout.
Thank you very much Kevin !!!
yes they can all exist right now.
give me a few to key out some examples for ya,
Here is an example if the display touch and SDCard are all on the same bus,
import lcd_bus
from machine import SPI # NOQA
from micropython import const # NOQA
# Display settings
_WIDTH = const(240)
_HEIGHT = const(320)
# Display SPI bus settings
_SPI_HOST = const(2) # I use vspi which is
_SPI_MISO = const(19)
_SPI_MOSI = const(23)
_SPI_SCK = const(18)
_LCD_FREQ = const(400000)
_LCD_CS = const(15)
_LCD_DC = const(13)
_LCD_BKL = const(27)
_SD_CS = const(5) # change this
_SD_FREQ = const(10000) # change this
_TS_CS = const(10) # change this
_TS_FREQ = const(1000) # change this
# create the SPI bus for the display
spi_bus = SPI(
_SPI_HOST,
_TS_FREQ,
mosi=_SPI_MOSI,
miso=_SPI_MISO,
sck=_SPI_SCK,
cs=_TS_CS
)
# create the SPI device on the bus for the display
display_bus = lcd_bus.SPIBus(
spi_bus=spi_bus,
dc=_LCD_DC,
freq=_LCD_FREQ,
cs=_LCD_CS
)
import lvgl as lv # NOQA
import ili9341 # NOQA
# create the display driver
display = ili9341.ILI9341(
data_bus=display_bus,
display_width=_WIDTH,
display_height=_HEIGHT,
reset_pin=None,
power_pin=None,
backlight_pin=_LCD_BKL,
color_space=lv.COLOR_FORMAT.RGB565,
rgb565_byte_swap=True
)
from machine import SDCard # NOQA
sdcard = SDCard(
spi_bus=spi_bus,
cs=_SD_CS,
freq=_SD_FREQ
)
display.init()
display.set_backlight(100)
import xpt2046 # NOQA
import task_handler # NOQA
touch = xpt2046.XPT2046(spi_bus)
th = task_handler.TaskHandler()
scrn = lv.screen_active()
label = lv.label(scrn)
label.set_text('HELLO WORLD!')
label.align(lv.ALIGN.CENTER, 0, 50)
I am going to very strongly suggest people use an esp32-s3 with octal flash and octal spiram. I was able to get a crap load of LVGL functions into IRAM which really steps up the performance. and I was also able to overclock both the spiram and the flash to 120Mhz (actually 240Hz because of DDR) which is a HUGE performance gain over the original 80 MHZ. I recommend adding some form of passive cooling to the ESP32 if you are going to do this.
I take it there "is" a pecking order for the spi bus whereby you can't load SD card display and touch in any order.
Trying to understand where you're going with this a little bit. Could we create a generic spi_bus spi_bus = SPI(2, miso=19, mosi=23, sck=18) with no baud rate or cs
Then display = ili9341.ILI9341( data_bus=(spi_bus=spi_bus, cs=15, freq=400_000, dc=13) ) touch = xpt2046.XPT2046( data_bus=(spi_bus=spi_bus, cs=5, freq=1_000) ) sd = SDCard(spi_bus=spi_bus, cs=14, freq=10_000)
So what is lcd_bus.SPIBus doing ?? And why the touch doesn't need a similar module.
My display background is black my text is white and my buttons are yellow. In the past the generic setup gave me... white background black text and blue button. Do I have a color issue?
btn_cb() needs lv.refr_now(None) or I don't see text changes
and the response seems to be a little bit slow. any speed tweaks?
import lvgl as lv
import ili9341
import xpt2046
import lcd_bus
import task_handler
import os, time
from machine import SDCard, SPI # NOQA
def scr_txt(x):
print(x)
try:
if lv.screen_active() == main:
msg_txt.set_text(x)
except Exception as e:
pass
def btn_cb(event):
if event.get_code() == lv.EVENT.CLICKED:
scr_txt('clicked')
lv.refr_now(None)
time.sleep(1)
msg_txt.set_text('I feel you')
lv.refr_now(None)
time.sleep(2)
msg_txt.set_text('Do Again !!')
lv.refr_now(None)
th
# Create the SPI bus
spi_bus = SPI(2, 1000, miso=19, mosi=23, sck=18, cs=5)
# SPI device for display
display_bus = lcd_bus.SPIBus(spi_bus=spi_bus, cs=15, freq=400_000, dc=13)
# Start display driver
display = ili9341.ILI9341(
data_bus=display_bus,
display_width=240,
display_height=320,
reset_pin=None,
power_pin=None,
backlight_pin=27,
color_space=lv.COLOR_FORMAT.RGB565,
rgb565_byte_swap=True
)
# Start touch driver
touch = xpt2046.XPT2046(spi_bus)
# Mount SD Card
try:
sd = SDCard(spi_bus=spi_bus, cs=14, freq=10000)
os.mount(sd, '/sd')
scr_txt('Flash Mounted')
scr_txt(os.listdir('/'))
except Exception as er:
scr_txt(f'Flash Error\n{er}\nrebooting')
time.sleep(5)
reset()
main = lv.obj()
msg_txt = lv.label(main)
msg_txt.align(lv.ALIGN.CENTER, 0, 50)
msg_txt.set_text('Hello World !!')
btn = lv.button(main)
btn.set_size(90,40)
btn.align(lv.ALIGN.CENTER, 0, 0)
btn.add_event_cb(btn_cb, lv.EVENT.CLICKED, None)
btn_lbl = lv.label(btn)
btn_lbl.set_text("Click Me !")
btn_lbl.align(lv.ALIGN.LEFT_MID, 0, 0)
lv.screen_load(main)
display.init()
display.set_backlight(100)
th = task_handler.TaskHandler()
print(lv.display_get_screen_active())
lv.display_get_screen_active() does not give me currently loaded screen. I get... AttributeError: 'module' object has no attribute 'display_get_screen_active' was hoping to get main.
from lvgl... lv_obj_t lv_display_get_screen_active(lv_display_t disp) Return a pointer to the active screen on a display Parameters: disp -- pointer to display which active screen should be get. (NULL to use the default screen) Returns: pointer to the active screen object (loaded by 'lv_screen_load()')
Thanks again Kevin this is great work !!!!
So went in and changed lv_conf.h and changed #define LV_THEME_DEFAULT_DARK 0 button is still yellow.
lv_display_get_screen_active is not at the module level. it is apart of the display you created
display = ili9341.ILI9341(
data_bus=display_bus,
display_width=240,
display_height=320,
reset_pin=None,
power_pin=None,
backlight_pin=27,
color_space=lv.COLOR_FORMAT.RGB565,
rgb565_byte_swap=True
)
scrn = display.get_screen_active()
But lets say you created the display in the main.py
file and in there you imported another python module, lets call it ui.py
. well you cannot import main.py
form ui.py
because of circular imports. So how is that handled? Like this...
this is ui.py
that is imported in main.py
import ili9341
display = ili9341.ILI9341.get_default()
scrn = display.get_screen_active()
I thought of damned near everything. 😃
There are some things that I will be addressing when I eventually decide to redo the C code generator. One of those things would solve this issue.
display = ili9341.ILI9341(
data_bus=display_bus,
display_width=240,
display_height=320,
reset_pin=None,
power_pin=None,
backlight_pin=27,
color_space=lv.COLOR_FORMAT.RGB565,
rgb565_byte_swap=True
)
scrn = display.get_screen_active()
print(display == scrn.get_display())
that evaluates to False
. This is because the display that is returned by get_display
is an instance of the lv_display_t
structure where as display
is a python wrapper around lv_display_t
. Even if I sub classing lv_display_t that doesn't fix that problem either.
The reason why it doesn't work as a sub class is because of how the binding code is written. in order for it to work properly the first field in every structure in LVGL would need to be a void pointer that could be used to hold a micropython base type. That would be the only way it could be done so that things would get returned properly.
I need to change the machine.SPI class so it is a little easier to understand what is happening. I need to make an SPIBus
class and an SPIDevice
class. I would ideally like to have these in the machine
module but because of how it is written it is not possible to do without rewriting the machine
module. That's the crappy part.
Also. There is a reason why I have the imports all scattered through the code. This is because of memory use. You want to have as much SRAM free as possible when the frame buffers get allocated. This is even more important if using the ESP32 because there is only a small amount of DMA memory available for the ESP32. With the ESP32-S3 you can allocate SPIRAM as DMA memory, this is not possible with the ESP32. So the more you import the less chance there is of having enough DMA'able memory for running double buffering.
For convenience purposes I have written the code so the display driver will create the frame buffer(s). It will first try to create the buffers as DMA in SRAM. If that fails then it will try and create the buffers in DMA SPIRAM. If that fails then it will try and create a single buffer in SRAM and if that fails then it will try and make it in SPIRAM. The best is going to be 2 buffers that are DMA in SRAM. That is going to give the best performance.
This is a good example that shows some really advanced use. It shows how to ensure that the frame buffers get allocated in SRAM using DMA memory and double buffering. It also shows how to check for the SDCard without having to keep on rebooting the ESP32. AND it even goes into using threads and a mutex/lock to ensure that everything is going to p[lay nice nice with each other. AND it also shows you how to use timers in LVGL instead of having to make a change and then telling LVGL to update the display then pausing the program before making another change.
Kevin... that code is giving me sensory overload !!! Even though it is well commented, it's going to take a while to digest it. I wasn't aware that the order of modules imported affected memory use.
it is not possible to do without rewriting the
machine
module. That's the crappy part.
It makes me question if MicroPython is ready for mainstream use when a basic interface has had an issue for so dam long.
The example had an error...
Traceback (most recent call last):
File "
I have 4mb ESP32-WROVER-E-N4R8 ... MicroPython v1.24.0-preview.39.g411d66586.dirty on 2024-06-17; Generic ESP32 module with SPIRAM with ESP32
lv_display_get_screen_active issue
what i needed was a way to determine what screen is currently active
if lv.screen_active() == main:
is working for me now.
By the way... what is the standard button color? Because I'm getting yellow sort of orange color.
the N4R8 is 4 MB of flash and 8mb of ram.
The reason why you are getting the memory error is because there is not enough DMA'able memory available. I forgot to divide the buffers sizes by 10.
changes the lines that read
fb1 = display_bus.allocate_framebuffer(240 * 320 * 2, lcd_bus.MEMORY_INTERNAL | lcd_bus.MEMORY_SPIRAM)
fb2 = display_bus.allocate_framebuffer(240 * 320 * 2, lcd_bus.MEMORY_INTERNAL | lcd_bus.MEMORY_SPIRAM)
to
fb1 = display_bus.allocate_framebuffer(int(240 * 320 * 2 / 10), lcd_bus.MEMORY_INTERNAL | lcd_bus.MEMORY_SPIRAM)
fb2 = display_bus.allocate_framebuffer(int(240 * 320 * 2 / 10), lcd_bus.MEMORY_INTERNAL | lcd_bus.MEMORY_SPIRAM)
some of the ILI series of displays for whatever reason hold the bytes in their internal GRAM reversed. so you need to change this code
display = ili9341.ILI9341(
data_bus=display_bus,
display_width=240,
display_height=320,
frame_buffer1=fb1,
frame_buffer2=fb2,
reset_pin=None,
power_pin=None,
backlight_pin=27,
color_space=lv.COLOR_FORMAT.RGB565,
rgb565_byte_swap=True
)
to read
display = ili9341.ILI9341(
data_bus=display_bus,
display_width=240,
display_height=320,
frame_buffer1=fb1,
frame_buffer2=fb2,
reset_pin=None,
power_pin=None,
backlight_pin=27,
color_space=lv.COLOR_FORMAT.RGB565,
color_byte_order=ili9341.BYTE_ORDER_BGR,
# if the colors still are not right then remove the comment marker for the next line
# rgb565_byte_swap=True
)
There is no intention of changing the way the SPI works in MicroPython. It's just how they decided to code it. They decided on a design that would make the API work across all boards and use less memory and flash space instead. You can make it work the way they have it but it ends up costing more in the end. The thought process is that most people only have a single device attached to the bus. The big issue is the SDCard CANNOT work properly if there is more than one device on the bus using the ESP32 series of MCU's. It is an impossibility to get it to work properly with the way the MicroPython SPI is done.
It's fine the way it is and it works... It's a shortcoming that I was able to rectify so not a big deal.
so the color is good to go...
color_space=lv.COLOR_FORMAT.RGB565,
color_byte_order=ili9341.BYTE_ORDER_BGR,
rgb565_byte_swap=True
frame buffer divided... incremented by 10 up to 80
fb1 = display_bus.allocate_framebuffer(int(240 * 320 * 2 / 80), lcd_bus.MEMORY_INTERNAL | lcd_bus.MEMORY_SPIRAM)
fb2 = display_bus.allocate_framebuffer(int(240 * 320 * 2 / 80), lcd_bus.MEMORY_INTERNAL | lcd_bus.MEMORY_SPIRAM)
still got...
Traceback (most recent call last):
File "<stdin>", line 105, in <module>
MemoryError: Unable to allocate frame buffer
I am wondering if the machine.SPI is chewing up all of the available SRAM and that is what is causing the allocation issue.
I am going to have to rework the machine.SPI driver so instead of needing to pass a machine.SPI instance\ it will instead take the host number.
Give me a day to hammer that out. That has got to be what is causing the problem.
Can you do one thing for me. change the buffer allocation code to read this...
try:
fb1 = display_bus.allocate_framebuffer(int(240 * 320 * 2 / 80), lcd_bus.MEMORY_INTERNAL | lcd_bus.MEMORY_SPIRAM)
except MemoryError:
raise RuntimeError('Failing on first buffer')
try:
fb2 = display_bus.allocate_framebuffer(int(240 * 320 * 2 / 80), lcd_bus.MEMORY_INTERNAL | lcd_bus.MEMORY_SPIRAM)
except MemoryError:
raise RuntimeError('Failing on second buffer')
tell me if it is failing on the first or second buffer.
Traceback (most recent call last):
File "<stdin>", line 112, in <module>
RuntimeError: Failing on first buffer
I also changed display freq to 40Mhz and is running snappy now.
import lcd_bus
display_bus = lcd_bus.SPIBus(spi_bus=spi_bus, cs=15, freq=40_000_000, dc=13)
So I'm not doing any animations or needing any kind of fast screen refreshes at all. I have a static screen that updates every 59 seconds and a couple of buttons. Honesty I never liked having the task handler running interference in the background. I really liked to refresh the screen when I want to. That is why I use lv.refr_now(None)
I am having an issue with a widget..
m_box = lv.msgbox(lv.screen_active(), "So", "Hellow World Again", {"Ok", "Cancel", None}, True)
getting error...
Traceback (most recent call last):
File "<stdin>", line 177, in <module>
TypeError: function takes 1 positional arguments but 5 were given
I posted on LVGL fourm also.
lv.msgbox
is like any other widget. You have to create the widget passing a parent or None
and set other things
There are these methods to set things
m_box = lv.msgbox(lv.screen_active())
m_box.add_title("some title")
m_box.add_header_button(image_data)
m_box.add_text("Hellow World Again")
m_box.add_footer_button("OK")
m_box.add_footer_button("Cancel")
m_box.add_close_button()
LVGL Doc...
one liner worked in past
In the past you were probably using version 8.3 of LVGL. This binding is using version 9.1
Absolutely true that but this is not as good looking as the old message box
I didn't make the change to it. You would have to open an issue in LVGL's repo about it.
I did make an issue for the documentation being incorrect tho.
As a suggestion. If you are going to use the LVGL documentation I would only use the API section of the documentation. That section is pulled directly from the C code so it is going to show the real functions and parameters/types that need to be passed to those functions.
There is some name mangling that ends up being done because of the binding but for the most part it isn't that hard to figure out.
You can see here https://docs.lvgl.io/master/API/widgets/msgbox/lv_msgbox.html exactly what msgbox_create takes for arguments.
You beat me to it... I was just gonna say that !! Also the old message box used to have a margin around it and fit within the screens proportions. I have a 240 wide screen and it's going right off the left and right sides. Also there is so much white space between the two buttons. And now you need a callback for each button... wtf
Hey, I only work here... you have to complain to the higher ups. It's not within my scope of work.. LOL
Just to let you know also.. the msgbox is a widget and like all widgets they are subclasses of lv.obj
so it inherits all of the methods of lv.obj
. so you would more then likely need to use msgbox.set_size(width, height) and then msgbox.center() to get it to the size and center it on the screen.
Aren't these widgets supposed to make our lives easier... Yeah I usually look at the example code but it's true sometimes the transition from c to the micropython isn't as easy as it looks.
Now having an issue with my portrait screen and touch relationship. Instead of us continuing in this thread I think you should have a widgets category. Where people can talk about their issues or funky things they're doing.
but it's true sometimes the transition from c to the micropython isn't as easy as it looks.
I still to this day have not figured out why things are organized the way they are. Or how some of the things are done. One of these days I am going to write the whole code generator over again and flatten the entire thing so it matches with the LVGL C API. I had attempted to flatten it using the script in it's current form but there are too many glitches in it because of all of the wrapper code to create the classes and that causes a lot of issues.
I like the idea of generating the code but the issue there is the code is not going to be efficient. This is because of general ways of handling things on a per use basis. I would also like to have LVGL run on the core that is not being used. This would greatly improve the performance of it. MicroPython pitches a fit when trying to do things like calling a python function from c code that is running on a different core. It has a complete heart attach about it. Unfortunately I do not think multi core processing will ever be available for the ESP32 in MicroPython which is kind of sad because 50% of the processing power goes unused.
If remember correctly that second core wasn't as powerfull... but still you could run a lot of dopey repetitive routines there. Weren't you originally trying to make LVGL a module?
Thank you Kevin I really appreciate that !!!
Also do you have any idea why the online simulator is busted ?
I am going to move this repo to that organization. Right now there is nothing in the organization as I just made it. So there is nothing to really do in there just yet.
When I move the repo you will be able to create a discussion group for widgets or whatever else you want. You have to give me a while to get things set up on it tho.
It has been a long time since I have used github organizations and a lot has changed. So I have to go back to school and learn it.
Got It !
the core of the binding is a module. Most of what you see is a build script. The single most important thing about Python is ease of use. Getting MicroPython to run is anything but easy. That is the reason why the build script is so damned elaborate. That is what makes it easy to use. The majority of the build commands apply across all ports. which makes it easier to use. It handles collecting build requirements that do not need to be installed into the system which is port dependent.
The big issue is having to modify the MicroPython build system and source code in order to get things to work properly. The script handles doing these things for you. Upgrading to a new MicroPython version is pretty simple to do and that holds true for LVGL as well. In fact every time a change gets made in LVGL's master branch those changes get populated into this binding without having to do anything.
I have developed a common API for display drivers and for touch drivers.. The importance of this is it's not port specific. It works the same across all ports. That allows code like this to run.
import sys
import lcd_bus
from micropython import const
_WIDTH = const(320)
_HEIGHT = const(480)
if sys.platform in ('darwin', 'linux'):
import sdl_display
DisplayDriver = sdl_display.SDLDisplay
display_bus = lcd_bus.SDLBus(flags=0)
color_byte_order = 0
rgb565_byte_swap = False
spi_bus = None
else:
from machine import SPI
import ili9341
# Create the SPI bus
spi_bus = SPI(2, 1000, miso=19, mosi=23, sck=18, cs=5)
display_bus = lcd_bus.SPIBus(spi_bus=spi_bus, cs=15, freq=400_000, dc=13)
color_byte_order = ili9341.BYTE_ORDER_BGR
rgb565_byte_swap = True
DisplayDriver = ili9341.ILI9341
fb1 = display_bus.allocate_framebuffer(_WIDTH * _HEIGHT * 2, lcd_bus.MEMORY_SPIRAM)
fb2 = display_bus.allocate_framebuffer(_WIDTH * _HEIGHT * 2, lcd_bus.MEMORY_SPIRAM)
import lvgl as lv
display = DisplayDriver(
data_bus=display_bus,
display_width=_WIDTH,
display_height=_HEIGHT,
frame_buffer1=fb1,
frame_buffer2=fb2,
reset_pin=None,
power_pin=None,
backlight_pin=27,
color_space=lv.COLOR_FORMAT.RGB565,
color_byte_order=color_byte_order,
rgb565_byte_swap=rgb565_byte_swap
)
display.init()
if sys.platform in ('darwin', 'linux'):
import sdl_pointer
touch = sdl_pointer.SDLPointer()
else:
import xpt2046
touch = xpt2046.XPT2046(spi_bus)
import task_handler
th = task_handler.TaskHandler()
scrn = lv.screen_active()
scrn.set_style_bg_color(lv.color_hex(0x000000), 0)
slider = lv.slider(scrn)
slider.set_size(_WIDTH - 50, 25)
slider.center()
so if you compile the Unix port either on macOS or Linux and run that script it will work. If you compile for the ESP32 and upload the same script it will work. This single feature allows you to develop your UI on a desktop without having to constantly upload to the MCU at all. That's because of the API being the same for the display drivers. The touch drivers get a little bit hairy due to accessing pins directly. I am still hammering that out to come up with something that is more consistent across them.
So im using WSL2... Build for unix... correct how do I run the (firmware.bin) or is it a .exe ???? never done this before !!!
This single feature allows you to develop your UI on a desktop
well you have to compile using this command
python3 make.py unix mpy_cross submodules DISPLAY=sdl_display INDEX=sdl_pointer --heap-size={BYTES}
Change {BYTES}
to how many bytes of RAM you want to make available to MicroPython...
then you run the following
chmod +rwx build/lvgl_micropy_unix
build/lvgl_micropy_unix
the directory you run it from is the root that micropython starts in. so if you want to import a module you created you must place the module into the folder you ran micropython from in this case it would be lvgl_micropython
if you cd into build and then run it using ./lvgl_micropy_unix
then you would place the module in the build
folder.
Everything is self contained in that single binary so you can move it to any directory of your choosing.
This is what I know still has an issue.
ST display drivers have issue with the rotation being out of sorts. This is causing the text to display backwards. I have to do a little bit more research on this issue. as per the data sheet the rotation I have set for it should be correct but for some reason it is not.
There were reports of the REPL not working, This is not happening for everyone and the people that are having the issue have a common thing. They are using Thonny. Thonny is known to cause problems and I recommend not using it. Download a program called Putty if you are running on Windows. I know this work properly when connecting to the ESP32. The other possibility could be with MicroPython and using the built in USB for the USB instead of the UART. I would need more information to confirm what MCU is being used and what the MicroPython default settings are to nail down what the issue might be.
There was a report of the TaskHandler causing the MCU to freeze. I want to say this was with the RGBBus. The Freeze should now be fixed but the RGBBus driver is still not working. I am in the process of sorting this one out. I am really wanting a way for LVGL to exit the refresh instead of sitting there spinning it's wheels waiting for the buffer to finish flushing. This is a complex issue I am trying to come up with the best way to handle it.
The touch screen calibration is not working. I have not done anything as of yet to get this sorted out. It is on my TODO list.
The Unix port crashes if the display window is resized. This is a work in progress. I made some changes locally but I have not tested them yet. I was attempting to reallocate the buffers and that would cause a crash. I don't recall what the exact error was but it some something that didn't help to isolate the issue. What my local code does is it frees the existing frame buffer and then allocates a new buffer. I am going to change the default heap size to 8mb. Right now it is at 4mb which is not going to be anywhere near enough if a display is created that is larger than 800x600x24 using double buffering. A single buffer for a display size of 1024x768x24 is 2,334,720 bytes.