Category: Linux

Debugging ESP32 with JTAG Adapter on Linux

Although Espressif’s website provides detailed instructions, this post offers a quick overview on steps required to setup a JTAG debugger adapter for ESP32 NodeMCU development board on Linux platform.


JTAG (Joint Test Action Group) is a useful interface for debugging and programming electronic devices such as microcontrollers and digital signal processors for several reasons:

  1. Non-Intrusive: JTAG is a non-intrusive interface, meaning it can be used to test and debug a device without affecting its normal operation. It provides a way to access the internal circuitry of a device, which is typically not accessible through other interfaces.
  2. Boundary Scan: JTAG includes a feature called boundary scan that allows designers to test and debug connections on printed circuit boards (PCBs). The boundary scan feature allows to test and diagnose problems with individual components or connection.
  3. In-System Programming: JTAG can be used to program a device while it is still mounted on a PCB or other system, which is known as in-system programming. This can be useful for updating firmware or debug device without needing to remove it from the system.
  4. Standardized Protocol: JTAG uses a standardized protocol defined by the IEEE 1149.1 standard, which ensures compatibility between devices and tools from different manufacturers.
  5. Semi-hosting: Provides a access to Host Resources, redirect “printf” or access to files on the host system as if they were on the local device, making it easier to test and debug software that relies on file input/output.
  6. Low-Cost: JTAG is a low-cost interface that is widely available on many microcontrollers and other electronic devices. This makes it a popular choice for developers and engineers who need to test and debug electronic systems.


  1. Linux host machine, in my case Linux Mint 21.1 distribution is used, but any modern Debian based Linux distribution should work.
  2. OpenOCD compatible JTAG debugger hadware, in my case FT2232D that was left over from a previous plug computer project.
    Reference design for this JTAG interface and connection schematics:
  3. Esp32 NodeMCU development board:

Setup OpenOCD

Because the version of OpenOCD available in this Linux release is outdated and lacks Xtensa support, install the latest release from the official repository:

For detailed instruction on howto build and install OpenOCD:

~/esp32$ git clone openocd-code
Cloning into 'openocd-code'...
remote: Enumerating objects: 77625, done.
Resolving deltas: 100% (64682/64682), done. 

Run “./bootstrap” when building from the git repository

~/esp32$ cd openocd-code/
~/esp32/openocd-code$ ./bootstrap
+ aclocal --warnings=all
+ libtoolize --automake --copy
+ autoconf --warnings=all
libjaylink/ installing 'build-aux/depcomp'
Bootstrap complete. Quick build instructions:
./configure ....

Now we can set some build options like hardware interface support, verbosity, installation directories and disable/enable other features.

~/esp32/openocd-code$ ./configure --enable-ftdi
checking for makeinfo... no
configure: WARNING: Info documentation will not be built.
checking for a BSD-compatible install... /usr/bin/install -c
checking whether build environment is sane... yes
OpenOCD configuration summary
MPSSE mode of FTDI based devices        yes
ST-Link Programmer                      yes (auto)
TI ICDI JTAG Programmer                 yes (auto)
Keil ULINK JTAG Programmer              yes (auto)
Altera USB-Blaster II Compatible        yes (auto)
Bitbang mode of FT232R based devices    yes (auto)
Versaloon-Link JTAG Programmer          yes (auto)
TI XDS110 Debug Probe                   yes (auto)
CMSIS-DAP v2 Compliant Debugger         yes (auto)
OSBDM (JTAG only) Programmer            yes (auto)
eStick/opendous JTAG Programmer         yes (auto)
Olimex ARM-JTAG-EW Programmer           yes (auto)
Raisonance RLink JTAG Programmer        yes (auto)
USBProg JTAG Programmer                 yes (auto)
Espressif JTAG Programmer               yes (auto)
CMSIS-DAP Compliant Debugger            no
Nu-Link Programmer                      no
Cypress KitProg Programmer              no
Altera USB-Blaster Compatible           yes (auto)
ASIX Presto Adapter                     yes (auto)
OpenJTAG Adapter                        yes (auto)
Linux GPIO bitbang through libgpiod     no
SEGGER J-Link Programmer                no
Bus Pirate                              yes (auto)
Use Capstone disassembly framework      yes (auto)

Then make and install as usual, be careful about conflicts with existing OpenOCD installation, remove or add prefix/suffix to the new OpenOCD executable.

~/esp32/openocd-code$ make -j16
~/esp32/openocd-code$ sudo make install

Add udev rules:

~/esp32/openocd-code$ nano /etc/udev/rules.d/10-ft2322D_jtag.rules
UBSYSTEMS=="usb", ATTRS{idVendor}=="9e88", ATTRS{idProduct}=="9e8f", MODE="0666"

Add user to dialout group:

~/esp32/openocd-code$ sudo usermod -aG dialout $USER

