Skip to main content

Welcome to a Grasp lesson

Create a free account to keep studying and create your own study path.

Lesson 13

ESP32 Logging for Debugging

Recommended resources

Hello! Welcome back to our course on C++ for the ESP32.

In the last lesson, you mastered the fundamental development cycle: building, flashing, and monitoring your application. You saw how to get basic output to your serial monitor using std::cout. While functional, this approach is like using a simple multimeter for all your diagnostics; it works, but for complex systems, you need more specialized tools.

This lesson introduces a professional-grade diagnostic tool: the ESP-IDF logging framework. You will learn how to use its powerful functions to generate structured, color-coded, and filterable debugging output. This is a crucial skill for writing and debugging any non-trivial embedded application. Specifically, we will focus on using the logging functions to output messages at different severity levels.

1. Beyond printf: The Power of Structured Logging

In any complex project, from mechanical systems to software, you need a way to monitor what's happening internally. Simply printing every piece of information (printf or std::cout) quickly leads to a cluttered and unmanageable stream of data. A structured logging system solves this by allowing you to categorize messages by importance and origin.

The ESP-IDF provides this through the esp_log.h library. To understand its core components, please read the following material.

ESP-IDF tutorial series: Logging

This article from Espressif's developer blog, 'ESP-IDF tutorial series: Logging', provides an excellent introduction to the core concepts of the logging framework. It clearly explains why it's superior to printf and details its fundamental building blocks.

Please read from the beginning of the article down to the end of the 'Verbosity' section. Focus on understanding the three core elements: Tags, Logging macros, and Verbosity levels.

As you've just read, the system is built on three key ideas:

  1. Tags: A simple string (e.g., "MOTOR_CONTROL", "WIFI_SETUP") used to label and group messages from a specific part of your code. This helps you immediately identify the source of a log message.
  2. Logging Macros: A family of functions, ESP_LOGx, where x corresponds to a severity level.
  3. Verbosity Levels: A setting that controls the minimum severity level of messages that will actually be printed.

2. The Five Levels of Logging

The ESP-IDF defines five severity levels, each with its own macro. In your C++ code, you will include the header #include "esp_log.h" to use them.

Macro Level Color Purpose
ESP_LOGE Error Red Critical errors, failures that halt a functionality.
ESP_LOGW Warning Yellow Unexpected events or potential issues.
ESP_LOGI Info Green Major lifecycle events (e.g., "WiFi connected").
ESP_LOGD Debug White Detailed information for debugging specific functions.
ESP_LOGV Verbose White The most detailed information, for fine-grained tracing.

The key concept is that setting a verbosity level (e.g., Info) will show messages at that level and all levels above it (Info, Warning, and Error).

ESP32 Logging Verbosity Diagram
This diagram clearly shows the hierarchy of logging levels. When you set a specific verbosity level, all messages at that level and those with higher priority (to the left) are displayed, while those with lower priority (to the right) are suppressed.

3. Putting Logging into Practice

Let's modify the cpp_project from our previous lessons to use the logging framework.

  1. Open your main.cpp file.
  2. Include the logging header at the top: #include "esp_log.h"
  3. Just before app_main, define a tag for this file: static const char* TAG = "MAIN";
  4. Replace the contents of your app_main function with the following code. This code logs one message at each severity level and then enters the familiar counting loop.
#include <iostream>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "esp_log.h"

// Define a tag for this file's log messages
static const char* TAG = "MAIN";

extern "C" void app_main(void)
{
    // Log one message at each level
    ESP_LOGE(TAG, "This is an error message.");
    ESP_LOGW(TAG, "This is a warning message.");
    ESP_LOGI(TAG, "This is an info message.");
    ESP_LOGD(TAG, "This is a debug message.");
    ESP_LOGV(TAG, "This is a verbose message.");

    int count = 0;
    while (1) {
        ESP_LOGI(TAG, "Loop count: %d", count++);
        vTaskDelay(pdMS_TO_TICKS(1000));
    }
}

Now, build, flash, and monitor your project using the combined command you learned in the last lesson:
idf.py flash monitor

You should see an output similar to this:

