Repository navigation
ESP32-S3: espressif/tinyusb 0.21.0~2 resets the chip when the host opens the CDC port; MicroPython's own 0.21 tag doesn't #48
Description
On espressif/tinyusb 0.21.01 and 0.21.02, an ESP32-S3 reboots the moment a host opens its CDC port. The chip trips the interrupt watchdog inside TinyUSB's DWC2 IN-endpoint interrupt handler. That's why micropython-pydevices#21 gives TinyUSB 0.21 to the ESP32-P4 only and leaves the S2 and S3 on the 0.18 cherry-pick. Until the S3 moves, an S3 sound card keeps #39's packet loss under load.
Update, 2026-09-25: the S3 is moving (micropython-pydevices#22). On the tag the T-Embed's port opens, and its sound card delivered 99.96 to 100.01 % busy and 99.84 to 100.00 % idle over 80 s each.
The S3 can move now. MicroPython's own tag v0.21.0.1-micropython1, the ref #21 gives the P4, doesn't crash. The T-Embed built on it opened its port, answered the REPL, and ran the sound card at 100.00 % with the interpreter busy. Moving it is one line in patch 0014 plus the S3's lockfile entry. Brad decides.
What happens
Board: LILYGO T-Embed (ESP32-S3, the chip's own full-speed USB, no USB-serial bridge). Firmware: MicroPython v1.29.0 with the PyDevices overlay, ESP-IDF v5.5.4, usbif compiled in, idle at the REPL in the default CDC-only costume.
- Flash the image. The board enumerates and Windows shows its COM port.
- Open the port with pyserial, .NET
SerialPortor esptool, any of which raises DTR. - The board resets at once. Opening it with DTR held low does not crash it.
The coredump, read back over ROM download mode, is the same on 0.21.01 and 0.21.02:
exccause 0x46 (InterruptWDTTimoutCPU1), in interrupt context on CPU1's idle task (IDLE1, inesp_pm_impl_waiti).- PC in
dcd_int_handler:handle_ep_irq(rhport=0, dir=IN), the loop over DAINT's IN-endpoint bits (dcd_dwc2.c:1146on ~2,:1106on ~1). - Stack:
_xt_lowint1→dwc2_int_handler_wrap(dwc2_esp32.h:102) →dcd_int_handler→handle_ep_irq. mp_taskidle inreadline()at the REPL prompt.
The cause, by bisection
| TinyUSB on the S3 | port opens? |
|---|---|
0.18 + ZLP cherry-pick (micropython/tinyusb-espressif cherrypick/dwc2_zlp_fix, MicroPython's pin) |
yes |
| espressif/tinyusb 0.21.0~2 (registry) | no, resets |
micropython/tinyusb-espressif v0.21.0.1 (= espressif 0.21.0~1) |
no, resets, same dump |
micropython/tinyusb-espressif v0.21.0.1-micropython1 |
yes |
The last two refs differ by exactly one commit, Damien George's src/class/cdc: don't flush pending data in cdcd_open (5c5c660). It's the fix for hathach/tinyusb#3807, merged upstream as hathach/tinyusb#3808 on 2026-09-03. With CFG_TUD_CDC_TX_PERSISTENT, which MicroPython sets, 0.21's cdcd_open() starts a transfer from the persistent TX FIFO while the device is still enumerating. On the RP2040 in #3807 that lost the banner. On the S3's full-speed DWC2 it leaves an IN-endpoint interrupt that fires forever once the host raises DTR. 0.21.0~2's other changes against ~1 (a DWC2 periodic-transfer option, off by default, and an NCM divisor) are unused here, so the bisection over ~1 covers ~2 as well.
The P4 at high speed ran 0.21.0~2 without crashing, though it carried the same flush.
Measured on the T-Embed (48 kHz shared, Windows WASAPI, quiet 440 Hz)
| image | load | delivered (1000 packets/s expected) |
|---|---|---|
| 0.18 + usbif#47 | idle | 99.91 to 99.93 % |
| 0.18 + usbif#47 | busy | 87.4 to 93.9 %, every window below the 99.5 % gate |
v0.21.0.1-micropython1 + usbif#47 |
busy | 100.00 %, 9 of 9 full windows |
Pitch at I2S was 440.0 Hz (a mono run, because the checker's tap only reads a mono sink). On 0.18 under load, the CDC console also went silent to the host, so the busy run logged to a file on the board.
When it can close
When the S2 and S3 take a TinyUSB that carries #3808 (MicroPython's tag does now) in patch 0014. After that, re-run this sound-card gate on the S3.
Upstream
Nothing to report upstream. The fix is already in TinyUSB as hathach/tinyusb#3808 and in MicroPython's v0.21.0.1-micropython1 tag, and micropython-pydevices#22 moves the S2 and S3 to that tag. This issue closes when #22 merges.
Activity
README: 0.21 is the P4's for now; the S3 stays on 0.18 (#48)
UAC: take each packet in the USB interrupt, so a busy interpreter no …