Introduction: Send and Receive MIDI With Arduino

Picture of Send and Receive MIDI With Arduino

This instructable will show you how to use an Arduino to send and receive a variety of MIDI messages so you can start building your own MIDI controllers and instruments. First I'll talk a little bit about MIDI protocol, if you're just looking for sample code skip ahead to steps 5-9.

If you know absolutely nothing about MIDI note, velocity, and pitchbend or are confused about what MIDI does and why you would want to use it, check out my What is MIDI? instructable.

Step 1: Bytes and Bits

Picture of Bytes and Bits

To understand MIDI communication, you have to understand a little about bytes and bits. A byte is a packet of data used to store information. In MIDI protocol, each byte is made up of 8 bits; bits can only equal to 0 or 1. A sample byte is given below:

11010111

Each 1 or 0 in this byte is a bit. The leftmost bit is called the most significant bit (or MSB) and the rightmost bit is called the least significant bit (or LSB).

Bytes of the form above are binary numbers because they are expressed using only 1's and 0's. We can convert this number to base ten as well:

11010111 in binary (base 2) = 215 in decimal (base 10)

If you need help converting numbers from binary to decimal or vice versa check out Wolfram Alpha. Type in a binary number followed with "from binary to decimal" to get the decimal equivalent. Wolfram Alpha is also great for converting to and from hexadecimal.

Wikipedia is a good resource for more information about bytes and binary.

Step 2: A Bit About MIDI Protocol

Picture of A Bit About MIDI Protocol

A really basic overview of MIDI terms and concepts is given here.

MIDI messages are comprised of two components: commands and data bytes. The command byte tells the MIDI instrument what type of message is being sent and the subsequent data byte(s) store the actual data. For example a command byte might tell a MIDI instrument that it going to send information about pitchbend, and the data byte describes how much pitchbend.

MIDI data bytes range from 0 to 127. Convert these numbers to binary and we see they range from 00000000 to 01111111, the important thing to notice here is that they always start with a 0 as the most significant bit (MSB). MIDI command bytes range from 128 to 255, or 1000000 to 11111111 in binary. Unlike data bytes, MIDI command bytes always start with a 1 as the MSB. This MSB is how a MIDI instrument differentiates between a command byte and a data byte.

MIDI commands are further broken down by the following system:

The first half of the MIDI command byte (the three bits following the MSB) sets the type of command. More info about the meaning on each of these commands is here.
10000000 = note off
10010000 = note on
10100000 = aftertouch
10110000 = continuous controller
11000000 = patch change
11010000 = channel pressure
11100000 = pitch bend
11110000 = non-musical commands

The last half of the command byte sets the MIDI channel. All the bytes listed above would be in channel 0, command bytes ending in 0001 would be for MIDI channel 1, and so on.

All MIDI messages start with a command byte, some messages contain one data byte, others contain two or more (see image above). For example, a note on command byte is followed by two data bytes: note and velocity.

I'm going to explain how to use note on, note off, velocity, and pitchbend in this instructable, since these are the most commonly used commands. I'm sure you will be able to infer how to set up the others by the end of this.

Step 3: Send MIDI Messages With Arduino- Hardware

Picture of Send MIDI Messages With Arduino- Hardware

Parts List:
MIDI connector Digikey CP-2350-ND
220Ohm 1/4watt resistor Digikey CF14JT220RCT-ND

Other Parts

(1x) Arduino Uno Amazon
(1x) usb cable Amazon
(1x) breadboard (this one comes with jumper wires) Amazon
(1x) jumper wires Amazon
(1x) MIDI to USB Cable Amazon

Following the schematic above, solder a 220Ohm resistor to MIDI pin 4. Connect ground to MIDI pin 2 and 5V to MIDI pin 5. If the pin numbering is unclear, refer to the pictures above.

Step 4: Plug in MIDI Out

Picture of Plug in MIDI Out

Connect the MIDI socket to a MIDI cable and plug the other end of the cable into your MIDI instrument of choice. I used a MIDI to USB cable and connected to my computer.

Step 5: Software Solution: Serial to MIDI Application

Picture of Software Solution: Serial to MIDI Application

You can bypass the MIDI adapter setup from the last two steps by using the Ardiuno's USB connection to send Serial messages to your computer, then run an app like Hairless MIDI to convert this the Serial messages to MIDI and route them to other applications on your computer (Ableton, Garageband, etc). The only difference in the code is that you will need to set the baud rate of your Serial connection to something that Hairless MIDI will accept, so be sure that the number in this line in the Arduino's setup() function:

Serial.begin(31250);

is the same number specified under Hairless MIDI >> Preferences >> Baud Rate (I used 9600, see the image above, I had to replace line Serial.begin(31250) with Serial.begin(9600) in all the example Arduino sketches in this instructable). Normally when you create MIDI with a MIDI connector you need to set the baud rate to 31250, but if you're connecting via USB to a Serial to MIDI application, you can use whatever baud rate you like.

To use Hairless MIDI you will need to select your board (something like usbmodemfd121) from the Serial Port menu and select the MIDI channel that you would like to send or receive MIDI to/from. Make sure you have the same MIDI channel selected in the preferences of whatever other MIDI applications you are running on your computer.

Another thing to be aware of is that you cannot program the Arduino while it is connected to Hairless MIDI, because the port is occupied (see the error in the second image). A quick way to bypass this without needing to quit Hairless MIDI each time you want to change your code is to select a different Serial Port from the Hairless MIDI interface, upload your new Arduino code, and then set the Serial Port in Hairless MIDI back to the correct one.

Step 6: Basic Note On, Note Off With Arduino

Picture of Basic Note On, Note Off With Arduino

This code sends MIDI messages out Arduino digital pin 1 using note on and note off commands.

As I explained in step 3, the MIDI commands for note on and note off are as follows:
noteON = 10010000 = 144
noteOFF = 10000000 = 128

Both of these commands are followed by two more bytes to make a complete MIDI message, the first is note and the second is velocity (for more info about what "note" and "velocity" mean check out my introductory MIDI instructable). Note and velocity can range from 0 to 127. In this example I used notes ranging from 50 to 69 (D3 to A4):
for (int note=50;note<70;note++){}
and I set the velocity to 100:
int velocity = 100;

So when the function MIDImessage() is called in the loop() of the arduino sketch, it sends the three bytes:
Serial.write(command);
Serial.write(MIDInote);
Serial.write(MIDIvelocity);

if the "command" in the MIDImessage() function is noteON then the note will start, if it is noteOFF the note will stop.

The code below plays the notes D3-A4 in a loop, it turns on a MIDI note for 300ms then wait 200ms before turning on the next note. I wrote a MaxMSP patch (you can download the runtime version for free) that displays the incoming MIDI messages and attached it below. Here is an example video:


<pre>/*
MIDI On/Off Messages
By Amanda Ghassaei
July 2012
https://www.instructables.com/id/Send-and-Receive-MIDI-with-Arduino/

 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 3 of the License, or
 * (at your option) any later version.

*/
 
 int velocity = 100;//velocity of MIDI notes, must be between 0 and 127
 //higher velocity usually makes MIDI instruments louder
 
 int noteON = 144;//144 = 10010000 in binary, note on command
 int noteOFF = 128;//128 = 10000000 in binary, note off command

void setup() {
  //  Set MIDI baud rate:
  Serial.begin(31250);
}

void loop() {
  for (int note=50;note<70;note++) {//from note 50 (D3) to note 69 (A4)
    MIDImessage(noteON, note, velocity);//turn note on
    delay(300);//hold note for 300ms
    MIDImessage(noteOFF, note, velocity);//turn note off
    delay(200);//wait 200ms until triggering next note
  }
}

//send MIDI message
void MIDImessage(int command, int MIDInote, int MIDIvelocity) {
  Serial.write(command);//send note on or note off command 
  Serial.write(MIDInote);//send pitch data
  Serial.write(MIDIvelocity);//send velocity data
}

Step 7: Note Off With 0 Velocity

Picture of Note Off With 0 Velocity

You may have noticed in the previous step's video, that the velocity appeared to set back to 0 when the note off message was sent. This is because a MIDI message with a note on command and velocity 0 is the same as a note off message. Sometimes when a software MIDI environment receives a note off message it will automatically translate it into a note on message with velocity 0 because they are the same. You will probably find that it is easier to program MIDI by sending these note on/velocity=0 messages rather than sending note off.

The code below does the exact same thing as the code in the last step, but it only uses note on commands. Essentially, I've replaced the following line:
MIDImessage(noteOFF, note, velocity);
with:
MIDImessage(noteON, note, 0);

