🛠 Dompter le double USB : Serial/JTAG et OTG de front
Objectives
Leverage the unique feature of the ESP32-S3: two simultaneous USB personalities. On one side, the USB-Serial/JTAG peripheral (serial console + hardware debugging without an external bridge); on the other, the USB-OTG controller (HID/MSC/CDC device or host). On boards that wire both controllers to two distinct connectors (some DevKitC-1, ESP32-S3-Box), you can flash and debug via one port while emulating a keyboard on the other. You will configure the Hardware CDC+JTAG mode, launch a debug session, and understand pin routing.
Hardware
- 1 ESP32-S3 board exposing both ports (or a UART adapter on GPIO43/44 as a fallback)
- 2 USB cables
- Arduino IDE (esp32 core >= 2.0.14) or ESP-IDF v5.x with OpenOCD for JTAG
Full Code
// Mode "Hardware CDC and JTAG" : console + debug sur le port Serial/JTAG,
// le port OTG (GPIO19/20) reste libre pour un peripherique TinyUSB.
// Outils Arduino : USB CDC On Boot = Enabled,
// USB Mode = "Hardware CDC and JTAG"
#include "Arduino.h"
// Point d'arret logiciel : si un debogueur JTAG est attache, il s'y connecte ;
// sinon la boucle courte evite toute surprise côté watchdog.
volatile int drapeau_debug = 0;
void setup {
Serial.begin(115200); // passe par USB-Serial/JTAG (pas GPIO43/44)
Serial.println("Boot sur USB-Serial/JTAG");
}
void loop {
static uint32_t n = 0;
if (drapeau_debug) { // breakpoint logiciel : pause pour GDB
while (drapeau_debug) { asm volatile("nop"); delay(1); }
}
if (++n % 50 == 0) Serial.printf("tick %u, heap %u\n", n, ESP.getFreeHeap);
delay(20);
}
On the ESP-IDF side, the JTAG console is enabled via idf.py menuconfig → Component config → ESP System → “Channel for console output” → “USB Serial/JTAG”, and debugging via idf.py openocd followed by idf.py gdb (or the VS Code launch configuration “OpenOCD GDB Server”).
Expected Verification
Connected to the Serial/JTAG port, the board presents a serial port without any bridge chip; the console works at USB link speed. In debug mode (ESP-IDF), GDB attaches via the same cable: breakpoints, stepping, and memory reading without external JTAG. On a board with two connectors, the second port remains available for a TinyUSB device: the two worlds truly coexist.
Common Errors
- Confusing the two controllers: “USB CDC On Boot” drives the hardware Serial/JTAG (internal pins), whereas TinyUSB requires GPIO19/20; enabling both simultaneously on the same pins results in a phantom or non-existent port.
- Lost console after switching to TinyUSB: the Serial/JTAG port is no longer initialized; reflash while holding BOOT + RESET (ROM mode) or connect a serial adapter to GPIO43 (RX) / GPIO44 (TX) / GND.
- Watch out for resets: the Serial/JTAG peripheral may trigger a DTR/RTS reset when opening the port; disable DTR/RTS in the terminal if your script gets reset.
- Console speed: with Serial/JTAG, the actual throughput depends on the host, not the configured baud rate; do not look for a “2000000” UART setting.
- GDB failing to attach: verify that the cable is connected to the correct connector (the Serial/JTAG one, not the OTG) and that no program is configuring GPIO19/20 as outputs.
Other tutorials for this board
Code examples for this board
clignotante.inoArduinobouton-led.inoArduinorespiration.inoArduinodht22.inoArduinooled-i2c.inoArduinoserveur-web.inoArduino