user11165: In order to finish a synth project that I'm working on I built a MIDI interface. To test it, I drew up some code which (is supposed to) light up an LED when any key is pressed. The light turns off again when the key is released. This will later be merged with another branch of code here to tell me which note is playing. This sounds simple enough but the devil's in the details. Due to my project's size constraints I am using ATMEL's ATtiny85 chip instead of my Arduino Uno. The ATtiny doesn't support the Arduino Serial library and by extension the standard MIDI library. As I can't make use of simple functions like MIDI.read() I've opted to work with an alternative Software Serial technique I adapted from an Instructable. Unfortunately, the results have been less than exemplary. Simply put, the LED almost never lights up. I have conducted two test only 1 of which somewhat succeeded:
1)
if(velocityByte > 0 ){digitalWrite(LED, HIGH);}
if(velocityByte == 0 ){digitalWrite(LED, LOW);}

Initially, I had figured that would be the most successful of my options. My keyboard* is binary about its ouput velocity. There are no pressure sensors so "on" is on and "off" is off. Unfortunately, however, the code seems to think otherwise.
2)
if(commandByte == noteOn ){digitalWrite(LED, HIGH);}
if(commandByte != noteOff ){digitalWrite(LED, LOW);}

Though originally less sure of this one, I've found it to be better than the other at catching notes. noteOn is the integer 144. While it does in fact trigger it does so only very rarely - once every 10 presses or so.
I believe I have however, whittled down the possible number of problems to 2:
1) Something is up with one or more of my calls to Serial. I've never really felt super confident with the author's method of obtaining data from Serial. For one, I have no confidence that I can insure I have collected the correct byte. If the code were to somehow "miss" counting a byte would it not assign, say, a commandByte to a noteByte? Is a better method available?
2) My keyboard's MIDI protocol is incompatible with my if-statement cases.
At first I was under the assumption that my keyboard never sends a true "note off" message of any kind but instead a velocity = 0 message. But my test casts much doubt on this being the case and because of this, I don't really know how the keyboard handles note off messages. Does anyone else know?
*A Yamaha YPT-230 
//MIDI INPUT LED TEST
//Adapted from code written
//by Instructables user Amanda Ghassaei

#include <SoftwareSerial.h>

SoftwareSerial mySerial(0, 1);  //RX, TX

#define LED 2

byte commandByte;
byte noteByte;
byte velocityByte;

byte noteOn = 144;
byte noteOff = 128;

void setup(){
  mySerial.begin(31250);
  digitalWrite(LED, LOW); 
  delay(1000);
}