<pre>/*
MIDI On Messages with 0 velocity
By Amanda Ghassaei
July 2012
https://www.instructables.com/id/Send-and-Receive-MIDI-with-Arduino/

 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 3 of the License, or
 * (at your option) any later version.

*/
 
 int velocity = 100;//velocity of MIDI notes, must be between 0 and 127
 //(higher velocity usually makes MIDI instruments louder)
 
 int noteON = 144;//144 = 10010000 in binary, note on command

void setup() {
  //  Set MIDI baud rate:
  Serial.begin(31250);
}

void loop() {
  for (int note=50;note<70;note++) {//from note 50 (D3) to note 69 (A4)
    MIDImessage(noteON, note, velocity);//turn note on
    delay(300);//hold note for 300ms
    MIDImessage(noteON, note, 0);//turn note off
    delay(200);//wait 200ms until triggering next note
  }
}

//send MIDI message
void MIDImessage(int command, int MIDInote, int MIDIvelocity) {
  Serial.write(command);//send note on or note off command 
  Serial.write(MIDInote);//send pitch data
  Serial.write(MIDIvelocity);//send velocity data
}

Step 8: Variable Velocity and Arduino

Picture of Variable Velocity and Arduino

In this code I've modified the variable velocity so that it increases with increasing note number. In the beginning of the loop() function the variable velocity is set to 20, then it is increased by five in each iteration of the for loop:
velocity += 5;
so for note = 50, velocity = 20, then for note = 51, velocity = 25, then for note = 52, velocity = 30... and so on.
once the end of the loop() function is reached, the velocity is reset back to 20.

Here's a video of the end result, notice how the volume increases with increasing velocity.


<pre>/*
MIDI note on messages with variable velocity
By Amanda Ghassaei
July 2012
https://www.instructables.com/id/Send-and-Receive-MIDI-with-Arduino/

 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 3 of the License, or
 * (at your option) any later version.

*/

int noteON = 144;//144 = 10010000 in binary, note on command

void setup() {
  //  Set MIDI baud rate:
  Serial.begin(31250);
}

void loop() {
  int velocity = 20;//set velocity to 20
  for (int note=50;note<70;note++) {//from note 50 (D3) to note 69 (A4)
    MIDImessage(noteON, note, velocity);//turn note on
    delay(300);//hold note for 300ms
    MIDImessage(noteON, note, 0);//turn note off
    delay(200);//wait 200ms until triggering next note
    velocity += 5;//ad 5 to current velocity value
  }
}

//send MIDI message
void MIDImessage(int command, int MIDInote, int MIDIvelocity) {
  Serial.write(command);//send note on or note off command 
  Serial.write(MIDInote);//send pitch data
  Serial.write(MIDIvelocity);//send velocity data
}

Step 9: Pitchbend and Arduino

Picture of Pitchbend and Arduino

Now that you know how to control note on and note off, you can try pitchbend.

Pitchbend information is stored in 2 data bytes, and most significant byte (MSB) and a least significant byte (LSB). Each of these bytes contains only 7 bits of information. This means that all pitchbend information is stored in 14 bits, with the most significant 7 bits stored in the MSB and the least significant 7 bits stored in the LSB.

For most applications you will only find yourself changing pitchbend via the MSB and just setting the LSB to 0. In this case you have 7 bits of resolution for pitchbend (128 steps). In MIDI protocol pitchbend = 64 is no pitchbend, pitchbend greater than 64 is pitchbends the frequency up, and less than 64 pitchbends the frequency down.

In the example below a note played and held, then played again while the pitchbend increments from 64 to its max value of 127, then played a third time while the pitchbend increments from 64 to its min value of 0. This sequence is looped forever. The images above show the output in ableton from this Arduino sketch.

<pre>/*
MIDI Pitchbend (msb)
By Amanda Ghassaei
July 2012
https://www.instructables.com/id/Send-and-Receive-MIDI-with-Arduino/

 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 3 of the License, or
 * (at your option) any later version.

*/
 

 
 int noteON = 144;//144 = 10010000 in binary, note on command
 //noteON data storage:
 int note = 60;//middle c
 int velocity = 100;//velocity of MIDI notes, must be between 0 and 127
 //(higher velocity usually makes MIDI instruments louder)
 
 int pitchbend = 224;//224 = 11100000 in binary, pitchbend command
 //pitchbend data storage:
 int lsb = 0;//least siginificant bit of pitchbend message
 int msb = 0;//most significant bit of pitchbend message

void setup() {
  //  Set MIDI baud rate:
  Serial.begin(31250);
}

void loop() {
  //first play note w/o pitchbend
  MIDImessage(pitchbend, 0, 64);//reset pitchbend to 0 (zero pitchbend is lsb = 0, msb = 64)
  MIDImessage(noteON, note, velocity);//turn note on
  delay(700);//sustain note
  MIDImessage(noteON, note, 0);//turn note off
  delay(500);//wait 500ms until triggering next note
  
  //then play with pitchbend up
  MIDImessage(pitchbend, 0, 64);//reset pitchbend to 0 (zero pitchbend is lsb = 0, msb = 64)
  MIDImessage(noteON, note, velocity);//turn note on
  for (msb=64;msb<=127;msb++){//increase pitchbend  msb 64 (no pitchbend) to 127
    MIDImessage(pitchbend, lsb, msb);//send pitchbend message
    delay(10);
  }
  MIDImessage(noteON, note, 0);//turn note off
  delay(500);//wait 500ms until triggering next note
  
  //then play with pitchbend down
  MIDImessage(pitchbend, 0, 64);//reset pitchbend to 0 (zero pitchbend is lsb = 0, msb = 64)
  MIDImessage(noteON, note, velocity);//turn note on
  for (msb=64;msb>=0;msb--){//decrease pitchbend msb from 64 (no pitchbend) to 0;
    MIDImessage(pitchbend, lsb, msb);//send pitchbend message
    delay(10);
  }
  MIDImessage(noteON, note, 0);//turn note off
  delay(500);//wait 200ms until triggering next note
}

//send MIDI message
void MIDImessage(int command, int data1, int data2) {
  Serial.write(command);//send command byte
  Serial.write(data1);//send data byte #1
  Serial.write(data2);//send data byte #2
}


Below is a video demonstration of the code above. For this piece of code, pitchbend will be most noticeable in instruments with a long sustain, such as a string instrument, keep that in mind when testing the code for yourself.

You will most likely be fine using only 128 steps of pitchbend resolution, but in case you must use all 16384 steps, see the code below. Basically what I've done here is defined a variable called pitchbendVal, which varies from 0 to 16383. As I said below the "zero" pitchbend value is msb = 64 and lsb = 0. In binary this is:

MSB = 64 = 01000000
LSB = 0 = 0000000

(remember MSB and LSB are 7 bit numbers)

putting these values together we get:

1000000 0000000
MSB LSB

or

10000000000000
which translates to 8192 in decimal

so now the "zero" pitchbend value is 8192.

You'll also notice I had to break the variable pitchbendVal into two 7 bit parts to send out via MIDI message:
MIDImessage(pitchbend, (pitchbendVal&127), (pitchbendVal>>7));
the first part, pitchbendVal&127, returns the least significant 7 bits of pitchbendVal
the second part, pitchbendVal>>7, returns the most significant 7 bits of pitchbendVal
see & and >> on the Arduino reference page for more info.
<pre>/*
MIDI Pitchbend (full resolution)
By Amanda Ghassaei
July 2012
https://www.instructables.com/id/Send-and-Receive-MIDI-with-Arduino/

 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 3 of the License, or
 * (at your option) any later version.

*/
 

 
 int noteON = 144;//144 = 10010000 in binary, note on command
 //noteON data storage:
 int note = 60;//middle c
 int velocity = 100;//velocity of MIDI notes, must be between 0 and 127
 //(higher velocity usually makes MIDI instruments louder)
 
 int pitchbend = 224;//224 = 11100000 in binary, pitchbend command
 //pitchbend data storage:
 int pitchbendVal = 8192;//value between 0 and 16383. zero pitchbend = 8192

void setup() {
  //  Set MIDI baud rate:
  Serial.begin(31250);
}

