Further Reading

Expanding your Arduino 

You've finished your Piano and now what next? You have two more Multiplexers and lots of material at your disposal to create more instruments. In this section we're going to break down how you can take the code you already have and repurpose it for an additional instrument. This code will be very similar to the final software code, but we're going to be adding a example instrument that is two keys. You can use all three multiplexers on the shield at one time, so three instruments can be played together.

The Shield 

At the end of the Piano tutorial you should have one Multiplexer soldered on. To extend the functionality of your MIDI controller we will have to add on another (or both remaining) Multiplexer(s). If by chance you hadn't soldered on the short

In the same way we soldered in the first multiplexer chip, we will solder in the remaining. Orient the multiplexer so the "U" shaped indent is closest to the silk screen label MUX. Press the chip in. Turn the shield over and solder a single pin to hold the chip. Double check that the chip is flush with the shield, then solder the remaining pins. Repeat this with the last multiplexer if needed.

If you hadn't soldered in the headers with the short leads, you will need them for using the new multiplexers. Take one of the headers and place it in the shield leads down. Flip the shield over, allowing the other headers to hold it up. You should be able to easily adjust the new header from this position, making sure it's flush with the shield and straight. Solder one pin down, and flip the shield over to check that it's correct before soldering the remaining pins. Repeat this for every multiplexer you have in your shield.

The Instrument 

The instrument is made up of three parts, the ground plane, the velostat, and the key styled switches that aren't more than 8 per multiplexer. These are your basic requirements for making a new instrument.

Instrument DiagramInstrument Diagram

You can use anything for the base of these pieces, but for this walk through a simple two key instrument will be the example. It will be using the wood cut case template piece as the ground plane's base, and the two small square wood pieces as the keys.

Ground Plane

In the same way you made your ground plane for the Piano, take your base and cut some copper tape 1 1/2 times the length of your base. The copper tape will need to be where the keys hit, so if you are mounting your key pieces in some way, make sure that's where you measure the tape. Apply the copper tape to the base, folding and turning the tape at the end into a 90 degree angle for where you will solder your wire down. The tape needs to be one solid piece for a stable connection, as tempting as two pieces could be. This is due to the adhesive on the copper tape being less conductive than the tape itself.

Velostat

Cut your velostat at the appropriate length and width for the base so it will cover the copper tape on your base piece. Lay it evenly over the tape, and secure it with two pieces of electrical tape.

Keys

Take your keys and cut some copper tape slightly longer than the length of the keys. When applying the tape, be aware of the contact point on the key and how much room you're giving yourself to solder on the wires.

Soldering and Wiring

You will need one wire for your ground plane, and one for each key you've made. The length of the wires is based on your preferences, but since this instrument will be attached to the piano it's helpful to have some wiggle room for their placement. Strip both ends of the wire, one side a little more for soldering and the other for placing into the header. Take the piece you're soldering, the soldering iron, and the solder. We want to create the same blob of solder on the tape as we did before for the Piano, and then remelt it for the wire. Make sure you have enough room on your pieces for soldering, if you're using the wood cut pieces you won't have much.

With all your connections soldered now comes the wiring. There are three ground pinouts on your shield, all highlighted with white silkscreen. Two are on side with the switch, and the last one is on the side with the LED. Plug in your ground plane. The wiring for the keys goes into the pinouts next to the multiplexers. But it is dependant on which multiplexer you are using, so make sure you wire to the pinout for the multiplexer you are using. (MUXP1 > MUX1 & MUXP2 > MUX2) Starting from the 0 key from the pinout (see the silkscreen next to MUXP0 for the key labels), attached your instrument keys to the pinouts. You don't need to use all eight pinouts, and this will be addressed in the code later.

Make sure your instrument is set up for easy calibration later on, which will be the same process as the piano keys. The calibration process run through each instrument in order without delay, so you want your setup to be in easy reach so you don't have to repeat the process.

The Code 

The code we'll be using is based off the final piano code, so you can either open up a new sketch or simply edit the one you already have.

The variables

Download file Copy to clipboard
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
#include <MIDI.h>



/*
 killall ttymidi             End all connections
 ttymidi -s /dev/ttyACM1 &   Set up New one, ACM1 Not always the Case
 aconnect -i                 Print inputs, Arduino is Connected to This ttymidi
 aconnect -o                 Print Outputs, LMMS instrument is the one you want
 aconnect 129:0 128:2        Connect the input/output like This, numbers
                             subject to change
*/

