Hi, I’ve had a rummage around the reference doco, and these forums, but cannot find an answer, so here’s asking:
Is there an unusually small limit on stack size/depth for Timer callback handlers? I’ve been using four timers and they’ve behaved exactly as expected, but I added a fifth and initially got an immediate stack-overflow, as soon as the timer was called. The timer contained two calls to Log.info() with several simple variables formatted into the output, but nothing else.
Its function was to provide an independent diagnostic output without being in the main event loop (where I was previously using a delay(3000) call to manage the frequency of serial output updates). It’s not a critical part of the code, and I’ve resolved the issue by just getting the timer to set a flag every n-seconds, and testing/resetting the flag in the event loop to print diagnostic output if set.
Mainly, I want to know if there is a limit, and how to find it? I’m more concerned with creating code that is just a smidge below some fatal stack threshold and getting random failures out in the field when the planets align!
I note the ApplicationWatchdog function has a configurable stack, but Timers don’t, and there’s no mention of a limit in this regard.
The docs say the limit is 10 timers; perhaps you have some other issue? Your ideas about setting a flag are valid, you want to keep the Timer to bare minimum… like you would an ISR.
I guess my question should really have been more focussed. Obviously, Electron (application area) RAM is fixed and relatively small at 20KB and, if my rusty C brain is correct, data+bss is what I’ve used of that. So the heap+stack should have ~15KB of freedom.
My application has no explicit malloc()s, but has interrupts (small, variable setting, with typically a call to digitalRead), timers (some of which do a fair bit of non-blocking logic and typically call Log.trace() but no other ‘deep’ functions that I am aware of), main loop structures (calls to Cellular.command(), with its callback containing sscanf(), strncpy() and Log.trace()).
I guess these could put a reasonable amount of local variable and constant string parameter data on the stack if they managed to synch up in normal operation. Even so, I can’t really see it getting anywhere more than a few KB total. I wasn’t expecting a stack overflow risk.
Is there any tooling that can be used to investigate the stack/heap size in real-time?
The stats returned by Particle build for my current program:
Output of arm-none-eabi-size:
text data bss dec hex
34316 124 3364 37804 93
In a nutshell:
Flash used 34440 / 110592 31.1 %
RAM used 3488 / 20480 17.0 %
The Photon and Electron have about 60K of free RAM. The number in Particle Build is wrong (it’s for the Core). The System.freeMemory() call will give you the amount of free heap memory at runtime.
There is no function to return the available stack. Note that it’s small: 6144 bytes on the main loop thread and 1024 bytes for software timer callbacks and can’t be changed (without a custom build of system firmware).
Thanks, @rickkas7 and @ScruffR for the feedback. Those notes are very useful information to have. The stack overflow crash seems to have been a one off, easily reversed by undoing the code change, but not really relatable to the code used. Maybe it was the proverbial straw. I will review my code for aggregate size of local vars and parameters pushed onto the stack, although I think I’ve been reasonably pessimistic in my design process.
I have a further question, with regard to the use of software timers. The ref. doc. indicates that timer handlers are effectively in a chain. Am I correct in inferring that, assuming they became synchronised into the same RTOS timer event, one handler must exit before the next is called and, therefore, the total stack available for each is 1KB, ie. it’s never ‘stacked’ with more than one software timer at a time?
Also (I lied, yet another question!) if the system goes to SOS mode, I presume the software based ApplicationWatchdog is not going to save the day. In these circumstances, a hardware watchdog may be required? Can you point me to any sample code/examples of implementing that feature?
Correct, software timers are called sequentially so the stack usage is of each timer callback individually, not combined.
After a SOS panic, the device should reboot automatically, so you don’t need to worry about that.
The application watchdog will help catch some, but not all, situations where the device “hangs.” Since the application watchdog uses a thread, anything that stops all thread from running will stop the application watchdog from running. Only an external hardware watchdog will get out of that at this time. The most common reason for that is either a power problem such as insufficient power, infinite looping with interrupts disabled, corrupted memory, or deadlock causing thread switching to not occur.