ESP32 Guru Meditation Errors: How to Debug Crashes (Serial Logs + Backtrace Decode)

When an ESP32 randomly resets, freezes, or shows a โ€œGuru Meditation Errorโ€, one of the simplest and most effective ways to find the problem is to log progress to the Serial Monitor.

The idea is very basic:

Add Serial.println("Line X"); after every line (or block) that might cause trouble.
Then check which โ€œLine Xโ€ is the last one printed before the ESP32 crashes.

This is called instrumenting your code and itโ€™s often enough to pinpoint the exact spot where things break.

1. Basic setup: enable Serial logging

First, you need Serial logging enabled in setup():

void setup() {
  Serial.begin(115200);  // Start serial at 115200 baud
  delay(1000);           // Give the USB serial some time to connect
  Serial.println();
  Serial.println("Booting...");
}

Open the Serial Monitor at 115200 baud so you can see the output.


2. The simplest example: increase a counter and print it

Letโ€™s start with a tiny example where we increase a counter and print it to Serial.

int counter = 0;

void setup() {
  Serial.begin(115200);
  delay(1000);
  Serial.println();
  Serial.println("Booting...");
}

void loop() {
  counter++;  // increase counter

  Serial.print("Counter value: ");
  Serial.println(counter);

  delay(500); // slow things down so we can read the output
}

This code does nothing fancy: it just increments counter and prints the value every 500 ms.

Now letโ€™s see how to apply the โ€œLine Xโ€ debugging pattern.


3. Adding Serial.println("Line X") to locate crashes

Imagine you have a more complex loop() and the ESP32 crashes sometimes. You suspect that something inside loop() is causing it, but you donโ€™t know exactly where.

Weโ€™ll use an example that deliberately crashes when the counter reaches 10, just to demonstrate the method:

int counter = 0;

void setup() {
  Serial.begin(115200);
  delay(1000);
  Serial.println();
  Serial.println("Booting...");
}

void loop() {
  Serial.println("Line 1: loop start");

  counter++;
  Serial.print("Line 2: counter = ");
  Serial.println(counter);

  // Some "suspicious" block that might cause a crash
  Serial.println("Line 3: before crash-prone code");

  if (counter == 10) {
    Serial.println("Line 4: about to do something dangerous");

    int *ptr = nullptr;      // null pointer (bug)
    *ptr = 123;              // CRASH HERE

    Serial.println("Line 5: after dangerous code (will never print)");
  }

  Serial.println("Line 6: end of loop");
  delay(500);
}

What happens:

  • For counter = 1 to 9, everything runs fine.
  • When counter becomes 10, we hit the if (counter == 10) block.
  • We dereference a null pointer (*ptr = 123;) which will cause a crash (Guru Meditation).

In the Serial Monitor you might see something like:

Line 1: loop start
Line 2: counter = 9
Line 3: before crash-prone code
Line 6: end of loop
Line 1: loop start
Line 2: counter = 10
Line 3: before crash-prone code
Line 4: about to do something dangerous
โ†ณ CRASH (no more lines)

Notice:

  • You see up to โ€œLine 4: about to do something dangerousโ€,
  • But you never see โ€œLine 5: after dangerous codeโ€.

Therefore the crash happens between Line 4 and Line 5 โ†’ specifically on the *ptr = 123; line.

Thatโ€™s the essence of this method.


4. How to apply this to real projects

In a real ESP32 project you donโ€™t deliberately crash the code โ€” you just get unexpected resets or Guru Meditation errors.

The debugging steps are the same:

  1. Pick the suspicious area
    For example, the part where you:
    • Access a sensor
    • Manipulate arrays
    • Work with pointers or dynamic memory
    • Use Wi-Fi, MQTT, SPI, IยฒC, etc.
  2. Add line markers before and after each risky line / block For example: Serial.println("Line A: before reading sensor"); readSensor(); // might cause issues Serial.println("Line B: after reading sensor"); Serial.println("Line C: before sending MQTT"); sendMqttMessage(); // might cause issues Serial.println("Line D: after sending MQTT");
  3. Observe the last line printed before the crash
    • If you see โ€œLine Aโ€ but not โ€œLine Bโ€, the crash is inside readSensor().
    • If you see โ€œLine Bโ€ and โ€œLine Cโ€ but not โ€œLine Dโ€, the crash is inside sendMqttMessage().
  4. Go inside the suspicious function and repeat
    • Inside readSensor() add more Serial.println("X1"), ("X2"), etc.
    • Narrow down until you hit the exact line.

5. Tips to keep output readable

A few small habits make your debug logs much easier to understand:

5.1 Use unique labels

Avoid reusing the same text everywhere. Instead of:

Serial.println("line");
Serial.println("line");
Serial.println("line");

Use:

Serial.println("L1: begin loop");
Serial.println("L2: sensor read OK");
Serial.println("L3: preparing data");
Serial.println("L4: sending over WiFi");

So when you scroll in the Serial Monitor, you instantly know where it stopped.

5.2 Include variable values

Printing just Line 1, Line 2, Line 3 helps, but printing usually both label + value is better:

Serial.print("L2: counter = ");
Serial.println(counter);

This way, if the crash happens only when counter = 37, you see that pattern.

5.3 Donโ€™t spam inside very tight loops

If you print inside a loop that runs thousands of times per second, you may flood the serial buffer and slow everything down.

For high-frequency code, print only occasionally:

if (counter % 1000 == 0) {
  Serial.print("L5: counter = ");
  Serial.println(counter);
}

6. Remove or reduce logging in the final version

Once youโ€™ve found and fixed the bug:

  • Either comment out the debug prints, or
  • Wrap them in a debug macro you can easily turn off later.

Example macro:

#define DEBUG 1

#if DEBUG
  #define DBG(x) Serial.println(x)
#else
  #define DBG(x)
#endif

Then use:

DBG("Line X: something happened");

When you set DEBUG to 0, all those debug messages disappear from the compiled code.


7. Summary

Debugging ESP32 crashes doesnโ€™t have to be complicated:

  1. Enable Serial logging with Serial.begin(115200);
  2. Add Serial.println("Line X"); before and after any code that might be risky.
  3. Watch the Serial Monitor and see which โ€œLine Xโ€ is the last one printed before the crash.
  4. Zoom in by adding more prints inside that suspicious area.

With just this simple technique and a few Serial.println("Line X") markers, you can quickly narrow down the exact line that is crashing your ESP32 โ€” even in quite large projects.

Share your love