Replies: 6 comments 2 replies
-
Now that we know how static DRAM and heap are related, we need to know why we sometimes have problems with memory and if there is anything we can do about it. If is also important to know, that you will only know at runtime if heap is big enough. Sometimes the ESP32 even needs to run for some time until you know that there is not enough heap. E.g. WiFi is known to need memory peaks at runtime in heap. That is the reason why WiFi sometimes breaks during usage or even the whole ESP32 reboots. Ok, here is the result of my measurements.
So the more static DRAM is used, the less is left for heap (heapSize) - as we expected. The worst memory consumers are (in that order):
WiFi and BLE take more from heapSize as it should be only from using some memory from DRAM. So they also allocate a lot at runtime in heap. What does this now mean for real life? During development, we have to keep an eye on:
There is nothing we can do about BLE and WiFi. If only one of them is used, we have no problem with memory needed by lvgl. We can give lvgl more than the 32k it has by default, and this can be done both in static DRAM and heap. Both works, and I will explain later how to do it. With that you can have more than 100 tabs. But as soon as both BLE and WiFi are used, things get complicated. I will write more about this in my third post. It will be about how to optimize lvgl memory usage. |
Beta Was this translation helpful? Give feedback.
-
Ok, this post will be about:
There are two different ways of memory management lvgl can use:
How can we tell lvgl what to use: Way 1: lv_conf.h
If you need more, you can increase "32U". If you get If you want to use the system heap, it would look like this:
The last four lines are automatically activated. If you want to use PSRAM (external RAM, only if you have an esp32-wrover), there is also an example in file "lv_conf.h". In that case lvgl also preallocates its own heap, but in the PSRAM, not in the static DRAM. Way 2: platformio.ini
Logging How to examine memory usage at runtime
Now we are ready to go and to see what is going on behind the scenes of lvgl ... |
Beta Was this translation helpful? Give feedback.
-
The fourth post will be about
Test 1: 100 tabs, static DRAM vs heap
Wow, heap needs 25% more memory than lvgl's own heap implementation. I didn't expect this.
Test 2: how much memory does a single slider or a single label need
A single label (without any styling) needs about:
Again, dynamic memory (heap) needs 27%-30% more memory. So is the heap good for anything? 100 tabs with 11 labels in each tab needed 213656 bytes in heap. This never would have been possible in static DRAM (124580 bytes max) In my further tests, when I only provide a single byte number for memory usage, this is always for static DRAM (lvgl's own heap implementation) Test 3: swiping through tabs Test 4: styling of objects Test 5: memory usage of "real live tabs"
Interim conclusion Ok, what could we do:
Let's give the third option a try. Test 6: dynamically create content (screen, tabview and tab) a) create and delete the tabs I tested it with only creating these three objects, with no content in it (no labels etc.) So I decided to go on with option b) delete the tabview. Test 7: dynamically create content It was not noticable that a tab was deleted and recreated (at another position in the tabview) after swiping. No flicker, nothing. So this looks promising. With that trick, you can have hundreds of tabs (in the code), and only 3 of them are in memory at the same time. And there should be enough memory for 3 tabs ... I read about this trick in the lvgl forum and that it is the best way to keep memory usage low, but without any advice on how to do it. Downsides As the tabs are deleted and recreated at runtime, the state will not be preserved. So if you have a slider or a switch, the state gets lost. For this you have to give the tab the possiblity to save it's state before it gets destroyed. When the tab is recreated again, the tab can restore its previous state. In my tests this worked well. Only "settings" and "smarthome" are using widgets having a state (the sliders and switches). The other downside I saw is that some widgets cannot be created with an inital state. A slider for example can be created with an initial value. But a switch can only be created "off", and you have to set it to "on" afterwards. And this indeed can be seen as a flickering of the switch.
Conclusion What do you think about it? Any ideas or comments about this? |
Beta Was this translation helpful? Give feedback.
-
First thing first, That was an awesome breakdown of memory!! I have been wanting to research this for a while now and I really appreciate you taking the time to do such a nice write up. A few random thoughts to some of the topics in your posts. I like how you called out replacing the lv_config.h for the platform.ini file. I think in my branch I actually completely eliminated the need for the lv_config.h at all. https://github.com/CoretechR/OMOTE/blob/abstraction/Platformio/platformio.ini I was also toying with this idea of dynamic creation to allow for more UI and that is part of the reason I did this wrapping of LV objects in You mention one issue of dynamic creation being state and we have had some conversations in the past about how we should be storing data in non-volatile memory but nothing has really ever come of those conversations. We really need some sort of light weight module that can use non-volatile memory to store all the info needed for the UI. I have looked at a few solutions to this and there are some modules that implement a simulation of EEPROM using the Flash. There are also some examples of carving off a portion of flash to create a filesystem. https://randomnerdtutorials.com/esp32-littlefs-arduino-ide/ I was thinking maybe we could store a JSON config in this littlefs that could be dynamically updated to save the current state of many things so that way the UI could be inflated off it.... But this is no small undertaking lol. |
Beta Was this translation helpful? Give feedback.
-
Dear @KlausMu I agree with the approach you proposed. However, what I don't fully understand is, why we want to have all scenes active on tabs at all times. I currently have a harmony remote and my parents are using two. On the harmoy remotes, you can always only have one scene (they call it activity but I like scene better) active at any given time. When you start a new scene, it would close the currently active one. However, what the harmony remote can do (and I'm not sure we can do the same easily) is, that devices which are active in one scene and are needed in the next scene are kept active instead of being turned off and on again. BR |
Beta Was this translation helpful? Give feedback.
-
Update: in lvgl 9, it is no longer
but
And also some other build flags taken from |
Beta Was this translation helpful? Give feedback.
-
Memory usage or lack of memory has been topic of several discussion in the past.
I would like to share the information I gained during my analysis and how the current situtation is, so that we can discuss how to proceed.
Different types of memory
First thing that needs to be known is that the ESP32 has different kinds of memory:
A simplified explanation is
An interesting detail is, that const data will also automatically be placed by the linker into the flash and can directly be used at runtime from flash. This is called DROM (Data stored in flash). So if you have problems with too little RAM, you could move data to flash if it is constant.
This is also explained (in more detail) on this page about Memory Types from Espressif.
So now we know that there exists IROM, DROM (both in flash) and DRAM (in SRAM).
Flash: Size of the code
As already said, the code goes into the flash. The size of the code was a problem in the past, but is no longer, because we are using the "huge_app.csv" partition layout. If you are interested in that topic, I could write a litte bit more about this and why e.g. it is not possible to use OTA (over the air updates) with that partition layout.
DRAM
The interesting part is the DRAM. The best page I found explaining the DRAM is this blog about the "ESP32 Programmers’ Memory Model".
You don't need to read and understand it completely. Here is what is important for us (again simplified):
DRAM has several regions which share the same total available amount of DRAM (320 kb)
Total size of DRAM is 320 kB, and this has to be shared between the three above. So if you have more static variables, you will have less heap and vice versa. With one important restriction: static variables (static DRAM) can never have more than 124580 bytes.
const appleTvIcon_map[] = { 0x00, 0x00, ...};
(a lot of data)static lv_color_t bufA[ screenWidth * screenHeight / 10 ];
lv_color_t * bufA = (lv_color_t *) malloc(sizeof(lv_color_t) * screenWidth * screenHeight / 10);
Limited static DRAM
As already said, static DRAM is limited to 124580 bytes. If you have more static content, then the linker will complain about it and is not able to create the file "firmware.elf". You will get a message like
You have to reduce the static content in your code until the linker can link the "firmware.elf".
If you ever have to examine the content of the static DRAM and why it is at the limit or even above the limit, I can recommend this page.
Once I had a problem that with WiFi and BLE activated, the static DRAM was not big enough. I analyzed the static DRAM, which looked like this:
So the solution to this issue was to move bufA and bufB from static DRAM to the heap. Static DRAM has a fixed limit, but heap has not. Heap simply takes the rest, and this is in fact more than the static DRAM. Of course no memory was gained with that trick, because as static DRAM usage decreased, heap usage increased. It simply overcame the limit of the static DRAM.
Heap usage
Heap usage can only been seen at runtime, e.g. like that
If you are running out of memory at runtime, normally the ESP32 will crash or at least will no longer work as expected.
Next part will be a detailed analysis on how many static DRAM and heap is used by the different software components used by OMOTE. I will do a separate post for this.
Beta Was this translation helpful? Give feedback.
All reactions