XIAO SAMD21, Arduino and MIDI – Part 6

This post looks at a series of relative straight forward MIDI control projects using the Seeedstudio XIAO SAMD21 and the XIAO Expansion Board.  The projects should work with either USB MIDI or serial MIDI via an external MIDI module.

Other 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_7097Warning! 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.
  • Seeed Studio XIAO Expansion Board.
  • Range of additional components depending on the project (see details below).
  • Grove compatible cables.
  • Optional: 3V3 compatible MIDI module, for example one of the Ready-Made MIDI Modules or DIY MIDI Interfaces.
  • MDI 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

There are additional ways to connect a MIDI interface described in part 1.  If MIDI USB is used, then an external MIDI module is not required.

Regardless, it is assumed that the XIAO will be USB powered.

Button Voice Select

Additional parts required:

  • None.

This uses the built-in button to act as a single Program Change selector.  Pressing the button will send a MIDI Program Change message for a specific voice to a connected MIDI device.

It is based on the code from MIDI Patch Button with the additional USB MIDI code as described in part 4.

The code initialises the following:

  • MIDI LED output using LED_BUILTIN.
  • Button input in INPUT_PULLUP using D1, the User button on the expansion board.
  • Serial MIDI on the built-in serial port (A6/A7) which is connected to the MIDI module.
  • USB MIDI.

When the user button is pressed a MIDI Program Change message is sent over serial and USB MIDI.

Find it on GitHub here.

Potentiometer Program/Control Change

Additional parts required:

  • Grove Rotary Angle Sensor such as this one from Seeed
  • OR (DIY option):
    • Grove cable/connector.
    • 10kΩ potentiometer

I cut a Grove cable in half and wired it to the potentiometer as shown below.  The yellow wire isn’t actually connected, only the white wire.  The Grove “Rotary Angle Sensor” is basically the same thing, a potentiometer on a PCB wired as shown below.

IMG_7095

Note there is common wiring for a “digital” grove link, but my cables seem to have yellow and white swapped over.  This is the wiring used above:

  • Pin 4 – Black – GND
  • Pin 3 – Red – VCC
  • Pin 2 – Yellow – Secondary digital IO (not used here)
  • Pin 1 – White – Primary digital IO

The code concept is loosely based on my Arduino MT-32 Program Changer but is really a combination of the Arduino Serial MIDI Program and Control Messenger and the OLED MIDI Display.

It is using the following peripherals from the expansion module:

  • UART for a serial MIDI module link.
  • I2C screen for displaying the program number.
  • A0 input as the control knob.

The code can be configured to send either a Program Change or a specific Control Change MIDI message with the value sent based on the value of the potentiometer.  It will send out over USB MIDI and serial MIDI.

The configuration happens at the top of the file

#define MIDI_CHANNEL 1
#define MIDI_CONTROLLER 1

To use Program Control messages instead, comment out MIDI_CONTROLLER

#define MIDI_CHANNEL 1
//#define MIDI_CONTROLLER 1

To use another Control Change message (1 = Modulation Wheel) just change the number (0 to 127).  Same with the MIDI channel which can be any of 1 to 16.

The main programming trick here is to translate the value from the potentiometer (0 to 1023) to a value suitable for use with MIDI (0 to 127).  A simple bit shift will do the trick, hence the line:

 int aval = analogRead(ALG_IO) >> 3;

Even here though there is some jitter sometimes apparent, so I only take a reading when I’ve managed to have five consecutive readings all at the same value.

I’ve opted to show the value on the OLED display.  If the code is configured for Program Change messages then it will show a value between 1 and 128.  If it is configured for Control Change messages it will show a value between 0 and 127.  There is a lot of code to initialise and use the display, but it is all placed inside separate functions so hopefully the rest of the code is still fairly readable.

As with part 4, this uses the USB-MIDI library as a transport to the Arduino MIDI Library.  For the display it uses the Adafruit GFX Library and SSD1306 driver.  All libraries should be available within the Arduino Library Manager to install.

Find it on GitHub here.

Additional Button IO

Additional components:

  • 6x Tactile buttons
  • Optional: solderless breadboard or stripboard.

I was intrigued to see if I could get some additional IO available while still using the expander.  The Seeed Wiki has a description of the pin usage of the expander as follows:

Seeed-Xiao-Expansion-pinout