void loop() {
  //first play note w/o pitchbend
  pitchbendVal = 8192;//reset pitchbend to "0" (zero pitchbend is pitchbendVal = 8192)
  MIDImessage(pitchbend, (pitchbendVal&127), (pitchbendVal>>7));//send pitchbend message
  MIDImessage(noteON, note, velocity);//turn note on
  delay(700);//sustain note
  MIDImessage(noteON, note, 0);//turn note off
  delay(500);//wait 500ms until triggering next note
  
  //then play with pitchbend up
  pitchbendVal = 8192;//reset pitchbend to "0" (zero pitchbend is pitchbendVal = 8192)
  MIDImessage(pitchbend, (pitchbendVal&127), (pitchbendVal>>7));//send pitchbend message
  MIDImessage(noteON, note, velocity);//turn note on
  for (pitchbendVal=8192;pitchbendVal<16384;pitchbendVal++){//increase pitchbend from 8192 to 16383
    MIDImessage(pitchbend, (pitchbendVal&127), (pitchbendVal>>7));//send pitchbend message
    delay(1);
  }
  MIDImessage(noteON, note, 0);//turn note off
  delay(500);//wait 500ms until triggering next note
  
  //then play with pitchbend down
  pitchbendVal = 8192;//reset pitchbend to "0" (zero pitchbend is pitchbendVal = 8192)
  MIDImessage(pitchbend, (pitchbendVal&127), (pitchbendVal>>7));//send pitchbend message
  MIDImessage(noteON, note, velocity);//turn note on
  for (pitchbendVal=8192;pitchbendVal>=0;pitchbendVal--){//decrease pitchbend 8192 to 0;
    MIDImessage(pitchbend, (pitchbendVal&127), (pitchbendVal>>7));//send pitchbend message
    delay(1);
  }
  MIDImessage(noteON, note, 0);//turn note off
  delay(500);//wait 500ms until triggering next note
}

//send MIDI message
void MIDImessage(int command, int data1, int data2) {
  Serial.write(command);//send command byte
  Serial.write(data1);//send data byte #1
  Serial.write(data2);//send data byte #2
}

Step 10: Receive MIDI Messages With Arduino

Picture of Receive MIDI Messages With Arduino

Most Arduino MIDI projects send MIDI messages out, but you can also use the Arduino to receive MIDI data. Here are some ideas:

an Arduino synthesizer that uses MIDI messages to construct audio waveforms
a device which uses MIDI to trigger mechanical events, like the ringing of different sized bells
a MIDI to control voltage(CV) device- communication between MIDI and analog synthesizers

Parts List:
MIDI connector Digikey CP-2350-ND
220Ohm 1/4watt resistor Digikey CF14JT220RCT-ND
1N4148 diode Digikey1N4148-TAPCT-ND
10kOhm 1/4watt resistor Digikey CF14JT10K0CT-ND
470 Ohm 1/4watt resistor Digikey CF14JT470RCT-ND (I used 2x220 instead)
6N138 optocoupler Digikey 751-1263-5-ND

Other Parts

(1x) Arduino Uno Amazon
(1x) usb cable Amazon
(1x) breadboard (this one comes with jumper wires) Amazon
(1x) jumper wires Amazon
(1x) MIDI to USB Cable Amazon

The hardware setup is slightly more complicated for receiving MIDI than it is for sending. As you can see in the schematic above, you have to set up an optoisolator in between the MIDI jack and the Arduino. If you are confused about the MIDI pin connections, refer to fig 1. I set this circuit up on a breadboard in figs 4 and 5.

The following code receives these messages, reads them, and stores them appropriately. See the comments for more information.
<pre>/*Receive Midi
By Amanda Ghassaei
July 2012
<a href="https://www.instructables.com/id/Send-and-Receive-MIDI-with-Arduino/">

https://www.instructables.com/id/Send-and-Receive-...>

 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 3 of the License, or
 * (at your option) any later version.

*/

byte commandByte;
byte noteByte;
byte velocityByte;

void setup(){
  Serial.begin(31250);
}

void checkMIDI(){
  do{
    if (Serial.available()){
      commandByte = Serial.read();//read first byte
      noteByte = Serial.read();//read next byte
      velocityByte = Serial.read();//read final byte
    }
  }
  while (Serial.available() > 2);//when at least three bytes available
}
    

void loop(){
  checkMIDI();
}
To make sure that everything is working properly, try the code below. This code turns the led at pin 13 on briefly when it receives a note on message for MIDI note 60 (middle C). Notice how I included "&& velocityByte>0" in the if statement- this makes sure that we are dealing with a note on statement, if you don't include this the light will blink for both note on and note on with velocity = 0 (note off) messages.
<pre>/*Receive MIDI and check if note = 60
By Amanda Ghassaei
July 2012
<a href="https://www.instructables.com/id/Send-and-Receive-MIDI-with-Arduino/">

https://www.instructables.com/id/Send-and-Receive-...>

 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 3 of the License, or
 * (at your option) any later version.

*/

byte commandByte;
byte noteByte;
byte velocityByte;

byte noteOn = 144;

//light up led at pin 13 when receiving noteON message with note = 60

void setup(){
  Serial.begin(31250);
  pinMode(13,OUTPUT);
  digitalWrite(13,LOW);
}

void checkMIDI(){
  do{
    if (Serial.available()){
      commandByte = Serial.read();//read first byte
      noteByte = Serial.read();//read next byte
      velocityByte = Serial.read();//read final byte
      if (commandByte == noteOn){//if note on message
        //check if note == 60 and velocity > 0
        if (noteByte == 60 && velocityByte > 0){
          digitalWrite(13,HIGH);//turn on led
        }
      }
    }
  }
  while (Serial.available() > 2);//when at least three bytes available
}
    

void loop(){
  checkMIDI();
  delay(100);
  digitalWrite(13,LOW);//turn led off
}


If you want to do a lot of stuff in the main loop, or if you are expecting to receive a lot of MIDI data and timing is important to you, you can also try using a timer interrupt to periodically pause the main loop and check if there is incoming MIDI. It will look something like this:
<pre>/*Receive Midi with interrupt
By Amanda Ghassaei
July 2012
<a href="https://www.instructables.com/id/Send-and-Receive-MIDI-with-Arduino/">

https://www.instructables.com/id/Send-and-Receive-...>

 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 3 of the License, or
 * (at your option) any later version.

*/

byte commandByte;
byte noteByte;
byte velocityByte;

void setup(){
  
  Serial.begin(31250);
  
  cli();//stop interrupts

  //set timer2 interrupt every 128us
  TCCR2A = 0;// set entire TCCR2A register to 0
  TCCR2B = 0;// same for TCCR2B
  TCNT2  = 0;//initialize counter value to 0
  // set compare match register for 7.8khz increments
  OCR2A = 255;// = (16*10^6) / (7812.5*8) - 1 (must be <256)
  // turn on CTC mode
  TCCR2A |= (1 << WGM21);
  // Set CS11 bit for 8 prescaler
  TCCR2B |= (1 << CS11);   
  // enable timer compare interrupt
  TIMSK2 |= (1 << OCIE2A);
  
  sei();//allow interrupts
  
}


ISR(TIMER2_COMPA_vect) {//checks for incoming midi every 128us
  do{
    if (Serial.available()){
      commandByte = Serial.read();//read first byte
      noteByte = Serial.read();//read next byte
      velocityByte = Serial.read();//read final byte
    }
  }
  while (Serial.available() > 2);//when at least three bytes available
}

void loop(){
  //do whatever here
}

Step 11: Examples

Picture of Examples

I recently built a MIDI controller with a built in accelerometer and gyroscope, as well as 16 backlit buttons. I'll be posting the full project soon (still need to finish enclosure and a few other things), but I've attached some example code that shows how I got the MIDI up and running. Here is a video of two programs I've written so far:

And here is the code for those applications:

single pixel moving around, triggering MIDI (only uses x and y accelerometer):
<pre>//accelerometer test- single pixel
//by Amanda Ghassaei 2012
//https://www.instructables.com/id/Send-and-Receive-MIDI-with-Arduino/

/*
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
*/


//pin connections
//#define ledLatchPin 6
//#define ledClockPin 5
//#define ledDataPin 7
//#define buttonLatchPin 4
//#define buttonClockPin 3
//#define buttonDataPin 2

//setup varibles for Gyroscope/Accelerometer
int xGyroRAW;
int yGyroRAW;
int xAccRAW;
int yAccRAW;
int zAccRAW;

byte xGyro;
byte yGyro;
byte xAcc;
byte yAcc;
byte zAcc;

//looping variables
byte i;
byte j;
byte k;

//storage for led states, 4 bytes
byte ledData[] = {0, 0, 0, 0};
//storage for buttons, 4 bytes
byte buttonCurrent[] = {0,0,0,0};
byte buttonLast[] = {0,0,0,0};
byte buttonEvent[] = {0,0,0,0};
byte buttonState[] = {0,0,0,0};
//button debounce counter- 16 bytes
byte buttonDebounceCounter[4][4];