#define LED 13

#define CE0 2 //Chip Enable 0
#define CE1 3
#define CE2 4

#define s0 5  //Select 0
#define s1 6
#define s2 7

The start of the program is the same as the old code, these variables don't need to be changed. As a quick refresher these are the multiplexer chips, select bits, and the LED pins. For a more in depth breakdown head over to the software module.

From here the code starts taking a different turn to make room for multiple instruments. In this example we are going to be calling the two key instrument a Drum, but you can differentiate it in any way you wish.

Download file Copy to clipboard
1
2
3
4
5
6
7
8
//For Piano Chip
#define number_of_keysP 7
//Max is zero so the true max of the reading will be captured
int sensorMaxP[number_of_keysP];
//True Min will be captured
int sensorMinP[number_of_keysP];
int sensorValueP[number_of_keysP];
int midi_NumP[number_of_keysP]= {60,62,64,65,67,69,71};

Here we are separating the different instruments sensor values and MIDI numbers. If you're using instruments with the same number of keys, differentiating number_of_keys isn't necessary.

Download file Copy to clipboard
1
2
3
4
5
6
7
8
//For Drum Chip
#define number_of_keysD 2
//Max is zero so the true max of the reading will be captured
int sensorMaxD[number_of_keysD];
//True Min will be captured
int sensorMinD[number_of_keysD];
int sensorValueD[number_of_keysD];
int midi_NumD[number_of_keysD]= {72,74};

Here's similar variables for our new instrument. The most important part is that the MIDI numbers, which equate to the notes being played on the instrument and their octave. There is a link for the MIDI numbers at the top of the page. Repeat this step if you're adding another instrument, make sure the amount for the keys is the same for your instrument.

commenting: No errors will appear if there isn't a physical key to match the digital key, so if you want to simplify the #define number_of_keys and share it between instruments you can do that. The empty key will come up in MIDI as a uncalibrated key and can give you a full volume noise when you first connect to LMMS.

Download file Copy to clipboard
1
2
3
4
5
6
7
8
9
10
11
12
13
boolean readKeyFlag = false;
boolean isKeyPressed = false;
int ISR_count = 0;
int r0 = 0;
int r1 = 0;
int r2 = 0;

struct MySettings : public midi::DefaultSettings
{
   static const long BaudRate = 115200;
};
MIDI_CREATE_CUSTOM_INSTANCE(HardwareSerial, Serial, MIDI, MySettings);

These are the last variables, which are the same as the old code.

Setup

The setup code is unchanged from the Piano software code. But the most important lines are the digitalWrite . This is where we are turning on the multiplexers. If the multiplexer is on set it to LOW, if it is off set it to HIGH. Adjust this to your setup, but otherwise copy this below the variables if you don't already have it:

Download file Copy to clipboard
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
void setup()
{
 //Remember, this code only
 pinMode(LED, OUTPUT);  //LED

 pinMode(s0, OUTPUT);  //s0
 pinMode(s1, OUTPUT);  //s1
 pinMode(s2, OUTPUT);  //s2

 pinMode(CE0 ,OUTPUT);   //CE0
 pinMode(CE1,OUTPUT);    //CE1
 pinMode(CE2,OUTPUT);    //CE2

 digitalWrite(CE0, LOW);  //ON
 digitalWrite(CE1, LOW); //ON
 digitalWrite(CE2, HIGH); //OFF

 calibrate_sensor();

 // TIMER 2 for interrupt frequency 1000 Hz(1ms):
 cli(); // stop interrupts
 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 1000 Hz increments
 OCR2A = 249; // = 16000000 / (64 * 1000) - 1 (must be <256)
 // turn on CTC mode
 TCCR2B |= (1 << WGM21);
 // Set CS22, CS21 and CS20 bits for 64 prescaler
 TCCR2B |= (1 << CS22) | (0 << CS21) | (0 << CS20);
 // enable timer compare interrupt
 TIMSK2 |= (1 << OCIE2A);
 sei(); // allow interrupts

 MIDI.begin(4);          // Launch MIDI and listen to channel 4
}

Loop MIDI notes

The loop function we originally had is being broken up into some function calls for multiple instruments. This is due to the MIDI note calls we originally make just for the piano.

Download file Copy to clipboard
1
2
3
4
5
6
7
8
9
10
11
void loop()
{
 if(readKeyFlag == true)
 {
   readKeysPiano();
   readKeysDrum();
   readKeyFlag = false;
 }
PianoMIDI();
DrumMIDI();
}

