Embedded Systems

ESP32 Beginner Course #6: Serial Monitor and Debugging Basics

Learn how to debug ESP32 projects using Serial Monitor, print variable values, inspect button and sensor readings, and solve common baud rate and wiring problems.

ESP32Serial MonitorDebuggingArduino IDEEmbedded Systems

Introduction

Every embedded developer eventually reaches the same moment: the code uploads, the circuit is connected, but the project does not behave as expected.

Maybe the LED never turns on. Maybe a button seems reversed. Maybe an analog sensor gives strange numbers. At that point, guessing is slow. You need a way to see what the ESP32 is doing.

Serial Monitor is the simplest debugging tool in Arduino IDE. It lets the ESP32 send text back to your computer so you can inspect values while the program runs.

What you will learn

By the end of this lesson, you should understand:

  • Why debugging matters
  • How Serial Monitor helps during development
  • How to use Serial.begin()
  • How to use Serial.print() and Serial.println()
  • How to print button and sensor values
  • How to check program timing with millis()
  • How to solve common Serial Monitor problems

Why debugging matters

When working with a microcontroller, you cannot directly look inside the chip. You need to ask questions:

  • Did the program start?
  • Is the button reading HIGH or LOW?
  • What ADC value is the sensor producing?
  • Is the code entering this if statement?
  • Is the timing correct?

Serial Monitor gives you a simple text output window for answering those questions.

Start Serial Monitor

To use Serial Monitor, start serial communication inside setup().

void setup()
{
    Serial.begin(115200);
    Serial.println("ESP32 started");
}

void loop()
{
}

After uploading, open Serial Monitor and set the baud rate to 115200.

If you see ESP32 started, you know:

  • The code uploaded correctly
  • The ESP32 restarted
  • Serial communication is working
  • Your baud rate is correct

That small message is often the first useful debug checkpoint.

Serial.print() vs Serial.println()

Serial.print() prints without moving to a new line.

Serial.println() prints and then moves to the next line.

Example:

Serial.print("Value: ");
Serial.println(123);

Output:

Value: 123

This pattern is very useful because labels make your debug output readable.

Debug a button input

In the push button lesson, we used INPUT_PULLUP. That means:

  • Released button reads HIGH
  • Pressed button reads LOW

Serial Monitor makes this easy to confirm:

const int buttonPin = 4;

void setup()
{
    Serial.begin(115200);
    pinMode(buttonPin, INPUT_PULLUP);
}

void loop()
{
    int buttonState = digitalRead(buttonPin);

    Serial.print("Button: ");
    Serial.println(buttonState);

    delay(200);
}

If the value never changes, check the wiring. If the value changes but feels reversed, remember that INPUT_PULLUP uses active-low logic.

Debug an analog input

For analog sensors, guessing is especially risky. Always print the raw value first.

const int sensorPin = 34;

void setup()
{
    Serial.begin(115200);
}

void loop()
{
    int adcValue = analogRead(sensorPin);
    float voltage = adcValue * (3.3 / 4095.0);

    Serial.print("ADC: ");
    Serial.print(adcValue);
    Serial.print("  Voltage: ");
    Serial.print(voltage);
    Serial.println(" V");

    delay(200);
}

This helps you answer two important questions:

  • Is the sensor changing?
  • Is the voltage in the expected range?

Before writing complex logic, first understand the numbers.

Debug timing with millis()

millis() returns the number of milliseconds since the ESP32 started running.

Serial.print("Time: ");
Serial.println(millis());

This is useful when checking delays, repeated events, and timing problems.

Later in the series, millis() becomes important when replacing blocking delay() code with non-blocking code.

Sometimes Serial Monitor becomes hard to read because the same value prints too quickly.

For a button, you can print only when the state changes:

const int buttonPin = 4;
int lastButtonState = HIGH;

void setup()
{
    Serial.begin(115200);
    pinMode(buttonPin, INPUT_PULLUP);
}

void loop()
{
    int buttonState = digitalRead(buttonPin);

    if (buttonState != lastButtonState)
    {
        Serial.print("Button changed to: ");
        Serial.println(buttonState);
        lastButtonState = buttonState;
    }

    delay(20);
}

This makes the output cleaner and easier to follow.

Common Serial Monitor mistakes

If Serial Monitor shows nothing:

  • Check the selected COM port
  • Check the baud rate
  • Confirm that Serial.begin() is inside setup()
  • Press the ESP32 reset button after opening Serial Monitor
  • Make sure the USB cable supports data, not only charging

If the output looks like random symbols, the baud rate is probably wrong.

If the output scrolls too fast, add a delay or print only when something changes.

Real engineering use cases

Serial debugging helps with:

  • Checking sensor values
  • Finding wiring mistakes
  • Confirming communication with modules
  • Understanding state machines
  • Debugging WiFi connection problems
  • Testing thresholds before adding automation

Even in professional embedded systems, logging is one of the most important debugging tools.

Engineering challenge

Create a debug sketch that prints:

  • A boot message
  • Button state
  • ADC value
  • Estimated voltage
  • LED state

Then make the output readable enough that someone else could understand the project state by looking only at Serial Monitor.

Next lesson

Next, we will use Serial Monitor with a real sensor project: an LDR light sensor. We will read light level, choose a threshold, and build a simple automatic night light.