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

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:


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.

<p>#include &lt;MIDI.h&gt;<br>#include &lt;Wire.h&gt;<br>#include &lt;Adafruit_MCP23017.h&gt;</p><p><br>int statusLed = 3;</p><p>Adafruit_MCP23017 mcp;</p><p>void setup() {<br> MIDI.begin(); <br> MIDI.setHandleNoteOn(HandleNoteOn); <br> MIDI.setHandleNoteOff(HandleNoteOff); </p><p> mcp.begin(); <br> <br>mcp.pinMode(0, OUTPUT);<br>mcp.pinMode(1, OUTPUT);<br>mcp.pinMode(2, OUTPUT);<br>mcp.pinMode(3, OUTPUT);<br>mcp.pinMode(4, OUTPUT);<br>mcp.pinMode(5, OUTPUT);<br>mcp.pinMode(6, OUTPUT);<br>mcp.pinMode(7, OUTPUT);<br>mcp.pinMode(8, OUTPUT);<br>mcp.pinMode(9, OUTPUT);<br>mcp.pinMode(10, OUTPUT);<br>mcp.pinMode(11, OUTPUT);<br>mcp.pinMode(12, OUTPUT);<br>mcp.pinMode(13, OUTPUT);<br>mcp.pinMode(14, OUTPUT);<br>mcp.pinMode(15, OUTPUT);<br>mcp.pinMode(16, OUTPUT);<br> <br>mcp.digitalWrite(0,LOW);<br>mcp.digitalWrite(1,LOW);<br>mcp.digitalWrite(2,LOW);<br>mcp.digitalWrite(3,LOW);<br>mcp.digitalWrite(4,LOW);<br>mcp.digitalWrite(5,LOW);<br>mcp.digitalWrite(6,LOW);<br>mcp.digitalWrite(7,LOW);<br>mcp.digitalWrite(8,LOW);<br>mcp.digitalWrite(9,LOW);<br>mcp.digitalWrite(10,LOW);<br>mcp.digitalWrite(11,LOW);<br>mcp.digitalWrite(12,LOW);<br>mcp.digitalWrite(13,LOW);<br>mcp.digitalWrite(14,LOW);<br>mcp.digitalWrite(15,LOW);<br>mcp.digitalWrite(16,LOW);</p><p><br>}<br>void loop() {<br> MIDI.read(); // HERE IS THE ERROR </p><p>exit status 1<br>'MIDI' was not declared in this scope</p><p> <br>}<br>void HandleNoteOn(byte channel, byte note, byte velocity){ </p><p>switch (note)<br>{<br> case 72:// C <br> mcp.digitalWrite(0, HIGH);<br> break;<br> case 74:// D <br> mcp.digitalWrite(1, HIGH);<br> break;<br> case 76: // E <br> mcp.digitalWrite(2, HIGH);<br> break;<br> case 77: // F <br> mcp.digitalWrite(3, HIGH);<br> break;<br> case 79: //G <br> mcp.digitalWrite(4, HIGH);<br> break;<br> case 81: // A<br> mcp.digitalWrite(5, HIGH);<br> break;<br> case 83:// B <br> mcp.digitalWrite(6, HIGH);<br> break;<br> case 84:// C+ <br> mcp.digitalWrite(7, HIGH);<br> break;<br> }<br>}<br>void HandleNoteOff(byte channel, byte note, byte velocity){ .</p><div><br>switch (note)<br>{<br> case 72:// C <br> mcp.digitalWrite(0, LOW);<br> break;<br> case 74:// D <br> mcp.digitalWrite(1, LOW);<br> break;<br> case 76: // E <br> mcp.digitalWrite(2, LOW);<br> break;<br> case 77: // F <br> mcp.digitalWrite(3, LOW);<br> break;<br> case 79: //G <br> mcp.digitalWrite(4, LOW);<br> break;<br> case 81: // A<br> mcp.digitalWrite(5, LOW);<br> break;<br> case 83:// B <br> mcp.digitalWrite(6, LOW);<br> break;<br> case 84:// C+ <br> mcp.digitalWrite(7, LOW);<br> break;<br> }<br>}</div>
<p>To save anyone the frustration, the midi baud rate is not compatible with Intel chip in the Arduino 101 &amp; Genuino 101. </p>
<p>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.</p><p>The intention is to make a trigger for edrums.</p>
Yea, You can do all of that with a midi thru port. <br>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.
<p>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?<br>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.<br>Or do you need the piezo to send messages to the edrum as well as the edrum sending it's current pads?</p>
<p>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.</p><p>I would also be interested to know if doing so would increase latency.</p><p>Greetings and thanks</p>
<p>Hello,</p><p>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 &quot;-1&quot;).<br><br></p>
<p>Only difference is the baud rate that i tried to set to 19200, 38400, 57600 instead of 31250.</p>
<p>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 &quot;PuTTY&quot; which allows you to see the data in real time.</p><p>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!</p>
<p>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!!</p>
<p>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.</p><p>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 <a href="http://www.midikits.net/." rel="nofollow">http://www.midikits.net/.</a></p><p>hope it is helpfull.</p><p>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?</p>
<p>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? </p>
<p>How would I be able to bypass Hairless, as in make the arduino a USB MIDI device?</p>
<p>This Code below here from you doesn't work to me.<br>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?</p><p>ISR(TIMER2_COMPA_vect) {//checks for incoming midi every 128us<br> do{<br> if (Serial.available()){<br> commandByte = Serial.read();//read first byte<br> noteByte = Serial.read();//read next byte<br> velocityByte = Serial.read();//read final byte<br> }<br> }<br> while (Serial.available() &gt; 2);//when at least three bytes available<br>}</p>
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)<br />
<p>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 &gt;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.</p>
<p>First of all, many thanks for this awesome tutorial! It really helped me a lot. </p><p>Unfortunately, I'm having the same problem. Removing &gt;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.</p>
<div>when I try to use the MIDI Hairless and try to connect with the port of arduino, the error &quot;this application has Requested the Runtime to terminate it in an unusual way&quot; shows how I can fix this?</div>
<p>Hi, I realize this is an old thread, but I am having the same issue. How did you resolve it?</p>
<p>I had the same problem on Windows 7..</p><p>Make sure your FTDI drivers are up to date and trying reinstalling them.<br>Make sure baud is 9600 on everything (including speaker settings).<br>I also had to go to my computer speaker properties and deselect exclusivity mode.<br>Do not output to microsoft gs wavetable synth as it lags.. I think this was the biggest problem.</p>
<p>Converting decimal numbers to hexadecimal and vv can be done easily with Windows Calculator in &quot;Programmers&quot; mode</p>
<p>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?</p>
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.
<p>Hello<br><br>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</p>
<p>Hi,<br>I have made a harp like you... send me message to talk...</p>
<p>Hello<br><br>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</p>
<p>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</p>
Why do the other tutorials use optocouplers and you don't? Can I use this with a drum machine
I do use an optocoupler to receive MIDI. This will work with anything that sends or receives MIDI.
<p>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?</p>
Or is it computer only *
<p>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! </p>
<p>What software are you using to send the MIDI?</p>
<p>Hi! I use Cubase.</p>
<p>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.</p><p>Thanks!</p>
<p>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?</p><p>Thanks again.</p>
<p>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</p>
<p>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 &quot;delay&quot; in the loop. I'm an arduino beginner. If you please could you say what I did wrong? Thank you! </p><p>byte commandByte;</p><p>byte noteByte;</p><p>byte velocityByte;</p><p>byte noteOn = 10010000;</p><p>void setup(){</p><p> Serial.begin(115200);</p><p> pinMode(12,OUTPUT);</p><p> }</p><p>void checkMIDI(){</p><p> if (Serial.available()){</p><p> commandByte = Serial.read();</p><p> noteByte = Serial.read();</p><p> velocityByte = Serial.read();</p><p> if (commandByte == noteOn){</p><p> if (noteByte == 95 &amp;&amp; velocityByte &gt; 0){</p><p> digitalWrite(12,HIGH);//turn on led</p><p>}</p><p>}</p><p> if (commandByte == noteOn){</p><p> if (noteByte == 93 &amp;&amp; velocityByte &gt; 0){</p><p> digitalWrite(12,LOW);</p><p>} </p><p>}</p><p>}</p><p>} </p><p>void loop()</p><p>{</p><p> checkMIDI();</p><p> delay(1);</p><p>}</p>
<p>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!</p>
<p>Thanks for the great 'ible.</p><p><iframe allowfullscreen="" frameborder="0" height="281" src="//www.youtube.com/embed/wUUEu4H2_tE" width="500"></iframe></p><p>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.</p>
<p>Hi there,</p><p>Have a look at this device you can get for 91 euros : the Midilektor.</p><p><a href="http://www.orgautomatech.com/epages/3b85d6ba-28b0-11e2-b53b-000d609a287c.sf/en_GB/?ObjectPath=/Shops/3b85d6ba-28b0-11e2-b53b-000d609a287c/Products/Midilec" rel="nofollow">http://www.orgautomatech.com/epages/3b85d6ba-28b0-...</a></p><p>I've bought is 5 years ago, and , combined with a midi decoder from the same provider, it works perfectly on a street organ .</p><p>Jean-Paul </p>
<p>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 ...</p>
<p>The &quot;Receive MIDI Messages&quot; 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.</p>
<p>Hi Amanda,</p><p>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</p>
<p>hey, sorry for the late reply. <a target="_blank" href="http://www.midi.org/techspecs/electrispec.phphttps://en.wikipedia.org/wiki/Opto-isolator">Optocouplers</a> 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 <a target="_blank" href="http://www.midi.org/techspecs/electrispec.phphttps://en.wikipedia.org/wiki/Opto-isolator">specs</a>.</p>
The optocoupler provides electrical insulation between equipments. This avoids several problems that plague audio devices, like ground loops and hum pickup.
<p>Hi Amanda!</p><p>I think your phrase: </p><p>&quot;The last half of the command byte sets the MIDI channel. All the bytes <br>listed above would be in channel 0, command bytes ending in 0001 would <br>be for MIDI channel 1, and so on.&quot;</p><p>is not exact, because in 4 bits values go from 0 to 15. So a byte ending 0001 means 2. </p>
<p>Hi your tutorial is very usefull :-)</p><p>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. </p><p>/*<br>By Amanda Ghassaei<br>July 2012<br><a href="https://www.instructables.com/id/Send-and-Receive-MIDI-with-Arduino/" rel="nofollow">https://www.instructables.com/id/Send-and-Receive-M...</a><br><br><br>*/<br><br>byte commandByte;<br>byte noteByte;<br>byte velocityByte;<br><br>byte noteOn = 144;<br><br><br>void setup(){<br> <br> Serial.begin(56000);<br> pinMode(13,OUTPUT);<br> pinMode(12,OUTPUT);<br> pinMode(11,OUTPUT);<br> <br> digitalWrite(13,LOW);<br> digitalWrite(12,LOW);<br> digitalWrite(11,LOW);<br> <br>}<br><br>void directblink13(){<br> digitalWrite(13,HIGH);//turn on led<br> delay(1000);<br> digitalWrite(13,LOW);<br> }<br>void directblink12(){<br> digitalWrite(12,HIGH);//turn on led<br> delay(1000);<br> digitalWrite(12,LOW);<br> }<br>void directblink11(){ <br> digitalWrite(11,HIGH);<br> delay(1000);<br> digitalWrite(11,LOW); <br> }<br><br>void checkMIDI(){<br> do{<br> if (Serial.available()){<br> commandByte = Serial.read();//read first byte<br> noteByte = Serial.read();//read next byte<br> velocityByte = Serial.read();//read final byte<br> if (commandByte == noteOn){//if note on message<br> //check if note == 60 and velocity &gt; 0<br> if (noteByte == 60 &amp;&amp; velocityByte &gt; 0){directblink13;}<br> <br> else if(noteByte == 61 &amp;&amp; velocityByte &gt; 0){directblink12;}<br> <br> else if(noteByte == 62 &amp;&amp; velocityByte &gt; 0){ directblink12;}<br> }<br> <br> <br> <br> }<br> <br> }<br> while (Serial.available() &gt; 2);//when at least three bytes available<br>}<br> <br><br>void loop(){<br> checkMIDI();<br> delay(1);<br><br>}</p><p>Thanks :-)</p>
<p>When you're calling your directblink functions, you're forgetting to add ()</p><p>So a call should be: directblink12();</p>
<p>Hey Amanda. I've written some very simple code that receives MIDI and turns on some LEDs via the pwm. I'm receiving the messages via Hairless so no need for the external MIDI wirings.</p><p>For some reason the program will only work when there is a short delay of minimum 4ms after the checkMIDI() function is called. If there is no delay, the LEDs dont light up. The only problem is, this causes some messages to be missed. Any idea why? </p><p>Below is the code:</p><p>byte commandByte;</p><p>byte secondByte;</p><p>byte thirdByte;</p><p>byte noteOn = 144;</p><p>byte noteOff = 128;</p><p>byte pitchBend = 224;</p><p>int pwmVal;</p><p>void setup()</p><p>{</p><p> Serial.begin(9600);</p><p> pinMode(3,OUTPUT);</p><p>}</p><p>void checkMIDI()</p><p>{</p><p> do</p><p> {</p><p> if (Serial.available())</p><p> {</p><p> commandByte = Serial.read();//read command byte</p><p> secondByte = Serial.read();//read next byte (MSB, or Note)</p><p> thirdByte = Serial.read();//read final byte (LSB or velocity)</p><p> if (commandByte == noteOn)</p><p> {</p><p> if (secondByte &gt; 0 &amp;&amp; thirdByte &gt; 0)</p><p> {</p><p> pwmVal = map(thirdByte,0,127,0,255);</p><p> analogWrite(3,pwmVal);//turn on led with velocity value</p><p> }</p><p> }</p><p> else if(commandByte == noteOff)</p><p> {</p><p> analogWrite(3,0);</p><p> }</p><p> }</p><p> }</p><p> while (Serial.available() &gt; 2);//when at least three bytes available</p><p>}</p><p>void loop()</p><p>{</p><p> checkMIDI();</p><p> delay(4);</p><p>}</p>

About This Instructable




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" IMU Twitter Controlled Pet Feeder 
Add instructable to: