Difficulty: Intermediate | Time: 30 Minutes | Target Board: ESP32-WROOM-32 DevKit V1 (4MB Flash)

When an ESP32 crashes in the field, it doesn't just stop—it panics. The Espressif panic handler throws a Guru Meditation Error and reboots, leaving you with a cryptic hex backtrace. To enable ESP32 coredump in Arduino IDE, you must select a partition scheme that includes a coredump partition (like 'Default 4MB with spiffs'), set the Core Debug Level to Verbose, and use the bundled espcoredump.py tool to extract and decode the flash memory after a panic.

This guide walks through the exact partition configuration, provides a compilable crash-test sketch, and details how to decode the resulting dump to find the exact line of C++ code that caused the failure.

Parts List and Pin Mapping

This build targets the ubiquitous 4MB flash variant. If you are using an 8MB or 16MB board (like the ESP32-S3), the partition schemes will differ slightly, but the core concepts remain identical.

ComponentExact Variant / SpecNotes
MicrocontrollerESP32-WROOM-32 DevKit V1 (4MB Flash)Must have minimum 4MB flash for standard coredump partition
Trigger Switch6x6mm Tactile PushbuttonUsed to manually trigger the null-pointer crash
Wiring22 AWG solid core jumper wiresFor connecting the button to GPIO 0
CableUSB-A to Micro-USB (Data + Power)Charge-only cables will fail during serial extraction

Pin Mapping Table

ESP32 GPIOComponentFunction
GPIO 0Pushbutton (to GND)Crash Trigger (Internal Pull-up enabled)
GPIO 2Onboard LEDSystem Heartbeat Indicator
GNDPushbutton CommonCircuit Ground

Step-by-Step: Enabling Coredump in Arduino IDE

The Arduino IDE doesn't have a simple 'Turn on Coredump' checkbox. Instead, coredump functionality is baked into the ESP32 hardware abstraction layer, but it requires a dedicated flash partition to store the memory snapshot when a crash occurs.

  1. Open the Boards Manager: Ensure you are using the official esp32 board package by Espressif Systems (v2.0.x or v3.0.x). Community forks often strip out the Python tooling required for decoding.
  2. Select the Partition Scheme: Go to Tools > Partition Scheme. You must choose a scheme that allocates space for a coredump. Select 'Default 4MB with spiffs (1.2MB APP/1.5MB SPIFFS)' or 'Huge APP (3MB No OTA/1MB SPIFFS)'. Both of these default partition CSVs include a 64KB coredump partition at the end of the flash map.
  3. Set Core Debug Level: Go to Tools > Core Debug Level and select Verbose. This ensures the panic handler prints the maximum amount of register state to the UART before writing to flash.
  4. Enable Erase All Flash (Optional but Recommended): If you suspect flash corruption from previous uploads, go to Tools > Erase All Flash Before Sketch Upload and set it to Enabled for the first upload. Turn it off afterward to preserve your WiFi credentials and SPIFFS data.
Bench Tip: If your sketch size exceeds the APP partition limit after enabling Verbose debugging, switch to the 'Huge APP' partition scheme. Coredumps only require 64KB of flash, which both schemes provide.

The Crash Code: Triggering and Capturing the Dump

To verify your setup, we need to intentionally crash the ESP32. The following code targets the ESP32-WROOM-32 DevKit V1. It blinks an LED to prove the system is alive, then waits for you to press the button on GPIO 0. When pressed, it intentionally dereferences a null pointer, triggering a hardware exception that the panic handler will catch and write to the coredump partition.

#include <Arduino.h>
#include <esp_system.h>

// Pin definitions for ESP32-WROOM-32 DevKit V1
#define CRASH_TRIGGER_PIN 0  // Active LOW, uses internal pull-up
#define STATUS_LED_PIN 2     // Onboard blue LED

// Volatile pointer initialized to NULL to guarantee a crash
volatile int* null_pointer = NULL;

void setup() {
  Serial.begin(115200);
  delay(1000); // Allow serial monitor to connect
  
  pinMode(CRASH_TRIGGER_PIN, INPUT_PULLUP);
  pinMode(STATUS_LED_PIN, OUTPUT);
  
  Serial.println("System Booted. Coredump partition is active.");
  Serial.println("Press the button on GPIO 0 to trigger a StoreProhibited panic.");
}

void loop() {
  // Heartbeat blink to prove the RTOS scheduler is running
  digitalWrite(STATUS_LED_PIN, HIGH);
  delay(250);
  digitalWrite(STATUS_LED_PIN, LOW);
  delay(250);
  
  // Check for crash trigger (Button pressed = LOW)
  if (digitalRead(CRASH_TRIGGER_PIN) == LOW) {
    Serial.println("Button pressed. Initiating intentional null-pointer dereference...");
    Serial.flush(); // Ensure serial buffer empties before the hard crash
    
    // This line causes a StoreProhibited Guru Meditation Error
    *null_pointer = 42; 
    
    // Execution will never reach here
    Serial.println("This will not print.");
  }
}

Decoding the Dump: First Three Things to Check When It Fails

When the code above crashes, your Serial Monitor will output a wall of text ending in a reboot. The exact error string you will see is:

Guru Meditation Error: Core 1 panic'ed (StoreProhibited). Exception was unhandled.
Core 1 register dump:
PC : 0x400d1234 PS : 0x00060130 A0 : 0x800d1250 A1 : 0x3ffb1f00

Because we enabled the coredump partition, this state is now saved in flash. To read it, you must use the espcoredump.py script located in your Arduino ESP32 hardware tools folder. However, before you even run the Python script, here are the first three things to check when analyzing any ESP32 crash:

1. Task Stack High Water Mark (Stack Overflow)

A StoreProhibited or LoadProhibited error is frequently caused by a stack overflow corrupting adjacent memory. In your loop() or task function, add this line to check remaining stack space:

Serial.printf("Stack High Water Mark: %d bytes\n", uxTaskGetStackHighWaterMark(NULL));

If this value drops below 200 bytes before the crash, your task is overflowing its allocated stack. Increase the stack size in your xTaskCreate call or move large local arrays to the heap using malloc or std::vector.

2. Uninitialized Pointers and Array Bounds

The StoreProhibited panic means the CPU tried to write to a memory address it doesn't own (often 0x00000000 or 0xFFFFFFFF). Check every pointer dereference. Did malloc() return NULL because the heap was fragmented? Are you iterating past the end of a C-style array? The coredump ELF parser will point you to the exact line number where the illegal write occurred.

3. Task Watchdog Timer (TWDT) Starvation

If your error string instead reads Task Watchdog got triggered, your code isn't crashing due to memory corruption; it's being assassinated by the RTOS for hogging the CPU. Ensure every tight while() or for() loop contains a yield(), delay(1), or vTaskDelay(1) to feed the watchdog and allow the WiFi/BT stack to process background events.

Extending and Simplifying Your Debug Build

Once you have the basics working, you can tailor the coredump behavior to fit your specific development workflow or production constraints.

How to Simplify the Build (UART Coredump)

Running Python scripts to extract flash dumps is tedious if you are sitting right in front of the device. You can simplify your workflow by forcing the coredump to print as a Base64 string directly to the Serial Monitor. To do this, you must create a custom sdkconfig file or use the ESP-IDF menuconfig (if using PlatformIO/ESP-IDF natively) to set CONFIG_ESP_COREDUMP_TO_UART=y. In the Arduino IDE, the easiest simplification is to rely on the backtrace decoder built into the ESP32 Arduino Core v2.0.x+, which automatically maps hex addresses to file names and line numbers in the Serial Monitor without needing external Python tools.

How to Extend the Build (Custom Partitions & ESP Insight)

If you are building a commercial product with OTA updates, flash space is premium. You can extend your build by writing a custom partition CSV that shrinks the coredump partition to exactly 32KB (enough for small RAM snapshots) and reclaims the rest for your APP. For fleet management, extend your firmware by integrating ESP Insight, an Espressif service that automatically captures coredumps, packages them with device metadata, and uploads them to a cloud dashboard via WiFi when the device reboots after a panic.

Frequently Asked Questions

How do I enable ESP32 coredump in Arduino IDE without installing ESP-IDF?

You don't need the full ESP-IDF environment installed to capture the dump; selecting the correct Partition Scheme in the Arduino IDE Tools menu handles the capture. However, to decode the binary flash dump into human-readable C++ line numbers, you must use the espcoredump.py script. This script is bundled inside the Arduino ESP32 hardware directory (usually under ~/.arduino15/packages/esp32/tools/esp32-arduino-libs/), so you can run it using the Python environment already installed on your OS without downloading the multi-gigabyte ESP-IDF framework.

Why is my coredump partition missing from the Arduino IDE Tools menu?

The Arduino IDE doesn't list 'coredump' as a standalone menu item. The coredump partition is embedded inside the Partition Scheme CSV files. If you select a scheme like 'No OTA (2MB APP)' or certain minimal 2MB flash schemes, the CSV file does not allocate a coredump row to save space for the application. Always choose a 4MB scheme labeled 'Default' or 'Huge APP' to guarantee the 64KB coredump partition is present at the end of the flash map.

Can I use ESP32 coredump over Wi-Fi or MQTT instead of Flash/UART?

Not natively at the exact moment of the crash. When a Guru Meditation Error occurs, the WiFi radio and TCP/IP stack are immediately suspended or corrupted, making network transmission impossible during the panic handler. The dump must be written to local Flash or UART first. To get it over MQTT, you must write a boot-up routine that checks if a coredump exists in flash (using esp_core_dump_image_check()), reads it, and transmits it via WiFi on the next successful boot before clearing the partition.

What does 'Core 1 panic'ed (StoreProhibited)' actually mean?

The ESP32 is a dual-core chip (Core 0 handles WiFi/BT, Core 1 runs your Arduino loop()). 'StoreProhibited' is a hardware-level Memory Protection Unit (MPU) exception. It means the CPU on Core 1 attempted to write data ('Store') to a memory address that is either unmapped, read-only (like IRAM or ROM), or protected by the RTOS. It is the embedded equivalent of a 'Segmentation Fault' in desktop Linux, almost always pointing to a bad pointer, a null reference, or a stack overflow.