Skip to content

machine/esp32s3: add USB-OTG device support - #6000

Merged
deadprogram merged 3 commits into
devfrom
esp32s3-usb-otg
Oct 24, 2026
Merged

deadprogram merged 3 commits into
devfrom
esp32s3-usb-otg

Conversation

@deadprogram

@deadprogram deadprogram commented Oct 23, 2026 •

Copy link
Copy Markdown
Member

This PR adds a driver for the ESP32-S3 USB-OTG (DWC2) controller. It runs the shared USB stack, so CDC, HID, MSC and MIDI work like on the rp2040 and samd boards.

The driver is opt in with -tags=usbotg. Without the tag the USB Serial/JTAG driver is used as before.

Changes

  • New driver in src/machine/machine_esp32s3_usb_otg.go, slave (FIFO) mode, CPU interrupt 8.
  • EnterBootloader hands the PHY back to USB Serial/JTAG and restarts into download mode, so a 1200 baud touch can reflash the board.
  • A cross core lock around FIFO and endpoint register access, for -scheduler=cores.
  • machine/usb/cdc resumes TX after the host configures the device again. A bus reset could drop the packet in flight and block writes. This is a separate commit since it touches all USB targets.
  • Smoke tests for echo, usb-storage and hid-joystick with -tags=usbotg.
  • Update to espflasher 0.9.2.

Flashing

