Skip to content

Raspberry Pi USB MIDI Gadget on Trixie: Setup and Troubleshooting

A Raspberry Pi with a USB device-mode port can appear to a computer as a class-compliant MIDI endpoint. This guide uses the kernel's single-function g_midi module, verifies the ALSA port, and sends a controlled test note from Python.

Do not combine gadget managers

g_midi, rpi-usb-gadget, and a ConfigFS gadget are alternative ways to claim the same USB Device Controller. Disable the active Ethernet or composite gadget before enabling g_midi. See the USB Gadget Mode comparison before changing an existing setup.

What you need

  • A Raspberry Pi with a device-mode/OTG-capable port. The Zero, Zero W, and Zero 2 W are common manual gadget targets; Pi 4 uses its USB-C dual-role port. Confirm other models against their hardware documentation and current kernel support.
  • A known data-capable USB cable. On a Pi Zero, connect it to the port labelled USB, not PWR IN.
  • Raspberry Pi OS Trixie and a separate SSH, serial, or local-console recovery path.
  • A host computer with a DAW, MIDI monitor, or software synthesizer.

The official Trixie rpi-usb-gadget package supports a broader model list for its managed Ethernet function. That list must not be treated as proof that this manual g_midi recipe works identically on every board.

1. Record the current state

Before editing boot settings, check the OS, architecture, controller, and competing gadget configuration:

1
2
3
4
5
cat /etc/os-release
uname -m
ls -la /sys/class/udc
lsmod | grep -E 'g_(ether|midi|mass_storage)|libcomposite'
find /sys/kernel/config/usb_gadget -maxdepth 2 -type f -name UDC -print -exec cat {} \; 2>/dev/null

If an existing gadget owns the UDC, disable it using that setup's documented procedure and reboot before continuing.

2. Enable the device-mode controller

Back up the boot configuration, then edit it:

sudo cp -a /boot/firmware/config.txt /boot/firmware/config.txt.pre-midi
sudo nano /boot/firmware/config.txt

Add this line once:

dtoverlay=dwc2,dr_mode=peripheral

The explicit dr_mode=peripheral prevents ambiguity about which role the dual-role controller should use. Do not change cmdline.txt for this procedure.

3. Load the MIDI gadget

Test the module manually before making it persistent:

1
2
3
4
sudo modprobe g_midi
lsmod | grep g_midi
ls -la /sys/class/udc
journalctl -b -k | tail -100

If that succeeds, add one line to /etc/modules:

g_midi

Reboot, connect the data cable to the correct device-mode port, and check the host's USB device list.

Optional device strings

Create /etc/modprobe.d/g_midi.conf only if you need recognizable strings during testing:

options g_midi iManufacturer="LocalLab" iProduct="Raspberry Pi MIDI"

Avoid copying arbitrary USB vendor/product IDs from examples. Shipping a product requires identifiers and descriptors appropriate to that product.

4. Verify ALSA before opening a DAW

Install the packaged tools and Python libraries from Trixie:

1
2
3
4
sudo apt update
sudo apt install alsa-utils python3-mido python3-rtmidi
amidi -l
aconnect -l

Using Debian packages avoids modifying the system Python environment with a global pip install. The gadget port commonly contains MIDI Gadget or f_midi, but do not assume its numeric ALSA address will always be hw:0,0.

Use a raw monitor on the host while sending a known message. This separates USB/ALSA detection from DAW track routing, plugins, audio devices, and speakers.

5. Send a one-second test note with Python

Save this as midi_send.py:

import time
import mido

names = mido.get_output_names()
print("Available MIDI outputs:")
for name in names:
    print(f"- {name}")

candidates = [
    name for name in names
    if "midi gadget" in name.lower() or "f_midi" in name.lower()
]

if not candidates:
    raise SystemExit("USB MIDI output not found; check amidi -l and g_midi")

with mido.open_output(candidates[0]) as port:
    port.send(mido.Message("note_on", note=60, velocity=64, channel=0))
    time.sleep(1)
    port.send(mido.Message("note_off", note=60, velocity=0, channel=0))

Run it without sudo first:

python3 midi_send.py

If permissions block access, inspect the device ownership and your audio-group/session configuration instead of routinely running an application as root.

Host and application checklist

Layer Proof to collect If it fails
USB enumeration Gadget appears in the host USB device list Check cable, port, power, UDC, and Pi kernel log
Host MIDI subsystem Gadget appears in Audio MIDI Setup, Device Manager/app list, or ALSA Reconnect once and inspect the host system log
Raspberry Pi ALSA amidi -l lists the gadget endpoint Check g_midi, dwc2, and competing gadgets
DAW input/output Correct gadget port is enabled on the intended track Check monitoring, channel, and MIDI routing
Audio output Synth receives note events and an audio device is selected Test with a MIDI monitor before changing gadget settings

Troubleshooting

The Pi powers on but the host sees no USB device

Replace the cable with a verified data cable and use the correct port. Then collect:

1
2
3
4
ls -la /sys/class/udc
lsmod | grep -E 'dwc2|g_midi'
journalctl -b -k | grep -iE 'dwc2|udc|gadget|usb'
vcgencmd get_throttled

modprobe g_midi reports that the device is busy

Another gadget probably owns the UDC. Disable rpi-usb-gadget, a ConfigFS service, or another legacy g_* module rather than trying to load several independent gadget drivers.

MIDI appears in the OS but not in the DAW

Restart the DAW after connecting the gadget, explicitly enable the port in its MIDI preferences, and select it on the track. Confirm events with a MIDI monitor before investigating audio latency.

Notes stick on the synthesizer

Always send matching note-off messages, handle application shutdown, and provide a local “all notes off” control. A cable disconnect can occur between note-on and note-off.

Latency varies

Measure end-to-end latency with a loopback or timestamped physical event. Record the host OS, DAW buffer, audio interface, sample rate, CPU load, and number of runs. Do not change the CPU governor or overclock until the measurement identifies a Raspberry Pi scheduling problem.

FAQ

Is USB MIDI audio?

No. MIDI carries musical events such as notes and controller values. A synthesizer and audio output are still required to produce sound.

Can the Pi be MIDI and USB Ethernet simultaneously?

Yes, but only as a deliberately constructed composite ConfigFS gadget with compatible descriptors and host drivers. Independent g_midi and g_ether modules should not be loaded together.

Does this require a custom driver on the host?

The kernel function is intended to expose a standard USB MIDI device. Actual recognition still depends on the host OS, descriptors, cable, and application, so test every supported host.

How do I undo the setup?

Remove the g_midi line from /etc/modules, remove or rename its modprobe file, restore the backed-up config.txt, and reboot. Confirm the UDC is no longer bound before enabling another gadget.