The functions PianoMIDI and DrumMIDI are these new function calls. Originally the code was a for loop, and simply repeating the for loops would cause delays for the new instruments. In this way the loop code simply calls the outside functions without having to wait for a for loop to complete. Here is the code for those functions.

Download file Copy to clipboard
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25

void PianoMIDI(){
  //set to channel 2
  for(int i = 0; i < number_of_keysP; i++)
  {
    if(sensorValueP[i] > 10){
      MIDI.sendNoteOn(midi_NumP[i], sensorValueP[i],2);
    }else{
      MIDI.sendNoteOff(midi_NumP[i], sensorValueP[i],2);
    }
  }
 }

 
void DrumMIDI(){
  //set to channel 3
  for(int i = 0; i < number_of_keysD; i++)
  {
    if(sensorValueD[i] > 10){
      MIDI.sendNoteOn(midi_NumD[i], sensorValueD[i],3);
    }else{
      MIDI.sendNoteOff(midi_NumD[i], sensorValueD[i],3);
    }
  }
 }

Note the main difference between these functions is that in MIDI.sendNoteOn//Off the functions end with different numbers. These are the channels the two instruments are calling to. We need the instruments on different channels so the digital instrument on LMMS gets the correct input. If you are wishing to extend the amount of keys for one instrument, then you would place it all on one channel.

Calibrate

Calibrate has been extended for multiple instruments without breaking it into function calls. The VERY IMPORTANT thing to note is the line sensorValueP[i] = analogRead(An); as this is dependant on the multiplexer the instrument is using. MUX0 outputs to A0, MUX1 outputs to A1, and MUX2 outputs to A2. Make sure this is correct to your instrument.

Download file Copy to clipboard
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
void calibrate_sensor()
{
  unsigned long adjusted_time;

//Piano
 for(int i = 0; i < number_of_keysP; i++)
  {
    sensorMaxP[i]   = 0;
    sensorMinP[i]   = 1023;
    sensorValueP[i] = 0;
    adjusted_time = millis();

    //Mux select
    r0 = bitRead(i,0);
    r1 = bitRead(i,1);
    r2 = bitRead(i,2);

    //Write the current select bits to the select pins
    digitalWrite(s0,r0);
    digitalWrite(s1,r1);
    digitalWrite(s2,r2);

    digitalWrite(LED, HIGH);
   // calibrate each key for five seconds
   while (millis() < adjusted_time + 5000)
   {
    delay(1);
	//Set to MUX0 using A0
     sensorValueP[i] = analogRead(A0);

     // record the maximum sensor value
     if (sensorValueP[i] > sensorMaxP[i])
     {
       sensorMaxP[i] = sensorValueP[i];
     }

     // record the minimum sensor value
     if (sensorValueP[i] < sensorMinP[i])
     {
       sensorMinP[i] = sensorValueP[i];
     }
   }
   digitalWrite(LED,LOW);
   delay(1000);
  }//end Piano

//Drum
   for(int i = 0; i < number_of_keysD; i++)
  {
    sensorMaxD[i]   = 0;
    sensorMinD[i]   = 1023;
    sensorValueD[i] = 0;
    adjusted_time = millis();

    //Mux select
    r0 = bitRead(i,0);
    r1 = bitRead(i,1);
    r2 = bitRead(i,2);

    //Write the current select bits to the select pins
    digitalWrite(s0,r0);
    digitalWrite(s1,r1);
    digitalWrite(s2,r2);

    digitalWrite(LED, HIGH);
   // calibrate each key for five seconds
   while (millis() < adjusted_time + 5000)
   {
    delay(1);
	//Set to MUX1 using A1
     sensorValueD[i] = analogRead(A1);

     // record the maximum sensor value
     if (sensorValueD[i] > sensorMaxD[i])
     {
       sensorMaxD[i] = sensorValueD[i];
     }

     // record the minimum sensor value
     if (sensorValueD[i] < sensorMinD[i])
     {
       sensorMinD[i] = sensorValueD[i];
     }
   }
   digitalWrite(LED,LOW);
   delay(1000);
  }//end Drum

  //repeat as necessary for your other instruments
  
}/*End of calibrate_sensor()*/

Read Keys

The original readKeys() function has been broken up into multiple functions, which are then called in the loop. Both functions are essentially the same minus their variables and the analogRead(An) call. Remember MUX0 outputs to A0, MUX1 outputs to A1, and MUX2 outputs to A2. Make sure this is correct to your instrument. Here is the function for the Piano