ESP32 Logging Output in VS Code Terminal
Example output from the ESP-IDF monitor. Notice the color-coding and the prefixes: `E` for Error, `W` for Warning, and `I` for Info. The timestamp in milliseconds and the log tag (`MAIN`) are also clearly visible.

You'll notice that only the Error, Warning, and Info messages are printed. The Debug and Verbose messages are missing. This is because the default log verbosity for a new ESP-IDF project is set to Info.


4. Controlling Global Log Verbosity

To see the Debug and Verbose messages, you need to change the project's default log verbosity level. The primary way to do this is through the project configuration menu, menuconfig.

ESP32 - Kconfig with ESP-IDF Tutorial

This video by Shawn Hymel provides a concise, practical demonstration of how to change the log verbosity. Watching someone go through the steps is often the quickest way to learn.

Watch from 8:28 to 10:02. This section shows exactly how to navigate the menuconfig interface to find and change the 'Default log verbosity'. Follow along and change the setting in your own project.

To summarize the process:

  1. Exit the monitor (Ctrl+]).
  2. Run idf.py menuconfig.
  3. Navigate to Component Config -> Log output.
  4. Select Default log verbosity.
  5. Change the setting from Info to Verbose.
  6. Save the new configuration and exit menuconfig.

Now, run idf.py flash monitor again. Because you changed the configuration, the project will recompile. Once it's running, you will see all five of your log messages appear in the monitor, including the white Debug and Verbose ones.

Test your understanding!

After setting the log level to Verbose and seeing all messages, you go back into menuconfig and change the Default log verbosity to Warning. After you save, build, and flash, which of your five initial ESP_LOGx messages will appear in the monitor?

Show answer

Only the ESP_LOGE (Error) and ESP_LOGW (Warning) messages will appear. The Warning level includes messages at that severity and any higher severities (in this case, only Error). Info, Debug, and Verbose messages will be suppressed.


5. Finer-Grained Control

While menuconfig is great for setting a project-wide default, sometimes you need more dynamic or specific control. The ESP-IDF provides two powerful ways to do this.

ESP-IDF tutorial series: Logging

The Espressif logging tutorial also covers more advanced filtering techniques. These are extremely useful for focusing on a specific problem without being overwhelmed by logs from other parts of the system.

Please read the section titled 'Filtering Logs', including the subsections 'Filtering at Runtime' and 'Filtering in the IDF Monitor'.

Runtime Control with esp_log_level_set()

You can change the verbosity for a specific tag while your code is running. This is perfect for temporarily increasing the detail level for a module you are actively debugging, without flooding the console with verbose logs from other, stable parts of your code.

For example, to temporarily silence all but the most critical messages from our MAIN tag, you could add this line:
esp_log_level_set(TAG, ESP_LOG_ERROR);
After this line executes, only ESP_LOGE(TAG, ...) messages will be printed for that tag.

Monitor-Side Filtering with --print-filter

This is arguably one of the most powerful features for debugging. It allows you to filter the log output on your computer without changing the code on the ESP32 at all. The ESP32 sends all messages (up to the menuconfig verbosity limit), but idf.py monitor only shows you what you ask for.

For example, to see only Debug-level messages (and higher) from the MAIN tag, and only Errors from all other tags (*), you would start the monitor like this:
idf.py monitor --print-filter="MAIN:D *:E"

This allows you to zero in on a problem area instantly.


Conclusion

You have now moved from basic printf-style debugging to a robust, structured logging system. This is a significant step toward writing clean, maintainable, and easily debuggable embedded software.

Key takeaways from this lesson include:

  • The ESP-IDF logging framework (esp_log.h) is superior to printf for its structure, severity levels, and configurability.
  • Messages are categorized using tags (for origin) and macros like ESP_LOGI (for severity).
  • The global log verbosity is set project-wide using idf.py menuconfig, which controls which messages are compiled into your binary.
  • You can dynamically adjust log levels for specific tags at runtime using esp_log_level_set().
  • You can filter the output in the idf.py monitor using the --print-filter argument for on-the-fly debugging without recompiling.

In the next lesson, we will use the skills you've just acquired to interpret the ESP32's boot messages. The flood of text you see when the device first starts is actually a series of log messages. Understanding them is key to diagnosing common startup issues, like problems with power, flashing, or hardware configuration.