Read Serial Output and Debug the Board
Learning Objectives
By the end of this lesson you can:
- Open the PlatformIO serial monitor and set its speed in
platformio.ini. - Print text, numbers, and formatted values from the ESP32 to your computer.
- Explain what a boot log, a Guru Meditation Error, and a silent port each tell you.
- Fix the two most common failures: an upload that cannot connect, and a board that prints nothing.
Quick Review
In the last lesson you flashed a Blink sketch. That single upload already proved four things work together: your computer, the USB cable, the PlatformIO toolchain, and the board itself.
Now we want to hear the board talk back. Serial output is the cheapest debugging tool you own, and you will use it in every later lesson while building the BLE gadget.
Theory
What "serial" actually means here
The ESP32 sends text over a serial line: one bit after another, on a single wire. On most dev boards that line goes through a small USB-to-serial chip (commonly CP2102 or CH340), which converts it into a USB signal your computer understands. Some newer boards (ESP32-S3, C3, and friends) can also do USB directly.
The catch: both sides must agree on the baud rate, the number of bits per second. If the board sends at 115200 and the monitor listens at 9600, you get readable garbage or nothing at all. Arduino-style ESP32 sketches almost always use 115200.
[!IMPORTANT] A baud mismatch is not an error. Nothing crashes. You simply see nonsense characters, or an empty window. Always check speed before you suspect broken hardware.
The three pieces you need
| Piece | What it does | Where it lives |
|---|---|---|
Serial.begin(115200) |
Opens the serial port at the chosen speed | Inside setup() |
Serial.print / println / printf |
Sends text to the computer | Inside setup() or loop() |
| Monitor | Displays what arrives | PlatformIO toolbar or pio device monitor |
Tell PlatformIO the speed once, in platformio.ini, so you never have to remember it:
[env:esp32dev]
platform = espressif32
board = esp32dev
framework = arduino
monitor_speed = 115200
monitor_filters = esp32_exception_decoder
monitor_filters = esp32_exception_decoder is a small gift to your future self: when the board crashes, it translates raw memory addresses into actual function names.
Two ways a board can fail to talk
- The upload fails. PlatformIO cannot reach the chip. Typical message:
Failed to connect to ESP32: Timed out waiting for packet header. - The upload succeeds but the board is silent. The program runs, but no text reaches the monitor.
These have completely different causes, so diagnose them separately.
flowchart TD
Start["π§©<br/>No output in monitor"] --> Upload{"Did the upload succeed?"}
Upload -- no --> Boot["Hold BOOT, tap EN<br/>then upload again"]
Upload -- yes --> Port{"Is a port selected?"}
Port -- no --> Pick["Choose the port in the toolbar"]
Port -- yes --> Baud{"Monitor speed 115200?"}
Baud -- no --> Fix["Set monitor_speed = 115200"]
Baud -- yes --> Begin{"Serial.begin in setup()?"}
Begin -- no --> Add["Add Serial.begin(115200)"]
Begin -- yes --> Crash["Look for a reset loop<br/>or Guru Meditation Error"]
Reading a crash
When the ESP32 hits a serious fault it prints a Guru Meditation Error, a line like Core 0 panic'ed (LoadProhibited), a register dump, and a backtrace of hex addresses. With the exception decoder filter on, those addresses turn into file names and line numbers. That backtrace is the single most useful thing serial output gives you.
Worked Examples
Example 1: Print something useful
#include <Arduino.h>
void setup() {
Serial.begin(115200);
delay(1000); // let the USB port settle on native-USB boards
Serial.println();
Serial.println("Booting BLE gadget...");
Serial.printf("Chip: %s, cores: %d\n", ESP.getChipModel(), ESP.getChipCores());
}
void loop() {
static uint32_t count = 0;
Serial.printf("tick %lu, free heap %u bytes\n", count++, ESP.getFreeHeap());
delay(1000);
}
Steps to verify it:
- Paste the code, then click Upload.
- Click the Serial Monitor plug icon in the PlatformIO toolbar.
- You should see the boot lines followed by one
tickline per second. - Press the EN button. The monitor restarts from
Booting BLE gadget..., which proves you are watching a live connection.
Example 2: "Failed to connect to ESP32"
- Confirm the correct port is selected, not
Auto. - Close any other program holding the port (Arduino IDE, another monitor tab).
- Hold the BOOT button, tap EN, release BOOT, then upload again. This forces the chip into download mode.
- If it still fails, try a different USB cable. Charge-only cables have no data wires, and this fools almost everyone once.
Example 3: Upload works, monitor is blank
- Check
monitor_speedmatchesSerial.begin. Both should be 115200. - Check that
Serial.begin(115200)is really insetup(). A sketch without it stays silent. - Look for a repeating boot banner. If the board prints the banner again every few seconds, it is crashing and restarting, so scroll up for the panic message.
- Check the free heap in your output. A number falling toward zero means a memory leak.
Discussion & Reflection Prompts
- Why is a baud mismatch harmless to the board but fatal to your debugging?
- Which single line of serial output would have saved you the most time in your last project?
- How would you debug a gadget once it is running on battery with no USB cable attached? (Hold that thought: this becomes real in Module 3.)
Expert Corner
- Print a
delay(1000)afterSerial.begin()on native-USB boards. Without it, the first lines vanish before the port is ready. - Add one startup line naming the firmware version. It tells you instantly whether the new upload actually landed.
- Avoid printing inside tight loops. Serial output is slow and will change the timing of your code.
- Use
Serial.printffor values andSerial.printlnfor labels; your logs stay readable. - Rule of thumb: if the board is silent, suspect the port, then the speed, then the code. In that order.
Summary & Next Steps
Serial output is a two-way contract: the board opens a port with Serial.begin(115200), and the monitor listens at the same speed. Most "broken" boards are simply unselected ports, wrong baud rates, or a missing Serial.begin. Upload failures are a separate problem, usually solved by the BOOT/EN trick or a better cable.
Quick self-check:
- The monitor speed is set by {{c1::monitor_speed}} in
platformio.ini. - The standard ESP32 monitor speed in this course is {{c2::115200}}.
- A crash report beginning with "Guru Meditation Error" includes a {{c3::backtrace}} of memory addresses.
- An upload that times out is often fixed by holding {{c4::BOOT}} while tapping EN.
- Garbage characters in the monitor point to a {{c5::baud rate}} mismatch.
[
{ "front": "What does Serial.begin(115200) do?", "back": "Opens the serial port at 115200 bits per second so the board can send text to the computer." },
{ "front": "Which platformio.ini setting sets the monitor speed?", "back": "monitor_speed = 115200" },
{ "front": "What happens if the monitor speed and Serial.begin speed differ?", "back": "You see garbage characters or nothing, but nothing breaks on the board." },
{ "front": "Which filter turns crash addresses into function names?", "back": "monitor_filters = esp32_exception_decoder" },
{ "front": "What does a Guru Meditation Error indicate?", "back": "A serious CPU fault such as an invalid memory access, usually followed by a backtrace." },
{ "front": "How do you force the ESP32 into download mode?", "back": "Hold BOOT, tap EN, release BOOT, then upload again." },
{ "front": "Upload times out with 'Failed to connect to ESP32'. Name one common cause.", "back": "Wrong or busy port, a charge-only USB cable, or no download mode entered." },
{ "front": "Upload succeeds but the monitor is empty. What do you check first?", "back": "The selected port, then the monitor_speed value matching Serial.begin." },
{ "front": "Why add delay(1000) after Serial.begin()?", "back": "It gives the USB serial port time to come up so the first lines are not lost." },
{ "front": "Which call prints formatted values with placeholders?", "back": "Serial.printf, for example Serial.printf(\"heap %u\\n\", ESP.getFreeHeap());" },
{ "front": "Repeating boot banner in the monitor means what?", "back": "The board is crashing and restarting in a loop; scroll up for the panic message." },
{ "front": "Why avoid Serial prints inside a tight loop?", "back": "Serial output is slow and changes the timing of your program." },
{ "front": "Which ESP32 pins carry UART0 by default?", "back": "GPIO1 for TX and GPIO3 for RX." },
{ "front": "What does a free heap value falling toward zero suggest?", "back": "A memory leak that will eventually crash the board." }
]
Next up is the Module 1 checkpoint, where you will connect the board, create a project, upload, and read serial output on your own. In Module 2 we put that same serial window to work while building the BLE gadget's GATT service.