Download file Copy to clipboard
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
void readKeysPiano()
{
 //Read Pins Piano
 for(int i = 0; i < number_of_keysP; i++)
 {
     delay(1);
     r0 = bitRead(i,0);
     r1 = bitRead(i,1);
     r2 = bitRead(i,2);

     digitalWrite(s0,r0);
     digitalWrite(s1,r1);
     digitalWrite(s2,r2);

     sensorValueP[i] = analogRead(A0);
     sensorValueP[i] = map(sensorValueP[i], sensorMinP[i], sensorMaxP[i], 127, 0);
     // in case the sensor value is outside the range seen during calibration
     sensorValueP[i] = constrain(sensorValueP[i], 0, 127);

   if(sensorValueP[i] <= 10){
     sensorValueP[i] = 0;
   }
 }//End Piano
}

And here is the code for the new instrument:

Download file Copy to clipboard
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25

void readKeysDrum()
{
 //Read Pins Drum
 for(int i = 0; i < number_of_keysD; i++)
 {
     delay(1);
     r0 = bitRead(i,0);
     r1 = bitRead(i,1);
     r2 = bitRead(i,2);

     digitalWrite(s0,r0);//any race conditions?
     digitalWrite(s1,r1);
     digitalWrite(s2,r2);

     sensorValueD[i] = analogRead(A1);
     sensorValueD[i] = map(sensorValueD[i], sensorMinD[i], sensorMaxD[i], 127, 0);
     // in case the sensor value is outside the range seen during calibration
     sensorValueD[i] = constrain(sensorValueD[i], 0, 127);

   if(sensorValueD[i] <= 10){
     sensorValueD[i] = 0;
   }
 }//End Drum
}

Make as many as you need for the instruments

ISR

The ISR code is the same as the old code:

Download file Copy to clipboard
1
2
3
4
5
6
7
8
9
10

ISR(TIMER2_COMPA_vect){
  //interrupt commands for TIMER 2 here
  if(ISR_count == 2)
  {
    readKeyFlag = true;
    ISR_count = 0;
  }
  ISR_count++;
}

And this finishes up code section. Verify to check for any mistakes and upload to your board. The calibration process will begin starting with the piano. Remember there are no breaks in the calibration as it goes down the line. When calibrating the new instruments the one plugged into Key 0 on the multiplexer pinout goes first, then down the lines

Software 

Hairless and loopMIDI

Now that you've got your code running let's begin our software again. Open up hairless and loopMIDI. We don't need to change anything on loopMIDI from the previous tutorial, as there is only one MIDI controller. On hairless make sure your input and output settings are correct to the port and the MIDI controller name. Make sure the Debug is selected so we can see what messages are being sent. The output we want to see will have multiple channels being listed for the multiple instruments. Hit the Debug button again to pause it. Are all your channel being called? If not go back and make sure you've distinguished them in the PianoMIDI() and DrumMIDI() functions. Are your channels being called but the keys didn't calibrate? You can redo your calibration process by hitting the button on the shield. If this doesn't work though and you keep getting the full volume (127) for your keys, the problem could be cause by a couple things:

  • Your multiplexer isn't turned on: Back in the setup function we have digitalWrite() calls that set the multiplexer chips to either a LOW or HIGH reading. We want our multiplexers to be LOW if we are using them. Check to make sure this is set correctly.
  • Your multiplexer isn't set to the correct Analog output: In calibrate_sensor() and readKeys we call on the Analog output for each instrument. But each multiplexer has a different one. MUX0 outputs to A0, MUX1 outputs to A1, and MUX2 outputs to A2. Make sure this is correct to your instrument.

LMMS

We can now open up LMMS and start playing. Delete any premade tracks on the Song Editor window by selecting the Gear icon and clicking 'Remove this Track'. On the left side tab of the LMMS window select the music note icon to open up the instruments list. Click and drag your instrument into the Song Editor. You'll need to redo this for the piano if you aren't using a old file from the previous tutorial. On the track, click on the instruments name to select a button that opens up the instruments window. In the instruments window, above the picture that says Audio File Processor is a menu bar. Select the MIDI tab. We then want to select the Enable MIDI Input button and change the channel to the correct channel for the instrument. In the example code the Piano is set to channel 2, and the Drum is set to channel 3.

In hairless restart your Serial<->MIDI connection. You may here a burst of notes coming from LMMS. Now you'll be able to play both instruments together. Have fun creating!