//variables for accelerometer pixel movement
boolean firstPress = 1;
byte movingPixel[] = {0, 0, 0, 0};
byte yPosition;
byte xPosition;
int timeX = 0;
int timeY = 0;
boolean dirX = 0;
boolean dirY = 0;
byte lastX = 4;
byte lastY = 4;

//MIDI variables
int velocity = 100;
int noteON = 144;
int MIDIoffset = 60;
byte currentX;


void setup() {
  
  DDRD = 0xFA;//set pins D7-D4 as output, D2 as input
  
  Serial.begin(31250);//MIDI baud rate 
//  Serial.begin(9600);
  
  cli();//stop interrupts

  //set timer1 interrupt at 1kHz
  TCCR1A = 0;// set entire TCCR1A register to 0
  TCCR1B = 0;// same for TCCR1B
  TCNT1  = 0;//initialize counter value to 0;
  // set timer count for 1khz increments
  OCR1A = 1999;// = (16*10^6) / (1000*8) - 1
  // turn on CTC mode
  TCCR1B |= (1 << WGM12);
  // Set CS11 bit for 8 prescaler
  TCCR1B |= (1 << CS11);   
  // enable timer compare interrupt
  TIMSK1 |= (1 << OCIE1A);
  
  sei();//allow interrupts
  
}

ISR(TIMER1_COMPA_vect) {//Interrupt at freq of 1kHz
  timeX++;//increment timeX
  timeY++;//increment timeY
  shift();//send data to leds
}

// buttonCheck - checks the state of a given button.
//this buttoncheck function is largely copied from the monome 40h firmware by brian crabtree and joe lake
void buttonCheck(byte row, byte index)
{
  if (((buttonCurrent[row] ^ buttonLast[row]) & (1 << index)) &&   // if the current physical button state is different from the
  ((buttonCurrent[row] ^ buttonState[row]) & (1 << index))) {  // last physical button state AND the current debounced state

    if (buttonCurrent[row] & (1 << index)) {                      // if the current physical button state is depressed
      buttonEvent[row] = 1 << index;              // queue up a new button event immediately
      buttonState[row] |= (1 << index);                         // and set the debounced state to down.
  }
    else{
      buttonDebounceCounter[row][index] = 12;
    }  // otherwise the button was previously depressed and now
    // has been released so we set our debounce counter.
  }
  else if (((buttonCurrent[row] ^ buttonLast[row]) & (1 << index)) == 0 &&  // if the current physical button state is the same as
  (buttonCurrent[row] ^ buttonState[row]) & (1 << index)) {        // the last physical button state but the current physical
    // button state is different from the current debounce 
    // state...
    if (buttonDebounceCounter[row][index] > 0 && --buttonDebounceCounter[row][index] == 0) {  // if the the debounce counter has
      // been decremented to 0 (meaning the
      // the button has been up for 
      // kButtonUpDefaultDebounceCount 
      // iterations///

      buttonEvent[row] = 1 << index;    // queue up a button state change event

      if (buttonCurrent[row] & (1 << index)){          // and toggle the buttons debounce state.
        buttonState[row] |= (1 << index);
      }
      else{
        buttonState[row] &= ~(1 << index);
      }
    }
  }
}


void shift(){
  
  for (i=0;i<4;i++){
    
    buttonLast[i] = buttonCurrent[i];
    
    byte dataToSend = (1 << (i+4)) | (15 & ~ledData[i]);
      
    // set latch pin low so the LEDs don't change while sending in bits
    PORTD&=B10111111;//digitalWrite(ledLatchPin, LOW);
    // shift out the bits of dataToSend 
    //shiftOut(ledDataPin, ledClockPin, LSBFIRST, dataToSend);  
    for (j=0;j<8;j++){
      PORTD&=B11011111;//digitalWrite(ledClockPin,LOW);
      //digitalWrite(ledDataPin,((dataToSend>>j)&1));
      if ((dataToSend>>j)&1){
        PORTD|=B10000000;
      }
      else{
        PORTD&=B01111111;
      }
      PORTD|=B00100000;//digitalWrite(ledClockPin,HIGH);
    }
    //set latch pin high so the LEDs will receive new data
    PORTD|=B01000000;//digitalWrite(ledLatchPin, HIGH);
    
    // SlowDown is put in here to waste a little time while we wait for the state of the output
    // pins to settle.  Without this time wasting loop, a single button press would show up as
    // two presses (the button and its neighbour)
    volatile int SlowDown = 0; 

    while (SlowDown < 15) 
    { 
      SlowDown++; 
    } 
      
    //once one row has been set high, receive data from buttons
    //set latch pin high
    PORTD|=B00010000;//digitalWrite(buttonLatchPin, HIGH);
    //shift in data
    //buttonCurrent[i] = shiftIn(buttonDataPin, buttonClockPin, LSBFIRST) >> 3;
    for (j=0;j<4;j++){
      PORTD&=B11110111;//digitalWrite(buttonClockPin,LOW);
      PORTD|=B00001000;//digitalWrite(buttonClockPin,HIGH);
    }
    for (j=0;j<4;j++){
      PORTD&=B11110111;//digitalWrite(buttonClockPin,LOW);
      if ((PIND>>2)&1){//digitalRead(buttonDataPin)
        buttonCurrent[i]|=1<<j;
      }
      else{
        buttonCurrent[i]&=~(1<<j); 
      }
      PORTD|=B00001000;//digitalWrite(buttonClockPin,HIGH);
    }
    //latchpin low
    PORTD&=B11101111;//digitalWrite(buttonLatchPin, LOW);
    
    for (k=0;k<4;k++){
      buttonCheck(i,k);
    }
  }
  
  //turn off leds- this way one row does not appear brighter than the rest
  
  // set latch pin low so the LEDs don't change while sending in bits
  PORTD&=B10111111;//digitalWrite(ledLatchPin, LOW);
  // shift out 0
  //shiftOut(ledDataPin, ledClockPin, LSBFIRST, 0);  
  for (j=0;j<8;j++){
    PORTD&=B11011111;//digitalWrite(ledClockPin,LOW);
    PORTD&=B01111111;
    PORTD|=B00100000;//digitalWrite(ledClockPin,HIGH);
  }
  //set latch pin high so the LEDs will receive new data
  PORTD|=B01000000;//digitalWrite(ledLatchPin, HIGH);
}

void checkFirstButton(){
  for (byte a=0;a<4;a++){
    if (buttonEvent[a]){
      for (byte b=0;b<4;b++){
        if (buttonState[a]&(1<<b)){
          //toggle firstPress variable
          firstPress = 0;
          //display pressed pixel
          ledData[a] = buttonEvent[a];
          //store current position
          yPosition = a;
          xPosition = 1<<b;
          //reset timers
          timeX = 0;
          timeY = 0;
          return;
        }
      }
    }
  }
}

byte scaleAcc(int RAW){
  if (RAW<=10 && RAW>=-10){
    return 5;
  }
  else if (RAW<-10){
    if (RAW<-50){
      return 0;
    }
    else if (RAW<-40){
      return 1;
    }
    else if (RAW<-30){
      return 2;
    }
    else if (RAW<-20){
      return 3;
    }
    else{
      return 4;
    }
  }
  else if (RAW>10){
    if (RAW>50){
      return 10;
    }
    else if (RAW>40){
      return 9;
    }
    else if (RAW>30){
      return 8;
    }
    else if (RAW>20){
      return 7;
    }
    else{
      return 6;
    }
  }
}

void checkAccelerometer(){
  //read values
  xGyroRAW = analogRead(A1);
  yGyroRAW = analogRead(A0);
  xAccRAW = analogRead(A4);
  yAccRAW = analogRead(A3);
  zAccRAW = analogRead(A2);
  
  //offset data
  xGyroRAW = 317-xGyroRAW;
  yGyroRAW = 183-yGyroRAW;
  xAccRAW = 282-xAccRAW;
  yAccRAW = 282-yAccRAW;
  zAccRAW = 282-zAccRAW;
  
  if (xAccRAW>0){
    dirX = 1;
  }
  else{
    dirX = 0;
  }
  if (yAccRAW>0){
    dirY = 1;
  }
  else{
    dirY = 0;
  }
  
  //convert to 0-10
  xAcc = scaleAcc(xAccRAW);
  yAcc = scaleAcc(yAccRAW);
}

int getTime(byte acceleration){
  switch (acceleration){
    case 0://max - acceleration
    return 25;
    break;
    case 1:
    return 25;
    break;
    case 2:
    return 50;
    break;
    case 3:
    return 100;
    break;
    case 4:
    return 150;
    break;
    case 5://lying flat
    return 0;
    break;
    case 6:
    return 150;
    break;
    case 7:
    return 100;
    break;
    case 8:
    return 50;
    break;
    case 9:
    return 25;
    break;
    case 10://max + acceleration
    return 25;
    break;
  }
}

