A comprehensive and efficient USB MIDI device library tailored for the WCH CH32X035 RISC-V microcontroller series.
This library leverages the hardware USBFS peripheral to provide a low-latency, class-compliant MIDI interface. No drivers are needed on host operating systems (Windows, macOS, Linux, Android, iOS).
- Class Compliant: Works natively without drivers on all major operating systems.
- Bi-directional Communication: Send and receive MIDI messages seamlessly.
- Event-Driven Architecture: Register efficient C++ callbacks to handle specific incoming messages (Note On/Off, Control Change, etc.), avoiding complex parsing in your main loop.
- Full Standard Support: Supports the entire range of standard channel voice messages and real-time system messages.
- Hardware Optimized: Built on top of the CH32X035 USBFS for minimal overhead.
This library supports sending and receiving the following MIDI specifications:
| Message Type | Send | Receive (Callback) | Description |
|---|---|---|---|
| Note On | ✅ | ✅ | Trigger a note |
| Note Off | ✅ | ✅ | Stop a note |
| Control Change (CC) | ✅ | ✅ | Knobs, faders, pedals |
| Program Change (PC) | ✅ | ✅ | Change instrument/preset |
| Pitch Bend | ✅ | ✅ | 14-bit pitch modification |
| Aftertouch (Channel) | ✅ | ✅ | Global pressure |
| Poly Pressure | ✅ | ✅ | Per-key pressure |
| Real-Time Messages | ✅ | ✅ | Clock, Start, Stop, Continue |
- Download this repository as a ZIP file.
- Open the Arduino IDE.
- Go to Sketch -> Include Library -> Add .ZIP Library...
- Select the downloaded ZIP file.
- Restart the IDE.
Include the library and initialize it in setup(). Crucial: You must call USBMIDI.poll() inside your loop() to process USB events.
#include <USBMIDI.h>
void setup() {
// Initialize USB MIDI stack
USBMIDI.begin();
}
void loop() {
// Must be called frequently to handle USB traffic
USBMIDI.poll();
}It is recommended to avoid delay() so that USBMIDI.poll() runs continuously. Here is a non-blocking example using millis():
unsigned long lastTime = 0;
bool noteOn = false;
void loop() {
// 1. ALWAYS poll USB first
USBMIDI.poll();
// 2. Non-blocking timer logic
unsigned long now = millis();
if (now - lastTime >= 500) {
lastTime = now;
if (!noteOn) {
USBMIDI.sendNoteOn(0, 60, 127); // Note On
noteOn = true;
} else {
USBMIDI.sendNoteOff(0, 60, 0); // Note Off
noteOn = false;
}
}
}Instead of checking buffers manually, define functions that will be called automatically when a message arrives.
// Define callback functions
void handleNoteOn(uint8_t channel, uint8_t note, uint8_t velocity) {
// Toggle an LED or play a sound
}
void handleControlChange(uint8_t channel, uint8_t controller, uint8_t value) {
// Update internal parameters
}
void handleSystemRealTime(uint8_t realtimebyte) {
if (realtimebyte == 0xF8) {
// MIDI Clock tick
}
}
void setup() {
USBMIDI.begin();
// Register callbacks
USBMIDI.setHandleNoteOn(handleNoteOn);
USBMIDI.setHandleControlChange(handleControlChange);
USBMIDI.setHandleRealTime(handleSystemRealTime);
}
void loop() {
USBMIDI.poll();
}This library was developed by NoNamedCat.
It is architecturally inspired by and built upon the CH32X035_USBSerial library.
Special thanks to jobitjoseph for his excellent work on the CH32X035 USB CDC implementation, which provided the essential foundation for the low-level USBFS initialization and endpoint management used in this project.
This project is licensed under the MIT License - see the LICENSE file for details.