Everything is accounted for so it is really designed for expanding via the Grove connectors rather than raw IO access.  I guess for my purposes I would have found it more useful to have more GPIO rather than the SD card tying up the SPI pins.  But there are plenty of musical applications for an SD card, I’m just not sure I want to bother with them via Arduino.  Maybe that is something that might be more interesting to explore with CircuitPython.

I was hoping I might be able to repurpose one of the two I2C Grove connectors as two general purpose IO, but that isn’t possible for two reasons: the I2C bus is still connected to the OLED display regardless; and the I2C links include pull-ups on the board to 3V3.  Ok, so with the latter they might be used for inputs, but the I2C link to the OLED means it won’t be ideal.

It was a shame that there wasn’t a secondary digital/analog IO pin on the first connector though.  That could easily have supported two inputs but appears to only be wired up to D0/A0.  And the last Grove connector is for the UART which is duplicated via header pins and so used for the serial MIDI module.

The remaining pins are connected to the SD card to support SPI access.  Looking at the circuit diagram for the expander, we can see that most of these include external pull-ups too (relevant part reproduced below):

Seeed-Xiao-Expansion-SD-Schematic

The bank of diodes at the bottom apparently provides ESD protection for high-speed buses, but it can be seen that D2, 9, 10 all have 4K7 pull-ups to 3V3.  So my understanding is that these could all be used for digital inputs if configured for external pull-ups.  D8 has no external pull-up but should be configurable with an internal pull-up.

Of course the user-button can be reused with an external switch too (using internal pull-ups) and as a final option there is a solder jumper on the back of the board that can be used to disable the on-board buzzer, freeing up D3/A3 too.

In terms of outputs, I think the options would be: maybe D8 (I’m not sure)?  D3 can be used if the buzzer is disabled I’d say alongside D0 of course.

D1 could be used if (and only if) the button is removed!  Whilst it would be enough in theory to just configure D1 as an OUTPUT and not press the button, if the button was pressed accidentally, and the output was set to HIGH then there would be a no resistance path straight to GND which would almost certainly damage the board!

I suspect though, if we’re getting to this stage then it is best to just use the XIAO without the expander.

The final configuration therefore allows for tactile buttons (or similar) on the following pins:

  • D0 – INPUT_PULLUP
  • D1 – INPUT_PULLUP
  • D2 – INPUT
  • D8 – INPUT_PULLUP
  • D9 – INPUT
  • D10 – INPUT

This gives a possible six-button interface.  I’ve assigned the buttons as follows:

  • D0/D1 – Program Change up/down (1 to 128).
  • D2/D8 – Bank Select up/down (0 to 16383) – allows a 14-bit bank selection using the MSB/LSB control change messages.
  • D9/D10 – Control Change up/down (0 to 127) – default to channel volume (CC 7).

XIAO-Expansion-Buttons_bb

The code for monitoring the switches looks for a HIGH to LOW transition to register a trigger, but it will also spot a LOW to LOW state which indicates the button is being held in a long press.  When first pressed, the button will change up/down by 1, but if held will change by 10.  There is a short delay after every button check for debouncing purposes and to keep a sensible difference between short and long presses.

The display is only updated when something changes and all MIDI handling is taken care of by a series of midiPCUp/Down type functions at the end of the code.  MIDI messages are sent over both serial and USB MIDI.

Find it on GitHub here.

Rotary Encoder Program Change

Having established that we can use some of the additional GPIO as digital inputs (with external pull-ups) we can now wire up a rotary encoder instead.

This is using one of the KY-040 rotary encoder modules, which include built-in pull-ups on the terminals to VCC, which in this case must be 3V3.  But it should also work with a bare encoder.

As there are three IO pins with no additional external pull-ups, it is easiest to use those for the encoder:

  • D0 – “A” or “DT” pin
  • D1 – “B” or “CLK” pin
  • D8 – switch

The KY-040 also requires 3V3 and GND.  A bare encoder would only require GND.

Turning the encoder scrolls through the values 1 to 128, wrapping around at either end.  When the encoder switch is pressed that program is sent in a Program Change message (translated into the 0 to 127 range) out over both serial and USB MIDI.

XIAO-Expansion-REnc_bb

Find it on GitHub here.

Closing Thoughts

The expander also includes an RTC and Lipo circuit, but I’m not too worried about using these for musical purposes.  Granted the button use-case is probably stretching the use of the expander to extremes, but having the UART connection and built-in screen is still pretty handy.

So this is pretty much as far as I’ve taken “raw” IO with the expander.  Next it might be interesting to make more use of those I2C bus Grove connections.

Kevin

IMG_7096

Leave a comment