JTAG connections:

ESP32 pinJTAG pin#J6
3V3VTRef (3.3V)1
Esp32 to JTAG connections
Sheevaplug JTAG pinout

Detailed description for JTAG configuration can be found on this page:

For ESP32 pinout refer to this page:

OpenOCD Configuration and Testing

Now if we run openocd binary we will se somethink like this:

~/esp32/bare_v1$ openocd 
Open On-Chip Debugger v0.11.0-esp32-20221026 (2022-10-26-14:47)
Licensed under GNU GPL v2
For bug reports, read
embedded:startup.tcl:28: Error: Can't find openocd.cfg
in procedure 'script' 
at file "embedded:startup.tcl", line 28
Info : Listening on port 6666 for tcl connections
Info : Listening on port 4444 for telnet connections
Error: Debug Adapter has to be specified, see "adapter driver" command
embedded:startup.tcl:28: Error: 
in procedure 'script' 
at file "embedded:startup.tcl", line 28

This error, because of unknown adapter. We need to instruct OpenOCD about our hardware interface and target. This can be done by creating a configuration file named ‘esp32_jtag.cfg’ with next settings:

# FT2232D JTAG Adapter Configuration 
adapter driver ftdi
ftdi device_desc "SheevaPlug JTAGKey FT2232D B"
ftdi vid_pid 0x9e88 0x9e8f
ftdi channel 0
ftdi layout_init 0x0608 0x0f1b
ftdi layout_signal nTRST -data 0x0200 -noe 0x0100
ftdi layout_signal nSRST -data 0x0800 -noe 0x0400

# The speed of the JTAG interface, in kHz. If you get DSR/DIR errors (and they
# do not relate to OpenOCD trying to read from a memory range without physical
# memory being present there), you can try lowering this.
# On DevKit-J, this can go as high as 20MHz if CPU frequency is 80MHz, or 26MHz
# if CPU frequency is 160MHz or 240MHz.
adapter speed 1000

# flash 3.3 V for ESP32-WROOM-32 

# Load esp32 target configuration
source [find target/esp32.cfg]

You can find a list of available interfaces and targets in the ‘openocd-code/tcl’ folder.

Running JTAG debugger

After launching the debugger, we should see something like this:

~/esp32$ openocd -f esp32_jtag.cfg
Open On-Chip Debugger 0.12.0+dev-00078-gfc30feb51 (2023-03-11-15:26)
Licensed under GNU GPL v2
For bug reports, read
force hard breakpoints
Info : Listening on port 6666 for tcl connections
Info : Listening on port 4444 for telnet connections
Info : clock speed 1000 kHz
Info : JTAG tap: esp32.cpu0 tap/device found: 0x120034e5 (mfg: 0x272 (Tensilica), part: 0x2003, ver: 0x1)
Info : JTAG tap: esp32.cpu1 tap/device found: 0x120034e5 (mfg: 0x272 (Tensilica), part: 0x2003, ver: 0x1)
Info : starting gdb server for esp32.cpu0 on 3333
Info : Listening on port 3333 for gdb connections
Info : Set GDB target to 'esp32.cpu0'

Open your favorite telnet console on localhost at port 4444

~$ telnet localhost 4444
Connected to localhost.
Escape character is '^]'.
Open On-Chip Debugger
> help

Brief command list:

help [command_name]Show full command help; command can be multiple tokens. (command valid any time)
versionshow program version (command valid any time)
reset [run|halt|init]Reset all targets into the specified mode. Default reset mode is run, if not given.
halt [milliseconds]request target to halt, then wait up to the specified number of milliseconds (default 5000) for it to complete
targets [target]change current default target (one parameter) or prints table of all targets (no parameters) (command valid any time)
flash banksDisplay table with information about flash banks. (command valid any time)
reg [(number|name)]
reg [(value|’force’)]
display (reread from target with “force”) or set a register; with no arguments, displays all registers and their values
resume [address]resume target execution from current PC or address
scan_chainprint current scan chain configuration (command valid any time) script
shutdownClose the OpenOCD server, disconnecting all clients (GDB, telnet, other). If option
error is used, OpenOCD will return a non-zero exit code to the parent process.
If user types CTRL-C or kills OpenOCD, the command shutdown will be automati-
cally executed to cause OpenOCD to exit.
arm semihosting [‘enable’|’disable’]Set the base directory for semihosting I/O.DEPRECATED! use arm activate support for semihosting operations
arm semihosting_basedir [dir]set the base directory for semihosting I/O operations
arm semihosting_fileio [‘enable’|’disable’]activate support for semihosting fileio operations
arm semihosting_redirect (disable | tcp [‘debug’|’stdio’|’all’])redirect semihosting IO
initInitializes configured targets and servers. Changes command mode from CONFIG to EXEC. Unless ‘noinit’ is called, this command is called automatically at the end of startup. (command valid any time)