tinygo flash sends a 1200 baud touch to the USB-OTG port, using espflasher 0.9.2 (tinygo-org/espflasher#68, tinygo-org/espflasher#71). The board restarts into download mode and is flashed over USB Serial/JTAG.

Testing

On a XIAO ESP32-S3:

  • CDC echo.
  • HID mouse together with CDC output.
  • MSC RAM disk, 20 KB file read and compare loops.
  • espradio Wi-Fi http.Get loop together with MSC reads and CDC output, 90 s to 120 s, with both the tasks and cores schedulers. No USB errors on the host.
  • 6 tinygo flash runs in a row through the 1200 baud touch, alternating tasks and cores builds.

On a Raspberry Pi Pico, to check the CDC change: echo, MSC reads, HID mouse with CDC output across port reopen.

Notes

  • The VID/PID is 303a:4001, following the PID scheme of Espressif's esp_tinyusb. Happy to change it.
  • Any switch of the shared PHY needs the D+ pull-up to drop for a while, or the host never enumerates the other controller.
  • While testing I found that S3 images with a flash segment of 1 MiB or more do not boot (ROM "Invalid image block"). That is not related to this PR.

@github-actions

github-actions Bot commented Oct 23, 2026 •

Copy link
Copy Markdown

Size difference with the dev branch:

Binary size difference
 flash                          ram
 before   after   diff          before   after   diff
   8856    8856      0   0.00%    2320    2320      0   0.00% tinygo build -size short -o ./build/test.hex -target=microbit ./examples/at24cx/main.go
  72400   72400      0   0.00%    3720    3720      0   0.00% tinygo build -size short -o ./build/test.hex -target=pinetime     ./examples/bma42x/main.go
  22501   22501      0   0.00%   16680   16680      0   0.00% tinygo build -size short -o ./build/test.bin -target=m5stamp-s3a ./examples/bmi270/spi/main.go
  15819   15819      0   0.00%   17176   17176      0   0.00% tinygo build -size short -o ./build/test.elf -target=m5stamp-s3a ./examples/bmm150/main.go
   9256    9256      0   0.00%    3360    3360      0   0.00% tinygo build -size short -o ./build/test.hex -target=bluepill ./examples/ds1307/sram/main.go
  23092   23092      0   0.00%    3592    3592      0   0.00% tinygo build -size short -o ./build/test.hex -target=bluepill ./examples/ds1307/time/main.go
   4776    4776      0   0.00%    2280    2280      0   0.00% tinygo build -size short -o ./build/test.hex -target=microbit ./examples/easystepper/main.go
   6992    6992      0   0.00%    2344    2344      0   0.00% tinygo build -size short -o ./build/test.hex -target=microbit ./examples/easystepper/async/main.go
   7524    7524      0   0.00%    2284    2284      0   0.00% tinygo build -size short -o ./build/test.hex -target=microbit ./examples/gc9a01/main.go
  23680   23680      0   0.00%    8588    8588      0   0.00% tinygo build -size short -o ./build/test.bin -target=esp32-coreboard-v2 ./examples/gdew0154m09/main.go
   5896    5896      0   0.00%    2280    2280      0   0.00% tinygo build -size short -o ./build/test.hex -target=microbit ./examples/hd44780/customchar/main.go
   5976    5976      0   0.00%    2280    2280      0   0.00% tinygo build -size short -o ./build/test.hex -target=microbit ./examples/hd44780/text/main.go
   6753    6753      0   0.00%     585     585      0   0.00% tinygo build -size short -o ./build/test.hex -target=arduino ./examples/hd44780/keypadshield/main.go
  16564   16564      0   0.00%    2428    2428      0   0.00% tinygo build -size short -o ./build/test.hex -target=microbit ./examples/hub75/main.go
  26616   26616      0   0.00%    2328    2328      0   0.00% tinygo build -size short -o ./build/test.hex -target=microbit ./examples/lsm303agr/main.go
  30776   30776      0   0.00%    4152    4152      0   0.00% tinygo build -size short -o ./build/test.hex -target=microbit ./examples/microbitmatrix/main.go
  30680   30680      0   0.00%    6196    6196      0   0.00% tinygo build -size short -o ./build/test.hex -target=microbit-v2 ./examples/microbitmatrix/main.go
   6692    6692      0   0.00%    3292    3292      0   0.00% tinygo build -size short -o ./build/test.hex -target=microbit ./examples/pcd8544/setbuffer/main.go
   5092    5092      0   0.00%    2284    2284      0   0.00% tinygo build -size short -o ./build/test.hex -target=microbit ./examples/pcd8544/setpixel/main.go
  14366   14366      0   0.00%   16840   16840      0   0.00% tinygo build -size short -o ./build/test.bin -target=m5stamp-s3a ./examples/pmw3901/motion/main.go
  13978   13978      0   0.00%   19312   19312      0   0.00% tinygo build -size short -o ./build/test.bin -target=m5stamp-s3a ./examples/pmw3901/frame/main.go
   2899    2899      0   0.00%     560     560      0   0.00% tinygo build -size short -o ./build/test.hex -target=arduino ./examples/servo
  59088   59088      0   0.00%    3728    3728      0   0.00% tinygo build -size short -o ./build/test.hex -target=microbit ./examples/sht3x/main.go
  59024   59024      0   0.00%    3728    3728      0   0.00% tinygo build -size short -o ./build/test.hex -target=microbit ./examples/sht4x/main.go
  59088   59088      0   0.00%    3728    3728      0   0.00% tinygo build -size short -o ./build/test.hex -target=microbit ./examples/shtc3/main.go
   6372    6372      0   0.00%    2284    2284      0   0.00% tinygo build -size short -o ./build/test.hex -target=microbit ./examples/ssd1331/main.go
   7236    7236      0   0.00%    2284    2284      0   0.00% tinygo build -size short -o ./build/test.hex -target=microbit ./examples/st7735/main.go
   6932    6932      0   0.00%    2284    2284      0   0.00% tinygo build -size short -o ./build/test.hex -target=microbit ./examples/st7789/main.go
  29462   29462      0   0.00%   17176   17176      0   0.00% tinygo build -size short -o ./build/test.elf -target=m5stamp-s3a ./examples/vl53l3cx/main.go
   6804    6804      0   0.00%    2324    2324      0   0.00% tinygo build -size short -o ./build/test.hex -target=microbit ./examples/waveshare-epd/epd2in13/main.go
   6484    6484      0   0.00%    2316    2316      0   0.00% tinygo build -size short -o ./build/test.hex -target=microbit ./examples/waveshare-epd/epd2in13x/main.go
   6676    6676      0   0.00%    2324    2324      0   0.00% tinygo build -size short -o ./build/test.hex -target=microbit ./examples/waveshare-epd/epd4in2/main.go
   6094    6094      0   0.00%    8464    8464      0   0.00% tinygo build -size short -o ./build/test.bin -target=m5stamp-c3          ./examples/ws2812
   1815    1815      0   0.00%     600     600      0   0.00% tinygo build -size short -o ./build/test.hex -target=arduino   ./examples/ws2812
   1280    1280      0   0.00%     182     182      0   0.00% tinygo build -size short -o ./build/test.hex -target=digispark ./examples/ws2812
   7748    7748      0   0.00%    8744    8744      0   0.00% tinygo build -size short -o ./build/test.bin -target=xiao-esp32c3 ./examples/ws2812
   3741    3741      0   0.00%     582     582      0   0.00% tinygo build -size short -o ./build/test.hex -target=arduino      ./examples/ws2812/neopixel-shield
   8180    8180      0   0.00%    3324    3324      0   0.00% tinygo build -size short -o ./build/test.hex -target=nucleo-f103rb ./examples/shiftregister/main.go
   7596    7596      0   0.00%    2288    2288      0   0.00% tinygo build -size short -o ./build/test.hex -target=hifive1b ./examples/ssd1351/main.go
  10600   10600      0   0.00%    5280    5280      0   0.00% tinygo build -size short -o ./build/test.hex -target=nucleo-l432kc ./examples/aht20/main.go
  12460   12460      0   0.00%    4692    4692      0   0.00% tinygo build -size short -o ./build/test.elf -target=m5stack-core2 ./examples/ft6336/basic/
  20040   20040      0   0.00%    4964    4964      0   0.00% tinygo build -size short -o ./build/test.elf -target=m5stack-core2 ./examples/ft6336/touchpaint/
  14400   14400      0   0.00%    5060    5060      0   0.00% tinygo build -size short -o ./build/test.hex -target=nucleo-wl55jc ./examples/sx126x/lora_rxtx/
 125180  125180      0   0.00%    8472    8472      0   0.00% tinygo build -size short -o ./build/test.hex -target=nucleo-wl55jc ./examples/lora/lorawan/atcmd/
  63720   63720      0   0.00%    3792    3792      0   0.00% tinygo build -size short -o ./build/test.hex -target=microbit ./examples/ndir/main_ndir.go
  22184   22232     48   0.22%    5864    5864      0   0.00% tinygo build -size short -o ./build/test.hex -target=metro-rp2350 ./examples/bno08x/i2c/main.go
  16048   16096     48   0.30%    6992    6992      0   0.00% tinygo build -size short -o ./build/test.hex -target=nano-33-ble ./examples/hts221/main.go
  11516   11564     48   0.42%    5996    5996      0   0.00% tinygo build -size short -o ./build/test.hex -target=pico2-w ./examples/ili9341/pico
  30424   30472     48   0.16%   38528   38528      0   0.00% tinygo build -size short -o ./build/test.hex -target=pyportal ./examples/ili9341/pyportal_boing
  11024   11072     48   0.44%    7184    7184      0   0.00% tinygo build -size short -o ./build/test.elf -target=wioterminal ./examples/axp192/m5stack-core2-blinky/
  63152   63216     64   0.10%    6624    6624      0   0.00% tinygo build -size short -o ./build/test.hex -target=itsybitsy-m0 ./examples/adt7410/main.go
  10576   10640     64   0.61%    5176    5176      0   0.00% tinygo build -size short -o ./build/test.hex -target=itsybitsy-m0 ./examples/adxl345/main.go
  17152   17216     64   0.37%    7240    7240      0   0.00% tinygo build -size short -o ./build/test.hex -target=pybadge ./examples/amg88xx
   9792    9856     64   0.65%    5136    5136      0   0.00% tinygo build -size short -o ./build/test.hex -target=itsybitsy-m0 ./examples/apa102/main.go
  13168   13232     64   0.49%    6992    6992      0   0.00% tinygo build -size short -o ./build/test.hex -target=nano-33-ble ./examples/apds9960/proximity/main.go
  12012   12076     64   0.53%    5148    5148      0   0.00% tinygo build -size short -o ./build/test.hex -target=itsybitsy-m0 ./examples/apa102/itsybitsy-m0/main.go
   9280    9344     64   0.69%    5176    5176      0   0.00% tinygo build -size short -o ./build/test.hex -target=itsybitsy-m0 ./examples/bh1750/main.go
   8576    8640     64   0.75%    5176    5176      0   0.00% tinygo build -size short -o ./build/test.hex -target=itsybitsy-m0 ./examples/blinkm/main.go
  67744   67808     64   0.09%    6592    6592      0   0.00% tinygo build -size short -o ./build/test.hex -target=itsybitsy-m0 ./examples/bmi160/main.go
  20464   20528     64   0.31%    5200    5200      0   0.00% tinygo build -size short -o ./build/test.hex -target=feather-m0 ./examples/bmi270/i2c/main.go
  28352   28416     64   0.23%    5176    5176      0   0.00% tinygo build -size short -o ./build/test.hex -target=itsybitsy-m0 ./examples/bmp180/main.go
  66448   66512     64   0.10%    6624    6624      0   0.00% tinygo build -size short -o ./build/test.hex -target=itsybitsy-m0 ./examples/bmp280/main.go
  12976   13040     64   0.49%    5208    5208      0   0.00% tinygo build -size short -o ./build/test.hex -target=trinket-m0 ./examples/bmp388/main.go
  30764   30828     64   0.21%    5408    5408      0   0.00% tinygo build -size short -o ./build/test.hex -target=itsybitsy-m0 ./examples/ds3231/alarms/main.go
  44636   44700     64   0.14%    5408    5408      0   0.00% tinygo build -size short -o ./build/test.hex -target=itsybitsy-m0 ./examples/ds3231/basic/main.go
   9320    9384     64   0.69%    5192    5192      0   0.00% tinygo build -size short -o ./build/test.hex -target=itsybitsy-m0 ./examples/hcsr04/main.go
  11328   11392     64   0.56%    5160    5160      0   0.00% tinygo build -size short -o ./build/test.hex -target=arduino-nano33 ./examples/hd44780i2c/main.go
  11072   11136     64   0.58%    7368    7368      0   0.00% tinygo build -size short -o ./build/test.hex -target=pyportal ./examples/ili9341/basic
  13440   13504     64   0.48%    5328    5328      0   0.00% tinygo build -size short -o ./build/test.hex -target=xiao ./examples/ili9341/basic
  12764   12828     64   0.50%    6004    6004      0   0.00% tinygo build -size short -o ./build/test.hex -target=pico ./examples/ili9341/pico
  11032   11096     64   0.58%    7368    7368      0   0.00% tinygo build -size short -o ./build/test.hex -target=pyportal ./examples/ili9341/scroll
  13464   13528     64   0.48%    5328    5328      0   0.00% tinygo build -size short -o ./build/test.hex -target=xiao ./examples/ili9341/scroll
 265904  265968     64   0.02%   47212   47212      0   0.00% tinygo build -size short -o ./build/test.hex -target=pyportal ./examples/ili9341/slideshow
  11648   11712     64   0.55%    5176    5176      0   0.00% tinygo build -size short -o ./build/test.hex -target=circuitplay-express ./examples/lis3dh/main.go
  15424   15488     64   0.41%    6992    6992      0   0.00% tinygo build -size short -o ./build/test.hex -target=nano-33-ble ./examples/lps22hb/main.go
  29152   29216     64   0.22%    7224    7224      0   0.00% tinygo build -size short -o ./build/test.hex -target=feather-m4 ./examples/lsm303dlhc/main.go
  13264   13328     64   0.48%    5184    5184      0   0.00% tinygo build -size short -o ./build/test.hex -target=arduino-nano33 ./examples/lsm6ds3/main.go
  11856   11920     64   0.54%    5176    5176      0   0.00% tinygo build -size short -o ./build/test.hex -target=itsybitsy-m0 ./examples/mag3110/main.go
  10728   10792     64   0.60%    5168    5168      0   0.00% tinygo build -size short -o ./build/test.hex -target=itsybitsy-m0 ./examples/mcp23017/main.go
  11128   11192     64   0.58%    5176    5176      0   0.00% tinygo build -size short -o ./build/test.hex -target=itsybitsy-m0 ./examples/mcp23017-multiple/main.go
  10992   11056     64   0.58%    5136    5136      0   0.00% tinygo build -size short -o ./build/test.hex -target=itsybitsy-m0 ./examples/mcp3008/main.go
  74120   74184     64   0.09%    6696    6696      0   0.00% tinygo build -size short -o ./build/test.hex -target=itsybitsy-m0 ./examples/mcp2515/main.go
   9312    9376     64   0.69%    5176    5176      0   0.00% tinygo build -size short -o ./build/test.hex -target=itsybitsy-m0 ./examples/mma8653/main.go
   9264    9328     64   0.69%    5176    5176      0   0.00% tinygo build -size short -o ./build/test.hex -target=itsybitsy-m0 ./examples/mpu6050/main.go
  79344   79408     64   0.08%    7856    7856      0   0.00% tinygo build -size short -o ./build/test.hex -target=p1am-100 ./examples/p1am/main.go
  13292   13356     64   0.48%    5788    5788      0   0.00% tinygo build -size short -o ./build/test.hex -target=feather-rp2040 ./examples/seesaw/soil-sensor
  12908   12972     64   0.50%    5788    5788      0   0.00% tinygo build -size short -o ./build/test.hex -target=qtpy-rp2040 ./examples/seesaw/rotary-encoder
   8816    8880     64   0.73%    7176    7176      0   0.00% tinygo build -size short -o ./build/test.hex -target=pybadge ./examples/shifter/main.go
  14296   14360     64   0.45%    8872    8872      0   0.00% tinygo build -size short -o ./build/test.hex -target=xiao-ble ./examples/ssd1306/
  12748   12812     64   0.50%    5828    5828      0   0.00% tinygo build -size short -o ./build/test.hex -target=xiao-rp2040 ./examples/ssd1306/
  17968   18032     64   0.36%    5128    5128      0   0.00% tinygo build -size short -o ./build/test.hex -target=circuitplay-express ./examples/thermistor/main.go
  11968   12032     64   0.53%    4960    4960      0   0.00% tinygo build -size short -o ./build/test.hex -target=circuitplay-bluefruit ./examples/tone
  11344   11408     64   0.56%    5128    5128      0   0.00% tinygo build -size short -o ./build/test.hex -target=arduino-nano33 ./examples/tm1637/main.go
  11728   11792     64   0.55%    5800    5800      0   0.00% tinygo build -size short -o ./build/test.hex -target=pico ./examples/touch/capacitive
  10168   10232     64   0.63%    7168    7168      0   0.00% tinygo build -size short -o ./build/test.hex -target=pyportal ./examples/touch/resistive/fourwire/main.go
  16616   16680     64   0.39%    5240    5240      0   0.00% tinygo build -size short -o ./build/test.hex -target=itsybitsy-m0 ./examples/vl53l1x/main.go
  15112   15176     64   0.42%    5240    5240      0   0.00% tinygo build -size short -o ./build/test.hex -target=itsybitsy-m0 ./examples/vl6180x/main.go
  26444   26508     64   0.24%   14148   14148      0   0.00% tinygo build -size short -o ./build/test.hex -target=feather-nrf52840-sense ./examples/waveshare-epd/epd1in54/main.go
  28924   28988     64   0.22%   18860   18860      0   0.00% tinygo build -size short -o ./build/test.hex -target=pico ./examples/waveshare-epd/epd2in66b/main.go
   7888    7952     64   0.81%    5168    5168      0   0.00% tinygo build -size short -o ./build/test.hex -target=circuitplay-express ./examples/ws2812
  65272   65336     64   0.10%    6368    6368      0   0.00% tinygo build -size short -o ./build/test.hex -target=feather-nrf52840 ./examples/is31fl3731/main.go
   7936    8000     64   0.81%    5128    5128      0   0.00% tinygo build -size short -o ./build/test.hex -target=arduino-zero ./examples/ws2812/neopixel-shield
  32720   32784     64   0.20%    5176    5176      0   0.00% tinygo build -size short -o ./build/test.hex -target=trinket-m0 ./examples/bme280/main.go
  17496   17560     64   0.37%    5120    5120      0   0.00% tinygo build -size short -o ./build/test.hex -target=circuitplay-express ./examples/microphone/main.go
  12224   12288     64   0.52%    5128    5128      0   0.00% tinygo build -size short -o ./build/test.hex -target=circuitplay-express ./examples/buzzer/main.go
  13200   13264     64   0.48%    5176    5176      0   0.00% tinygo build -size short -o ./build/test.hex -target=trinket-m0 ./examples/veml6070/main.go
   7632    7696     64   0.84%    5128    5128      0   0.00% tinygo build -size short -o ./build/test.hex -target=arduino-nano33 ./examples/l293x/simple/main.go
   9568    9632     64   0.67%    5128    5128      0   0.00% tinygo build -size short -o ./build/test.hex -target=arduino-nano33 ./examples/l293x/speed/main.go
   7600    7664     64   0.84%    5128    5128      0   0.00% tinygo build -size short -o ./build/test.hex -target=arduino-nano33 ./examples/l9110x/simple/main.go
   9984   10048     64   0.64%    5128    5128      0   0.00% tinygo build -size short -o ./build/test.hex -target=arduino-nano33 ./examples/l9110x/speed/main.go
  14432   14496     64   0.44%    5176    5176      0   0.00% tinygo build -size short -o ./build/test.hex -target=circuitplay-express ./examples/lis2mdl/main.go
  12128   12192     64   0.53%    5152    5152      0   0.00% tinygo build -size short -o ./build/test.hex -target=arduino-nano33 ./examples/max72xx/main.go
  79616   79680     64   0.08%    6768    6768      0   0.00% tinygo build -size short -o ./build/test.hex -target=feather-m0 ./examples/dht/main.go
  72784   72848     64   0.09%    6816    6816      0   0.00% tinygo build -size short -o ./build/test.hex -target=xiao ./examples/pcf8563/alarm/
   8256    8320     64   0.78%    5168    5168      0   0.00% tinygo build -size short -o ./build/test.hex -target=xiao ./examples/pcf8563/clkout/
  67408   67472     64   0.09%    6816    6816      0   0.00% tinygo build -size short -o ./build/test.hex -target=xiao ./examples/pcf8563/time/
  72624   72688     64   0.09%    6816    6816      0   0.00% tinygo build -size short -o ./build/test.hex -target=xiao ./examples/pcf8563/timer/
   9760    9824     64   0.66%    5176    5176      0   0.00% tinygo build -size short -o ./build/test.hex -target=feather-m0 ./examples/ina260/main.go
  13952   14016     64   0.46%    5176    5176      0   0.00% tinygo build -size short -o ./build/test.hex -target=feather-m0 ./examples/ina219/main.go
  11344   11408     64   0.56%    5192    5192      0   0.00% tinygo build -size short -o ./build/test.hex -target=feather-m0 ./examples/ina3221/main.go
  62816   62880     64   0.10%    8632    8632      0   0.00% tinygo build -size short -o ./build/test.hex -target=feather-m4 ./examples/i2csoft/adt7410/
   9848    9912     64   0.65%    5728    5728      0   0.00% tinygo build -size short -o ./build/test.uf2 -target=pico ./examples/xpt2046/main.go
  46460   46524     64   0.14%    9344    9344      0   0.00% tinygo build -size short -o ./build/test.hex -target=pybadge ./examples/sx127x/lora_rxtx/
  11596   11660     64   0.55%    5808    5808      0   0.00% tinygo build -size short -o ./build/test.uf2 -target=pico ./examples/scd4x/main.go
   8808    8872     64   0.73%    5136    5136      0   0.00% tinygo build -size short -o ./build/test.uf2 -target=circuitplay-express ./examples/makeybutton/main.go
  10480   10544     64   0.61%    5152    5152      0   0.00% tinygo build -size short -o ./build/test.hex -target=arduino-nano33 ./examples/ds18b20/main.go
  12748   12812     64   0.50%    5788    5788      0   0.00% tinygo build -size short -o ./build/test.uf2 -target=pico ./examples/mpu6886/main.go
   8688    8752     64   0.74%    5128    5128      0   0.00% tinygo build -size short -o ./build/test.hex -target=arduino-nano33 ./examples/ttp229/main.go
  67440   67504     64   0.09%    6656    6656      0   0.00% tinygo build -size short -o ./build/test.hex -target=arduino-nano33 ./examples/ndir/main_ndir.go
  10444   10508     64   0.61%    5780    5780      0   0.00% tinygo build -size short -o ./build/test.uf2 -target=pico ./examples/mpu9150/main.go
   9396    9460     64   0.68%    6220    6220      0   0.00% tinygo build -size short -o ./build/test.hex -target=macropad-rp2040 ./examples/encoders/quadrature-interrupt
  45252   45316     64   0.14%    7876    7876      0   0.00% tinygo build -size short -o ./build/test.uf2 -target=pico ./examples/tmc2209/main.go
  13892   13956     64   0.46%    4956    4956      0   0.00% tinygo build -size short -o ./build/test.uf2 -target=nicenano ./examples/sharpmem/main.go
  64268   64332     64   0.10%    6404    6404      0   0.00% tinygo build -size short -o ./build/test.hex -target=feather-nrf52840 ./examples/max6675/main.go
  13900   13964     64   0.46%    5796    5796      0   0.00% tinygo build -size short -o ./build/test.hex -target=pico ./examples/ens160/main.go
  22804   22868     64   0.28%    7148    7148      0   0.00% tinygo build -size short -o ./build/test.hex -target=pico ./examples/w5500/main.go
 376428  376492     64   0.02%   14344   14344      0   0.00% tinygo build -size short -o ./build/test.hex -target=pyportal -stack-size 8kb ./examples/net/http-get/
 125240  125304     64   0.05%   11880   11880      0   0.00% tinygo build -size short -o ./build/test.hex -target=elecrow-rp2350 -stack-size 8kb ./examples/net/ntpclient/
  17816   17896     80   0.45%    6624    6624      0   0.00% tinygo build -size short -o ./build/test.hex -target=feather-rp2040 ./examples/adafruit4650
  72980   73060     80   0.11%    7444    7444      0   0.00% tinygo build -size short -o ./build/test.hex -target=itsybitsy-m0 ./examples/flash/console/spi
  68868   68948     80   0.12%    9484    9484      0   0.00% tinygo build -size short -o ./build/test.hex -target=pyportal ./examples/flash/console/qspi
  69180   69260     80   0.12%    6832    6832      0   0.00% tinygo build -size short -o ./build/test.hex -target=feather-m0 ./examples/gps/i2c/main.go
  69648   69728     80   0.11%    6932    6932      0   0.00% tinygo build -size short -o ./build/test.hex -target=feather-m0 ./examples/gps/uart/main.go
  13260   13340     80   0.60%    5804    5804      0   0.00% tinygo build -size short -o ./build/test.hex -target=pico ./examples/pca9685/main.go
  14852   14932     80   0.54%    5868    5868      0   0.00% tinygo build -size short -o ./build/test.hex -target=pico     ./examples/sgp30
  12684   12764     80   0.63%    5820    5820      0   0.00% tinygo build -size short -o ./build/test.hex -target=thumby ./examples/ssd1306/
  18888   18968     80   0.42%    6360    6360      0   0.00% tinygo build -size short -o ./build/test.hex -target=waveshare-rp2040-pizero ./examples/st7789/waveshare-1in3-lcd-hat/
  13268   13348     80   0.60%    7428    7428      0   0.00% tinygo build -size short -o ./build/test.hex -target=pyportal ./examples/touch/resistive/pyportal_touchpaint/main.go
  38252   38332     80   0.21%    6372    6372      0   0.00% tinygo build -size short -o ./build/test.hex -target=feather-rp2040 ./examples/pcf8523/
  13436   13516     80   0.60%    5796    5796      0   0.00% tinygo build -size short -o ./build/test.hex -target=pico ./examples/qmi8658c/main.go
  12028   12108     80   0.67%    5788    5788      0   0.00% tinygo build -size short -o ./build/test.hex -target=feather-rp2040 ./examples/pcf8591/
  76328   76408     80   0.10%   11240   11240      0   0.00% tinygo build -size short -o ./build/test.hex -target=feather-m4 ./examples/sdcard/console/
  35456   35536     80   0.23%    7496    7496      0   0.00% tinygo build -size short -o ./build/test.uf2 -target=pico ./examples/ssd1289/main.go
  12508   12588     80   0.64%    6780    6780      0   0.00% tinygo build -size short -o ./build/test.hex -target=pico ./examples/irremote/main.go
  13348   13428     80   0.60%    5804    5804      0   0.00% tinygo build -size short -o ./build/test.hex -target=badger2040 ./examples/uc8151/main.go
  13092   13172     80   0.61%    6248    6248      0   0.00% tinygo build -size short -o ./build/test.hex -target=badger2040 ./examples/waveshare-epd/epd2in9v2/main.go
  14976   15056     80   0.53%    5840    5840      0   0.00% tinygo build -size short -o ./build/test.uf2 -target=pico ./examples/scd30/main.go
  69420   69500     80   0.12%    7276    7276      0   0.00% tinygo build -size short -o ./build/test.hex -target=pico ./examples/ndir/main_ndir.go
  12456   12536     80   0.64%    5848    5848      0   0.00% tinygo build -size short -o ./build/test.hex -target=macropad-rp2040 ./examples/sh1106/macropad_spi
  67772   67852     80   0.12%    7236    7236      0   0.00% tinygo build -size short -o ./build/test.uf2 -target=pico ./examples/mcp9808/main.go
  13096   13176     80   0.61%    5736    5736      0   0.00% tinygo build -size short -o ./build/test.hex -target=pico ./examples/tmc5160/main.go
  17380   17460     80   0.46%    5900    5900      0   0.00% tinygo build -size short -o ./build/test.hex -target=pico ./examples/si5351/main.go
  90736   90816     80   0.09%    7596    7596      0   0.00% tinygo build -size short -o ./build/test.hex -target=pico ./examples/sd
 125992  126072     80   0.06%    8812    8812      0   0.00% tinygo build -size short -o ./build/test.hex -target=arduino-nano33 -stack-size 8kb ./examples/net/tcpclient/
 354912  354992     80   0.02%   12840   12840      0   0.00% tinygo build -size short -o ./build/test.hex -target=nano-rp2040 -stack-size 8kb ./examples/net/websocket/dial/
 423736  423816     80   0.02%   17176   17176      0   0.00% tinygo build -size short -o ./build/test.hex -target=matrixportal-m4 -stack-size 8kb ./examples/net/webstatic/
 170964  171044     80   0.05%    8880    8880      0   0.00% tinygo build -size short -o ./build/test.hex -target=arduino-mkrwifi1010 -stack-size 8kb ./examples/net/tlsclient/
 122088  122168     80   0.07%   14104   14104      0   0.00% tinygo build -size short -o ./build/test.hex -target=wioterminal -stack-size 8kb ./examples/net/webclient/
 372456  372536     80   0.02%   19644   19644      0   0.00% tinygo build -size short -o ./build/test.hex -target=wioterminal -stack-size 8kb ./examples/net/webserver/
 370624  370704     80   0.02%   18496   18496      0   0.00% tinygo build -size short -o ./build/test.hex -target=wioterminal -stack-size 8kb ./examples/net/mqttclient/paho/
  17520   17616     96   0.55%    7408    7408      0   0.00% tinygo build -size short -o ./build/test.uf2 -target=pico ./examples/as560x/main.go
  89488   89584     96   0.11%    7672    7672      0   0.00% tinygo build -size short -o ./build/test.hex -target=challenger-rp2040 ./examples/net/ntpclient/
 108220  108316     96   0.09%   10792   10792      0   0.00% tinygo build -size short -o ./build/test.hex -target=metro-m4-airlift -stack-size 8kb ./examples/net/socket/
 161880  161976     96   0.06%    9584    9584      0   0.00% tinygo build -size short -o ./build/test.hex -target=nano-rp2040 -stack-size 8kb ./examples/net/mqttclient/natiu/
 178060  178156     96   0.05%   11992   11992      0   0.00% tinygo build -size short -o ./build/test.hex -target=elecrow-rp2040 -stack-size 8kb ./examples/net/tlsclient/
7086380 7095292   8912   0.00% 1199329 1199329      0   0.00%

@davecheney

Copy link
Copy Markdown
Contributor

@deadprogram i've got my xtensa build up and running and am flashing this now. You'll need to rebase for the change to the size-test test

@davecheney

Copy link
Copy Markdown
Contributor

Tested on a XIAO ESP32-S3. Flashing works in both directions between the default build and -tags=usbotg, and CDC echo works after each flash.

Every flash that ends with the board running the usbotg build prints a reset warning, although the board does reset and enumerates as 303a:4001.

Flashing from the default Serial/JTAG echo to the usbotg echo:

$ tinygo flash -target=xiao-esp32s3 -tags=usbotg ./src/examples/echo
...
MD5 verified: 6518363fc074da91311f93e7880c175c

Warning: device may not have been reset: broken pipe

Reflashing the usbotg echo over the 1200 baud touch:

Warning: device may not have been reset: input/output error

It looks like espflasher still writes to the Serial/JTAG port after the app has moved the PHY to USB-OTG.

@deadprogram

Copy link
Copy Markdown
Member Author

@deadprogram i've got my xtensa build up and running and am flashing this now. You'll need to rebase for the change to the size-test test

on it now.

A bus reset can drop the IN packet in flight, so its completion never
arrives and the TX pump stays claimed. Writes then block once the ring
is full. Reset the in-flight state from the CDC reset handler and resend
from the ring.
Add a driver for the DWC2 USB-OTG controller, enabled with -tags=usbotg.
It runs the shared USB stack, so CDC, HID, MSC and MIDI work as on other
targets. Without the tag the USB Serial/JTAG driver is used as before.

EnterBootloader hands the PHY back to USB Serial/JTAG and restarts into
download mode, so a 1200 baud touch can reflash the board.
@davecheney

Copy link
Copy Markdown
Contributor

usb joystick HID example works; https://github.com/davecheney/tinygo/tree/badge-usb-joystick demo program added to examples/

@deadprogram

Copy link
Copy Markdown
Member Author

Tested on a XIAO ESP32-S3. Flashing works in both directions between the default build and -tags=usbotg, and CDC echo works after each flash.

Every flash that ends with the board running the usbotg build prints a reset warning, although the board does reset and enumerates as 303a:4001.

Flashing from the default Serial/JTAG echo to the usbotg echo:

$ tinygo flash -target=xiao-esp32s3 -tags=usbotg ./src/examples/echo
...
MD5 verified: 6518363fc074da91311f93e7880c175c

Warning: device may not have been reset: broken pipe

Reflashing the usbotg echo over the 1200 baud touch:

Warning: device may not have been reset: input/output error

It looks like espflasher still writes to the Serial/JTAG port after the app has moved the PHY to USB-OTG.

I am working on a fix in espflasher for that right now.

@davecheney davecheney left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM.

On a TinyGo Conf 2026 badge (ESP32-S3-DevKit) built with -target=xiao-esp32s3 -tags=usbotg.

Test Result
CDC echo pass
Reflash over the 1200 baud touch pass, prints the reset warning above
Flash between the default build and -tags=usbotg pass, both directions
HID joystick with the badge's analog stick and buttons pass
usb-storage 5 of 5 pass
usb-storage with -scheduler=cores 20 of 20 pass

The usb-storage loops copy a random file to the disk, remount it and compare. 20 KB for the default scheduler and 40 KB with -scheduler=cores. No USB errors in the host kernel log.

The input/output error after flashing remains, I don't see that as a blocker, it just adds character.

This version sends a 1200 baud touch to a USB-OTG CDC port, so tinygo
flash works with -tags=usbotg on the ESP32-S3. It also no longer warns
when the port goes away after reset as the app moves to USB-OTG.
@deadprogram

Copy link
Copy Markdown
Member Author

Thanks for testing @davecheney

You are right. On USB-JTAG/Serial the chip resets as soon as espflasher asserts RTS. The usbotg app boots and moves the PHY to USB-OTG within the 200 ms before espflasher releases RTS, so that last write fails even though the reset already happened.

This is fixed in espflasher v0.9.2 (tinygo-org/espflasher#71), which this PR now uses. On a XIAO ESP32-S3 every flash between the default and usbotg builds now prints "Device reset." and CDC echo works after each one.

@davecheney

Copy link
Copy Markdown
Contributor

Updating esp flasher fixed the reset on flash 👍

@deadprogram
deadprogram merged commit dce69fb into dev Oct 24, 2026
37 checks passed
@deadprogram
deadprogram deleted the esp32s3-usb-otg branch October 24, 2026 10:21
@deadprogram

Copy link
Copy Markdown
Member Author

Thanks for reviews and testing @davecheney merged stack

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants