USB HID device on ESP32 has a problem!
Port, board and/or hardware
ESP32S3
MicroPython version
I created an issue on USB-LIB but I think the problem must be solved here...
https://github.com/micropython/micropython-lib/issues/1044
Reproduction
The code for reproducing the problem has been tried on MicroPython 1.26.1 and also on the new 1.27 releases.
It is not ESP board related. I tried the examples on different boards with different MicroPython versions.
I can also share Wireshark logs if necessary, but the bug is easily reproducible with the unmodified USB HID device samples from the lib directory.
Expected behaviour
The samples should "just work" also for the ESP32S3 boards.
Observed behaviour
The LED out reports are arriving nicely in the code, but all keystrokes that are sent are arriving with all bytes "zero" at the host. (Confirmed by Wireshark )
Additional Information
See the debug code changes documented in the original lib-issue!
Code of Conduct
Yes, I agree
esp32: Re-initialise USB after soft reset.
Summary
Necessary if runtime USBDevice has been active on ESP32-S3 along with the default USB-CDC REPL. Without this fix, two things happen after a soft reset:
- The USB host sees the device disappear and not come back.
- Inside MicroPython, the CDC driver thinks it's still connected and active but eventually the TX FIFO fills up and significantly slows down stdout (visible on boards with a separate UART REPL).
This also fixes the init order for mp_usbd_init() so that USB is initialised after boot.py completes, the same as on other ports like rp2. This allows setting up a custom USB device in boot.py without it enumerating twice.
Testing
Note this PR needs to be cherry-picked onto #18332 or #18407 if PSRAM is enabled.
mpremote a0 run pr18332.py- where the file has the sample code from #18332- If using the USB-CDC interface for
mpremote runthen mpremote will error out as it disconnects to change USB device config. mpremote a0- Use Ctrl-C and Ctrl-B to stop the sample code and drop to an interactive REPL.
- Type Ctrl-D to soft reset.
Without fix: USB-CDC disappears along with the keyboard device, never comes back. Connecting to the UART REPL and interacting with it will eventually slow UART TX down to a trickle (quickest way to trigger is print("a"*500).)
With fix: USB-CDC disappears and re-enumerates again as plain USB-CDC device.
Second test is to put some custom usb device init code in boot.py and check that initial enumerationand soft reset work as expected there. I used:
import usb.device
from usb.device.mouse import MouseInterface
m = MouseInterface()
usb.device.get().init(m, builtin_driver=True)
Related bugs
There is still a related bug here, because calling TinyUSB tud_disconnect() on ESP32-S3 doesn't trigger any DCD_EVENT_UNPLUGGED or DCD_EVENT_BUS_RESET event. This means that the USB device driver state goes out of sync (i.e. TinyUSB thinks it's still connected).
This PR avoids this in soft reset because the host will reconfigure the USB device and the state will correct itself, but there's still a problem if Python code does something like this:
mpremote a0 run pr18332.py- (errors out as expected)
mpremote a0- Ctrl-C, Ctrl-B to get interactive REPL.
import machine; u = machine.USBDevice(); u.active(0)
In this situation the USB device disappears from the host as expected, but the issue with very slow stdout will happen as the CDC device driver still thinks it's connected and configured.
I've opened a Discussion on the TinyUSB repo to try and find out the expected behaviour: https://github.com/hathach/tinyusb/discussions/3331
This work was funded through GitHub Sponsors.