void checkMIDI(){
  do{
    commandByte = mySerial.read();//read first byte
    noteByte = mySerial.read();//read next byte
    velocityByte = mySerial.read();//read final byte

    if (commandByte == noteOn){
      digitalWrite(LED, HIGH);
    }
    if(commandByte != noteOn){
      digitalWrite(LED, LOW);
    }  

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


void loop(){
  checkMIDI();
}
/*
void gotNoteOn(commandByte, noteByte, velocityByte){
  digitalWrite(LED,HIGH);
}

void gotNoteOff(commandByte, noteByte, velocityByte){
  digitalWrite(LED,LOW);
}
*/

UPDATE Following a suggesting, I have replaced the do .. while loop with an ordinary while loop. Relevant changes below:
void checkMIDI(){
  while(mySerial.available() > 2){
    commandByte = mySerial.read();
    if (velocityByte > 0x00){//if note on message
      digitalWrite(LED, HIGH);
    }
    if(velocityByte == 0x00){
      digitalWrite(LED, LOW);
    }  
  }
}  

I'd like to think it's an improvement but if so only a slight one. Curiously, it almost works backwards - The default state of the LED is on while pressing a key produces no change pressing and holding causes the light to briefly shut off. But even this is sporadic. It may also be worth noting that I elected to use hex '0x00' as opposed to decimal '0'.
UPDATE II / NOTE Having slept on the problem I think I now understand why I am encountering this issue in the first place. The author of the Instructables code I adapted used an Arduino for both MIDI In as well as MIDI Out. Had they simply hooked one Arduino Uno to the other in an in/out configuration they could have perfectly ensured there were no running status issues by just never sending any running status messages. This would explain the lack of conformity to the actual realities of MIDI on the input side. If you can control exactly what data you are sending on one end (MIDI Out) it becomes fairly trivial to receive that data on the other end (MIDI In). Generalizing, the author may have assumed that a true keyboard behave the same thing and failed to actually verify this. Thoughts?
UPDATE III Following the lead of CL but deviating somewhat because I didn't fully understand what CL was doing, I have written up a highly derivative, new code. It is based around what I now know to be the highly fluid nature of MIDI messages and seeks to intelligently discriminate important messages from non-important messages based on content. It does not work (except for LED test at the beginning) but seems like a promising step nonetheless.
//MIDI Input LED Test

#include <SoftwareSerial.h>

SoftwareSerial mySerial(0, 1);  //RX, TX

#define LED 2  //Test LED

void setup(){
  mySerial.begin(31250);
  digitalWrite(LED, HIGH); //Test
  delay(1000);
  digitalWrite(LED, LOW); 
  delay(1000);
}

void loop(){
  checkMIDI();
} 

void checkMIDI(){
/*Two of the three independent types of bytes are always
distinct and can be identified simply by their values.
A Status byte (on channel 1 at least) is always either
0x80 (off) or 0x90 (on). No other type of message
(Note, Velocity) can ever take this value and so if
this is the value of the byte received I can be
assured it is a Status byte.

The Note and Velocity bytes are a bit (haha) dicier.
They have overlapping ranges with both taking values of
anywhere between 0x00 to 0x7F. *Ordinarily*. A caveat
is that my Yamaha YPT-230 is binary as far as velocity
goes. A YPT-230 data is either 0x00 (full off) or 0x7F
(full on). As long as a Note byte is never 0 or 127 I can
use this to discriminate between these two types
in this manner.
*/   

  if(mySerial.available() > 0){
    byte b = mySerial.read();

    if(b == 0x80 || 0x90){
      byte commandByte = b;
    }else if(b == 0x00 || 0x7F){
      if(b == 0x00){
        digitalWrite(LED, LOW);  //If vel = 0 turn LED off
      }else if(b == 0x7F){
        digitalWrite(LED, HIGH); //If vel = 127 turn LED on
      }
      //velocityByte = b;
    }else{
      byte noteByte = b;  //Do nothing with this for right now.
    }
  }
}


user6802: This is not how the MIDI protocol works.
The incoming stream can contain other commands than note-on or note-off, and with running status, it is impossible to predict the length of any single command.
You need at least some kind of state machine to remember where in the command you are:
byte nr;

for (;;) {
    byte b = mySerial.read();
    if (b & 0x80) { // status byte
        if (b >= 0xf8) {
            // real-time command; ignore
        } else {
            commandByte = b;
            nr = 1;
        }
    } else { // data byte
        if ((commandByte & 0xf0) == 0x80 ||
            (commandByte & 0xf0) == 0x90) {
            if (nr == 1) {
                noteByte = b;
                nr = 2;
            } else if (nr == 2) {
                velocityByte = b;
                nr = 1;

                if ((commandByte & 0xf0) == 0x90 &&
                    velocityByte > 0) {
                    // note on
                } else {
                    // note off
                }
            } 
        }
    }
}


user10794: I suggest you first find what your Yamaha is sending you. I have a sketch that decodes MIDI for the Uno (assuming you have a Uno lying around). Run that, and see if you got note on with velocity zero, or note off. Then you are a little closer to solving the problem.

This part is quite wrong:
  do{
    commandByte = mySerial.read();//read first byte
    noteByte = mySerial.read();//read next byte
    velocityByte = mySerial.read();//read final byte

  ...

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

You are reading 3 bytes, even if none are available. At least change it to:
while (mySerial.available() > 2)
  {
        commandByte = mySerial.read();//read first byte
        noteByte = mySerial.read();//read next byte
        velocityByte = mySerial.read();//read final byte

      ...

  }

The do ... while construct tests at the end of the loop. You must ensure you have 3 bytes "available" before reading 3 of them, not after.

Example code for ATtiny85
The code below is an adaptation of the MIDI decoder in my link above. This one just reads the incoming MIDI and turns on the LED on a "note on" and turns it off on a "note off" (or note on with velocity zero). Tested, works perfectly.
//  MIDI_decoder for ATtiny85
//  
// ATMEL ATTINY 25/45/85 / ARDUINO
// Pin 1 is /RESET
//
//                  +-\/-+
// Ain0 (D 5) PB5  1|    |8  Vcc
// Ain3 (D 3) PB3  2|    |7  PB2 (D 2) Ain1 
// Ain2 (D 4) PB4  3|    |6  PB1 (D 1) pwm1
//            GND  4|    |5  PB0 (D 0) pwm0
//                  +----+


#include <SoftwareSerial.h>

const byte LED = 0;   // pin 5 on ATtiny85

// Plug MIDI into pin D2 (MIDI in serial) (pin 7 on ATtiny85)

SoftwareSerial midi (2, 3);  // Rx, Tx

const int noRunningStatus = -1;

int runningStatus;
unsigned long lastRead;
byte lastCommand;

void setup() {
  //  Set MIDI baud rate:
  midi.begin(31250);
  runningStatus = noRunningStatus;
  pinMode (LED, OUTPUT);
} // end of setup

void RealTimeMessage (const byte msg)
  {
    // ignore realtime messages
  } // end of RealTimeMessage

// get next byte from serial (blocks)
int getNext ()
  {

  if (runningStatus != noRunningStatus)
    {
    int c = runningStatus;
    // finished with look ahead
    runningStatus = noRunningStatus;
    return c;
    }

  while (true)
    {
    while (midi.available () == 0)
      {}
    byte c = midi.read ();
    if (c >= 0xF8)  // RealTime messages
      RealTimeMessage (c);
    else
      return c;
    }
  } // end of getNext

const char * notes [] = { "C ", "C#", "D ", "D#", "E ", "F ", "F#", "G ", "G#", "A ", "A#", "B " };

byte note;
byte octave;
byte velocity;

// interpret a note in terms of note name and octave
void getNote ()
  {
  note = getNext ();
  octave = note / 12;
  note %= 12;
  }  // end of getNote

void getVelocity ()
  {
  velocity = getNext ();
  }

// show a control message 
void showControlMessage ()
  {
   byte message =  getNext () & 0x7F;
   byte param = getNext ();
  }  // end of showControlMessage

// read a system exclusive message 
void showSystemExclusive ()
  {
  int count = 0;
  while (true)
    {
    while (midi.available () == 0)
      {}
    byte c = midi.read ();
    if (c >= 0x80)
      {
      runningStatus = c;
      return;  
      }

    } // end of reading until all system exclusive done
  }  // end of showSystemExclusive

void loop() 
{
  byte c = getNext ();
  unsigned int parameter;

  if (((c & 0x80) == 0) && (lastCommand & 0x80))
    {
    runningStatus = c;
    c = lastCommand; 
    }

  // channel is in low order bits
  int channel = (c & 0x0F) + 1;

  // messages start with high-order bit set
  if (c & 0x80)
    {
    lastCommand = c;
    switch ((c >> 4) & 0x07)
      {
      case 0:   // Note off
        getNote ();
        getVelocity ();
        digitalWrite (LED, LOW);
        break;

      case 1:   // Note on
        getNote ();
        getVelocity ();
        if (velocity == 0)
          digitalWrite (LED, LOW);
        else
          digitalWrite (LED, HIGH);
        break;

      case 2:  // Polyphonic pressure
        getNote ();
        parameter = getNext ();  // pressure
        break;

      case 3: // Control change
        showControlMessage ();
        break;

      case 4:  // Program change
        parameter = getNext ();  // program
        break;

      case 5: // After-touch pressure
        parameter = getNext (); // value
        break;

      case 6: // Pitch wheel change 
        parameter = getNext () |  getNext () << 7; 
        break;

      case 7:  // system message
        {
        lastCommand = 0;           // these won't repeat I don't think
        switch (c & 0x0F)
          {
          case 0: // Exclusive
            parameter = getNext (); // vendor ID
            showSystemExclusive ();
            break;

          case 1: // Time code
            parameter = getNext () ;
            break;

          case 2:  // Song position 
            parameter = getNext () |  getNext () << 7; 
            break;

          case 3: // Song select
            parameter = getNext () ;  // song
            break;

          case 4:    // reserved
          case 5:    // reserved
          case 6:    // tune request
          case 7:    // end of exclusive
          case 8:    // timing clock
          case 9:    // reserved
          case 10:   // start
          case 11:   // continue
          case 12:   // stop
          case 13:   // reserved
          case 14:   // active sensing
          case 15:   // reset
             break;

          }  // end of switch on system message  

        }  // end system message
        break;
      }  // end of switch
    }  // end of if
  else
    {
    // unexpected, ignore 
    }

}  // end of loop


Suggested schematic


From a comment:

the 4N35 is too slow to work reliably with MIDI signals

Below is an updated schematic that uses a faster opto-coupler.

Admittedly the 4N35 is below spec for its rise time (it is supposed to be  less than 2 μs rise time, however the 4N35 is slower at between 3 µs and 7 µs). However during my testing it worked OK. It sounds like the faster rise time is specified to allow for multiple devices to be connected in series, a slow rise time will introduce distortion.

11165: <p>In order to finish a synth project that I'm working on I built a MIDI interface. To test it, I drew up some code which (is supposed to) light up an LED when any key is pressed. The light turns off again when the key is released. This will later be merged with another branch of code <a href="https://arduino.stackexchange.com/questions/13354/writing-midi-notes-more-quickly-to-a-shift-register-with-arduino/13362#13362">here</a> to tell me <em>which</em> note is playing. This sounds simple enough but the devil's in the details. Due to my project's size constraints I am using ATMEL's ATtiny85 chip instead of my Arduino Uno. The ATtiny doesn't support the Arduino Serial library and by extension the standard MIDI library. As I can't make use of simple functions like MIDI.read() I've opted to work with an alternative Software Serial technique I adapted from an <a href="http://www.instructables.com/id/Send-and-Receive-MIDI-with-Arduino/?ALLSTEPS" rel="nofollow noreferrer">Instructable</a>. Unfortunately, the results have been less than exemplary. Simply put, the LED almost never lights up. I have conducted two test only 1 of which <em>somewhat</em> succeeded:</p>

<p>1)</p>

<pre><code>if(velocityByte &gt; 0 ){digitalWrite(LED, HIGH);}
if(velocityByte == 0 ){digitalWrite(LED, LOW);}
</code></pre>

<p>Initially, I had figured that would be the most successful of my options. My keyboard* is binary about its ouput velocity. There are no pressure sensors so "on" is on and "off" is off. Unfortunately, however, the code seems to think otherwise.</p>

<p>2)</p>

<pre><code>if(commandByte == noteOn ){digitalWrite(LED, HIGH);}
if(commandByte != noteOff ){digitalWrite(LED, LOW);}
</code></pre>

<p>Though originally less sure of this one, I've found it to be better than the other at catching notes. noteOn is the integer 144. While it does in fact trigger it does so only very rarely - once every 10 presses or so.</p>

<p>I believe I have however, whittled down the possible number of problems to 2:</p>

<p>1) Something is up with one or more of my calls to Serial. I've never really felt super confident with the author's method of obtaining data from Serial. For one, I have no confidence that I can insure I have collected the correct byte. If the code were to somehow "miss" counting a byte would it not assign, say, a commandByte to a noteByte? Is a better method available?</p>