TP-LINK MR3420 hardware

TL-MR3420 is Wireless N Router with USB 2.0 Port for UMTS/HSPA/EVDO USB Modem.

Model Name
6.9 x 4.4 x 1.2 in. (174 x 111 x 30 mm)
9VDC / 0.85A
#1 – USB 2.0
#1 – Ethernet WAN 10/100Mbps
#4 – Ethernet LAN 10/100Mbps
#2 – 3dBi Detachable Omni Directional Antennas
Wireless Transmit Power
20dBm (Max. EIRP)
Atheros AR7241@400MHz
32 MB
4 MB

Gentoo default Python interpreter

Gentoo default Python interpreter is set to version 3.1:
~ # eselect python list
Available Python interpreters:
[1] python2.6
[2] python3.1 *

Command to change default interpreter:
~ # eselect python set 1
And now interpreter version is 2.6:
~ # eselect python list
Available Python interpreters:
[1] python2.6 *
[2] python3.1

Installing version 2.7:
~ # nano /etc/portage/package.accept_keywords
^X (Ctrl+X) --> exit & save
~ # emerge --ask =dev-lang/python-2.7*

Rebuild Python modules:
~ # python-updater
* Starting Python Updater...
* Main active version of Python: 2.7
* Active version of Python 2: 2.7
* Active version of Python 3: 3.1

Happy python coding!

Gentoo web2py daemon

Gentoo RC init script for web2py framework

Save as “/etc/init.d/web2py”

# Gentoo web2py init script




  need net

start() {
  ebegin "Starting web2py"
  start-stop-daemon --start --quiet --background --chdir "$WORK_PATH" \
                    --pidfile $PIDFILE --exec "$PYTHON" \
                    -- "$WORK_PATH/$WORK_FILE" \
                    --nogui --password="$PASSWORD" \
                    --pid_filename="$PIDFILE" \
                    --ip=$SRV_ADDR --port=$SRV_PORT \
                    --ssl_certificate="$SRV_CRT" --ssl_private_key="$SRV_KEY"
  eend $?

stop() {
  ebegin "Stopping web2py"
  start-stop-daemon --stop --pidfile $PIDFILE
  eend $?

To make a file executable:
chmode +x /etc/init.d/web2py
Add to default runlevel:
rc-update add web2py default
Start daemon:
rc-config start web2py

Fonera 2.0n USB boot script

Some time ago I wrote script for Hacking Fonera Access Point. This script move root file system to USB device and permit any kind of installation and easily revert any changes to original state without stress for internal flash drive. In fact it’s stress USB device but it’s better than damage the internal flash!

How it works:
First time the script copy entire Fonera root file system to USB device, next time everything except kernel is loaded from external USB. If no valid USB drive/partition is detected it’s automatically load internal root file system after about 20s.

Preparing USB drive:
Create on USB device one or more partition with ext2 or ext3 file system. Finally add an empty file named “usbrootfsready” on the root of partition.

Preparing Fonera:
We need to gain access to Fonera shell and create file named “/sbin/usbrootfs” with this contents.

# Fonera2.0n usbrootfs
# Copyright (C) 2009 iwi \at\ hotmail \it\
# exec > /home/usbroot.log 2>&1


/sbin/insmod ext2
/sbin/insmod jbd
/sbin/insmod ext3
/sbin/insmod dwc_otg

for CNTR in $(seq 1 1 10)
    sleep 2s
    if [ -b "$USBROOT" ]; then 
        mount -o rw "$USBROOT" "$USBMNTP" && {
            if [ -f "$USBMNTP$USBFLAG" ]; then
                # Copy to new root
                rm "$USBMNTP$USBFLAG"
                cd /rom
                tar -c * | tar -x -C $USBMNTP
                cd /jffs
                tar -c * | tar -x -C $USBMNTP
                cd $USBMNTP
                rm `find | grep META_`
                rmdir jffs
                #rm var
                #mkdir var
                mkdir /usr/lib/opkg
                cd /
            if [ -f "$USBMNTP/sbin/init" ]; then
                # remount usb as rootfs
                mount -o move /proc $USBMNTP/proc && {
                    pivot_root $USBMNTP $USBMNTP/rom && {
                        mount -o move /rom/dev /dev
                        mount -o move /rom/sys /sys
                        mount -o move /rom/tmp /tmp
                    mount -o move $USBMNTP/proc /proc
            umount "$USBROOT"

Now delete or rename “sbin/init” and create new init file.

# Fonera2.0n init override
# Copyright (C) 2009 Toropov Ivan


exec /bin/busybox init

Don’t forget to mark this files as executables.

chmod +x /sbin/init
chmod +x /sbin/usbrootfs

It’s all. Reboot and enjoy hacking!