XIAO SAMD21, Arduino and MIDI – Part 5

This part looks at USB host device support on the SAMD21 based XIAO.  This means that rather than plug the XIAO into something else, like a computer, instead other things, like USB MIDI keyboards and controllers, can be plugged into the XIAO.

Unfortunately this wasn’t as smooth and successful as other posts, but there still might be something of interest here.

Previous parts in this series:

  • Part 1 – Introduction to the XIAO SAMD21 and some projects to get started.
  • Part 2 – Looking at accessing additional serial ports and using them for MIDI.
  • Part 3 – Mozzi FM synthesis using the DAC on the XIAO.
  • Part 4 – USB MIDI on the XIAO.
  • Part 5 – XIAO as a USB MIDI Host.
  • Part 6 – MIDI control using the expansion board.
  • Part 7 – XIAO Expansion I2C MIDI control.
  • Part 8 – XIAO MIDI PCBs.

Note: Although I’ve had vouchers from Seeed Studio for their Fusion service to support the manufacturing of some of my PCBs, there is no support for my use of the XIAO boards for this post.  I bought these a while ago, bought my own expansion board more recently, and just wanted a bit of a play to see what I could come up with.

IMG_7066

Warning! I strongly recommend using old or second hand equipment for your experiments.  I am not responsible for any damage to expensive instruments!

These are the key tutorials for the main concepts used in this project:

If you are new to microcontrollers, see the Getting Started pages.

Parts list

  • Seeed Studio XIAO SAMD21.
  • 3V3 compatible MIDI module, for example one of the Ready-Made MIDI Modules or DIY MIDI Interfaces.
  • USB and Serial MIDI devices as required to source or sink MIDI.
  • Breadboard and jumper wires.

See the notes in part 1 about uploading to the XIAO and what to do if there are issues!

The Circuit

XIAO-MIDI-Tri-USB-Host

The main difference between this and the previous experiments is that as the XIAO is acting as a USB host it will be expected to source 5V power through its USB port.  This means it needs to be externally powered with 5V via its 5V line.

The rest is pretty much the same as the previous experiments, so the MIDI modules are again connected as follows:

  • MIDI IN/OUT 1 to SERCOM 4 (A6/A7)
  • MIDI IN/OUT 2 to SERCOM 2 (A4/A5)
  • MIDI IN/OUT 3 to SERCOM 0 (A10/A9)

Note that the above was the aim for this project, but unfortunately the USB Host code that is most compatible with using three serial ports was having performance problems, so in the end for the most reliable operation, I ended just using a single serial MIDI module on SERCOM 4 (A6/A7).

The Code

With an AVR based Arduino there is a USB Host library that can be used with a “USB Host Shield”, and I talk about that in this post: Simple USB-MIDI to MIDI.

There is a SAMD21 port of the USB Host library too and I talk about that in this post: USB-MIDI to MIDI Revisited.

There is also a USB Host MIDI Transport for the Arduino MIDI Library, which can be found here: https://github.com/YuuichiAkagawa/Arduino-USBHSAMD-MIDI.

First Attempt: MIDI Library USB Host

My first attempt used the USBHSAMD-MIDI transport for the MIDI library.  This should be a simple case of replacing the USB device configuration from the code used in Part 4 with USB host functionality.

#include <MIDI.h>
#include <USBHSAMD-MIDI.h>

USBHost UsbH;
USBHSAMDMIDI_CREATE_INSTANCE(&UsbH, 0, UHMIDI);

void setup() {
  UHMIDI.begin(MIDI_CHANNEL_OMNI);
  UHMIDI.turnThruOff();
  ...
}

void loop()
{
  UsbH.Task();
  if (UHMIDI.read()) {
    midiThru(0, 1, UHMIDI.getType(),
                   UHMIDI.getData1(),
                   UHMIDI.getData2(),
                   UHMIDI.getChannel());
  }
  ...
}

And this almost works!  The problem is that occasionally there are dropped MIDI messages.  Unfortunately as MIDI replies on NoteOn and NoteOff messages being paired for accurate reproduction of playing, the result is often “stuck” notes.

Second Attempt: USB Host SAMD Library Example

There are a number of examples for USB Host MIDI that come with the SAMD21 version of the USB Host Library.  The following looked particularly interesting:

  • bidirectional_converter – a two-way USB MIDI to Serial MIDI converter
  • USB_MIDI_converter – a one-way USB MIDI to Serial MIDI converter
  • USB_MIDI_converter_multi – a multiple USB MIDI to single Serial MIDI converter

The second example builds, loads and seems to work well. It is the basis for the code I’ve used before, as described in my “update” to this post: USB-MIDI to MIDI Revisited.  But it is one-way only – USB MIDI to serial MIDI only.

The bidirectional_converter should act in both directions (as the name implied) but unfortunately there seems to be a problem building this one.  It uses the Arduino MIDI Library for the serial MIDI side, but something in that Library wouldn’t build for this example for some reason.  I gave up trying to work out why and tried something else.

The last example isn’t particularly useful for me, so I didn’t try that one.

Third Attempt: Hybrid Approach

In an attempt to gain the efficiencies that seemed to be in play with the USB_midi_converter but still support three serial MIDI links, I tried to build in the USB Host MIDI handling into the previous Part 4 code:

#include <MIDI.h>
#include <usbh_midi.h>
#include <usbhub.h>

USBHost UsbH;
USBH_MIDI Midi(&UsbH);

void setup()
{
  if (UsbH.Init()) {
    while (1); //halt
  }
}

void midiThru(int in, int out, midi::MidiType cmd, midi::DataByte d1, midi::DataByte d2, midi::Channel ch) {
  if (out == 1) {
    MIDI.send(cmd, d1, d2, ch);
  } else {
    uint8_t msg[4];
    msg[0] = cmd | (ch-1);
    msg[1] = d1;
    msg[2] = d2;
    Midi.SendData(msg, 0); 
  }
}

void loop()
{
  UsbH.Task();

  uint8_t outbuf[3];
  uint8_t sz;
  do {
    if ((sz = Midi.RecvData(outbuf)) > 0) {
      Serial1.write(outbuf, sz);
    }
  } while (sz > 0);
}

Unfortunately this still suffered with losing MIDI messages.

Conclusion

For several of these options I did try changing the order of scanning and the ratio of USB handling to serial MIDI handling.  I also tried slowing down the “USB task” so it didn’t run each scan – I think this handles the plug-and-play aspects of USB.

Unfortunately it would seem that there are some performance issues with the SAMD21 version of the USB Host stack.  There is some discussion of this in the following places:

I even seem to have commented myself in one of the above threads last time I looked at the issue!

The latter example shows how it is possible (although quite complex) to use an interrupt driven USB Host stack and it seems that this performs a lot better than the polling stack used by default.

So for now, if USB Host functionality is required, it is limited to the USB MIDI to Serial MIDI direction and the best approach is to use the USB_MIDI_converter example from the SAMD USB Host library.

Closing Thoughts

I’d completely forgotten about the performance issues so it was slightly disappointing not to be able to “just” add USB host support to my previous three-MIDI module solution.

So whilst this appears possible in the simple, uni-directional USB MIDI to serial MIDI case, for more complex scenarios I wonder if I might have to look elsewhere.

Kevin

Leave a comment