<p>2) My keyboard's MIDI protocol is incompatible with my if-statement cases.
At first I was under the assumption that my keyboard never sends a true "note off" message of any kind but instead a velocity = 0 message. But my test casts much doubt on this being the case and because of this, I don't <em>really</em> know how the keyboard handles note off messages. Does anyone else know?</p>

<p>*A Yamaha YPT-230 </p>

<pre><code>//MIDI INPUT LED TEST
//Adapted from code written
//by Instructables user Amanda Ghassaei

#include &lt;SoftwareSerial.h&gt;

SoftwareSerial mySerial(0, 1);  //RX, TX

#define LED 2

byte commandByte;
byte noteByte;
byte velocityByte;

byte noteOn = 144;
byte noteOff = 128;

void setup(){
  mySerial.begin(31250);
  digitalWrite(LED, LOW); 
  delay(1000);
}

void checkMIDI(){
  do{
    commandByte = mySerial.read();//read first byte
    noteByte = mySerial.read();//read next byte
    velocityByte = mySerial.read();//read final byte

    if (commandByte == noteOn){
      digitalWrite(LED, HIGH);
    }
    if(commandByte != noteOn){
      digitalWrite(LED, LOW);
    }  

  }
  while (mySerial.available() &gt; 2);//when at least three bytes available
}


