Skip to main content

Debugging with ESP-IDF VS Code extension: Part 1

·9 mins·
Debugging ESP-IDF ESP32-C3 ESP32-S3
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. This first part covers the debugging process, hardware setup and connections, and starting the openOCD server.

Introduction
#

Debugging is a critical part of the development process, especially when working with embedded systems. It allows developers to identify and fix errors, ensuring that the firmware interacts correctly with the peripherals and the external hardware. While there are several ways to debug code, the most powerful method is to use a debugger, which provides real-time insights into how the code runs on the hardware.

Many developers start with basic debugging, often using printf statements to track program flow and check variable values. While helpful, this method has major limits. Printf-based debugging forces you to change the code and recompile it each time you test something new. In contrast, debuggers let you step through code, inspect memory, and set breakpoints without touching the source.

In this guide, we’ll walk through the steps necessary to set up debugging for Espressif devices using Visual Studio Code (VS Code) and the ESP-IDF Extension. The ESP-IDF Extension manages the whole toolchain, but knowing the tools it uses, like openOCD and gdb, gives you more control and helps you fix problems that may arise along the way.

This guide will show you the two available hardware paths to debug Espressif devices (JTAG pins and USB pins) and the interacting tools which make debugging possible.

This article has two parts. In this first part, we will explain the debugging process, describe the tools, set up the hardware, and start the openOCD server. In the second part, we will run gdb and explore its options for debugging the code.

In the rest of the article, we’ll use “Espressif device" to refer to both the modules and SoCs, since the steps below apply to both.

Prerequisites

Before you begin, make sure you have the following:

  • An Espressif development board based on one of the following series: ESP32-C3, ESP32-C6, or ESP32-S3

  • Either an expendable USB cable (that supports data transfer) or the ESP-PROG debug probe

  • Visual Studio Code (VS Code)
    If not installed, follow the installation instructions

  • ESP-IDF Extension for VS Code
    If not installed, follow the setup instructions

  • Ability to use ESP-IDF extension for VS Code to build, flash, and monitor a simple example project, such as blink or hello_world
    If you’re unsure, check Create an ESP-IDF Project

Debugging process
#

The debugging process requires several tools working together. You interact with the IDE, which manages the compiler, the debugger and the flasher (software) tool. The flasher tool communicates to a (hardware) programmer which can upload the firmware to the flash memory of the microcontroller and manage JTAG interactions.

With USB enabled devices, the programmer/debugger is built into the SoC (but remains external to the workstation).

You can find an overview of the debugging process in Fig. 1.

Fig.1 - Debugging Workflow

Fig.1 - Debugging Workflow

We will use VSCode IDE with ESP-IDF Extension, which manages the following tools for both flashing and debugging:

  • Compiling and flashing: The extension uses idf.py, which is a CLI front-end tool which takes care of the compilation (CMake/ninja) and the flashing (esptool.py) processes.
  • Debugging: The extension connects seamlessly with esp-gdb which derives from the venerable GNU Debugger (gdb). The esp-gdb interfaces with a forked version of openOCD called openocd-esp32 which supports newly released SoC series sooner than the original openOCD. We will use the terms openocd-esp32 and openOCD interchangeably. The same goes for esp-gdb and gdb.
With older ESP-IDF plugin versions, you needed to install openOCD separately. Now it comes directly with ESP-IDF plugin.

Testing openOCD availability

Let’s check if your version of VS Code extension comes with openOCD installed.

Most commands in VSCode are executed through the Command Palette, which you can open by pressing CTRL+SHIFT+P (or CMD+SHIFT+P if you’re on mac-os). In this guide, commands to enter in the Command Palette are marked with the symbol > .
  • Lauch VS Code.
  • Open a new Terminal.
    > ESP-IDF: Open ESP-IDF-Terminal
  • In the terminal type
    openocd --version
    

You should read something like

Open On-Chip Debugger v0.12.0-esp32-20241016 (2024-10-16-14:27)

Now that we ensured we’re using an updated IDE, let’s set up the hardware.

Hardware for debugging
#

Espressif devices usually require separate hardware for flashing and debugging via JTAG. By default, you flash Espressif devices using the serial port (UART0) which does not support JTAG. Note that most of Espressif development boards do not include a JTAG probe. A notable exception is the ESP32-WROVER-KIT. They only offer a USB-to-UART connection for flashing and monitoring.

To debug an development board—or any Espressif-based board—you have two options:

  1. JTAG over JTAG pins (with a JTAG probe) – briefly described below for completeness
  2. JTAG over USB pins – used in the rest of the article

All Espressif devices support the first option. The second option works only with USB-equipped devices, such as the ESP32-S3 or ESP32-C3. Although both options are shown, in the rest of the article, we will use the JTAG over USB pins option.

JTAG over JTAG pins
#

To debug using the JTAG pins, you first need a JTAG probe. Espressif offers a probe called ESP-PROG, whose JTAG connector is shown in Fig.2.

Fig.2 - ESP-PROG JTAG connector

Fig.2 - ESP-PROG JTAG connector

To enable JTAG programming, connect the four pins TMS, TCK, TDO, TDI of the JTAG probe to the corresponding Espressif module pins.

In the table below, you can find the pins for three common modules.

JTAGESP-PROGESP32-C3-MINI-1ESP32-S3-WROOM-1ESP32-C6-WROOM-1
TMSESP_TMSGPIO 4GPIO 42GPIO 4
TCKESP_TCKGPIO 6GPIO 39GPIO 6
TDOESP_TDOGPIO 7GPIO 40GPIO 7
TDIESP_TDIGPIO 5GPIO 41GPIO 5

