USB (tested with midi) not working on pico 2 (rp2350 in ARM mode only)
Port, board and/or hardware
rp2 port, pico 2 board
MicroPython version
MicroPython v1.24.0 on 2024-10-25; Raspberry Pi Pico2 with RP2350
Reproduction
This is the script I used:
# MicroPython USB MIDI example
#
# This example demonstrates creating a custom MIDI device.
#
# To run this example:
#
# 1. Make sure `usb-device-midi` is installed via: mpremote mip install usb-device-midi
#
# 2. Run the example via: mpremote run midi_example.py
#
# 3. mpremote will exit with an error after the previous step, because when the
# example runs the existing USB device disconnects and then re-enumerates with
# the MIDI interface present. At this point, the example is running.
#
# 4. To see output from the example, re-connect: mpremote connect PORTNAME
#
#
# MIT license; Copyright (c) 2023-2024 Angus Gratton
import usb.device
from usb.device.midi import MIDIInterface
import time
class MIDIExample(MIDIInterface):
# Very simple example event handler functions, showing how to receive note
# and control change messages sent from the host to the device.
#
# If you need to send MIDI data to the host, then it's fine to instantiate
# MIDIInterface class directly.
def on_open(self):
super().on_open()
print("Device opened by host")
def on_note_on(self, channel, pitch, vel):
print(f"RX Note On channel {channel} pitch {pitch} velocity {vel}")
def on_note_off(self, channel, pitch, vel):
print(f"RX Note Off channel {channel} pitch {pitch} velocity {vel}")
def on_control_change(self, channel, controller, value):
print(f"RX Control channel {channel} controller {controller} value {value}")
m = MIDIExample()
# Remove builtin_driver=True if you don't want the MicroPython serial REPL available.
usb.device.get().init(m, builtin_driver=True)
print("Waiting for USB host to configure the interface...")
while not m.is_open():
time.sleep_ms(100)
print("Starting MIDI loop...")
# TX constants
CHANNEL = 0
PITCH = 60
CONTROLLER = 64
control_val = 0
while m.is_open():
time.sleep(1)
print(f"TX Note On channel {CHANNEL} pitch {PITCH}")
m.note_on(CHANNEL, PITCH) # Velocity is an optional third argument
time.sleep(0.5)
print(f"TX Note Off channel {CHANNEL} pitch {PITCH}")
m.note_off(CHANNEL, PITCH)
time.sleep(1)
print(f"TX Control channel {CHANNEL} controller {CONTROLLER} value {control_val}")
m.control_change(CHANNEL, CONTROLLER, control_val)
control_val += 1
if control_val == 0x7F:
control_val = 0
time.sleep(1)
print("USB host has reset device, example done.")
Expected behaviour
This example code works on a pico 1, here is the output:
MicroPython v1.24.0 on 2024-10-25; Raspberry Pi Pico with RP2040
Type "help()" for more information.
>>> import midi_example
Waiting for USB host to configure the interface...
device disconnected
then mpremote disconnects. I can reconnect and it displays the log output from the example.
kernel log display:
6041.587646] usb 7-1.1: new full-speed USB device number 9 using xhci_hcd
[ 6041.722135] usb 7-1.1: New USB device found, idVendor=2e8a, idProduct=0005, bcdDevice= 1.00
[ 6041.722144] usb 7-1.1: New USB device strings: Mfr=1, Product=2, SerialNumber=3
[ 6041.722149] usb 7-1.1: Product: Board in FS mode
[ 6041.722153] usb 7-1.1: Manufacturer: MicroPython
[ 6041.722156] usb 7-1.1: SerialNumber: e6613008e343372e
[ 6041.787350] cdc_acm 7-1.1:1.0: ttyACM0: USB ACM device
[ 6259.833061] usb 7-1.1: USB disconnect, device number 9
[ 6260.104404] usb 7-1.1: new full-speed USB device number 10 using xhci_hcd
[ 6260.239359] usb 7-1.1: New USB device found, idVendor=2e8a, idProduct=0005, bcdDevice= 1.00
[ 6260.239362] usb 7-1.1: New USB device strings: Mfr=1, Product=2, SerialNumber=3
[ 6260.239364] usb 7-1.1: Product: Board in FS mode
[ 6260.239365] usb 7-1.1: Manufacturer: MicroPython
[ 6260.239366] usb 7-1.1: SerialNumber: e6613008e343372e
[ 6260.285551] cdc_acm 7-1.1:1.0: ttyACM0: USB ACM device
[ 6260.334768] mc: Linux media interface: v0.10
[ 6260.411764] usbcore: registered new interface driver snd-usb-audio
Observed behaviour
This is the output on the rp2 REPL:
>>> import midi_example
Waiting for USB host to configure the interface...
and it hangs, control characters have no effect.
Meanwhile here is the kernel log:
[ 5138.193121] usb 7-1.1: new full-speed USB device number 4 using xhci_hcd
[ 5138.327835] usb 7-1.1: New USB device found, idVendor=2e8a, idProduct=0005, bcdDevice= 1.00
[ 5138.327845] usb 7-1.1: New USB device strings: Mfr=1, Product=2, SerialNumber=3
[ 5138.327850] usb 7-1.1: Product: Board in FS mode
[ 5138.327855] usb 7-1.1: Manufacturer: MicroPython
[ 5138.327858] usb 7-1.1: SerialNumber: 1c59c033388f3e4e
[ 5138.437887] cdc_acm 7-1.1:1.0: ttyACM0: USB ACM device
[ 5138.437924] usbcore: registered new interface driver cdc_acm
[ 5138.437927] cdc_acm: USB Abstract Control Model driver for USB modems and ISDN adapters
Nothing else is printed.
Additional Information
This is probably a core issue with customizable USB on this new port.
Thank you very much for your work on micropython and also for providing this template.
Code of Conduct
Yes, I agree
MicroPython USB-CDC Regression on RP2350 (Pico 2W)
Port, board and/or hardware
RP2350 Pico 2W
MicroPython version
MicroPython versions v1.26.0 through v1.27.0 (and likely v1.28.0+) fail to enumerate USB-CDC on the RP2350 (Raspberry Pi Pico 2W). The device does not appear as a serial port on the host. The last working version is v1.25.0-preview (commit f187c77da).
Reproduction
- Build MicroPython v1.27.0 for
BOARD=RPI_PICO2_W - Flash the .uf2 to a Raspberry Pi Pico 2W
- Connect via USB -- no serial port appears (
ls /dev/tty.usbmodem*on macOS returns nothing) - Apply the two-line fix above, rebuild, reflash
- USB serial port appears and works normally
Expected behaviour
you should be able to connect usb to mac or windows (mac in this case)
Observed behaviour
Plug in usb and nothing happens
Additional Information
Root Cause
In the transition from v1.25.0 to v1.26.0, the --wrap=dcd_event_handler linker flag and its corresponding __wrap_dcd_event_handler() function were removed from ports/rp2/CMakeLists.txt and shared/tinyusb/mp_usbd.c.
The removal was intentional -- TinyUSB added a tud_event_hook_cb() callback (called from usbd.c:383) meant to replace the linker wrap. MicroPython v1.26.0+ implements this hook via MICROPY_WRAP_TUD_EVENT_HOOK_CB in shared/tinyusb/mp_usbd.c.
However, on the RP2350, the TinyUSB hook alone is not sufficient for USB device enumeration to succeed. The linker wrap is still needed. The RP2040 may not be affected (untested).
The Fix
Two changes restore USB functionality on RP2350 while keeping all v1.27.0 features (including BLE security):
1. ports/rp2/CMakeLists.txt -- Re-add linker wrap
target_link_options(${MICROPY_TARGET} PRIVATE
-Wl,--defsym=__micropy_c_heap_size__=${MICROPY_C_HEAP_SIZE}
+ -Wl,--wrap=dcd_event_handler
-Wl,--wrap=runtime_init_clocks
)
2. shared/tinyusb/mp_usbd.c -- Re-add wrapper function
Add before the tud_event_hook_cb function:
extern void __real_dcd_event_handler(dcd_event_t const *event, bool in_isr);
TU_ATTR_FAST_FUNC void __wrap_dcd_event_handler(dcd_event_t const *event, bool in_isr) {
__real_dcd_event_handler(event, in_isr);
mp_usbd_schedule_task();
mp_hal_wake_main_task_from_isr();
}
Both the hook (tud_event_hook_cb) and the wrap (__wrap_dcd_event_handler) can coexist safely -- v1.25.0-preview had both. The wrap ensures the USB task is scheduled from the ISR context, which appears to be required on RP2350 for enumeration to complete.
Affected Versions
| Version | USB on RP2350 | BLE Security |
|---|---|---|
| v1.25.0-preview (f187c77da) | Works | No (config params unknown) |
| v1.26.0 | Broken | Yes |
| v1.27.0 | Broken | Yes |
| v1.27.0 + patch (above) | Works | Yes |
| v1.28.0-preview | Broken (untested, assumed) | Yes |
Discovered
2026-03-24 by LofiFren (PicoCalc project)
https://github.com/LofiFren/PicoCalc
Code of Conduct
Yes, I agree