void loop(){
  checkMIDI();
}
/*
void gotNoteOn(commandByte, noteByte, velocityByte){
  digitalWrite(LED,HIGH);
}

void gotNoteOff(commandByte, noteByte, velocityByte){
  digitalWrite(LED,LOW);
}
*/
</code></pre>

<p><strong>UPDATE</strong> Following a suggesting, I have replaced the do .. while loop with an ordinary while loop. Relevant changes below:</p>

<pre><code>void checkMIDI(){
  while(mySerial.available() &gt; 2){
    commandByte = mySerial.read();
    if (velocityByte &gt; 0x00){//if note on message
      digitalWrite(LED, HIGH);
    }
    if(velocityByte == 0x00){
      digitalWrite(LED, LOW);
    }  
  }
}  
</code></pre>

<p>I'd like to think it's an improvement but if so only a slight one. Curiously, it almost works backwards - The default state of the LED is on while pressing a key produces no change pressing and <em>holding</em> causes the light to briefly shut off. But even this is sporadic. It may also be worth noting that I elected to use hex '0x00' as opposed to decimal '0'.</p>

<p><strong>UPDATE II / NOTE</strong> Having slept on the problem I think I now understand why I am encountering this issue in the first place. The author of the Instructables code I adapted used an Arduino for both MIDI In as well as MIDI Out. Had they simply hooked one Arduino Uno to the other in an in/out configuration they could have perfectly ensured there were no running status issues by just never sending any running status messages. This would explain the lack of conformity to the actual realities of MIDI on the input side. If you can control exactly what data you are sending on one end (MIDI Out) it becomes fairly trivial to receive that data on the other end (MIDI In). Generalizing, the author may have assumed that a true keyboard behave the same thing and failed to actually verify this. Thoughts?</p>