void moveXPixel(int timeComp){
  if (timeComp==0){
  }
  else{
    if (timeX>timeComp){
      timeX = 0;
      if (dirX){
        if (xPosition==8){
        }
        else{
          xPosition = xPosition<<1;
        }
      }
      else{
        if (xPosition==1){
        }
        else{
          xPosition = xPosition>>1;
        }
      }
    }
  }
}

void moveYPixel(int timeComp){
  if (timeComp==0){
  }
  else{
    if (timeY>timeComp){
      timeY = 0;
      if (dirY){
        if (yPosition==3){
        }
        else{
          yPosition = yPosition+=1;
        }
      }
      else{
        if (yPosition==0){
        }
        else{
          yPosition = yPosition-=1;
        }
      }
    }
  }
}

void checkMIDI(){
  //convert xPosition to decimal
  switch (xPosition){
    case 1:
    currentX = 0;
    break;
    case 2:
    currentX = 1;
    break;
    case 4:
    currentX = 2;
    break;
    case 8:
    currentX = 3;
    break;
  }
  //if pixel has moved send midi
  if (lastX != currentX || lastY != yPosition){
    MIDImessage(noteON,(lastX+5*lastY+MIDIoffset),0);//turn off last note
    MIDImessage(noteON,(currentX+5*yPosition+MIDIoffset),velocity);//turn on next note
  }
  lastX = currentX;
  lastY = yPosition;
}
  

void MIDImessage(int command, int MIDInote, int MIDIvelocity) {//send s a MIDI message
  Serial.write(command);//send note on or note off command 
  Serial.write(MIDInote);//send pitch data
  Serial.write(MIDIvelocity);//send velocity data
}

void loop() {
  if (firstPress){
    checkFirstButton();
  }
  else{
    for (byte pixel=0;pixel<4;pixel++){
      if (pixel==yPosition){
        ledData[pixel]=xPosition;
      }
      else{
        ledData[pixel] = 0;
      }
    }
    checkAccelerometer();
    moveXPixel(getTime(xAcc));
    moveYPixel(getTime(yAcc));
    checkMIDI();
  }
}


four pixels bouncing (only uses x accelerometer, uses x gyro to clear pixels):
<pre>//accelerometer test- bounce
//by Amanda Ghassaei 2012
//https://www.instructables.com/id/Send-and-Receive-MIDI-with-Arduino/

/*
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
*/

//pin connections
//#define ledLatchPin 6
//#define ledClockPin 5
//#define ledDataPin 7
//#define buttonLatchPin 4
//#define buttonClockPin 3
//#define buttonDataPin 2

//setup varibles for Gyroscope/Accelerometer
int xGyroRAW;
int yGyroRAW;
int xAccRAW;
int yAccRAW;
int zAccRAW;

byte xGyro;
byte yGyro;
byte xAcc;
byte yAcc;
byte zAcc;

//looping variables
byte i;
byte j;
byte k;

//storage for led states, 4 bytes
byte ledData[] = {0, 0, 0, 0};
//storage for buttons, 4 bytes
byte buttonCurrent[] = {0,0,0,0};
byte buttonLast[] = {0,0,0,0};
byte buttonEvent[] = {0,0,0,0};
byte buttonState[] = {0,0,0,0};
//button debounce counter- 16 bytes
byte buttonDebounceCounter[4][4];

//variables for accelerometer pixel movement
boolean firstPress[] = {0, 0, 0, 0};
byte movingPixel[] = {0, 0, 0, 0};
byte xPosition[4];
int timeX[] = {0, 0, 0, 0};
boolean dirX;
boolean dirY;
boolean prevDirX = 0;
boolean bounceDirection[]= {0, 0, 0, 0};
boolean toggle[] = {1, 1, 1, 1};
byte peakHeight[4];
byte lastX = 4;
byte lastY = 4;

//MIDI variables
int velocity = 100;
int noteON = 144;
int MIDIoffset = 60;
byte currentX;
byte note[] = {60, 64, 67, 72};


void setup() {
  
  DDRD = 0xFA;//set pins D7-D4 as output, D2 as input
  
  Serial.begin(31250);//MIDI baud rate 
  
  cli();//stop interrupts

  //set timer1 interrupt at 1kHz
  TCCR1A = 0;// set entire TCCR1A register to 0
  TCCR1B = 0;// same for TCCR1B
  TCNT1  = 0;//initialize counter value to 0;
  // set timer count for 1khz increments
  OCR1A = 1999;// = (16*10^6) / (1000*8) - 1
  // turn on CTC mode
  TCCR1B |= (1 << WGM12);
  // Set CS11 bit for 8 prescaler
  TCCR1B |= (1 << CS11);   
  // enable timer compare interrupt
  TIMSK1 |= (1 << OCIE1A);
  
  sei();//allow interrupts
  
}

ISR(TIMER1_COMPA_vect) {//Interrupt at freq of 1kHz
  for (byte a=0;a<4;a++){
    timeX[a]++;//increment each element of timeX
  }
  shift();
}

// buttonCheck - checks the state of a given button.
//this buttoncheck function is largely copied from the monome 40h firmware by brian crabtree and joe lake
void buttonCheck(byte row, byte index)
{
  if (((buttonCurrent[row] ^ buttonLast[row]) & (1 << index)) &&   // if the current physical button state is different from the
  ((buttonCurrent[row] ^ buttonState[row]) & (1 << index))) {  // last physical button state AND the current debounced state

    if (buttonCurrent[row] & (1 << index)) {                      // if the current physical button state is depressed
      buttonEvent[row] = 1 << index;              // queue up a new button event immediately
      buttonState[row] |= (1 << index);                         // and set the debounced state to down.
  }
    else{
      buttonDebounceCounter[row][index] = 12;
    }  // otherwise the button was previously depressed and now
    // has been released so we set our debounce counter.
  }
  else if (((buttonCurrent[row] ^ buttonLast[row]) & (1 << index)) == 0 &&  // if the current physical button state is the same as
  (buttonCurrent[row] ^ buttonState[row]) & (1 << index)) {        // the last physical button state but the current physical
    // button state is different from the current debounce 
    // state...
    if (buttonDebounceCounter[row][index] > 0 && --buttonDebounceCounter[row][index] == 0) {  // if the the debounce counter has
      // been decremented to 0 (meaning the
      // the button has been up for 
      // kButtonUpDefaultDebounceCount 
      // iterations///

      buttonEvent[row] = 1 << index;    // queue up a button state change event

      if (buttonCurrent[row] & (1 << index)){          // and toggle the buttons debounce state.
        buttonState[row] |= (1 << index);
      }
      else{
        buttonState[row] &= ~(1 << index);
      }
    }
  }
}


void shift(){
  
  for (i=0;i<4;i++){
    
    buttonLast[i] = buttonCurrent[i];
    
    byte dataToSend = (1 << (i+4)) | (15 & ~ledData[i]);
      
    // set latch pin low so the LEDs don't change while sending in bits
    PORTD&=B10111111;//digitalWrite(ledLatchPin, LOW);
    // shift out the bits of dataToSend 
    //shiftOut(ledDataPin, ledClockPin, LSBFIRST, dataToSend);  
    for (j=0;j<8;j++){
      PORTD&=B11011111;//digitalWrite(ledClockPin,LOW);
      //digitalWrite(ledDataPin,((dataToSend>>j)&1));
      if ((dataToSend>>j)&1){
        PORTD|=B10000000;
      }
      else{
        PORTD&=B01111111;
      }
      PORTD|=B00100000;//digitalWrite(ledClockPin,HIGH);
    }
    //set latch pin high so the LEDs will receive new data
    PORTD|=B01000000;//digitalWrite(ledLatchPin, HIGH);
    
    // SlowDown is put in here to waste a little time while we wait for the state of the output
    // pins to settle.  Without this time wasting loop, a single button press would show up as
    // two presses (the button and its neighbour)
    volatile int SlowDown = 0; 

    while (SlowDown < 15) 
    { 
      SlowDown++; 
    } 
      
    //once one row has been set high, receive data from buttons
    //set latch pin high
    PORTD|=B00010000;//digitalWrite(buttonLatchPin, HIGH);
    //shift in data
    //buttonCurrent[i] = shiftIn(buttonDataPin, buttonClockPin, LSBFIRST) >> 3;
    for (j=0;j<4;j++){
      PORTD&=B11110111;//digitalWrite(buttonClockPin,LOW);
      PORTD|=B00001000;//digitalWrite(buttonClockPin,HIGH);
    }
    for (j=0;j<4;j++){
      PORTD&=B11110111;//digitalWrite(buttonClockPin,LOW);
      if ((PIND>>2)&1){//digitalRead(buttonDataPin)
        buttonCurrent[i]|=1<<j;
      }
      else{
        buttonCurrent[i]&=~(1<<j); 
      }
      PORTD|=B00001000;//digitalWrite(buttonClockPin,HIGH);
    }
    //latchpin low
    PORTD&=B11101111;//digitalWrite(buttonLatchPin, LOW);
    
    for (k=0;k<4;k++){
      buttonCheck(i,k);
    }
  }
  
  //turn off leds- this way one row does not appear brighter than the rest
  
  // set latch pin low so the LEDs don't change while sending in bits
  PORTD&=B10111111;//digitalWrite(ledLatchPin, LOW);
  // shift out 0
  //shiftOut(ledDataPin, ledClockPin, LSBFIRST, 0);  
  for (j=0;j<8;j++){
    PORTD&=B11011111;//digitalWrite(ledClockPin,LOW);
    PORTD&=B01111111;
    PORTD|=B00100000;//digitalWrite(ledClockPin,HIGH);
  }
  //set latch pin high so the LEDs will receive new data
  PORTD|=B01000000;//digitalWrite(ledLatchPin, HIGH);
}