For other modules, consult the Pin Description section of the datasheet and look for MTMS, MTCK, MTDO, MTDI pins.

The JTAG peripheral is generally enabled by default and can be disabled in production and for security reasons using the appropriate eFuses (EFUSE_DISABLE_JTAG or EFUSE_HARD_DIS_JTAG depending on the SoC series).

While the ESP-PROG works with most Espressif devices, it requires additional hardware and uses up to four pins. In newer cores, you have a second option via USB, as shown Debugging workflow illustration in Fig.1.

JTAG over USB
#

Most newer Espressif SoC series integrate the USB peripheral and it can be used for JTAG debugging. Take a thick usb cable (designed for both charging and data transmission) and strip it using a wire stripper or a pair of scissors. You should see the four wires inside, as shown in Fig.3.

Fig.3 - USB cable pins

Fig.3 - USB cable pins

Now connect GND , D+ and D- to the corresponding Espressif module pins. If you’re using an development board you can connect VCC to the +5V of the pin (not the 3v3 pin!).

USB supply pin is about 5V. To power your board with it, connect VCC to the input of an LDO regulator that outputs 3.3V for the Espressif module.

In the table below, you can find the pins for three common modules.

ESP32-C3-MINI-1ESP32-S3-WROOM-1ESP32-C6-WROOM-1
D+GPIO 19GPIO 19GPIO 13
D-GPIO 18GPIO 20GPIO 12

For other modules, consult the Pin Description section of the datasheet and look for USB_D+ and USB_D-.

Now that the hardware is ready, we can move back to the IDE. We will use the JTAG over USB pin option.

OpenOCD connection
#

OpenOCD uses a server-client model to debug embedded systems. The OpenOCD server connects to the target hardware through a debug adapter (usually via JTAG). It exposes a network interface that clients, such as gdb or telnet, can use to send commands or load code.

Next, we’ll start the openOCD server and test the connection with the hardware. The steps below are for an ESP32-C3 board, but you can easily adjust them for your module. We will use the JTAG over USB pins option. If you’re using the ESP-PROG, modify steps 2 and 3 accordingly.

  1. Create a project from the hello_world example.
    If you’re unsure, please check the documentation
  2. > ESP-IDF: Select Port to Use (COM, tty, usbserial) - Choose the port you’re connected to. You may find the SoC name and package next to the correct option (e.g. ESP32-C3 (QFN32) (revision v0.3))
  3. >ESP-IDF: Set Espressif Device targetesp32c3ESP32-C3 chip (via Built-in USB-JTAG)
    ("esp32c3" should now appear at the bottom left corner)
  4. >ESP-IDF: Select OpenOCD Board ConfigurationESP32-C3 chip (via Built-in USB-JTAG)
  5. >ESP-IDF: OpenOCD ManagerStart OpenOCD
The Scan for kits notification may appear. You can allow it to automatically set up the port and board, but be sure to check the result!

After completing the steps, you should see:

Open On-Chip Debugger v0.12.0-esp32-20241016 (2024-10-16-14:27)
Licensed under GNU GPL v2
For bug reports, read
	http://openocd.org/doc/doxygen/bugs.html
debug_level: 2

Info : only one transport option; autoselecting 'jtag'

Info : esp_usb_jtag: VID set to 0x303a and PID to 0x1001
Info : esp_usb_jtag: capabilities descriptor set to 0x2000

Info : Listening on port 6666 for tcl connections
Info : Listening on port 4444 for telnet connections

Info : esp_usb_jtag: serial (84:F7:03:42:8C:A8)

Info : esp_usb_jtag: Device found. Base speed 40000KHz, div range 1 to 255

[...]

Info : [esp32c3] Examination succeed
Info : [esp32c3] starting gdb server on 3333
Info : Listening on port 3333 for gdb connections

Congratulations, openOCD server is now waiting for a connection. In the second part, we’ll start gdb and connect to it.

Troubleshooting
#

Some issues may have logs that are hard for beginners to understand. Below are two common problems and how to fix them.

Address already in use
#

You could gets

❌ Error: couldn't bind tcl to socket on port 6666: Address already in use

it means that openOCD is already running. To close it, you have to:

  • Open an ESP-IDF terminal: ≫ESP-IDF: Open ESP-IDF Terminal
  • Find the process number: ps aux | grep openocd (e.g. 13819)
  • Kill the process: kill <process-nr> (e.g. kill 13819)

Could not find or open device
#

You could get Error: esp_usb_jtag: could not find or open device (ESP32-S3-DevKitC-1) (OCD-631) #219

it may mean that the firmware inside the board does not expect the connection.

You may need to

  • Build the project
    > ESP-IDF: Build your project.
  • Flash the firmware
    > ESP-IDF: Flash (UART) your project.

If you followed the steps above, the flashing is performed via the JTAG peripheral through the USB pins.

If it’s still not working, connect your board via UART0 pins (which in an development board can be accessed using the development board USB port)

  • Choose the right port
    ESP-IDF: Select Port to Use (COM, tty, usbserial)
  • Flash it again
    > ESP-IDF: Flash (UART) your project.

Conclusion
#

In this tutorial, you learned how to connect your Espressif module via JTAG and start the openOCD server. In part two, we’ll launch gdb and explore the code using various commands.

Related

ESP32 bootstrapping in Zephyr
·15 mins
ESP32 ESP32-S2 ESP32-S3 ESP32-C3 ESP32-C6 ESP-IDF Zephyr
ESP-IDF Tutorials: Soft-AP
·10 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 and logging. 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.
Espressif part numbers explained: A complete guide - Modules
·7 mins
ESP32 ESP32-S2 ESP32-S3 ESP32-H2 ESP32-C3