RAc, regarding the task memory, I think the reason that tasks were created and deleted on an as-needed basis, so occurred frequently, was that we originally had the product on an AVR ATxmega128A1U with 8 KB RAM and 128 KB Flash using external BLE and Wi-Fi chips. So the idea of being tightly resource constrained and only allocating/using what one needed temporarily was the initial mindset when porting to the ESP32 with FreeRTOS especially by the low-level firmware and hardware developer. In fact, our judicious use of RAM which had shared sections (i.e. one at a time usage for non-overlapping usage types) was still needed in the ESP32 in order to use Deep Sleep which only has 8 KB of RTC (slow) preservied-during-sleep memory even though it had 520 KB of additional RAM when awake. Ours is an IoT battery-operated product so minimizing power consumption is essential.
Also, is it just TCB memory? Isnât it also thread local stack memory? I think itâs the latter (as you later mentioned) that rather quickly added up when tasks were created/deleted without calling the idle task. It didnât take that many (a few dozen?) create/delete of tasks before we ran out of memory.
Since most of our hardware is âone-at-a-timeâ usage, the hardware low-level firmware developer doesnât pre-allocate all the tasks and with stack memory that would use up an awful lot of memory given the many hardware components and functions available. Multiple PWM motors, LEDs, photodiodes (ADC), accelerometer, humidity sensor, moisture sensor, water flow sensor, temperature, voltage, plus Wi-Fi tasks. Given that the device is asleep most of the time and only wakes up to do the heaviest measurements once or a few times per day and only checks in with the cloud every 30 minutes, it doesnât make sense to have everything pre-allocated all at once, especially when we were using Deep Sleep that did not retain most of the memory (of course one is starting from scratch in that case so wouldnât run out of memory) and where a wake up most of the time would go back to sleep so you didnât want to do a lot during startup. The ESP32-C3 we moved to uses Light Sleep so retains memory due to integrated BLE that wakes up every second (or so) for advertising so has to be kept alive (i.e. keep memory) though light sleep does have more of an energy consumption penalty (130 ”A vs. 10 ”A for deep sleep not counting additional leakage currents from our components).
We donât only do serial debugging and do GDB/breakpoint and GPIO signal and other debugging (mostly looking at external signals) in the very rare cases it is needed. Most low-level bugs are in hardware/library interaction where examining registers/states is most helpful even printing those out over serial. Pretty much the only timing bugs we see are certain required waits for other devices or library code (i.e. not doing certain calls back-to-back) which really are flaws of others we have to work around (unless they are documented). Because of how we write our code, we donât have multi-processor (ESP32 had 2 cores) deadlocks and in fact I found an ESP-IDF bug that did have a race condition assertion and I created the trivial fix (they had two source code lines out of order) but it took me a month to convince Espressif to fix it though once they put a senior developer looking at it he immediately committed it and backported the fix to earlier releases as well. But mostly, serial is used extensively for all kinds of product testing (i.e. firmware application and low-level code toegether) and initiating functionality locally not requiring the mobile app the user would use.
If we step up from the weeds here the high-level point isnât freeing memory or tickling the watchdog timer but rather why these are deferred to an idle task in the first place? For self-deleted tasks, couldnât the scheduler deal with that freeing of memory instead of waiting for the idle task to run to do it? As for tickling the watchdog timer, shouldnât that be something the developer does in their event/sleep loop since they know when they are in an effectively idle execution path whether they end up blocking or sleeping or not? Having the watchdog tickle be an option for a priority 0 idle task is fine but having it be unavoidable is an issue as FreeRTOS configuration currently only allows full on/off for task watchdog, not just turning it off for the idle task (which you donât allow) but still using it elsewhere (which you do allow) nor having it as an explicit tickle function call, not just watching to see if a task is run.
It seems like the FreeRTOS design doesnât just encourage fully blocked-till-interrupt design (i.e. no busy status or wait loop) but fully requires it in spite of the real-world dealing with 3rd party (MCU vendor) libraries that may not play that way. As aggarg stated in his response, we have a workaround in our code and you want to avoid promoting bad designs, but the real-world sometimes doesnât cooperate with ideals. As for avoiding 3rd party libraries, that is impossible with some chips like the Espressif ESP32 variants because their integrated RF code is opaque (not open-source) and they also have undocumented ROM functions but also most of their library code works well and is useful. On balance, itâs been pretty fast to develop with their development environment.