Low-cost ESP32 In-circuit Debugging

Manuel Bl.
7 min readJul 7, 2019

If you have experience in software development, then you’ve probably never liked the Arduino IDE. Arduino is a great ecosystem with an incredible number of libraries and a variety of supported boards. Yet the IDE lacks essential features such a code completion and immediate error checking.

Chances are you have already switched to PlatformIO, a modern IDE that adds the missing features while still retaining the simple Arduino model and the benefits of the huge ecosystem. If you haven’t done so yet, switch now because PlatformIO also allows you to debug your program directly on the device— a valuable feature that will take you to a new level of micro controller programming. The PlatformIO features relevant for makers are free — including debugging.

Not all boards are capable of being run under the control of a debugger. The ATmega is such an example. However, more powerful chips are — in particular the chips with an ARM core and the ESP32.

This is an instruction how to setup on-device debugging in PlatformIO for the ESP32. Three different adapters for debugging are shown, the least expensive one for USD 8 including shipping.

The instructions assume that you have already installed PlatformIO (the version with Visual Studio Code) and have a working project that uploads and runs code on your ESP32 board.

Debugging the ESP32

In order to debug code on the ESP32, PlatformIO runs OpenOCD in the background. It’s an open-source on-chip debugger — hence the name. You don’t need to install it, PlatformIO will take care of it.

OpenOCD communicates with the ESP32 via the JTAG protocol and port. JTAG is an industry standard for communicating with the internals of a chip. JTAG uses the five signals (plus ground):

  1. TDI (Test Data In)
  2. TDO (Test Data Out)
  3. TCK (Test Clock)
  4. TMS (Test Mode Select)
  5. TRST (Test Reset) optional.

On the ESP32, the JTAG pins are GPIO pins at the same time. So during debugging, you cannot use them. They are:

  • GPIO12 — TDI
  • GPIO15 — TDO
  • GPIO13 — TCK
  • GPIO14 — TMS

The reset pin (TRST) is not fully supported by OpenOCD. Fortunately, it’s optional.

To connect OpenOCD to the ESP32, a JTAG adapter is needed. The JTAG adapter has a USB and a JTAG port, similar to a USB-to-Serial adapter. It is connected between your computer and the ESP32 board. The direct USB connection to the ESP32 board is still needed. So the overall setup looks like so:

PlatformIO will upload the program using the esptool.py via the regular USB connection to the ESP32 board. The debugger uses the other connection that goes via USB to the JTAG adapter and then via the JTAG protocol to the ESP32 board.

Adapter 1: ESP-Prog

The ESP-Prog Board is the official debugging board of Espressif, the designers of the ESP32 chip. The design is open source — schematic and PCB layout are available publicly. The boards can be bought from Ebay, Aliexpress and many local distributors and start at about USD 18 including shipping.

ESP-Prog board

Connecting the board

To debug your program, connect the JTAG and ground pins to the ESP32 boards, e.g. using Dupont wires. The JTAG pins can be found on the biggest connector on the board:

JTAG pins on the ESP-Prog board

Then connect both the ESP32 board and the ESP-Prog board via USB to your computer.

ESP-Prog adapter connected to an ESP32 development board

Driver management

When you connect the ESP-Prog board, your computer will most likely detect it automatically, load a driver and create two virtual serial ports. The main chip on the board is a FTDI FT2232HL, a two channel USB-to-UART (and additional protocols) IC. So that’s the expected behaviour. Unfortunately, that’s not what’s needed for debugging. The serial driver prevents OpenOCD from using the JTAG pins. So it must be unloaded.

Unload driver on macOS

MacOS comes with a FTDI driver out of the box and it will create two serial ports named /dev/cu.usbserial-00006014A and /dev/cu.usbserial-00006014B. To unload the driver and get rid of the serial ports, run the following command in the terminal:

sudo kextunload /Library/Extensions/FTDIUSBSerialDriver.kext

Unload driver on Windows

On Windows, a tool called Zadig is used to unload the driver. Download and run it:

Zadig for unloading the FTDI driver
  • In the Options menu, check List All Devices
  • Select Dual RS232-HS (Interface 0) from the main drop down
  • Click Replace Driver

The FTDI driver will be unloaded, and the WinUSB driver from Microsoft will be loaded instead. It’s a generic USB driver that gives applications full control over the USB communication with the device.

Configuring your PlatformIO project

Next go to your PlatformIO project and two lines to the project’s platform.ini file:

