Skip to main content

Debugging with ESP-IDF VS Code extension: Part 2

·8 mins·
Debugging ESP-IDF ESP32-C3 ESP32-S3 Vscode
Author
Francesco Bez
Developer Relations at Espressif
Table of Contents
This two-part guide shows how to set up VS Code with the ESP-IDF extension to debug Espressif boards using JTAG. In this second part, we will debug a simple project using gdb through Espressif’s VSCode extension. We will explore the debugging options for navigating the code and inspecting the variables.

Introduction
#

In the first part, we introduced the essential tools for firmware debugging and explained how to establish an openOCD connection with the Espressif device.

In this second part, we’ll launch a debugging session, step through the code using navigation commands, and inspect variables in real time. By the end of this tutorial, you’ll have a solid understanding of what a debugger can do and why it’s often the most effective tool for diagnosing and fixing malfunctioning firmware.

In this article, we will:

  1. Get an overview of the debug window and available commands.
  2. Learn the differences between key debugging navigation commands.
  3. Explore the tools available for inspecting variables.

Prerequisites

This article is the second part of a series. For the setup, please refer to the first part.

Debug window overview
#

A debugger gives you control over how a program runs, letting you pause execution, inspect variables, and step through code one instruction at a time. This level of control is what sets debugging apart from simply running the program normally.

Breakpoint
#

One way to use this control is by stepping line by line through the code, watching how variables change and how the program flows. While this can be helpful, it quickly becomes impractical in larger programs. Instead, it’s often more effective to let the program run freely until it reaches a specific point of interest. That’s where breakpoints come in. A breakpoint pauses execution at a defined line, giving you the opportunity to examine the program’s state and decide what to do next.

Starting a debugging section
#

In this first debugging session, we’ll set a breakpoint and reach it using the first navigation command: Continue.

  • Open hello_world_main.c from the hello_world example project.
    If the project isn’t open yet, refer to the first part of this article.
  • Click on line 24 to set a breakpoint. A red dot will appear next to the line number (see Fig. 1).
  • Press F5 (or go to Run → Start Debugging). This starts the debugging session.
    If a dropdown menu appears instead, check that a launch.json file exists in the .vscode folder. You can find a basic launch.json here.
  • Hit F5 again (Continue) to reach the breakpoint (see Fig.2).
Fig.1 - Breakpoint set

Fig.1 - Breakpoint set

Fig.2 - Debugging session started

Fig.2 - Debugging session started

Navigation commands explanation#

In the debugging interface, the available commands are located in the top-right corner. They are:

Here’s the polished version of your list:

  • Continue: Runs the code until the next breakpoint is reached.
  • Step Over: Executes the current instruction or the entire function, then moves to the next instruction at the same level.
  • Step Into: Executes the current instruction (like Step Over), but if it’s a function call, it enters the function and allows you to debug it line by line.
  • Step Out: Executes the remaining code in the current function and exits to the calling function.
  • Restart: Restarts the debugging session from the beginning.
  • Disconnect: Disconnects from openOCD, effectively stopping the debugging session.

All the commands are self-explanatory, except for the subtle yet important difference between Step Over and Step Into.

In other words, Step Into allows you to enter a function call and debug it line by line, while Step Over executes the entire function without stepping into it, moving directly to the next line in the current function.

Navigation commands#

Now, let’s put the navigation commands into action to see how they work in practice.

In the remainder of this section, we will:

  1. Modify the hello_world_main.c example.
  2. Start a new debugging session.
  3. Explore the difference between Continue and Step Over.
  4. Examine the difference between Step Over and Step Into.

Modifying hello_world_main.c
#

Let’s add a simple summation function before the main function void app_main(void):

// Function to calculate the summation of all integers from 1 to 'number'
int summation(int number){
  int sum = 0;
  for(int i=1;i<=number;i++){
    sum += i;
  }
  return sum;
}

And change the void app_main(void) content to

void app_main(void)
{
    printf("Hello world!\n");
    int final_number = 6;
    int sum = summation(final_number);
    printf("The summation up to %d is %d\n",final_number,sum);
}

Your hello_world_main.c should now look like this gist.

Start a new debugging session
#

Now, let’s start a new debugging session:

  • Save the updated hello_world_main.c file.
  • Set a breakpoint at the line int final_number=6.
  • Press F5 to begin the debugging session. Your screen should now resemble Scr.A, as shown in Fig.1.

The debugger stops execution at the beginning of the app_main function and waits for commands.

Continue vs Step Over
#

Let’s look at the difference between Continue and Step Over with the help of Fig. 3.

Fig.3 - Continue vs Step-over

Fig.3 - Continue vs Step-over

  1. Continue (F5) – Scr A → Scr B
    Execution proceeds normally until it reaches the breakpoint we previously set.
  2. Step-Over (F10) – Scr A → Scr C
    The debugger executes the current line and then moves to the next line in the same function.

Before moving on, let’s restart the debugging session.

Restarting the debugging session

  • Press ⇧+F5 to exit the debugging session.
  • Press F5 to enter a new debugging session
    We are in Scr A (Fig. 3) again
  • Press F5 again to reach the breakpoint
    We’re now in Scr. B (Fig. 1) which is the same as Scr. α (Fig.4)

