Arduino Serial MIDI Program and Control Messenger

I believe I have all the components of both sending and receiving MIDI Program and Control messages over the full range of serial interfaces from previous projects, but I’m not sure I’ve ever written it down in those terms.  So this project aims to put that right!

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 Arduino tutorials for the main concepts used in this project:

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

Parts list

The Circuit

All that is required for this project is an Uno or similar with its 5V, GND, RX and TX connected to an off-the-shelf MIDI module.

If software serial support is being used then it will be 5V, GND and the two pins selected for use with the software serial library.  D2 and D3 are the defaults in the code, but check for limitations for your own board here: https://www.arduino.cc/en/Reference/softwareSerial

By default the code is using the built-in LED to show MIDI reception, but you can add your own LED or disable that as required.

If the code is built as a sender, then the MIDI OUT port should be connected to the MIDI IN port on your MIDI device.  If the code is the receiver, then the MIDI OUT port of your MIDI Controller should be connected to the MIDI IN port of the Arduino.

The Code

The code supports three serial modes for MIDI:

  • Using the default hardware serial port:
    • This will be UART 0 (“Serial”) on boards such as the Arduino Uno or Nano – boards based on the ATmega328.
    • This will be UART 1 (“Serial1”) on boards that support a native USB interface such as the Arduino Leonard or a Sparkfun Pro Micro – boards based on the ATmega32U4.
  • Using an explicit second hardware serial port: UART 1 (“Serial1”) on those boards that have it:
    • This is the same as the default for the Arduino Leonard or Sparkfun Pro Micro.
    • This is UART 1 for boards based on an ATmega2560 that has four serial ports (UART 0 to UART 3).
  • Using a software serial port.  By default, it is configured to use D2/D3 which is fine for an Uno or Nano, but you may have to select alternative pins for other boards – see the “limitations” section from the Software Serial Library documentation.

It only supports one at a time and works by defining the MIDI object to point to the appropriate transport depending on the initialisation used, which is selected by uncommenting the appropriate line near the top of the code:

MIDI_CREATE_DEFAULT_INSTANCE();
//MIDI_CREATE_INSTANCE(HardwareSerial, Serial1, MIDI);
//#define MIDI_SOFT_SERIAL

The code shows examples of how to send or receive MIDI Program Change and Control Change messages.  The MIDI parameters chosen are again set up at the top of the file, as well as the indication of whether this code is sending or receiving.

#define MIDI_CHANNEL 1  // MIDI Channel number (1 to 16, or MIDI_CHANNEL_OMNI)
#define VOICE_NUMBER 32 // Patch to use (0 to 127)
#define CONTROLLER 1    // Modulation Wheel
#define MIDI_RECV 1     // Comment out to be the sender

All serial port MIDI links include “software THRU” functionality by default which echoes all MIDI messages received at IN to the OUT port.  This can be disabled in the code if required.

The code shows an example of using MIDI callbacks to handle reception.  The code to handle Program Change and Control Change messages should be put in the following two functions as required:

void handleProgramChange(byte channel, byte program) { 
  if (program == VOICE_NUMBER) {
    ledFlash();
  }
}

void handleControlChange(byte channel, byte control, byte value) { 
  if (control == CONTROLLER) {
    if (value > 63) {
      ledOn();
    } else {
      ledOff();
    }
  }
}

In the above example, the LED will flash once if the right voice (voice 33) is selected via a Program Change message; and will turn on if the modulation wheel is turned up high, and off if turned down low.

For sending, there are two functions to be used when appropriate in the code.

MIDI.sendProgramChange(VOICE_NUMBER, MIDI_CHANNEL);
MIDI.sendControlChange(CONTROLLER, value, MIDI_CHANNEL);

These are used in the example in the Arduino’s loop() to send the appropriate MIDI messages every 5 seconds.  The loop also has to keep calling MIDI.read() both to allow the receiver to process incoming messages but also to allow the software THRU functionality to work.

Find it on GitHub here.

Closing Thoughts

I would quite like to put together a series of posts related to key MIDI techniques that are a little more theoretical than some of my other projects and this could be one of them.

But for now, a list of projects that I think aren’t bad for talking in detail about specific MIDI, microcontroller, or electronic techniques can be found on my Techniques page.

Kevin

Leave a comment