EVE driver with lv_binding possible memory leak

Hi,

I’ve got a custom board with an STM32L496 running micropython and lv_binding_micropython as a C module to interface with a Riverdi 5" display.

I created a separate C module to handle the hardware level and to call lv_draw_eve_display_create() from micropython.

It seems to work. I’ve written a small test script that initialises everything and draws a button with some text and changes the text when the button is pressed. That all works…for a while.

After some time the app crashes. I added some code to display the free memory according to the garbage collector. With every lv.timer_handler() call, the free memory decreases 112 bytes. I read that I should call gc.collect() to get an accurate measure of free memory, but that causes the button to become unresponsive.

I’m still learning about micropython, so I may have made a silly mistake there. Can anyone help, please?

import asyncio
import machine
import gc
import lvgl as lv

# EVE driver module for EVE display create and hardware callback function.
import eve_gpu  

# Bring up native peripherals matching standard configuration paradigms
spi_hardware = machine.SPI(1, baudrate=1000000, polarity=0, phase=0)
cs = machine.Pin('H7', machine.Pin.OUT, value=1)
pd = machine.Pin('H12', machine.Pin.OUT, value=1)

async def async_timer(delay):
    while True:
        await asyncio.sleep_ms(delay)
        lv.tick_inc(delay)
        lv.timer_handler()

# Define event callback for the button
def btn_event_cb(e):
    code = e.get_code()
    if code == lv.EVENT.CLICKED:
        print("Button was clicked!")
        label.set_text("Touched!")
        
scr = None
btn = None
label = None
display = None
timer_task = None

async def main():
    global scr, btn, label, display, timer_task
    
    if not lv.is_initialized():
        lv.init()

    # Must start the tick increments before create display.
    # It uses ticks for a delay during display initialisation
    timer_task = asyncio.create_task(async_timer(33))
    
    # Arguments: (SPI_ID, CS_Pin, PD_Pin, Width, Height)
    display = eve_gpu.create_display(1, cs, pd, 800, 480)

    # Draw widgets normally using standard LVGL interfaces
    scr = lv.obj()
    btn = lv.button(scr)
    btn.align(lv.ALIGN.CENTER, 0, 0)
    label = lv.label(btn)
    label.set_text("Native lv_draw_eve Active")

    # Add the event to the button
    btn.add_event_cb(btn_event_cb, lv.EVENT.ALL, None)

    # Load the screen
    lv.screen_load(scr)

    fmem = gc.mem_free()
    
    while True:
        await asyncio.sleep_ms(1000)
        #gc.collect()
        nfmem = gc.mem_free()
        if (nfmem != fmem):
            print(f"free {fmem} diff {nfmem-fmem}")
            fmem = nfmem

# Start the event loop
try:
    asyncio.run(main())
except KeyboardInterrupt:
    print("Program stopped")

scr.delete()

More info…

I made the button callback look like;

# Define event callback for the button
def btn_event_cb(e):
    global label
    code = e.get_code()
    if code == lv.EVENT.CLICKED:
        mf = gc.mem_free()
        print(f"mem free {mf}")
        label.set_text(f"mem free {mf}")

With every button press the free memory decreases. If I keep pressing the button, the free memory decreases to a point that the app crashes.

J.

More info.

I have made a few changes to my micropython test script and it appears that there is a problem in LVGL. If I run the micropython garbage collector, the app either stops working or crashes. If I run the app normally, then stop the timer tick and timer handler call backs before calling gc.collect(), I can see that the app continues to run, but of course the display is unresponsive.

So it seems to me that somewhere in LVGL there is memory allocated that is being freed by the garbage collector in micropython, thus causing corruption.

J.