Arduino I2C MIDI Interface – Part 2

Following on from my Arduino I2C MIDI Interface, this project looks at what is required to get “raw I2C MIDI” working between two Arduinos.

  • In part 1, I used a I2C/IIC to serial module to develop an I2C MIDI interface.
  • In this part, I’ve developed a “raw” I2C to MIDI transport layer.
  • In part 3, I show how to use that to build a general Serial MIDI to I2C MIDI relay.
  • In part 4, I show one I2C MIDI controller sending to two I2C MIDI peripherals.

This is very much just “proof of concept” at this stage, and as shown in the video below, I’ve only used the simplest possible set up at present.

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 Arduino, see the Getting Started pages.

Parts list

  • 2x Arduino Uno, Nano, or similar
  • Optional: old 8Ω loudspeaker
  • Optional: 220Ω resistor (for the loudspeaker)

The Circuit

ArduinoI2CMIDITest_bb

The two Arduinos need to have their I2C bus linked up.  This requires the following connections:

  • A4 to A4 (SDA)
  • A5 to A5 (SCL)
  • GND to GND

The Arduino that is to act as the MIDI receiver can optionally have a loudspeaker connected to D8 via a 220Ω resistor.

Both Arduino’s need power, e.g. over their USB connection.

IMG_5679

The Code

I’ve created a “I2C MIDI transport” for the Arduino MIDI Library.  Like my “IIC Serial MIDI transport”, it is pretty much a copy of the built-in “hardware serial” transport with a few tweaks.  This has been uploaded to GitHub.

In the examples area are the I2CMIDITransmit and I2CMIDIReceive sketches – one for each Arduino.  In the default configuration the transmitter is configured as the I2C CONTROLLER and the receiver is the I2C PERIPHERAL on address 0x40.

Limitations:

  • This is very experimental code.  Use at your own risk. In fact, you probably shouldn’t be using it at all.
  • The best performance will be when the I2C CONTROLLER is sending data to the I2C PERIPHERAL although it will work the other way round too (mostly).
  • The code only supports one CONTROLLER talking MIDI to one PERIPHERAL at present, but I believe all 16 MIDI channels should be available to use.
  • It is assumed you have the same flavour of Arduino at each end. The code doesn’t care, but you might have to worry about IO voltage levels if you try to connect up the I2C buses of two different varieties.  Or you may not – you’ll have to check.
  • I genuinely have no idea how this would interact with other I2C devices if you tried to use them at the same time.
  • To be honest, I’m slightly amazed it works at all.  Especially in the PERIPHERAL to CONTROLLER direction (which is shown in the video). I did say you shouldn’t be using this – right?

The I2C bus is configured to run in “fast mode” which is 400kbps.  There are faster modes available, but this is a “safe” fast mode for the most universal compatibility (or so I believe).

Find it on GitHub here.

Discussion

There are lots of introductions to I2C.  Here are some good ones:

All we really need to know to understand what is going on with this code is that on I2C you can only send data when invited to by the CONTROLLER.  This presents some interesting issues for MIDI which assumes you can always write to a MIDI OUT port and have the MIDI IN device listening in.

Going from CONTROLLER to PERIPHERAL is straight forward.  The CONTROLLER just sends the data to the address corresponding to the PERIPHERAL which it is always listening.  However the PERIPHERAL cannot initiate a transfer to the CONTROLLER unless asked to do so.

In order to support MIDI IN and OUT at both ends, I have made some assumptions about the interactions between the MIDI library and the I2C bus as follows.

  • On a CONTROLLER, the MIDI library will be using the beginTransmission() and endTransmission(), which can be used to gain control of the I2C bus ready for a transaction.
  • On a PERIPHERAL, two callbacks are required:
    • onReceive – for when data is received from a CONTROLLER.
    • onRequest – for when data is being requested (i.e. “pulled”) by a CONTROLLER.
  • On a CONTROLLER, any calls to send MIDI data will be sent straight out to the I2C bus via the I2CMIDI.write() function.
  • On a CONTROLLER, any calls to receive MIDI data will be driven by checking I2CMIDI.available() to see if there is any data “on the I2C bus”, followed by a call to I2CMIDI.read() to actually retrieve the data.
  • On a CONTROLLER, if there is no data available when I2CMIDI.available() is called, then a request is sent out over the I2C bus to the PERIPHERAL to send some data back to the CONTROLLER which will be checked on the next call to I2CMIDI.available().
  • On a PERIPHERAL, calls to MIDI.write() have to store up data locally until the CONTROLLER requests data to be sent over the I2C bus, which happens in the onRequest callback function.
  • On a PERIPHERAL, calls to MIDI.read() have to return data already stored locally from when the CONTROLLER decided to send it.  This would have been received by the PERIPHERAL via the onReceive callback function.

I’ve tried to illustrate this below.  Note when the arrows are the same colour, this indicates they are all sort of part of the same transaction across the bus.

I2CMIDI

There is so much that could go wrong in the sequencing in all this.  It assumes a great deal about when the MIDI library requests data and checks for new data being available.  And then how that will interact with the Wire library itself.  And then how that interacts with the actual I2C bus.  And then how that triggers things to happen in the device on the other end…

Ultimately, at any point, if one part works faster or slower than the others, there is a good chance data will get lost.

Closing Thoughts

I’m still not sure of the practicalities of this setup. I did see there is at least once modular synth manufacturer that has I2C as an option for inter-module communications, but I’m not sure I’ve seen it for synth to synth MIDI.

I really can’t imagine using this for anything serious at present (as much as any of my projects are in any way “serious” at all), but it is a fun thing to tinker with. I’d like to investigate if one CONTROLLER could talk to several PERIPHERALs.

If so, that could be a really, really interesting option for any future Lo-Fi Orchestra design…

Kevin

Leave a comment