<p><strong>UPDATE III</strong> Following the lead of CL but deviating somewhat because I didn't fully understand what CL was doing, I have written up a highly derivative, new code. It is based around what I now know to be the highly fluid nature of MIDI messages and seeks to intelligently discriminate important messages from non-important messages based on content. It does not work (except for LED test at the beginning) but seems like a promising step nonetheless.</p>

<pre><code>//MIDI Input LED Test

#include &lt;SoftwareSerial.h&gt;

SoftwareSerial mySerial(0, 1);  //RX, TX

#define LED 2  //Test LED

void setup(){
  mySerial.begin(31250);
  digitalWrite(LED, HIGH); //Test
  delay(1000);
  digitalWrite(LED, LOW); 
  delay(1000);
}

void loop(){
  checkMIDI();
} 

void checkMIDI(){
/*Two of the three independent types of bytes are always
distinct and can be identified simply by their values.
A Status byte (on channel 1 at least) is always either
0x80 (off) or 0x90 (on). No other type of message
(Note, Velocity) can ever take this value and so if
this is the value of the byte received I can be
assured it is a Status byte.

The Note and Velocity bytes are a bit (haha) dicier.
They have overlapping ranges with both taking values of
anywhere between 0x00 to 0x7F. *Ordinarily*. A caveat
is that my Yamaha YPT-230 is binary as far as velocity
goes. A YPT-230 data is either 0x00 (full off) or 0x7F
(full on). As long as a Note byte is never 0 or 127 I can
use this to discriminate between these two types
in this manner.
*/   

  if(mySerial.available() &gt; 0){
    byte b = mySerial.read();

    if(b == 0x80 || 0x90){
      byte commandByte = b;
    }else if(b == 0x00 || 0x7F){
      if(b == 0x00){
        digitalWrite(LED, LOW);  //If vel = 0 turn LED off
      }else if(b == 0x7F){
        digitalWrite(LED, HIGH); //If vel = 127 turn LED on
      }
      //velocityByte = b;
    }else{
      byte noteByte = b;  //Do nothing with this for right now.
    }
  }
}
</code></pre>
 6802: <p>This is not how the <a href="http://www.blitter.com/~russtopia/MIDI/~jglatt/tech/midispec.htm" rel="nofollow">MIDI protocol</a> works.</p>