void checkPress(byte Y){
  if (buttonEvent[Y]){
    for (byte b=0;b<4;b++){
      if (buttonState[Y]&(1<<b)){
        //toggle firstPress variable
        firstPress[Y] = 1;
        //display pressed pixel
        ledData[Y] = (1<<b);
        //store current position
        xPosition[Y] = (1<<b);
        //store peak height
        peakHeight[Y] = (1<<b);
        //reset timers
        timeX[Y] = 0;
        return;
      }
    }
  }
}

byte scaleAcc(int RAW){
  if (RAW<=10 && RAW>=-10){
    return 5;
  }
  else if (RAW<-10){
    if (RAW<-50){
      return 0;
    }
    else if (RAW<-40){
      return 1;
    }
    else if (RAW<-30){
      return 2;
    }
    else if (RAW<-20){
      return 3;
    }
    else{
      return 4;
    }
  }
  else if (RAW>10){
    if (RAW>50){
      return 10;
    }
    else if (RAW>40){
      return 9;
    }
    else if (RAW>30){
      return 8;
    }
    else if (RAW>20){
      return 7;
    }
    else{
      return 6;
    }
  }
}

void checkAccelerometerGyro(){
  //read values
  xGyroRAW = analogRead(A1);
  yGyroRAW = analogRead(A0);
  xAccRAW = analogRead(A4);
  yAccRAW = analogRead(A3);
  zAccRAW = analogRead(A2);
  
  //offset data
  xGyroRAW = 317-xGyroRAW;
  yGyroRAW = 183-yGyroRAW;
  xAccRAW = 282-xAccRAW;
  yAccRAW = 282-yAccRAW;
  zAccRAW = 282-zAccRAW;
  
  //convert to 0-10
  xAcc = scaleAcc(xAccRAW);
  yAcc = scaleAcc(yAccRAW);
  
  if (xAccRAW>5){
    dirX = 1;
  }
  else if (xAccRAW<5){
    dirX = 0;
  }
  if (yAccRAW>5){
    dirY = 1;
  }
  else if (yAccRAW>5){
    dirY = 0;
  }
  
}

int getTime(byte acceleration){
  switch (acceleration){
    case 0://max - acceleration
    return 100;
    break;
    case 1:
    return 100;
    break;
    case 2:
    return 150;
    break;
    case 3:
    return 200;
    break;
    case 4:
    return 250;
    break;
    case 5://lying flat
    return 0;
    break;
    case 6:
    return 250;
    break;
    case 7:
    return 200;
    break;
    case 8:
    return 150;
    break;
    case 9:
    return 100;
    break;
    case 10://max + acceleration
    return 100;
    break;
  }
}

void moveXPixel(byte Y, int timeComp){
  if (timeComp==0){
  }
  else{
    if (timeX[Y]>timeComp){
      timeX[Y] = 0;
      if (dirX){
        if (peakHeight[Y]==8&&xPosition[Y]==8){
          if(toggle[Y]){
            MIDImessage(noteON,note[Y],0);//send midi
            toggle[Y]=0;
            ledData[Y]=0;
          }
          else{
            MIDImessage(noteON,note[Y],velocity);
            toggle[Y]=1;
          }
        }
        else{
          toggle[Y]=1;
          if (xPosition[Y]==peakHeight[Y]){//if at peak
            bounceDirection[Y]=1;//falling
            MIDImessage(noteON,note[Y],0);//turn note off
          }
          if (xPosition[Y]==8){//if hitting bottom
            bounceDirection[Y]=0;//rising
            MIDImessage(noteON,note[Y],velocity);//turn note on
          }
          if (xPosition[Y]==1){
            bounceDirection[Y]=1;
          }
          if (bounceDirection[Y]){
            xPosition[Y] = xPosition[Y]<<1;
          }
          else{
            xPosition[Y] = xPosition[Y]>>1;
          }
        }
      }
      else{
        if (peakHeight[Y]==1&&xPosition[Y]==1){
          if(toggle[Y]){
            MIDImessage(noteON,note[Y],0);//send midi
            toggle[Y]=0;
            ledData[Y]=0;
          }
          else{
            MIDImessage(noteON,note[Y],velocity);
            toggle[Y]=1;
          }
        }
        else{
          toggle[Y]=1;
          if (xPosition[Y]==peakHeight[Y]){//if at peak
            bounceDirection[Y]=0;//falling
            MIDImessage(noteON,note[Y],0);//turn note off
          }
          if (xPosition[Y]==8){
            bounceDirection[Y]=0;
          }
          if (xPosition[Y]==1){//if hitting bottom
            bounceDirection[Y]=1;//rising
            MIDImessage(noteON,note[Y],velocity);//turn note on
          }
          if (bounceDirection[Y]){
            xPosition[Y] = xPosition[Y]<<1;
          }
          else{
            xPosition[Y] = xPosition[Y]>>1;
          }
        }
      }
    }
  }
}

void shake2Clear(){
  if (abs(xGyroRAW)>300){
    for (byte a=0;a<4;a++){
      firstPress[a]=0;
      ledData[a]=0;
    }
  }
}

void MIDImessage(int command, int MIDInote, int MIDIvelocity) {//send s a MIDI message
  Serial.write(command);//send note on or note off command 
  Serial.write(MIDInote);//send pitch data
  Serial.write(MIDIvelocity);//send velocity data
}

void loop() {
  checkAccelerometerGyro();
  shake2Clear();
  for (byte column=0;column<4;column++){
    checkPress(column);
    if (firstPress[column]){
    moveXPixel(column, getTime(xAcc));
    if (toggle[column]){
      ledData[column]= xPosition[column];
    } 
    }
  }
}

Comments

lmenny (author)2017-07-20

Hi, i tried your code but didn't work... the issue is that if i try to read the three MIDI bytes in the same cycle the arduino can't read useful data after the first byte...
I tried by copy and paste your code that turns on led 13 on note 60 but didn't work... do you know why?

I can't explain why works with you and not with me, i'm using an arduino UNO board, hairless as bridge and DigitalPerformer to send MIDI data.

Thanks for help, very cool instructables

DamonB23 (author)2017-05-19

Could you explain the hardware a bit more? Are you plugging a midi usb keyboard in and then sending midi notes to the midi DIN socket? According to everything i've read this is impossible without a USB host shield. Will this setup work without a computer?

For reference i want to go:

usb controller --> your device --> synth with midi DIN. So no computer.

Thanks!

EnesY4 (author)2017-02-27

#include <MIDI.h>
#include <Wire.h>
#include <Adafruit_MCP23017.h>


int statusLed = 3;

Adafruit_MCP23017 mcp;

