Not enough DMA-able memory to allocate display buffer

Hi! Hope you are all well.

So I have been using lvgl with a m5stack and micropython with great success until now.

When I tried to add wifi and a WiFi manager I got the following error: “Not enough DMA-able memory to allocate display buffer”.

I would thought that with 520kb ram I would have enough to work with but something is wrong. Anyone have experience with this issue?

Thank you!

Have a look at these:

520KB is a lot, and should be enough for Micropython+lvgl+wifi.
Which display driver do you use? ili9341.py?

Try to add wifi, but don’t initialize the display. Then check how much free RAM you have:

>>> import gc
>>> gc.collect()
>>> gc.mem_free()

You may need to change WiFi parameters, and/or display driver parameters.

Thank you for the links, I had already found #2. I have my m5stack working with no problem unless I try to use the wifi. More specifically with something like this https://randomnerdtutorials.com/micropython-wi-fi-manager-esp32-esp8266/
I will have to understand #1 method and give it a try!

Yes I am using the pure micropython driver. I have it configured for single buffer mode.

So when I check the memory I get:

  1. main.py only importing the wifimgr.py code I get 97280 free after connecting to wifi

  2. main.py empty I get 105376

  3. main.py my normal program 82720

So I guess is not a memory problem.

PS: Also, If I make print(gc.mem_free()) right before the display initialization, but after connecting to wifi, I get 71360 without gc.collect and 78832 after gc.collect()

One more thing you can do is change the factor parameter to 8 or to 16.
This parameter controls what factor of the screen is buffered, so larger number means smaller buffer (and possibly lower performance).

Actually, even if there is enough memory, there might not be enough DMA-able RAM.
Not all RAM is DMa-able on ESP32, only about 328KB.
image
This DMA-able RAM is probably consumed by the WiFi driver.

Here are some things you can try:

  • Try to initialize the ili9341 driver before importing and using WiFi, so it would be the first one to allocate DMA-able RAM. WiFi will have less, but perhaps enough RAM.
  • Change WiFi RAM allocation configuration. There are probably some H files or other configuration files that control this (that would probably require rebuilding the firmware).

You can check how much DMA-able RAM you have with the heap_caps_print_heap_info function. I think it’s available on espidf module so you can call it from a Micropython script or REPL.

This actually worked! I haven’t remember of doing it before! Thanks!

Thank you for the explanation. I will try the other fixes anyway as soon as I have the time.

Never mind… It doesn’t work… I don’t get the error but my program starts to behave weirdly. I’ ll have to try one of the other options.

If I run:

import network

gc.collect()


disp = ili9341() # Create a display driver


ssid = 'xxx'
password = 'xxx'

station = network.WLAN(network.STA_IF)

station.active(True)
station.connect(ssid, password)

while station.isconnected() == False:
  pass

print('Connection successful')
print(station.ifconfig())

and then continue with my normal workflow I get stuck. Like a loop is running and when I click on buttons they don’t behave like they should.

So what is happening is, I connect to wifi and my program starts as it should, but then while I am connected I can’t do anything and I get the previous behavior… But if I disconnect from wifi it starts working again… Anyone had any similar problem?

Just an odd question: Is there anything appearing on the REPL when you try what you describe in your previous post? I mean other than the tuple shown as a result of print(station.ifconfig()).

I get the tuple with my IP etc and then just the normal micropython version info and then I get the repl console >>>.

If I run just that bit it is ok. The problem is when I add for example some UI and a list… I get noises from my device (like it is looping or something) and I can’t select any option from the list… the pointer always go back up and is very difficult to select something.

BUT if I disconnect the Wifi it starts working properly…!

I get this behavior whit the following test code :

main.py

import machine
import gc
import network
import lvgl as lv
from m5_lvgl import ButtonsInputEncoder, EncoderInputDriver
from ili9341 import ili9341
import gc
import utime

import micropython
micropython.alloc_emergency_exception_buf(100)


AUTHENTICATED = False

OPTION1 = False
OPTION2 = False
OPTION3 = False
OPTION4 = False


def connect():
 
    ssid = "xxx"
    password =  "xxx"
 
    station = network.WLAN(network.STA_IF)
 
    if station.isconnected() == True:
        print("Already connected")
        return
 
    station.active(True)
    station.connect(ssid, password)
 
    while station.isconnected() == False:
        pass
 
    print("Connection successful")
    print(station.ifconfig())

disp = ili9341() # Create a display driver

connect()

def event_handler(obj, event):
    """
    Called when a button is released.
    Parameters
    ----------
    btn :
        The Button that triggered the event.
    event :
        The triggering event.
    """
    global OPTION1, OPTION2, OPTION3, OPTION4
    if event == lv.EVENT.RELEASED:
        print("Clicked: %s" % lv.list.get_btn_text(obj))
        if lv.list.get_btn_text(obj) == "Option1":
            
            OPTION1 = True
 
        elif lv.list.get_btn_text(obj) == "Option2":
            
            OPTION2 = True

        elif lv.list.get_btn_text(obj) == "Option3":

            OPTION3 = True
            
        elif lv.list.get_btn_text(obj) == "Option4":
            
            OPTION4 = True

screen = lv.obj()

button_encoder = ButtonsInputEncoder()
button_driver = EncoderInputDriver(button_encoder)

