Initial dev environment setup for ESP32-C3 and ESP32-C6 programming, tailored towards Rust development. Some of this will also apply to the ESP32-C2 as well as the ESP32-H2 and ESP32-P4 when they are released.
-
Install ESP tooling:
NOTE:
cargo-espflash
is different thanespflash
. The former is useful when working withcargo
. The latter is useful for developing in non-Rust environments.pip install esptool # Programs like espefuse.py, esptool.py cargo install cargo-espflash # So you can run "cargo espflash" rustup target install riscv32imc-unknown-none-elf # ESP32-C3 rust toolchain rustup target install riscv32imac-unknown-none-elf # ESP32-C6 rust toolchain
-
Setup host-side debug-probe software:
-
probe-rs:
NOTE: Must build from source with
--features ftdi
instead if using an FTDI-based debug probe.# If you want to setup an Embed.toml for your project cargo install cargo-embed # If you want to run manually (used later in verifying connectivity to board) cargo install probe-rs-cli
-
OpenOCD:
# Will install OpenOCD into /opt/openocd/esp32 as `openocd-esp32` # NOTE: Remember to add /opt/openocd/esp32/bin to PATH git clone https://github.com/espressif/openocd-esp32.git cd openocd-esp32 ./bootstrap ./configure --prefix=/opt/openocd/esp32/ --exec-prefix=/opt/openocd/esp32/ --program-suffix="-esp32" make -j $(nproc) sudo make install
- If using a custom debug probe or debug probe without a config file in the fork, add its config file to the debug probe config file directory (in OpenOCD terms, the interface/ directory). If following the above setup verbatim, this directory is /opt/openocd/esp32/share/openocd/scripts/interface/. My debug probe is a FTDI-based 1BitSquared Tigard.
-
-
Setup access to serial devices (otherwise you'll need to be super user to flash your board):
- Add the following to an existing or new udev rule (see this guide for details):
# ESP USB JTAG Serial ATTRS{idVendor}=="303a", ATTRS{idProduct}=="1001", MODE="0666", ENV{ID_MM_DEVICE_IGNORE}="1", ENV{ID_MM_PORT_IGNORE}="1"
- Reload udev rules:
sudo udevadm control --reload-rules && sudo udevadm trigger
- Add the following to an existing or new udev rule (see this guide for details):
NOTE: ESP32-C6 boards are supported by cargo-espflash >= v2.0.0
- Connect a USB cable to either:
- ESP32-C3 Micro USB port
- ESP32-C6 USB-C port labeled "UART"
- Test the connection by running
cargo espflash monitor
. Note the serial output, including build date, in the following example:% cargo espflash monitor # This command will also display serial output from board UART [2023-03-15T06:54:11Z INFO ] Serial port: '/dev/ttyUSB0' [2023-03-15T06:54:11Z INFO ] Connecting... Commands: CTRL+R Reset chip CTRL+C Exit ESP-ROM:esp32c6-20220919 Build:Sep 19 2022 rst:0x1 (POWERON),boot:0xc (SPI_FAST_FLASH_BOOT)
Assuming the above steps worked, you're now good to flash programs via USB JTAG. For example, flash your board with an example serial program when run in either the esp32c3-hal/
or esp32c6-hal/
directory of the ESP Rust HAL repo:
# Add --monitor to flash and then interact w/ board serial
cargo espflash flash --example serial_interrupts --features direct-boot --format=direct-boot
NOTE: When using direct-boot
, the target is not flashed with the ESP IDF second-stage bootloader
(as it is normally w/o direct-boot
). The target instead boots directly from flash. See the ESP32-C6 Rust HAL for more info.
CAUTION: This process will irreversibly modify the behavior of your board.
By default, the ESP32-C3 and ESP32-C6 come with only USB JTAG configured. To use GPIO-based JTAG, you will need to burn fuses on the board using espefuse.py
. There are a couple ways you can do this for each board. Here, I will show you how to enable GPIO JTAG while still allowing for USB JTAG.
In both chips, there is a fuse called JTAG_SEL_ENABLE
which allows the user to select which JTAG method the chip will listen to. By burning this fuse (permanently setting it to 1), the chip will select GPIO JTAG when a chip-specific GPIO pin is strapped (connected to ground) on boot. For the ESP32-C3, this pin is GPIO 10. For the ESP32-C6, this pin is GPIO 15.
If your ESP32-C3 is like mine, the board revision is too early to have the JTAG_SEL_ENABLE
fuse. As a workaround, burning the DIS_USB_JTAG
fuse will enable GPIO JTAG at the cost of permanently disabling USB JTAG. That said, my board still seems to listen to USB JTAG even after flashing this, (possibly due to a bug in silicon). YMMV ¯_(ツ)_/¯. See this GitHub issue for more info.
For more details on JTAG selection, see section 6.2.4 of the ESP32-C6 Technical Reference Manual. The ESP32-C3 reference is sparse on details JTAG selection details, so see the previously-linked ESP32-C6 reference as are similar if not equivalent.
- Burn
JTAG_SEL_ENABLE
fuse (sets it to 1 permanently)- Connect a USB cable to the board (USB-C port labeled "UART" for the ESP32-C6)
- Run the following:
espefuse.py summary --port /dev/ttyUSB0 # Should print False espefuse.py burn_efuse JTAG_SEL_ENABLE # Prompts you to ensure correct efuse selected espefuse.py summary --port /dev/ttyUSB0 # Should print True
- Test GPIO JTAG works
-
Connect your debug probe to the board pins (in addition to GND and chosen power, 3.3V or 5V):
Pin Function ESP32-C3 Pin ESP32-C6 Pin TMS 4 4 TDI 5 5 TCK 6 6 TDO 7 7 Any GND pin 10 15 -
Verify your machine recognizes the board:
-
Via probe-rs:
# No SWD err expected as ESP32C3 & ESP32C6 only implement JTAG % probe-rs-cli info # ESP32-C3 Probing target via JTAG No DAP interface was found on the connected probe. Thus, ARM info cannot be printed. RISCV Chip: IDCODE: 0000005c25 Version: 0 Part: 5 Manufacturer: 1554 (Espressif Systems (Shanghai) Co Ltd) Probing target via SWD Error identifying target using protocol SWD: Probe does not support SWD % probe-rs-cli info # ESP32-C6 Probing target via JTAG No DAP interface was found on the connected probe. Thus, ARM info cannot be printed. RISCV Chip: IDCODE: 000000dc25 Version: 0 Part: 13 Manufacturer: 1554 (Espressif Systems (Shanghai) Co Ltd) Probing target via SWD Error identifying target using protocol SWD: Probe does not support SWD
-
Via OpenOCD:
# Substitute your debug probe config file for "interface/ftdi/tigard-jtag.cfg" % openocd-esp32 -f interface/ftdi/tigard-jtag.cfg -f target/esp32c3.cfg # ESP32-C3 Open On-Chip Debugger v0.12.0-esp32-20230313 (2023-03-14-21:36) Licensed under GNU GPL v2 For bug reports, read http://openocd.org/doc/doxygen/bugs.html Warn : Transport "jtag" was already selected Info : Listening on port 6666 for tcl connections Info : Listening on port 4444 for telnet connections Info : clock speed 2000 kHz Info : JTAG tap: esp32c3.cpu tap/device found: 0x00005c25 (mfg: 0x612 (Espressif Systems), part: 0x0005, ver: 0x0) Info : datacount=2 progbufsize=16 Info : Examined RISC-V core; found 1 harts Info : hart 0: XLEN=32, misa=0x40101104 Info : starting gdb server for esp32c3 on 3333 Info : Listening on port 3333 for gdb connections % openocd-esp32 -f interface/ftdi/tigard-jtag.cfg -f target/esp32c6.cfg # ESP32-C6 Open On-Chip Debugger v0.12.0-esp32-20230313 (2023-03-14-21:36) Licensed under GNU GPL v2 For bug reports, read http://openocd.org/doc/doxygen/bugs.html Warn : Transport "jtag" was already selected Info : Listening on port 6666 for tcl connections Info : Listening on port 4444 for telnet connections Info : clock speed 2000 kHz Info : JTAG tap: esp32c6.cpu tap/device found: 0x0000dc25 (mfg: 0x612 (Espressif Systems), part: 0x000d, ver: 0x0) Info : datacount=2 progbufsize=16 Info : Examined RISC-V core; found 2 harts Info : hart 0: XLEN=32, misa=0x40903105 Info : starting gdb server for esp32c6 on 3333 Info : Listening on port 3333 for gdb connections
-
-