<p>The incoming stream can contain other commands than note-on or note-off, and with running status, it is impossible to predict the length of any single command.</p>

<p>You need at least some kind of state machine to remember where in the command you are:</p>

<pre class="lang-c prettyprint-override"><code>byte nr;

for (;;) {
    byte b = mySerial.read();
    if (b &amp; 0x80) { // status byte
        if (b &gt;= 0xf8) {
            // real-time command; ignore
        } else {
            commandByte = b;
            nr = 1;
        }
    } else { // data byte
        if ((commandByte &amp; 0xf0) == 0x80 ||
            (commandByte &amp; 0xf0) == 0x90) {
            if (nr == 1) {
                noteByte = b;
                nr = 2;
            } else if (nr == 2) {
                velocityByte = b;
                nr = 1;

                if ((commandByte &amp; 0xf0) == 0x90 &amp;&amp;
                    velocityByte &gt; 0) {
                    // note on
                } else {
                    // note off
                }
            } 
        }
    }
}
</code></pre>
 10794: 

<p>I suggest you first find what your Yamaha is sending you. I have a <a href="http://www.gammon.com.au/forum/?id=12746" rel="nofollow noreferrer">sketch that decodes MIDI</a> for the Uno (assuming you have a Uno lying around). Run that, and see if you got note on with velocity zero, or note off. Then you are a little closer to solving the problem.</p>

<hr>

<p>This part is quite wrong:</p>

<pre class="lang-C++ prettyprint-override"><code>  do{
    commandByte = mySerial.read();//read first byte
    noteByte = mySerial.read();//read next byte
    velocityByte = mySerial.read();//read final byte

  ...

  }
  while (mySerial.available() &gt; 2);//when at least three bytes available
</code></pre>

<p>You are reading 3 bytes, even if none are available. At least change it to:</p>

<pre class="lang-C++ prettyprint-override"><code>while (mySerial.available() &gt; 2)
  {
        commandByte = mySerial.read();//read first byte
        noteByte = mySerial.read();//read next byte
        velocityByte = mySerial.read();//read final byte

      ...

  }
</code></pre>

<p>The <code>do ... while</code> construct tests at the end of the loop. You must ensure you have 3 bytes "available" <strong>before</strong> reading 3 of them, not after.</p>

<hr>

<h2>Example code for ATtiny85</h2>

<p>The code below is an adaptation of the MIDI decoder in my link above. This one just reads the incoming MIDI and turns on the LED on a "note on" and turns it off on a "note off" (or note on with velocity zero). Tested, works perfectly.</p>

<pre class="lang-C++ prettyprint-override"><code>//  MIDI_decoder for ATtiny85
//  
// ATMEL ATTINY 25/45/85 / ARDUINO
// Pin 1 is /RESET
//
//                  +-\/-+
// Ain0 (D 5) PB5  1|    |8  Vcc
// Ain3 (D 3) PB3  2|    |7  PB2 (D 2) Ain1 
// Ain2 (D 4) PB4  3|    |6  PB1 (D 1) pwm1
//            GND  4|    |5  PB0 (D 0) pwm0
//                  +----+


#include &lt;SoftwareSerial.h&gt;

const byte LED = 0;   // pin 5 on ATtiny85

// Plug MIDI into pin D2 (MIDI in serial) (pin 7 on ATtiny85)

SoftwareSerial midi (2, 3);  // Rx, Tx

const int noRunningStatus = -1;

int runningStatus;
unsigned long lastRead;
byte lastCommand;

void setup() {
  //  Set MIDI baud rate:
  midi.begin(31250);
  runningStatus = noRunningStatus;
  pinMode (LED, OUTPUT);
} // end of setup

void RealTimeMessage (const byte msg)
  {
    // ignore realtime messages
  } // end of RealTimeMessage

// get next byte from serial (blocks)
int getNext ()
  {

  if (runningStatus != noRunningStatus)
    {
    int c = runningStatus;
    // finished with look ahead
    runningStatus = noRunningStatus;
    return c;
    }

  while (true)
    {
    while (midi.available () == 0)
      {}
    byte c = midi.read ();
    if (c &gt;= 0xF8)  // RealTime messages
      RealTimeMessage (c);
    else
      return c;
    }
  } // end of getNext

