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 = 1to9, everything runs fine. - When
counterbecomes 10, we hit theif (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:
- 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.
- 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"); - 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().
- If you see โLine Aโ but not โLine Bโ, the crash is inside
- Go inside the suspicious function and repeat
- Inside
readSensor()add moreSerial.println("X1"),("X2"), etc. - Narrow down until you hit the exact line.
- Inside
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:
- Enable Serial logging with
Serial.begin(115200); - Add
Serial.println("Line X");before and after any code that might be risky. - Watch the Serial Monitor and see which โLine Xโ is the last one printed before the crash.
- 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.