list1 = lv.list(screen)
AUTHENTICATED = True
if AUTHENTICATED:
    list1.set_size(300, 154)
    list1.align(None, lv.ALIGN.CENTER, 0, -5)   
    
            # Add buttons to the list

    list_btn = list1.add_btn(lv.SYMBOL.FILE, "Option1")
    list_btn.set_event_cb(event_handler)
    
    list_btn = list1.add_btn(lv.SYMBOL.DIRECTORY, "Option2")
    list_btn.set_event_cb(event_handler)
    
    list_btn = list1.add_btn(lv.SYMBOL.DIRECTORY, "Option3")
    list_btn.set_event_cb(event_handler)     
else:
    list1.set_size(300, 100)
    list1.align(None, lv.ALIGN.CENTER, 0, -5) 
    
            # Add buttons to the list
    list_btn = list1.add_btn(lv.SYMBOL.FILE, "Option2")
    list_btn.set_event_cb(event_handler)
    
    list_btn = list1.add_btn(lv.SYMBOL.DIRECTORY, "Option3")
    list_btn.set_event_cb(event_handler)
    
group = lv.group_create() # Create a group
lv.group_add_obj(group, list1)
button_driver.group =group
lv.group_set_style_mod_cb(group, None)
lv.group_set_style_mod_edit_cb(group,None)
lv.group_set_editing(group, True)
   
lv.scr_load(screen)

You also need:

m5_lvgl.py

import gc
import struct

import lvgl as lv
import lvesp32
import machine
import utime

DEFAULT_ENCODER_ADDR = 0x5E  # (94)


__all__ = ['ButtonsInputEncoder', 'FacesEncoderInputEncoder',
           'EncoderInputDriver', 'general_event_handler', 'init_ili9341']


class ButtonsInputEncoder:
    def __init__(self, left=39, right=37, press=38):
        self._left = 0
        self._right = 0
        self._pressed = False

        def on_press_left(*args):
            self._left_time = utime.ticks_ms()
            self._left += 1

        def on_press_right(*args):
            self._right_time = utime.ticks_ms()
            self._right += 1

        def on_toggle_press(pin):
            self._press_time = utime.ticks_ms()
            self._pressed = not pin.value()

        btn_left = machine.Pin(left, machine.Pin.IN, machine.Pin.PULL_UP)
        btn_left.irq(trigger=machine.Pin.IRQ_RISING, handler=on_press_left)
        btn_right = machine.Pin(right, machine.Pin.IN, machine.Pin.PULL_UP)
        btn_right.irq(trigger=machine.Pin.IRQ_RISING, handler=on_press_right)
        btn_press = machine.Pin(press, machine.Pin.IN, machine.Pin.PULL_UP)
        btn_press.irq(trigger=machine.Pin.IRQ_FALLING | machine.Pin.IRQ_RISING,
                      handler=on_toggle_press)

    @property
    def diff_peek(self):
        return self._right - self._left

    @property
    def diff(self):
        diff = self._right - self._left
        self._left = 0
        self._right = 0
        return diff

    @property
    def pressed(self):
        return self._pressed


class EncoderInputDriver:
    def __init__(self, encoder, group=None):
        def input_callback(drv, data):
            data.enc_diff = encoder.diff
            if encoder.pressed:
                data.state = lv.INDEV_STATE.PR
            else:
                data.state = lv.INDEV_STATE.REL
            gc.collect()
            return False

        self.drv = lv.indev_drv_t()
        self.encoder = encoder
        lv.indev_drv_init(self.drv)
        self.drv.type = lv.INDEV_TYPE.ENCODER
        self.drv.read_cb = input_callback
        self.win_drv = lv.indev_drv_register(self.drv)
        self.group = group

    @property
    def group(self):
        return self._group

    @group.setter
    def group(self, value):
        self._group = value
        if self._group is not None:
            lv.indev_set_group(self.win_drv, self._group)

Also @amirgon if I do this:

I no longer get the DMA error, thank you! So that solves that issue. But the network problems from above persist… should I open a new topic and mark the solution for the DMA problem? :thinking:

This is a possible solution. Another option is to lower the RAM required by the WiFi driver.
You can open a new topic if you want.

I’m trying to understand if this is related to rendering / screen refresh or to input device handling.
Please try to add some animation which is not triggered by an input device.
Then try to see if the animation behaves differently when WiFi is on/off.
If animation works fine with WiFi, that would narrow the problem to your input device (buttons/encoder)

I have done what you suggested, as I think it is not a related problem, I will follow up the network conflict with the interface here Problems with interface after connecting to network. I already posted there what you asked.
Thank you!

I had problems with the encoder driver you use, and went on using a keypad driver: the three buttons sending lv.KEY.PREV, lv.KEY.ENTER en lv.KEY.NEXT respectively. I’ll post sources on my github account tomorrow morning: the code needs a bit of paring down. Several things can explain the problems you are having:

  • the wifi connection eanting up part of the processor resources and reducing the time microPython has to work.
  • lvgl eating up resources. With wifi running and a few buttons, the button event handler is called about 25-30 times/second.
  • ESP32 or the RTOS’s poor irq handling: its interrupts are sometimes slow… to interrupt.
1 Like

Thank you @fstengel I will definitely give a try to your code!

Even though I think the problem is not related with the driver as I found out in my other post… Give it a look if you have the time!

And yup, those are all true I still have to figure out a way to deal better with the interrupts (i get too much double clicking or not detecting sometimes… but that is a whole other story ahah)