Arduino Playing Glockenspiel

I have a Glockenspiel I have been driving with hardware and thought it would be fun to play with an Arduino. Here’s what I did. MIDI data comes from Power Tracks Pro Audio (or a keyboard) into a opto circuit. Output from the opto goes to a Software Serial port on the Arduino. This allows using the serial port for the Serial Monitor to debug this mess. I only look for MIDI commands to turn notes on or off and just ignore the rest. If I get a MIDI on, I read the note and the velocity, and convert the velocity to either on or off. There are only 24 notes, so I need three bytes of data sent to two 4x4 Logos Driver Shields. The driver shields powers the door bell magnets that are mounted on top of the bells. I don’t use any librarys other than for the software serial port and the turnaround time to ring a bell is in the order of 2 milliseconds. I’m not a programmer but rather in the spirit of all of this, stole most of the code in the sketch below. A video off the glock is at this link

Enjoy.
Bill

  #include <SoftwareSerial.h>
  SoftwareSerial mySerial(5,6); // RX, TX
  int value;
  byte incomingByte;
  byte note;
  byte notes[24];
  byte notetoplaynow;
  byte velocity;  
  byte highByte;
  byte lowByte;
  byte lastByte;
  unsigned long lasttimenotesent;
  byte channel = 3;     //set the MIDI channel here
    
  void setup() {
    mySerial.begin(31250);
    Serial.begin(38400);  
    pinMode(5,INPUT);   
    pinMode(6,OUTPUT);  //MIDI out not used 
    pinMode(7,OUTPUT);  //Register Latch
    pinMode(8,OUTPUT);  //Register Latch
    pinMode(9,OUTPUT);  //Middle C light
    pinMode(11,OUTPUT); //Register Input
    pinMode(13,OUTPUT);} //Clock Pin for 4x4

   void loop (){  //This only looks for on or off messages and the note and velocity  MIDI 0 files do not work
      incomingByte = mySerial.read(); 
      //If the incomming byte just read was a command byte to turn a note on or off, read the next two bytes.  Ignore everything else.
      //1001nnnn (144=channel) is a turn on command byte.  1000nnnn (128) is a note off command where nnnn is the channel.
      if (incomingByte== (127+16+channel) || incomingByte== (127+channel)){
        note = mySerial.read();
        lasttimenotesent=millis();    
        velocity = mySerial.read();
        if (note>=57 && note<=80)playNote(note, velocity); }  // Only play with useful notes
      else if((millis()-lasttimenotesent)>1000)  SendNotes(0, 0, 0); }//Don't leave notes stuck on  
     
   void playNote(byte note, byte velocity) {
    value=LOW;   //Set the note to either on or off regardless of velocity
    if (velocity >10) value=HIGH;  
    notetoplaynow=note-57;  //convert notetoplaynow to the right output register
    if (value==HIGH) notes[notetoplaynow]=1;  //notes[turn notetoplaynow] on
    else if (value==LOW) notes[notetoplaynow]=0; //turn notes[notetoplaynow] off
    
    byte lowByte = 0;
    byte highByte = 0;
    lastByte=0;
    for ( byte i=0; i < 8; ++i )
     {
     lowByte |= (notes[i] << i );  //shift each note one register and keep oring them
     highByte |= (notes[8+i] << i );
     lastByte |= (notes[16+i] << i );
     }
   SendNotes(highByte, lowByte, lastByte);   //go play the note
   }
   
  void SendNotes(byte highByte, byte lowByte, byte lastByte){
   digitalWrite(7, LOW); // Prepares latch
   digitalWrite(8, HIGH); // Deactivates master reset
   shiftOut(11, 13, MSBFIRST, 0);
   shiftOut(11, 13, MSBFIRST, lastByte);   
   shiftOut(11, 13, MSBFIRST, highByte); // shift data for OUT8-OUT15
   shiftOut(11, 13, MSBFIRST, lowByte);  // shift data for OUT0-OUT7
   digitalWrite(7, HIGH);}