Last active
October 10, 2025 13:46
-
-
Save tecteun/6094e9afc404d32d0ec2062618f76946 to your computer and use it in GitHub Desktop.
XIAO MIDI Synthesizer RP2040 USB midi HOST/DEVICE hybrid
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// be sure to run at / enable 240mhz | |
// enable operating system "FreeRTOS" | |
// otherwise it crashes somewhere, after few minutes | |
/********************************************************************* | |
MIT license, check LICENSE for more information | |
Copyright (c) 2023 rppicomidi | |
Modified from device_info.ino from | |
https://github.com/sekigon-gonnoc/Pico-PIO-USB/blob/main/examples/arduino/device_info/device_info.ino | |
2nd Copyright notice below included per license terms. | |
*********************************************************************/ | |
/********************************************************************* | |
Adafruit invests time and resources providing this open source code, | |
please support Adafruit and open-source hardware by purchasing | |
products from Adafruit! | |
MIT license, check LICENSE for more information | |
Copyright (c) 2019 Ha Thach for Adafruit Industries | |
All text above, and the splash screen below must be included in | |
any redistribution | |
*********************************************************************/ | |
#ifdef __OPTIMIZE_SIZE__ | |
#error "Please use the Tools->Optimize: Menu to choose any Optimize setting other than -Os" | |
#endif | |
#ifndef ARDUINO_ARCH_RP2040 | |
#error "This program only works with RP2040-based boards" | |
#endif | |
#if defined(USE_TINYUSB_HOST) || !defined(USE_TINYUSB) | |
#error "Please use the Menu to select Tools->USB Stack: Adafruit TinyUSB" | |
#endif | |
#include "pio_usb.h" | |
// The following definitions apply to the custom hardware decribed in the | |
// README.md file for the usb_midi_host library and for the commercial | |
// Adafruit RP2040 Feather with USB A Host board. If you have your own | |
// hardware, please change the definitions below | |
//#include <SoftwareSerial.h> | |
//SoftwareSerial SSerial(D7, D6); // RX, TX | |
//#define COM_SERIAL SSerial | |
// Pin D+ for host, D- = D+ + 1 | |
#ifndef PIN_USB_HOST_DP | |
#define PIN_USB_HOST_DP D4 | |
#endif | |
#include "Adafruit_TinyUSB.h" | |
// USB MIDI object | |
Adafruit_USBD_MIDI usb_midi; | |
// USB Host object | |
Adafruit_USBH_Host USBHost; | |
// holding the device address of the MIDI device | |
uint8_t dev_idx = TUSB_INDEX_INVALID_8; | |
// constants won't change. Used here to set a pin number: | |
const int ledPin = PIN_LED_G; // the number of the LED pin | |
// Variables will change: | |
int ledState = LOW; // ledState used to set the LED | |
// Generally, you should use "unsigned long" for variables that hold time | |
// The value will quickly become too large for an int to store | |
unsigned long previousMillis = 0; // will store last time LED was updated | |
// constants won't change: | |
const long interval = 500; // interval at which to blink (milliseconds) | |
//https://wiki.seeedstudio.com/XIAO-RP2040-with-Arduino/#rgb-led | |
int Power = 11; | |
int PIN = 12; | |
#define NUMPIXELS 1 | |
#include <Adafruit_NeoPixel.h> | |
Adafruit_NeoPixel pixels(NUMPIXELS, PIN, NEO_GRB + NEO_KHZ800); | |
#include "SAM2695Synth.h" | |
SAM2695Synth<HardwareSerial> synth = SAM2695Synth<HardwareSerial>::getInstance(); | |
volatile byte instrumentmt32 = 0; // variable that will be updated in the ISR | |
volatile byte instrument = 0; // variable that will be updated in the ISR | |
void gpio_callback(uint gpio, uint32_t /*event_mask*/) | |
{ | |
switch(gpio){ | |
case D0: | |
if(instrument >= 127){ | |
instrument = 0; | |
}else{ | |
instrument ++; | |
} | |
synth.setInstrument(0,CHANNEL_0,instrument); | |
pixels.setPixelColor(0, pixels.Color(255, 0, 0)); | |
pixels.show(); | |
break; | |
case D1: | |
if(instrument <= 0){ | |
instrument = 127; | |
}else{ | |
instrument--; | |
} | |
synth.setInstrument(0,CHANNEL_0,instrument); | |
pixels.setPixelColor(0, pixels.Color(0, 255, 0)); | |
pixels.show(); | |
break; | |
case D2: | |
if(instrumentmt32 >= 127){ | |
instrumentmt32 = 0; | |
}else{ | |
instrumentmt32 ++; | |
} | |
synth.setInstrument(127,CHANNEL_0,instrumentmt32); | |
pixels.setPixelColor(0, pixels.Color(255, 0, 0)); | |
pixels.show(); | |
break; | |
case D3: | |
if(instrumentmt32 <= 0){ | |
instrumentmt32 = 127; | |
}else{ | |
instrumentmt32--; | |
} | |
synth.setInstrument(127,CHANNEL_0,instrumentmt32); | |
pixels.setPixelColor(0, pixels.Color(0, 255, 0)); | |
pixels.show(); | |
break; | |
} | |
Serial1.flush(); | |
} | |
// the setup function runs once when you press reset or power the board | |
void setup() | |
{ | |
pinMode(ledPin, OUTPUT); | |
usb_midi.begin(); | |
Serial1.setTX(D6); | |
Serial1.setRX(D7); | |
synth.begin(Serial1, MIDI_SERIAL_BAUD_RATE); //Serial1.begin(31250); | |
synth.setInstrument(0,CHANNEL_0,unit_synth_instrument_t::Kalimba); | |
} | |
void loop() | |
{ | |
uint8_t packet[4]; | |
if(usb_midi.available() > 0){ | |
usb_midi.readPacket(packet); | |
Serial1.write(packet, 4); | |
} | |
Serial1.flush(); | |
} | |
// core1's setup | |
void setup1() { | |
pixels.begin(); | |
pinMode(Power,OUTPUT); | |
digitalWrite(Power, HIGH); | |
pixels.setPixelColor(0, pixels.Color(255, 0, 0)); // Red color | |
pixels.show(); | |
pinMode(D0, INPUT_PULLUP); | |
pinMode(D1, INPUT_PULLUP); | |
pinMode(D2, INPUT_PULLUP); | |
pinMode(D3, INPUT_PULLUP); | |
gpio_set_irq_enabled_with_callback(D0, GPIO_IRQ_EDGE_RISE, true, &gpio_callback); | |
gpio_set_irq_enabled_with_callback(D1, GPIO_IRQ_EDGE_RISE, true, &gpio_callback); | |
gpio_set_irq_enabled_with_callback(D2, GPIO_IRQ_EDGE_RISE, true, &gpio_callback); | |
gpio_set_irq_enabled_with_callback(D3, GPIO_IRQ_EDGE_RISE, true, &gpio_callback); | |
pio_usb_configuration_t pio_cfg = PIO_USB_DEFAULT_CONFIG; | |
pio_cfg.pin_dp = PIN_USB_HOST_DP; | |
USBHost.configure_pio_usb(1, &pio_cfg); | |
USBHost.begin(1); | |
} | |
// core1's loop | |
void loop1() | |
{ | |
irq_set_enabled(USBCTRL_IRQ, false); | |
USBHost.task(); //10us timeout | |
unsigned long currentMillis = millis(); | |
if (currentMillis - previousMillis >= interval) { | |
// save the last time you blinked the LED | |
previousMillis = currentMillis; | |
// if the LED is off turn it on and vice-versa: | |
if (ledState == LOW) { | |
ledState = HIGH; | |
pixels.setPixelColor(0, pixels.Color(random(0,255), random(0,255), random(0,255))); | |
pixels.show(); | |
} else { | |
ledState = LOW; | |
pixels.setPixelColor(0, pixels.Color(0, 0, 0)); | |
pixels.show(); | |
} | |
// set the LED with the ledState of the variable: | |
digitalWrite(ledPin, ledState); | |
} | |
irq_set_enabled(USBCTRL_IRQ, true); | |
} | |
//--------------------------------------------------------------------+ | |
// TinyUSB Callbacks | |
//--------------------------------------------------------------------+ | |
// Invoked when device with hid interface is mounted | |
// Report descriptor is also available for use. tuh_hid_parse_report_descriptor() | |
// can be used to parse common/simple enough descriptor. | |
// Note: if report descriptor length > CFG_TUH_ENUMERATION_BUFSIZE, it will be skipped | |
// therefore report_desc = NULL, desc_len = 0 | |
void tuh_midi_mount_cb(uint8_t idx, const tuh_midi_mount_cb_t* mount_cb_data) | |
{ | |
if (dev_idx == TUSB_INDEX_INVALID_8) { | |
// then no MIDI device is currently connected | |
dev_idx = idx; | |
} | |
} | |
// Invoked when device with hid interface is un-mounted | |
void tuh_midi_umount_cb(uint8_t idx) | |
{ | |
if (idx == dev_idx) { | |
dev_idx = TUSB_INDEX_INVALID_8; | |
} | |
} | |
void tuh_midi_rx_cb(uint8_t idx, uint32_t num_packets) | |
{ | |
if (dev_idx == idx) { | |
if (num_packets != 0) { | |
/* | |
uint8_t buf[4]; | |
while (tuh_midi_packet_read(dev_addr, buf)) { | |
Serial1.write(buf, 4); | |
} | |
pixels.setPixelColor(0, pixels.Color(2*buf[0], 2*buf[1], 2*buf[2])); | |
pixels.show(); | |
*/ | |
uint8_t cable_num; | |
uint8_t buffer[48]; | |
while (1) { | |
uint32_t bytes_read = tuh_midi_stream_read(dev_idx, &cable_num, buffer, sizeof(buffer)); | |
if (bytes_read == 0) | |
return; | |
for (uint32_t jdx = 0; jdx < bytes_read; jdx++) { | |
Serial1.write(buffer[jdx]); | |
} | |
//if(bytes_read > 2){ | |
// pixels.setPixelColor(0, pixels.Color(2*buffer[bytes_read], 2*buffer[bytes_read-1], 2*buffer[bytes_read-2])); | |
// pixels.show(); | |
//} | |
} | |
} | |
} | |
} | |
void tuh_midi_tx_cb(uint8_t idx, uint32_t num_bytes) | |
{ | |
(void)idx; | |
(void)num_bytes; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
getting midi pio usb host and midi usb device working simultaneously for
https://wiki.seeedstudio.com/xiao_midi_synthesizer/