debug_tool = esp-prog
debug_init_break = tbreak setup

That’s it: setup completed. You’re ready for debugging.

Start debugging your code

In Platform IO / Visual Studio Code:

  • Click the Debug icon on the left side to open the debugger view
  • Click the green Start Debugging button at the top to start your app with the debugger.
  • The debugger will automatically break (stop) the code at the beginning of the setup() function.
  • You can then set additional breakpoints by clicking left of the line number. A red dot will appear.
Debugging in PlatformIO

For more instructions how to use the debugger, watch Andreas Spiess’ YouTube video Free Inline Debugging for ESP32 and Arduino Sketches.

Additional features of the ESP-Prog board

The ESP-Prog board isn’t just a JTAG adapter. It also provides:

  • RX/TX pins for serial communication (they can be used concurrently as the FT2232HL is a dual-channel chip),
  • a voltage regular that can provide 3.3V,
  • the typical circuitry to enable and reset the ESP32 chip via the DTR and RTS signals of the serial connection.

None of these features are used in the above setup as they are already present on the ESP32 development board. They might be quite useful with a naked ESP32 module. But since there are not needed, a simpler board also works for debugging…

Adapter 2: Generic FT2232HL Board

As mentioned, the ESP-Prog board has a FT2232HL chip at its core. Most of the circuitry around it is for features that are not needed in this setup. So a generic FT2232HL board also works as a JTAG adapter. In fact, they are a drop-in replacement. They can be bought from Ebay and Aliexpress, starting at about USD 10 including shipping.

CJMCU FT2232HL board

The JTAG port is on the pins AD0 to AD3. So connect them to the ESP32 board like so:

  • GPIO12 — AD1 (TDI)
  • GPIO15 — AD2 (TDO)
  • GPIO13 — AD0 (TCK)
  • GPIO14 — AD3 (TMS)
  • GND — GND
FT2232HL board (in dead bug style) connected to an ESP32 development board

That’s it. Everything else (unloading the FTDI driver, configuring the PlatformIO project etc.) is exactly the same as above.

Adapter 3: Generic FT232H board

There is yet another, even less expensive option: generic FT232H or FT232HL boards (L stands for lead-free). The FT232HL has the same features as the FT2232HL chip — except it hast just a single channel. The boards are available from Ebay and Aliexpress starting at USD 8 including shipping.

CJMCU FT232H board

The connections are the same as for the other generic FTDI boards:

  • GPIO12 — AD1 (TDI)
  • GPIO15 — AD2 (TDO)
  • GPIO13 — AD0 (TCK)
  • GPIO14 — AD3 (TMS)
  • GND — GND
FT232H board (in dead bug style) connected to an ESP32 development board

Unloading the driver and extending platform.ini is the same as above. However, if you start the debugger, you will find that it reports an error because the device cannot be found:

Error: unable to open ftdi device with vid 0403, pid 6010, description '*', serial '*' at bus location '*'

The hexadecimal numbers 0403 and 6010 are the USB vendor and product ID of the FT2232HL chip and OpenOCD looks for them. Unfortunately, the FT232H chip has the vendor/product ID 0403/6014 (also see USB ID Database). That’s also visible in Zadig on Windows and in macOS’ System Report (accessible from About This Mac).

So an additional change is needed: The vendor/product ID have to be added to an OpenOCD configuration file. Open the file esp32_devkitj_v1.cfg in an editor. It can be found in the directory packages/tool-openocd-esp32/share/openocd/scripts/interface/ftdi within your PlatformIO installation. The typical installation directories are /Users/username/.platformio on macOS and C:\Users\username\.platformio on Windows.

Then add the vendor/product ID pair 0x0403 0x6014 as shown below in bold:

# Driver for the FT2232H JTAG chip on the Espressif DevkitJ board

interface ftdi
ftdi_vid_pid 0x0403 0x6010 0x0403 0x6014
# interface 1 is the uart
ftdi_channel 0
# just TCK TDI TDO TMS, no reset
ftdi_layout_init 0x0008 0x000b
reset_config none

That’s it. Add the following lines to your project’s platform.ini (the same as for the other adapters) and start debugging:

debug_tool = esp-prog
debug_init_break = tbreak setup

Platform Updates

Every update of the Espressif 32 platform within PlatformIO will reset the configuration file to its original state. So you will have to reapply the simple configuration change after every update.

Happy coding and debugging…