Step Over vs Step Into
#

Let’s now look at the difference between Step Over and Step Into with the help of Fig. 4.

Fig. 4 - Step-over vs step-into

Fig. 4 - Step-over vs step-into

Since the current line in Scr. α involves a function call, it’s the perfect case to highlight the difference between Step Over and Step Into.

  • Step-over (F10) – Scr. α → Scr. β
    The function is executed, and the debugger moves to the next line in the current function.
  • Step-into (F11) – Scr. α → Scr. γ
    The debugger steps into the function, allowing you to inspect it line by line.

To continue inspecting the function, you can keep using the Step Over command. When you’re ready to exit the function call, you have two options:

  • Step Out (⇧+F11) — Scr. β
    Executes the remaining lines of the function and resumes at the line following the function call.

  • Continue (F5) — Scr. α
    Resumes execution until the next breakpoint is hit.

These navigation commands help you move through your code with precision. Next, let’s look at how to inspect variables during debugging.

Inspecting variables
#

So far, we’ve navigated the code flow, but without inspecting variables, it’s hard to grasp what’s really happening.

In this section, we’ll look into the inspecting variables tools with the help of Fig.5.

Fig.5 - Variable inspection

Fig.5 - Variable inspection

Let’s start a new debugging session and reach the line sum += i.

Restarting the debugging session

  • Press ⇧+F5 to exit the debugging session.
  • Set a breakpoint at line sum += i
  • Press F5 to enter a new debugging session
  • Press F5 again to reach the breakpoint
As you know by now, we have a few options to get to the sum +=1 line. The easiest one (followed above) is to set a breakpoint on the line and use Continue.

Available inspection tools
#

At this point of the code, three variables are defined : number, i and sum. The next step is to read their value.

You can inspect the variable in three places:

  1. In the VARIABLES pane under Local on the left (see Fig 5)
  2. In the DEBUG CONSOLE, by typing the variable name and hitting Enter
  3. By adding them to the WATCH pane

VARIABLES displays all variables currently defined in the function scope, including local variables, global variables, and register values.

DEBUG CONSOLE is the most convenient tool for quick inspections. Use it to evaluate expressions and examine memory.

WATCH is useful for monitoring variables and expressions you want to track. Just click the + icon on the right side of the pane (see Fig. 5).

VARIABLES pane
#

When the breakpoint is first reached, VARIABLESLocal displays:

sum = 0
i = 1

Remember, the core hasn’t executed the highlighted line yet—that’s why sum is still 0.

Pressing Continue (F5) again results in what we expect to see:

sum=1
i=2

DEBUG console
#

Among the available methods to view variable values during execution, the DEBUG CONSOLE — highlighted at the bottom of Fig.5 - is the most flexible.

At any moment during the debugging session, you can write an expression in the DEBUG CONSOLE which is evaluated when hitting Enter ⏎.

Type (sum+1)*2 on the DEBUG CONSOLE (next to the > sign, as shown in Fig. 5). As expected, you will get 4.

WATCH pane
#

As a final step, let’s explore the WATCH tool. It allows you to enter full expressions, just like in the DEBUG CONSOLE. These expressions are saved and automatically evaluated in every debugging session, with their current values shown.

To see the WATCH pane in action:

  • Click + on the right side of WATCH pane. A text input “Expression to watch” appears.
  • Type sum*sum inside of the text input
  • Start a new session (⇧+F5 followed by F5)
  • Hit F5 again to reach the breakpoint

In the WATCH pane a sum*sum= 0 appeared.

This field is updated according to the expression value. Press F5 repeatedly to generate the sequence 0, 1, 9, 36, ... which corresponds to the square of the sum.

The calculation is performed in the IDE, not on the core.

Conclusion
#

In this second part, we explored the various functions of the debugger. We first examined different ways to navigate through the code and highlighted the differences between Continue, Step-into, and Step-over. Then, we focused on tools for inspecting variables, including the DEBUG CONSOLE, the VARIABLES pane, and the WATCH tool. With these tools at your disposal, you’re now equipped to enhance your coding efficiency, identify issues more easily, and improve the overall quality of your code.

Related

Debugging with ESP-IDF VS Code extension: Part 1
·9 mins
Debugging ESP-IDF ESP32-C3 ESP32-S3
This two-part guide shows how to set up VS Code with the ESP-IDF extension to debug Espressif boards using JTAG. This first part covers the debugging process, hardware setup and connections, and starting the openOCD server.
ESP32 bootstrapping in Zephyr
·15 mins
ESP32 ESP32-S2 ESP32-S3 ESP32-C3 ESP32-C6 ESP-IDF Zephyr
ESP-IDF Tutorials: Soft-AP
·9 mins
Soft-AP ESP32-C3 ESP-IDF
This tutorial guides you through setting up a soft-AP using an Espressif module and ESP-IDF. It covers the process of creating a project, configuring Wi-Fi, and handling connection events through event loops. Upon completion, you’ll be able to establish a soft-AP and manage Wi-Fi connections. It is the first step to building more advanced networking applications.