const char * notes [] = { "C ", "C#", "D ", "D#", "E ", "F ", "F#", "G ", "G#", "A ", "A#", "B " };

byte note;
byte octave;
byte velocity;

// interpret a note in terms of note name and octave
void getNote ()
  {
  note = getNext ();
  octave = note / 12;
  note %= 12;
  }  // end of getNote

void getVelocity ()
  {
  velocity = getNext ();
  }

// show a control message 
void showControlMessage ()
  {
   byte message =  getNext () &amp; 0x7F;
   byte param = getNext ();
  }  // end of showControlMessage

// read a system exclusive message 
void showSystemExclusive ()
  {
  int count = 0;
  while (true)
    {
    while (midi.available () == 0)
      {}
    byte c = midi.read ();
    if (c &gt;= 0x80)
      {
      runningStatus = c;
      return;  
      }

    } // end of reading until all system exclusive done
  }  // end of showSystemExclusive

void loop() 
{
  byte c = getNext ();
  unsigned int parameter;

  if (((c &amp; 0x80) == 0) &amp;&amp; (lastCommand &amp; 0x80))
    {
    runningStatus = c;
    c = lastCommand; 
    }

  // channel is in low order bits
  int channel = (c &amp; 0x0F) + 1;

  // messages start with high-order bit set
  if (c &amp; 0x80)
    {
    lastCommand = c;
    switch ((c &gt;&gt; 4) &amp; 0x07)
      {
      case 0:   // Note off
        getNote ();
        getVelocity ();
        digitalWrite (LED, LOW);
        break;

      case 1:   // Note on
        getNote ();
        getVelocity ();
        if (velocity == 0)
          digitalWrite (LED, LOW);
        else
          digitalWrite (LED, HIGH);
        break;

      case 2:  // Polyphonic pressure
        getNote ();
        parameter = getNext ();  // pressure
        break;

      case 3: // Control change
        showControlMessage ();
        break;

      case 4:  // Program change
        parameter = getNext ();  // program
        break;

      case 5: // After-touch pressure
        parameter = getNext (); // value
        break;

      case 6: // Pitch wheel change 
        parameter = getNext () |  getNext () &lt;&lt; 7; 
        break;

      case 7:  // system message
        {
        lastCommand = 0;           // these won't repeat I don't think
        switch (c &amp; 0x0F)
          {
          case 0: // Exclusive
            parameter = getNext (); // vendor ID
            showSystemExclusive ();
            break;

          case 1: // Time code
            parameter = getNext () ;
            break;

          case 2:  // Song position 
            parameter = getNext () |  getNext () &lt;&lt; 7; 
            break;

          case 3: // Song select
            parameter = getNext () ;  // song
            break;

          case 4:    // reserved
          case 5:    // reserved
          case 6:    // tune request
          case 7:    // end of exclusive
          case 8:    // timing clock
          case 9:    // reserved
          case 10:   // start
          case 11:   // continue
          case 12:   // stop
          case 13:   // reserved
          case 14:   // active sensing
          case 15:   // reset
             break;

          }  // end of switch on system message  

        }  // end system message
        break;
      }  // end of switch
    }  // end of if
  else
    {
    // unexpected, ignore 
    }

}  // end of loop
</code></pre>

<hr>

<h2>Suggested schematic</h2>

<p><a href="https://i.stack.imgur.com/WZxOG.png" rel="nofollow noreferrer"><img src="https://i.stack.imgur.com/WZxOG.png" alt="MIDI schematic"></a></p>

<hr>

<p>From a comment:</p>

<blockquote>
  <p>the 4N35 is too slow to work reliably with MIDI signals</p>
</blockquote>

<p>Below is an updated schematic that uses a faster opto-coupler.</p>

<p><a href="https://i.stack.imgur.com/TNCq2.png" rel="nofollow noreferrer"><img src="https://i.stack.imgur.com/TNCq2.png" alt="MIDI schematic with 6N137"></a></p>

<p>Admittedly the 4N35 is below spec for its rise time (it is supposed to be  less than 2 μs rise time, however the 4N35 is slower at between 3 µs and 7 µs). However during my testing it worked OK. It sounds like the faster rise time is specified to allow for multiple devices to be connected in series, a slow rise time will introduce distortion.</p>