void setup() {
MIDI.begin();
MIDI.setHandleNoteOn(HandleNoteOn);
MIDI.setHandleNoteOff(HandleNoteOff);

mcp.begin();

mcp.pinMode(0, OUTPUT);
mcp.pinMode(1, OUTPUT);
mcp.pinMode(2, OUTPUT);
mcp.pinMode(3, OUTPUT);
mcp.pinMode(4, OUTPUT);
mcp.pinMode(5, OUTPUT);
mcp.pinMode(6, OUTPUT);
mcp.pinMode(7, OUTPUT);
mcp.pinMode(8, OUTPUT);
mcp.pinMode(9, OUTPUT);
mcp.pinMode(10, OUTPUT);
mcp.pinMode(11, OUTPUT);
mcp.pinMode(12, OUTPUT);
mcp.pinMode(13, OUTPUT);
mcp.pinMode(14, OUTPUT);
mcp.pinMode(15, OUTPUT);
mcp.pinMode(16, OUTPUT);

mcp.digitalWrite(0,LOW);
mcp.digitalWrite(1,LOW);
mcp.digitalWrite(2,LOW);
mcp.digitalWrite(3,LOW);
mcp.digitalWrite(4,LOW);
mcp.digitalWrite(5,LOW);
mcp.digitalWrite(6,LOW);
mcp.digitalWrite(7,LOW);
mcp.digitalWrite(8,LOW);
mcp.digitalWrite(9,LOW);
mcp.digitalWrite(10,LOW);
mcp.digitalWrite(11,LOW);
mcp.digitalWrite(12,LOW);
mcp.digitalWrite(13,LOW);
mcp.digitalWrite(14,LOW);
mcp.digitalWrite(15,LOW);
mcp.digitalWrite(16,LOW);


}
void loop() {
MIDI.read(); // HERE IS THE ERROR

exit status 1
'MIDI' was not declared in this scope


}
void HandleNoteOn(byte channel, byte note, byte velocity){

switch (note)
{
case 72:// C
mcp.digitalWrite(0, HIGH);
break;
case 74:// D
mcp.digitalWrite(1, HIGH);
break;
case 76: // E
mcp.digitalWrite(2, HIGH);
break;
case 77: // F
mcp.digitalWrite(3, HIGH);
break;
case 79: //G
mcp.digitalWrite(4, HIGH);
break;
case 81: // A
mcp.digitalWrite(5, HIGH);
break;
case 83:// B
mcp.digitalWrite(6, HIGH);
break;
case 84:// C+
mcp.digitalWrite(7, HIGH);
break;
}
}
void HandleNoteOff(byte channel, byte note, byte velocity){ .


switch (note)
{
case 72:// C
mcp.digitalWrite(0, LOW);
break;
case 74:// D
mcp.digitalWrite(1, LOW);
break;
case 76: // E
mcp.digitalWrite(2, LOW);
break;
case 77: // F
mcp.digitalWrite(3, LOW);
break;
case 79: //G
mcp.digitalWrite(4, LOW);
break;
case 81: // A
mcp.digitalWrite(5, LOW);
break;
case 83:// B
mcp.digitalWrite(6, LOW);
break;
case 84:// C+
mcp.digitalWrite(7, LOW);
break;
}
}
jackthebox (author)2017-02-16

To save anyone the frustration, the midi baud rate is not compatible with Intel chip in the Arduino 101 & Genuino 101.

franciscog34 (author)2017-02-10

Hello dear, I would like to know if there is any way to go through the midi port in receiving the messages of edrums, add messages of electrical piezo and all send it to the port midi out.

The intention is to make a trigger for edrums.

RowanCant (author)franciscog342017-02-16

Yea, You can do all of that with a midi thru port.
Basically, find an instructable for how to make a midi electronic drumkit, build that however you like, but add an input that is wired to the output as a midi thru port. That way, everything being sent from your edrum goes straight out the out cable, no delays. The new arduino midi drum will send the new notes down the same cable, but the notes are so fast that you should never have a problem with the messages getting confused. It's a 16 bit message, so it's all sent in 16 milliseconds. The chances of anyone being that accurate a drummer are pretty low.

RowanCant (author)franciscog342017-02-15

If I understand correctly, you have an edrum that you are wanting to add some extra pads to and send the midi out to something that plays all the pads?
Maybe create an arduino midi drumkit with a midi thru port? Then the output from your edrum can go into your arduino in and out the thru port with the new extra midi messages from your piezo pads.
Or do you need the piezo to send messages to the edrum as well as the edrum sending it's current pads?

franciscog34 (author)RowanCant2017-02-15

Dear, I need arduino to receive the midi messages from the edrums and add the midi notes that deliver the piezos and all that send it to midi out.

I would also be interested to know if doing so would increase latency.

Greetings and thanks

HugoC43 (author)2016-04-21

Hello,

I'm trying to receive MIDI notes from an Axiom 49 keyboard using your tutorial and exact resistor values, but nothing comes out when i try the middle C (only "-1").

HugoC43 (author)HugoC432016-04-21

Only difference is the baud rate that i tried to set to 19200, 38400, 57600 instead of 31250.

JimM306 (author)HugoC432017-01-28

I think the problem with these MIDI sketches lies in the impossibility of reading the output data on a (PC) screen, because PCs do not have 31250 baud rate. A solution I found is "PuTTY" which allows you to see the data in real time.

The interrupt-driven MIDI receive sketch above does not return the expected data, most likely because it is too slow. I'm working on it!

HéctorC73 (author)2016-10-10

hola! necesitaria ayuda con mover servos a traves de un editor de partituras que lo lea Arduino y lo ejeucte a traves de un midi in fisico.Gracias!!

Jesper MartinS (author)2016-10-09

I know this is an old instruction, but ican see it is used as reference for recent MIDI posts, so thats why i post here.

I didn't have the optocoupler used here, but i have the common used EL817, as you can see in the picture, i added a bc547, this works very well, risetime is about 5us. the schmatic are originally from http://www.midikits.net/.

hope it is helpfull.

I have just started to use Arduino with MIDI, and for now I can't seem to get it stabel, when i use a MIDI keyboard it sometimes miss keystroke og leave the diode on, anybody solved this?

yankeemike (author)2016-10-06

Hello Amanda, Thanks for the detailed explanation of MIDI protocol and very clear project explanation. This helped me tremendously to understand MIDI for a project I am working on. I had a few questions if you can help me....in the first part of the project where the Arduino is sending(TXing) note info.....where do you connect the USB of the MIDI/USB cable? Are you connecting the USB back to computer to control the software that produces the sound in your video?

MidiGuy (author)2016-10-03

How would I be able to bypass Hairless, as in make the arduino a USB MIDI device?

forgoden (author)2014-04-23

This Code below here from you doesn't work to me.
The Serial.read() only put values in commandByte, but not noteByte and velocityByte? I can be sure that commandByte is always bigger than 127 and the other values 0-127 can be noteByte or velocityByte?

ISR(TIMER2_COMPA_vect) {//checks for incoming midi every 128us
do{
if (Serial.available()){
commandByte = Serial.read();//read first byte
noteByte = Serial.read();//read next byte
velocityByte = Serial.read();//read final byte
}
}
while (Serial.available() > 2);//when at least three bytes available
}

amandaghassaei (author)forgoden2014-04-30

