You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Copy file name to clipboardExpand all lines: doc/faq/a02-my-esp-crashes.rst
+72
Original file line number
Diff line number
Diff line change
@@ -10,6 +10,7 @@ My ESP crashes running some code. How to troubleshoot it?
10
10
- `Exception <#exception>`__
11
11
- `Watchdog <#watchdog>`__
12
12
- `Check Where the Code Crashes <#check-where-the-code-crashes>`__
13
+
- `Other Causes for Crashes <#other-causes-for-crashes>`__
13
14
- `If at the Wall, Enter an Issue
14
15
Report <#if-at-the-wall-enter-an-issue-report>`__
15
16
- `Conclusion <#conclusion>`__
@@ -229,6 +230,77 @@ dropped. The same procedure applies to crashes caused by exceptions.
229
230
able to correctly decode the stack trace dropped by some other
230
231
application not compiled and loaded from your Arduino IDE.
231
232
233
+
234
+
Other Causes for Crashes
235
+
~~~~~~~~~~~~~~~~~~~~~~~~
236
+
237
+
Interrupt Service Routines
238
+
By default, all functions are compiled into flash, which means that the
239
+
cache may kick in for that code. However, the cache currently can't be used
240
+
during hardware interrupts. That means that, if you use a hardware ISR, such as
241
+
attachInterrupt(gpio, myISR, CHANGE) for a GPIO change, the ISR must have the
242
+
ICACHE_RAM_ATTR attribute declared. Not only that, but the entire function tree
243
+
called from the ISR must also have the ICACHE_RAM_ATTR declared.
244
+
Be aware that every function that has this attribute reduces available memory.
245
+
246
+
In addition, it is not possible to execute delay() or yield() from an ISR,
247
+
or do blocking operations, or operations that disable the interrupts, e.g.: read
248
+
a DHT.
249
+
250
+
Finally, an ISR has very high restrictions on timing for the executed code, meaning
251
+
that executed code should not take longer than a very few microseconds. It is
252
+
considered best practice to set a flag within the ISR, and then from within the loop()
253
+
check and clear that flag, and execute code.
254
+
255
+
Asynchronous Callbacks
256
+
Asynchronous CBs, such as for the Ticker or ESPAsync* libs, have looser restrictions
257
+
than ISRs, but some restrictions still apply.
258
+
It is not possible to execute delay() or yield() from an asynchronous callback.
259
+
Timing is not as tight as an ISR, but it should remain below a few milliseconds. This
260
+
is a guideline. The hard timing requirements depend on the WiFi configuration and
261
+
amount of traffic. In general, the CPU must not be hogged by the user code, as the
262
+
longer it is away from servicing the WiFi stack, the more likely that memory corruption
263
+
can happen.
264
+
265
+
Memory, memory, memory
266
+
Running out of heap is the most common cause for crashes. Because the build process for
267
+
the ESP leaves out exceptions (they use memory), memory allocations that fail will do
268
+
so silently. A typical example is when setting or concatenating a large String. If
269
+
allocation has failed internally, then the internal string copy can corrupt data, and
270
+
the ESP will crash.
271
+
272
+
In addition, doing many String concatenations in sequence, e.g.: using operator+()
273
+
multiple times, will cause memory fragmentation. When that happens, allocations may
274
+
silently fail even though there is enough total heap available. The reason for the
275
+
failure is that an allocation requires finding a single free memory block that is large
276
+
enough for the size being requested. A sequence of String concatenations causes many
277
+
allocations/deallocations/reallocations, which makes "holes" in the memory map. After
278
+
many such operations, it can happen that all available holes are too small to comply
279
+
with the requested size, even though the sum of all holes is greater than the requested
280
+
size.
281
+
282
+
So why do these silent failures exist? On the one hand, there are specific interfaces that
283
+
must be adhered to. For example, the String object methods don't allow for error handling
284
+
at the user application level (i.e.: no old-school error returns).
285
+
On the other hand, some libraries don't have the allocation code accessible for
286
+
modification. For example, std::vector is available for use. The standard implementations
287
+
rely on exceptions for error handling, which is not available for the ESP, and in any
288
+
case there is no access to the underlying code.
289
+
290
+
*Some techniques for reducing memory usage*
291
+
292
+
* Don't use const char * with literals. Instead, use const char[] PROGMEM. This is particularly true if you intend to, e.g.: embed html strings.
293
+
* Don't use global static arrays, such as uint8_t buffer[1024]. Instead, allocate dynamically. This forces you to think about the size of the array, and its scope (lifetime), so that it gets released when it's no longer needed. If you are not certain about dynamic allocation, use std libs (e.g.: std:vector, std::string), or smart pointers. They are slightly less memory efficient than dynamically allocating yourself, but the provided memory safety is well worth it.
294
+
* If you use std libs like std::vector, make sure to call its ::reserve() method before filling it. This allows allocating only once, which reduces mem fragmentation, and makes sure that there are no empty unused slots left over in the container at the end.
295
+
296
+
Stack
297
+
The amount of stack in the ESP is tiny at only 4KB. For normal developement in large systems, it
298
+
is good practice to use and abuse the stack, because it is faster for allocation/deallocation, the scope of the object is well defined, and deallocation automatically happens in reverse order as allocation, which means no mem fragmentation. However, with the tiny amount of stack available in the ESP, that practice is not really viable, at least not for big objects.
299
+
* Large objects that have internally managed memory, such as String, std::string, std::vector, etc, are ok on the stack, because they internally allocate their buffers on the heap.
300
+
* Large arrays on the stack, such as uint8_t buffer[2048] should be avoided on the stack and be dynamically allocated (consider smart pointers).
301
+
* Objects that have large data members, such as large arrays, should be avoided on the stack, and be dynamicaly allocated (consider smart pointers).
0 commit comments