Not enough HEAP on idf for sending http Requests #14081
-
Checks
Port, board and/or hardwareLILYGO_TTGO_LORA32 MicroPython versionLILYGO_TTGO_LORA32-20240222-v1.22.2.bin ReproductionI have a program that fetches data from a serial interface and send it via http to a server. This code is put before calling the http request function and right after the receiption of the answer. mem = esp32.idf_heap_info(esp32.HEAP_DATA) Output before sending: Output on receive: As 4032 bytes are far to small for the next send process the device crashes. Is there a way to extend the heap memory for the idf? Expected behaviourNo response Observed behaviour2024-03-01T13:17:47 - CRITICAL - - Task exception wasn't retrieved ; Details: Wifi Internal Error ; IoT-Device version 0.3.2 - Exception: This fails obviosly because of too short memory on the idf heap. Additional InformationNo, I've provided everything above. |
Beta Was this translation helpful? Give feedback.
Replies: 12 comments 2 replies
-
Hi @ds-digid, Recent versions of Micropython will automatically grow the Python heap as needed, meaning at some point during the request step the ESP32 would otherwise run out of memory for Python so it's expanded the heap (taking from IDF). So the best approach is to figure out which Python code requires a lot of memory and find ways to use less, or less at the one time. If the HTTP response is large then this depend on how it's handled, although not possible to make suggestions without seeing the code. If there isn't anything obvious, one candidate may be compiling large .py files on demand. You can freeze packages into the firmware or use mpy-cross to produce .mpy files on the host and copy these over instead of the .py files. |
Beta Was this translation helpful? Give feedback.
-
Hi @projectgus, |
Beta Was this translation helpful? Give feedback.
-
For more information: I use a custom async request library for http requests:
|
Beta Was this translation helpful? Give feedback.
-
@ds-digid there is aiohttp now, which I made to replace this "custom async request" library, so try it and see if the problem persists. 👍🏼 |
Beta Was this translation helpful? Give feedback.
-
Hmm, OK. The point is that instead of running out of heap, Python will claim more memory from IDF - so you don't get any Python MemoryError, but you do get lower free IDF heap instead. To rule this out as a problem, call |
Beta Was this translation helpful? Give feedback.
-
@Carglglz: I have tried the aiohttp library but the results are the same. Here are some more informations with mem_info() on the specific points.
|
Beta Was this translation helpful? Give feedback.
-
Btw, is there any chance to print out the installed idf version? |
Beta Was this translation helpful? Give feedback.
-
|
Beta Was this translation helpful? Give feedback.
-
Thank you |
Beta Was this translation helpful? Give feedback.
-
Thanks @ds-digid . From the numbers for the failed sequence above, between "before" and "after" snapshots MicroPython finds it has run out of heap and grows the total Python heap from 111936 to 147904 to avoid failure (+35968). This drops the available IDF memory from 39196 to 3248, causing the Wi-Fi internal failure after that. Do you have an idea of what is using 106640 bytes of Python heap in the "before" snapshot? If you can find the cause of that, before the request even starts, and get this usage number down, then the later failure will go away. |
Beta Was this translation helpful? Give feedback.
-
@projectgus
another example:
The imports of this 3 classes alone take ~4kB of RAM. This is just insane in my view. There is not that much going on... So that there is not that much free heap space for the running program. |
Beta Was this translation helpful? Give feedback.
-
Right, thanks for the extra info. There's a few things you can do here, depending on how deep you want to go. It seems like your current setup almost has enough RAM, so with luck you don't need to change much to get it all working smoothly:
There are some similar discussions in other threads which may have other tips, for example.
It can be a bit tricky to accurately measure this kind of thing. In particular, the first time you import something from the filesystem then there may be some additional filesystem initialisation that happens in the background and uses some RAM. I compiled those classes down to a 790 byte .mpy file with mpy-cross (mostly strings for names), and as best as I can tell importing it uses 1760 bytes of RAM on ESP32 (although some of this is probably space for strings that isn't all used, so the next import can use some of that too.) To accurately measure I also created a 0 byte file empty.py and compiled it to empty.mpy, to take out any initialisation costs from the first import. Full session log - with and without empty.mpy - if you're interested
|
Beta Was this translation helpful? Give feedback.
Right, thanks for the extra info. There's a few things you can do here, depending on how deep you want to go. It seems like your current setup almost has enough RAM, so with luck you don't need to change much to g…