Yes the command byte is always >127 and the note and velocity (or whatever other parameters come after the command are <= 127. This code can be really finicky depending on your setup. Try removing the > 2 in the code able and see if it helps. You might want to add some additional logic to be sure that the command byte is set correctly (check if it is > 127)

I too am having the same issues using the Timer version as forgoden and sjonniesjon. It only seems to read the commandByte correctly, the noteByte and velocityByte always return 255. Maybe the bytes following the commandByte are missed due to the timer? Do you have any recommendations besides removing the >2 in the while clause to get this to work like the checkMIDI() function does? The checkMIDI() version works fine (despite the polyphony issues of course). This timer version does not work properly from your provided code. I know this is an old thread, perhaps there are better tutorials on this type of MIDI functionality by now? I'm open to suggestions here for better MIDI Input and Output processing code.

First of all, many thanks for this awesome tutorial! It really helped me a lot.

Unfortunately, I'm having the same problem. Removing >2 doesn't seem to help. With additional logic, I've discovered that noteByte reads 255, no matter wich note I play.The commandByte, noteOn and noteOff are working fine.

Leinard (author)2014-09-17
when I try to use the MIDI Hairless and try to connect with the port of arduino, the error "this application has Requested the Runtime to terminate it in an unusual way" shows how I can fix this?
JohnW225 (author)Leinard2015-12-25

Hi, I realize this is an old thread, but I am having the same issue. How did you resolve it?

(author)JohnW2252016-09-17

I had the same problem on Windows 7..

Make sure your FTDI drivers are up to date and trying reinstalling them.
Make sure baud is 9600 on everything (including speaker settings).
I also had to go to my computer speaker properties and deselect exclusivity mode.
Do not output to microsoft gs wavetable synth as it lags.. I think this was the biggest problem.

theking2 (author)2016-09-14

Converting decimal numbers to hexadecimal and vv can be done easily with Windows Calculator in "Programmers" mode

sdas8 (author)2016-06-04

Is it possible to receive text data via MIDI (USB) on the controller from a software? I'm working a foot controller for Guitar Rig. I want to show the patch names (and other info) on a LCD on the controller. Is that possible?

amandaghassaei (author)sdas82016-06-18

you could hack something together to make that work over midi, but it would probably be easier to use serial. You might also look into osc, i think it could support something like that - not sure though.

thomas89400 made it! (author)2016-04-10

Hello

I'm trying to do a laser harp using midi. But I always sound ! Could someone help me find the error in the code? Thanks

magnocampos (author)thomas894002016-05-19

Hi,
I have made a harp like you... send me message to talk...

thomas89400 made it! (author)2016-04-10

Hello

I'm trying to do a laser harp using midi. But I always sound ! Could someone help me find the error in the code? Thanks

hfc123 (author)2016-04-06

Hi im trying to receive midi to control solenoids, so ive it set up exactly as its shown but when i play a file the led wont blink, im using cubase to play the files. Im just wondering if theres some sort of setup im mssing? Thanks

JasonT85 (author)2016-03-31

Why do the other tutorials use optocouplers and you don't? Can I use this with a drum machine

amandaghassaei (author)JasonT852016-04-01

I do use an optocoupler to receive MIDI. This will work with anything that sends or receives MIDI.

JasonT85 (author)amandaghassaei2016-04-04

Ah! Thank you so much for the response, didn't see the last part. Can note on and note off be used as clock signals as well to send to drum machines etc?

JasonT85 (author)JasonT852016-03-31

Or is it computer only *

AntonB30 (author)2016-03-02

Hello Amanda! Thank you for the tutorial!! I have a question about step 10. Everything works great. I can turn the LED on with a midi note. But what if I want to turn it off with the same note? And what if I want to do it permanently but not once. I mean how can I make some kind of switch out of that note? What should I add to the code or how it should be transformed? Thank you!

GonzaloV11 (author)AntonB302016-03-16

What software are you using to send the MIDI?

AntonB30 (author)GonzaloV112016-03-16

Hi! I use Cubase.

GonzaloV11 (author)2016-03-16

Hello! Im very interested in this tutorial. I want to receive MIDI with the Arduino, but you don't say anything about how to send it from the Computer. Do you know any Software that can send MIDI songs to the Arduino? I'm really having a headache with this point.

Thanks!

jojo59650 (author)2016-03-16

Hi, thank you for this instructable. You 're talking in step 10 about using timers of the arduino if we care about timing. Would it be a good solution to use external interrupt?

Thanks again.

joewrigg (author)2016-03-08

Hi, great Instructable thank you. Has anyone done anywork on reading USB class compliant devices as these are quite predominant in the market. I was interested in terms of Midi note remapping. I can do this easily in Max but it would be great to have a standalone USB device. Thanks

AntonB30 (author)2016-03-05

Hello again! I changed your code a bit to turn one LED on/off with the exact midi notes. Everything works except one thing the led sometimes doesn't want to go off. And besides nothing works without the "delay" in the loop. I'm an arduino beginner. If you please could you say what I did wrong? Thank you!

byte commandByte;

byte noteByte;

byte velocityByte;

byte noteOn = 10010000;

void setup(){

Serial.begin(115200);

pinMode(12,OUTPUT);

}

void checkMIDI(){

if (Serial.available()){

commandByte = Serial.read();

noteByte = Serial.read();

velocityByte = Serial.read();

if (commandByte == noteOn){

if (noteByte == 95 && velocityByte > 0){

digitalWrite(12,HIGH);//turn on led

}

}

if (commandByte == noteOn){

if (noteByte == 93 && velocityByte > 0){

digitalWrite(12,LOW);

}

}

}

}

void loop()

{

checkMIDI();

delay(1);

}

gustavo.silveira.167 (author)2016-02-26

Hi, great tutorial! I've been making midi controllers for a while, but just with midi out, now I want to have som feedback from the software. I've made a controllers with a couple encoders and what I want to do is to update the cc value of the encoders when I play in the knobs in the software. any clue on how to do? Thanks!

theredbryophyte (author)2015-12-23

Thanks for the great 'ible.

Do you know how to do something like this. I basically want to be able to read midi files from the Arduino and connect it to a MIDI device. I'm planning (if this is possible!) to have LEDs which light up when a specific note is played - it could also display the MIDI velocity by changing its brightness with PWM.

Hi there,

Have a look at this device you can get for 91 euros : the Midilektor.

http://www.orgautomatech.com/epages/3b85d6ba-28b0-...

I've bought is 5 years ago, and , combined with a midi decoder from the same provider, it works perfectly on a street organ .

Jean-Paul

Glauco Danilo made it! (author)2016-02-14

congratulations for detailed instructable I could make my MIDI OUT and MIDI IN connections thanks to the tutorial, the MIDI OUT connection could first since the MIDI IN messed up enough, first mistake was to connect the MIDI input to the contrary (pin 2 to pin 4 and pin 4 on pin 2), the second was the diode, called the entrance 4 6N139 to inves entry 3, the third and final error was carrying coding in arduino with zero connected pin, gave several errors ... Finally I watched one toturial to set the MIDI OUT on the fruit loops and is now running smoothly ... once again, thank you! I am Brazilian and I'm using google translator, sorry for the grammatical errors ...

mosstech (author)2016-02-12

The "Receive MIDI Messages" diagram is incorrect, and does not match the picture of the board. The MIDI jack wires should go to the opposite IC pins as shown in the diagram. Took me a while to figure this out.

chrisrobertsonuk (author)2014-09-24

Hi Amanda,

Great Tutorial. However when it comes to Electronics my knowledge is limited at best. I was wondering whether you would mind further explaining the use of the optocoupler for receiving data... If I could understand WHY we use it the rest will become clearer to me. Many Thanks. Chris

hey, sorry for the late reply. Optocouplers isolate circuits from each other by using light to transmit data instead of voltage. This can be useful if two circuits need to communicate with each other and they use different voltages to transmit data signals (eg 5V vs 3.3V logic). In this case it's not a voltage difference, I think the optocoupler is there to protect from current spikes. It is required in the specs.

AldoN2 (author)amandaghassaei2015-10-28

The optocoupler provides electrical insulation between equipments. This avoids several problems that plague audio devices, like ground loops and hum pickup.

CarraN (author)2015-10-18

Hi Amanda!

I think your phrase:

"The last half of the command byte sets the MIDI channel. All the bytes
listed above would be in channel 0, command bytes ending in 0001 would
be for MIDI channel 1, and so on."

is not exact, because in 4 bits values go from 0 to 15. So a byte ending 0001 means 2.

romain.knobloch.3 (author)2015-01-15

Hi your tutorial is very usefull :-)

I just have a probleme. I tried you receive and check midi if note = 60 and it work. So i decided to modify it to light up 8 led when i play on my midi keyboard. So i made this programme but it does'nt work.

/*
By Amanda Ghassaei
July 2012
https://www.instructables.com/id/Send-and-Receive-M...


*/

byte commandByte;
byte noteByte;
byte velocityByte;

byte noteOn = 144;


void setup(){

Serial.begin(56000);
pinMode(13,OUTPUT);
pinMode(12,OUTPUT);
pinMode(11,OUTPUT);

digitalWrite(13,LOW);
digitalWrite(12,LOW);
digitalWrite(11,LOW);

}

void directblink13(){
digitalWrite(13,HIGH);//turn on led
delay(1000);
digitalWrite(13,LOW);
}
void directblink12(){
digitalWrite(12,HIGH);//turn on led
delay(1000);
digitalWrite(12,LOW);
}
void directblink11(){
digitalWrite(11,HIGH);
delay(1000);
digitalWrite(11,LOW);
}

void checkMIDI(){
do{
if (Serial.available()){
commandByte = Serial.read();//read first byte
noteByte = Serial.read();//read next byte
velocityByte = Serial.read();//read final byte
if (commandByte == noteOn){//if note on message
//check if note == 60 and velocity > 0
if (noteByte == 60 && velocityByte > 0){directblink13;}

else if(noteByte == 61 && velocityByte > 0){directblink12;}

else if(noteByte == 62 && velocityByte > 0){ directblink12;}
}



}

}
while (Serial.available() > 2);//when at least three bytes available
}


void loop(){
checkMIDI();
delay(1);

}

Thanks :-)

About This Instructable

560,657views

542favorites

License:

Bio: I'm a grad student at the Center for Bits and Atoms at MIT Media Lab. Before that I worked at Instructables, writing code for ... More »
More by amandaghassaei:OTCA Metapixel - Conway's Game of Life"9 Degrees of Freedom" IMUTwitter Controlled Pet Feeder
Add instructable to: