Introduction: Arduino, Sensors, and MIDI
Now that you're up to speed on using Arduino's inputs and outputs, this Instructable will give you everything you need to get started using sensors to trigger MIDI notes from Arduino. This post is the last installment in a series of workshops I led at Women's Audio Mission. The first two classes are Intro to Arduino and Working with Arduino Inputs and Outputs.
Parts List:
(1x) Arduino Uno Amazon or you can pick one up at a local Radioshack
(1x) usb cable Amazon
(1x) breadboard (this one comes with jumper wires) Amazon
(1x) jumper wires Amazon
(1x) 220Ohm resistors Digikey CF14JT220RCT-ND
(1x) led Digikey C503B-RCN-CW0Z0AA1-ND
(1x) 10kOhm resistor Digikey CF14JT10K0CT-ND
(1x) tact button Digikey 450-1650-ND
(1x) tilt switch Adafruit 173
(1x) 10kOhm potentiometer Digikey PDB181-K420K-103B-ND
(1x) light sensitive resistor Digikey PDV-P8103-ND
(1x) 33kOhm resistor Digikey 33KQBK-ND
(1x) 1MOhm resistor Digikey 1.0MQBK-ND
(1x) piezo sensor Sparkfun SEN-10293
Step 1: Serial to MIDI Converter
In this class we'll be using the Ardiuno's USB connection to send Serial messages to you computer, then we'll 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). I chose this software solution because it is easiest and cheapest to setup for an entire class, you could also use a 5 pin MIDI plug and a MIDI cable to plug directly into other MIDI instruments. There are a few things you will need to be aware of with this setup:
Be sure that the baud rate you specify in Serial.begin() in your Arduino sketch is the same number selected under Hairless MIDI >> Preferences >> Baud Rate (I used 9600 so I used the command Serial.begin(9600) in all example Arduino sketches, see the first two images above). If you choose to wire up a 5 pin MIDI plug you have 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. I sent my MIDI to IAC Driver Bus 1, and then setup Garage Band or Ableton to receive MIDI on this same channel. If you do not see any MIDI output options in Hairless MIDI, scroll down to the FAQ and troubleshoot your setup.
You cannot program the Arduino while it is connected to Hairless MIDI, because the two applications are competing for the same port (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 2: MIDI Protocol
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 on which MIDI channel, and the subsequent data byte(s) store the actual data. For example: a command byte might tell a MIDI instrument that it has information about a note, and the following data bytes will describe which note and how loud. A command byte could also tell a MIDI instrument that it going to send information about pitchbend, then the following data bytes would describe how much pitchbend. A command byte and the data bytes following it make up one "MIDI message".
A byte is a data type (other data types we've seen so far are int, boolean, and long). Bytes store positive integers between 0 and 255. MIDI messages are made up of a series of bytes, and they can be decoded based on their value to understand what they mean.
Here is a list of common command bytes in their decimal (base ten) form:
Note Off = 128
Note On = 144
Pitchbend = 224
Command bytes are always greater than 127 and data bytes are always between 0 and 127, in fact, that's how a MIDI instrument can tell the difference between a command byte and a data byte. Here's how we would send a MIDI message to turn on Middle C with high volume:
144, 60, 127
The first number, 144, is the command byte, it tells the MIDI instrument that this MIDI message is a Note On message. The second number, 60, is a data byte. The first data byte in a Note On MIDI message is "note" - this Note On command turns on MIDI note 60 (Middle C, you can find a list of note/MIDI conversions here). The last number is also a data byte, the second data byte in a Note On MIDI message is "velocity", which is used to control the loudness of a note. Since data bytes are between 0 and 127, 127 is the max volume for a note.
Each MIDI note starts with a Note On message and ends with a Note Off message. Some percussive instruments will sound like they've turned off if you hold them for a long time, but the won't actually be off until you send a note off message. It's important to remember to turn a note off before you try to turn it on again to avoid inconsistent results. There are two ways to turn a MIDI note off, this first is using a Note Off command:
128, 60, 0
This command will turn note 60 off, it starts with the command byte for Note off, sets note = 60, and velocity = 0 (velocity is usually not very noticeable for Note Off, whatever number you want to pick is fine). You can also turn a note off by sending a Note On message with velocity = 0:
144, 60, 0
This is a more common approach in MIDI (from my experience) so it's how we'll be dealing with Note Off in this class.
If you're interested in learning more about MIDI protocol, binary, and bits, check out this article and this table.
Step 3: Generating MIDI With Arduino
Upload the following code onto the Arduino, it turns MIDI note 60 (middle C) on, waits for 300ms, then turns it off and waits for another 200ms.
byte noteON = 144;//note on command void setup() { Serial.begin(9600); } void loop() { MIDImessage(noteON, 60, 100);//turn note on delay(300);//hold note for 300ms MIDImessage(noteON, 60, 0);//turn note off (note on with velocity 0) delay(200);//wait 200ms until triggering next note } //send MIDI message void MIDImessage(byte command, byte data1, byte data2) { Serial.write(command); Serial.write(data1); Serial.write(data2); }
In this sketch I created a helper function called MIDImessage that accepts a command the two data bytes and sends them out the Arduino's USB connection using Serial.write(). Serial.write is like Serial.print, but it converts whatever's inside it to binary before sending it out.
Try rewriting the sketch to play a series of notes, cycling through MIDI notes 50-79, turning each note on and then off:
byte noteON = 144;//note on command void setup() { Serial.begin(9600); } void loop() { for (byte note=50;note<80;note++) {//from note 50 (D3) to note 79 (G5) MIDImessage(noteON, note, 100);//turn note on delay(300);//hold note for 300ms MIDImessage(noteON, note, 0);//turn note off (note on with velocity 0) delay(200);//wait 200ms until triggering next note } } //send MIDI message void MIDImessage(byte command, byte data1, byte data2) { Serial.write(command); Serial.write(data1); Serial.write(data2); }
Step 4: Arduino Analog Inputs and MIDI
Let's wire up a potentiometer to analog pin 0 and use the data from it to control the pitch of a MIDI note:
byte noteON = 144;//note on command int potPin = A0; void setup() { Serial.begin(9600); } void loop() { int potVal = analogRead(potPin);//read data from potentiometer //we have to scale the potentiometer data to fit between 0 and 127 (this is the range of MIDI notes) byte note = map(potVal, 0, 1023, 0, 127); MIDImessage(noteON, note, 100);//turn note on delay(300);//hold note for 300ms MIDImessage(noteON, note, 0);//turn note off (note on with velocity 0) delay(200);//wait 200ms until triggering next note } //send MIDI message void MIDImessage(byte command, byte data1, byte data2) { Serial.write(command); Serial.write(data1); Serial.write(data2); }
Now try using the potentiometer to control the velocity of a MIDI note:
byte noteON = 144;//note on command int potPin = A0; void setup() { Serial.begin(9600); } void loop() { int potVal = analogRead(potPin);//read data from potentiometer //we have to scale the potentiometer data to fit between 0 and 127 (this is the range of MIDI notes) byte velocity = map(potVal, 0, 1023, 0, 127); byte note = 60; MIDImessage(noteON, note, velocity);//turn note on delay(300);//hold note for 300ms MIDImessage(noteON, note, 0);//turn note off (note on with velocity 0) delay(200);//wait 200ms until triggering next note } //send MIDI message void MIDImessage(byte command, byte data1, byte data2) { Serial.write(command); Serial.write(data1); Serial.write(data2); }
This will sound different depending on what type of instrument you have your MIDI hooked up to, but generally it should sound louder. The sound may also be sharper, as if you were striking a key hard, or blowing into a wind instrument with a lot of force.
Step 5: Trigger a MIDI Note With a Button
Using the simple button debounce code from the first class (you could also use this debounce code that uses millis() to keep time), wire up a button to digital pin 7 and use it to trigger a MIDI note:
byte noteON = 144;//note on command int buttonPin = 7; boolean currentState = LOW;//stroage for current button state boolean lastState = LOW;//storage for last button state void setup(){ pinMode(buttonPin, INPUT);//this time we will set the pin as INPUT Serial.begin(9600);//initialize Serial connection } void loop(){ currentState = digitalRead(buttonPin); if (currentState == HIGH && lastState == LOW){//if button has just been pressed MIDImessage(noteON, 60, 127);//turn note 60 on with 127 velocity delay(2);//crude form of button debouncing } else if(currentState == LOW && lastState == HIGH){ MIDImessage(noteON, 60, 0);//turn note 60 off delay(2);//crude form of button debouncing } lastState = currentState; } //send MIDI message void MIDImessage(byte command, byte data1, byte data2) { Serial.write(command); Serial.write(data1); Serial.write(data2); }
In this sketch, each time the button is pressed we send a Note On message, and each time the button is released, we send a Note Off message. Now try adding in the potentiometer (connected to A0) to control the pitch of the note:
byte noteON = 144;//note on command byte note;//storage for currently playing note int buttonPin = 7; int potPin = A0; boolean currentState = LOW;//stroage for current button state boolean lastState = LOW;//storage for last button state void setup(){ pinMode(buttonPin, INPUT);//this time we will set the pin as INPUT Serial.begin(9600);//initialize Serial connection } void loop(){ currentState = digitalRead(buttonPin); if (currentState == HIGH && lastState == LOW){//if button has just been pressed int currentPotVal = analogRead(potPin); note = map(currentPotVal, 0, 1023, 0, 127); MIDImessage(noteON, note, 127);//turn note on with 127 velocity delay(2);//crude form of button debouncing } else if(currentState == LOW && lastState == HIGH){ MIDImessage(noteON, note, 0);//turn note off delay(2);//crude form of button debouncing } lastState = currentState; } //send MIDI message void MIDImessage(byte command, byte data1, byte data2) { Serial.write(command); Serial.write(data1); Serial.write(data2); }
Now we can add a few more notes into the mix, this sketch plays a three note phrase on each button press. You can change the base note with the potentiometer:
byte noteON = 144;//note on command byte note;//storage for currently playing note int buttonPin = 7; int potPin = A0; boolean currentState = LOW;//stroage for current button state boolean lastState = LOW;//storage for last button state void setup(){ pinMode(buttonPin, INPUT);//this time we will set the pin as INPUT Serial.begin(9600);//initialize Serial connection } void loop(){ currentState = digitalRead(buttonPin); if (currentState == HIGH && lastState == LOW){//if button has just been pressed int currentPotVal = analogRead(potPin); note = map(currentPotVal, 0, 1023, 0, 127); int noteLength = 200; byte noteVelocity = 127; MIDImessage(noteON, note, noteVelocity);//base note delay(noteLength); MIDImessage(noteON, note, 0);//turn note off MIDImessage(noteON, note+7, noteVelocity);//fifth delay(noteLength); MIDImessage(noteON, note+7, 0);//turn note off MIDImessage(noteON, note+12, noteVelocity);//octave delay(noteLength); MIDImessage(noteON, note+12, 0); } lastState = currentState; } //send MIDI message void MIDImessage(byte command, byte data1, byte data2) { Serial.write(command); Serial.write(data1); Serial.write(data2); }
Finally, we can use a while loop to continue the arpeggio as long as we press the button. While loops are basically loopable if statements. If the argument inside the while loop's parentheses evaluated to true, then the commands inside the while loop are executed. At the end of the while loop, the Arduino checks to see if the argument inside the while loops's parentheses is true again. If it is still true, the while loop executes all the commands in the culy braces again, this continues forever until the argument inside the while loop's parentheses evaluates to false. In the example below, I use the while loop to keep playing the three note phrase for as long as digitalRead(buttonPin) == HIGH.
byte noteON = 144;//note on command byte note;//storage for currently playing note int buttonPin = 7; int potPin = A0; boolean currentState = LOW;//stroage for current button state boolean lastState = LOW;//storage for last button state void setup(){ pinMode(buttonPin, INPUT);//this time we will set the pin as INPUT Serial.begin(9600);//initialize Serial connection } void loop(){ currentState = digitalRead(buttonPin); if (currentState == HIGH && lastState == LOW){//if button has just been pressed int currentPotVal = analogRead(potPin); note = map(currentPotVal, 0, 1023, 0, 127); int noteLength = 200; byte noteVelocity = 127; while(digitalRead(buttonPin) == HIGH){//as long as the button is pressed, repeat the arpeggio MIDImessage(noteON, note, noteVelocity);//base note delay(noteLength); MIDImessage(noteON, note, 0);//turn note off MIDImessage(noteON, note+7, noteVelocity);//fifth delay(noteLength); MIDImessage(noteON, note+7, 0);//turn note off MIDImessage(noteON, note+12, noteVelocity);//octave delay(noteLength); MIDImessage(noteON, note+12, 0); } } lastState = currentState; } //send MIDI message void MIDImessage(byte command, byte data1, byte data2) { Serial.write(command); Serial.write(data1); Serial.write(data2); }
Step 6: Arduino and Tilt Switch
A tilt switch is a mechanical switch that is open when you hold it in one direction and closed when you flip it upsidedown. It has a tiny metal ball on the inside of it that can roll around, when you hold it in a certain orientation, the ball creates an electrical connection between two sides of the switch.
The tilt switch has two leads coming out of it, you can pull the push button out of your circuit and replace it with a tilt switch, orientation of the switch in the circuit (long vs short lead) doesn't matter.
Step 7: Arduino and Light Sensitive Resistors
A light sensitive resistor (LSR) is a type of variable resistor that responds to light. The LSRs I got for this class have a range of 16-33kOhms of resistance, so in total darkness they have a resistance of 33kOhms and in light they have a resistance of 16kOhms. The circuit that measures the LSR requires another regular resistor, the resistor acts as a benchmark to determine how the LSR is changing. Whenever you're measuring variable resistance of a component (flex sensor, pressure sensors, and many others) you want to pair it with a regular resistor that has a resistance that is about equal to the max resistance of your variable resistance component. Since I'm using a 16-33kOhm LSR, I'll use a 33kOhm resistor in my circuit.
The circuit looks like this:
5V -> 33kOhm resistor -> light sensitive resistor -> Ground (see image above)
and the Arduino analog pin (I'm using A0) connects to the junction between the LSR and the resistor. The LSR has no polarity, so orientation of the component in the circuit does not matter.
Run the following code to get a sense of the range of the LSR:
int analogPin = A0;//junction between LSR and resistor attached to pin A0 void setup(){ Serial.begin(9600); } void loop(){ int lsr = analogRead(analogPin); Serial.println(lsr); }
I found that my resistor readings ranged from about 0 in full light to about 900 in darkness. Now map this to note, notice where I threw in the 0-900 range
byte noteON = 144;//note on command int analogPin = A0; void setup() { Serial.begin(9600); } void loop() { int analogVal = analogRead(analogPin);//read data //we have to scale the lsr data to fit between 0 and 127 (this is the range of MIDI notes) byte note = map(analogVal, 0, 900, 0, 127);//use the 0-900 range I measured MIDImessage(noteON, note, 100);//turn note on delay(300);//hold note for 300ms MIDImessage(noteON, note, 0);//turn note off (note on with velocity 0) delay(200);//wait 200ms until triggering next note } //send MIDI message void MIDImessage(byte command, byte data1, byte data2) { Serial.write(command); Serial.write(data1); Serial.write(data2); }
This setup creates notes with higher pitch in darkness and low pitch in light. If I wanted to reverse the relation ship between light and pitch I would just reverse my range in the map function:
byte note = map(analogVal, 900, 0, 0, 127);
Notice how I've reversed my range from 0-900 to 900-0, this makes more light create higher pitched notes and low light create low pitched notes.
This sketch is spanning a lot of notes right now, if I wanted to narrow the scope of notes I can play with the circuit, I can use the following line:
byte note = map(analogVal, 900, 0, 40, 90);
Now the full range of resistor light sensitivity will return a range of notes between 40 and 90.
Step 8: Arduino and Pressure Sensor
I made a few pressure sensors from conductive foam, copper tape, stranded wire, and electrical tape. As the conductive foam is compressed it becomes less resistive, making it a pressure sensitive variable resistor.
Any variable resistor can be wire up according to the schematic from the previous step. I used a multimeter to measure the max resistance of my pressure sensors at about 1MOhm, so I wired up a 1MOhm resistor in series with the pressure sensor (I used the exact same schematic as the light sensitive resistor, replacing the light sensitive resistor with the pressure sensor, and the 33kOhm resistor with the 1MOhm resistor). Then I used the code from the last step to measure the range of readings form the pressure sensors at about 50-500. From there you can run the same code to change the pitch of a note with the pressure sensor, but change the line that maps the sensor measurement to note to account for the range 50-500:
byte note = map(analogVal, 50, 500, 0, 127);
Step 9: Arduino and Flex Sensor
Flex sensors are another type of variable resistor, they can be wired up in exactly the same way as the light sensitive and pressure sensitive resistors. The flex sensors I used in class have a resistance between 10kOhm-20kOhm, so they should be paired with a resistor on approximately the same value. The code from the light sensitive resistor step will work with a flex sensor, but remember to adjust the 0, 900 in the line:
byte note = map(analogVal, 0, 900, 0, 127);
to whatever range you measure for your flex sensor.
Step 10: Arduino and Piezo Sensor
Piezo sensors turn pressure or vibrations into electrical charge which can be measured using one of the Arduino's analog inputs. The circuit requires a 1MOhm resistor wired in parallel to the piezo sensor, with one side connected tog round, and the other connected the an Arduino analog pin (image #1 above). Monitor the sensor using the following code:
int piezo = A0; void setup(){ Serial.begin(9600); } void loop(){ int piezoVal = analogRead(piezo); Serial.println(piezoVal); }
I saw that my piezo sensor was stable about about 0-1 normally, then when I hit it, it jumped up to about 150 (image #2 above). We can use the piezo like a switch by looking for these spikes, in the code below I create a MIDI note each time the piezo pin measures a signal over 50.
int noteOn = 144; int piezo = A0; int threshold = 50;//anything over fifty means we've hit the piezo void setup(){ Serial.begin(9600); } void loop(){ int piezoVal = analogRead(piezo); if (piezoVal>threshold){ MIDImessage(noteOn, 60, 127); delay(300); MIDImessage(noteOn, 60, 0); } } //send MIDI message void MIDImessage(byte command, byte data1, byte data2) { Serial.write(command); Serial.write(data1); Serial.write(data2); }
We can also use the force of the hit to control velocity:
int noteOn = 144; int piezo = A0; int threshold = 50;//anything over fifty means we've hit the piezo void setup(){ Serial.begin(9600); } void loop(){ int piezoVal = analogRead(piezo); if (piezoVal>threshold){ int maxPiezoVal = getMaxVal(piezoVal); byte velocity = map(maxPiezoVal, 0, 1023, 50, 127);//velocity between 50 and 127 based on max val from piezo MIDImessage(noteOn, 60, velocity); delay(500); MIDImessage(noteOn, 60, 0); } } //send MIDI message void MIDImessage(byte command, byte data1, byte data2) { Serial.write(command); Serial.write(data1); Serial.write(data2); } int getMaxVal(int lastVal){ int currentVal = analogRead(piezo); while (currentVal>lastVal){ lastVal = currentVal; currentVal = analogRead(piezo); } return lastVal; }
In the code above, I created a helper function called getMaxVal that uses a while loop to record the max displacement of the piezo, so that it can be used to set the velocity of a MIDI note. This code works by measuring the piezo and checking to see if the measurement is greater than the last recorded measurement, if it is then we hold onto the new measurement, and repeat the process. As soon as the new measurement from the piezo is lower than the last recorded measurement, we know that the piezo has reached its max displacement and it will continue to return lower and lower numbers from analogRead(), so the function returns the max value.
The number returned from getMaxVal() is somewhere between 0 and 1023, so I used a map() function to scale it to a velocity between 50 and 127.
Step 11: Pitchbend MIDI Commands With Arduino
To send a Pitchbend MIDI message, you use the command 224. Like Note On, Pitchbend messages have two data bytes, the first data byte is fine pitchbend and the second is coarse pitchbend. Like note and velocity, coarse and fine pitchbend are number between 0 and 127. As you would imagine, coarse pitchbend gives you course control and fine pitchbend gives you fine control. Fine pitchbend gives you an additional 128 steps of control between each adjacent course step; fine pitchbend is so fine that I don't often find myself needing to use it. Setting coarse pitchbend = 64 and fine pitchbend = 0 will give no pitchbend, anything higher than that will pitch bend your notes up, and anything lower will pitchbend your notes down.
Here's how you send a pitchbend message:
MIDImessage(pitchbendCmd, finePitchbend, coarsePitchbend);
I connected the piezo from the last step to analog pin A1, and wired up a potentiometer to analog pin A0 and ran the following code:
byte noteOn = 144; byte pitchbendCmd = 224; int piezo = A1; int analogPB = A0; int threshold = 50;//anything over fifty means we've hit the piezo void setup(){ Serial.begin(9600); } void loop(){ int piezoVal = analogRead(piezo); if (piezoVal>threshold){ MIDImessage(noteOn, 60, 127); int time = 0; while(time<300){ int analogPBVal = analogRead(analogPB); byte pitchbend = map(analogPBVal, 0, 1023, 0, 127); MIDImessage(pitchbendCmd, 0, pitchbend); delay(1); time++; } MIDImessage(noteOn, 60, 0); } } //send MIDI message void MIDImessage(byte command, byte data1, byte data2) { Serial.write(command); Serial.write(data1); Serial.write(data2); }
In the code above I used the following lines to read data from my potentiometer, scale it to a pitchbend value between 0 and 127:
int analogPBVal = analogRead(analogPB);
byte pitchbend = map(analogPBVal, 0, 1023, 0, 127);
I used this command to send a pitchbend message, I used my pitchbend val to set the coarse pitchbend, fine pitchbend was set to 0.
MIDImessage(pitchbendCmd, 0, pitchbend);
Step 12: Arduino and Proximity (Ping) Sensor
Also called a Ping sensor, proximity sensors use an ultrasonic pulse to measure its distance to an obstacle by echolocation. This sensor is really easy to get up and running, it only requires three connections to the Arduino: ground, 5V, and one connection to an Arduino digital pin (I used pin 7). Arduino has a great piece of sample code on their website that I've repeated below for getting the proximity sensor up and running:
int pingPin = 7; void setup() { Serial.begin(9600); } void loop(){ //trigger ping by pulsing pingPin HIGH for 2 microseconds pinMode(pingPin, OUTPUT); digitalWrite(pingPin, LOW); delayMicroseconds(2); digitalWrite(pingPin, HIGH); delayMicroseconds(5); digitalWrite(pingPin, LOW); //set pingPin to INPUT and count the amount of time it takes to receive a ping back pinMode(pingPin, INPUT); long duration = pulseIn(pingPin, HIGH); // convert the time into a distance long inches = microsecondsToInches(duration); long cm = microsecondsToCentimeters(duration); //print out the results Serial.print(inches); Serial.print("in, "); Serial.print(cm); Serial.print("cm"); Serial.println(); delay(100); } long microsecondsToInches(long microseconds) { // According to Parallax's datasheet for the PING))), there are // 73.746 microseconds per inch (i.e. sound travels at 1130 feet per // second). This gives the distance travelled by the ping, outbound // and return, so we divide by 2 to get the distance of the obstacle. // See: <a href="http://www.parallax.com/dl/docs/prod/acc/28015-PING-v1.3.pdf"> <a href="http://www.parallax.com/dl/docs/prod/acc/28015-PI...</a"> <a href="http://www.parallax.com/dl/docs/prod/acc/28015-PI...</a"> http://www.parallax.com/dl/docs/prod/acc/28015-PI...</a>>> return microseconds / 74 / 2; } long microsecondsToCentimeters(long microseconds) { // The speed of sound is 340 m/s or 29 microseconds per centimeter. // The ping travels out and back, so to find the distance of the // object we take half of the distance travelled. return microseconds / 29 / 2; }
One unusual thing about this code is that the pinMode of pingPin is changed while the Arduino loop() is running. So far we've only used this command in the setup. Since the pin is being used as an output to send the ultrasonic pulse, and then as an input to listen for the pulse, it's mode has to change dynamically. Also delayMicroseconds() is used to set a delay for a given number of microseconds (as opposed to delay(), which uses an argument in milliseconds to create a delay).
From here it's a simple exercise of using the map() function to mad distance to whatever type of MIDI data you're interested in, just like in the previous steps. Here are some examples of how you can use a proximity sensor:
Proximity controlled chiptunes-style buzzer:
Obstacle avoiding robot - I like the way they've attached a motor to the ping sensor to make it a little more effective:
Step 13: Arduino and Touch Screen
If you're interested in touch interfaces, there's a relatively cheap ($8 + $4 connector) resistive touch screen or (the more expensive) trackpad from Adafruit that measures x and y position and pressure so you can make projects like this:
The touch screen connect to the Arduino with four pins labelled X+, X-, Y+, and Y-; the pin connections are given in the code below. I used Adafruit's touch screen library to interface with the screen (here's how to add a library to Arduino). Here's a simple sketch to get started:
#include <stdint.h> #include "TouchScreen.h" int YPlus = A0; // must be an analog pin int XMinus = A1; // must be an analog pin int YMinus = 8; // can be a digital pin int XPlus = 9; // can be a digital pin // For better pressure precision, we need to know the resistance // between X+ and X-, Use any multimeter to read it // For the one I'm using, it's 654 ohms TouchScreen ts = TouchScreen(XPlus, YPlus, XMinus, YMinus, 654); void setup(void) { Serial.begin(9600); } void loop(void) { // a point object holds x y and z coordinates Point p = ts.getPoint();//get data from screen if (p.z > ts.pressureThreshhold) {//if pressure is above a certain threshold Serial.print("X = "); Serial.print(p.x); Serial.print(" Y = "); Serial.print(p.y); Serial.print(" Pressure = "); Serial.println(p.z); } delay(100); }
Here's a really simple MIDI implementation of the Kaossilator, mapping x position to note and y position to velocity:
#include <stdint.h> #include "TouchScreen.h" byte noteON = 144;//note on command int YPlus = A0; // must be an analog pin, use "An" notation! int XMinus = A1; // must be an analog pin, use "An" notation! int YMinus = 8; // can be a digital pin int XPlus = 9; // can be a digital pin // For better pressure precision, we need to know the resistance // between X+ and X- Use any multimeter to read it // For the one we're using, its 654 ohms across the X plate TouchScreen ts = TouchScreen(XPlus, YPlus, XMinus, YMinus, 654); void setup(void) { Serial.begin(9600); } void loop(void) { // a point object holds x y and z coordinates Point p = ts.getPoint(); // we have some minimum pressure we consider 'valid' // pressure of 0 means no pressing! if (p.z > ts.pressureThreshhold) { byte note = map(p.x, 0, 1023, 0, 127); byte velocity = map(p.y, 0, 1023, 0, 127); MIDImessage(noteON, note, velocity); delay(100); MIDImessage(noteON, note, 0);//turn note off } } //send MIDI message void MIDImessage(byte command, byte data1, byte data2) { Serial.write(command); Serial.write(data1); Serial.write(data2); }
And here's a video:
Step 14: Sensors You Can Build Yourself
When you start to build projects with lots of inputs, the cost of some sensors can get in the way. Here's some ideas for making your own sensors from cheap materials like pencil lead, copper tape, conductive fabric or thread, conductive paint, and conductive foam (you get this for free when you order chips like the 595 from the last instructable, it's part of the packaging).
Graphite Capacitive Touch Sensor:
"Touche" Multitouch Capacitive Sensor:
Force Sensitive Resistor from Conductive Foam:
Bend Sensor from conductive fabric:
Bend Sensors with tape and conductive thread:
Step 15: Other Sensors for Arduino and Project Ideas
There are tons of other sensors out there to try out, Adafruit and Sparkfun are good places to look, and they have lots of example code for hooking up sensors to Arduino. You can find lots of Arduino project ideas on google, youtube, the Arduino website and blog, and you can find full step by step documentation for any of the Arduino projects here on Instructables.
Here are some example projects that use sensors I didn't have time to cover in this class:
Temperature Sensor (used in Arduino Sous Vide Cooker):
Gas/Methane Sensor, this is the Twittering office chair:
Step 16: Arduino MIDI IN
Finally, you can send MIDI into the Arduino and use it to control things connected to the Arduino's outputs. Here's a robotic drum kit, made by my coworker, randofo.
Also a hacked guitar hero that shoots flames:
Here's a simple example of how you can parse an incoming MIDI message with Arduino and use the information to control the birghtness of an LED. In my example, I'll turn on an LED when note 60 (middle c) is triggered, and I'll scale the brightness of the LED according to the velocity of the incoming note:
byte noteOn = 144; int ledPin = 9; byte noteToCheck = 60; //light up led at pin 13 when receiving noteON message with note = 60 void setup(){ Serial.begin(9600); pinMode(ledPin,OUTPUT); } void loop(){ checkForNote(); delay(10); } void checkForNote(){ while (Serial.available()){//while there is serial data availble byte incomingByte = Serial.read();//read first byte if (incomingByte>127){//if a command byte if (incomingByte == noteOn){//if note on message byte noteByte = Serial.read();//read next byte byte velocityByte = Serial.read();//read final byte if (noteByte == noteToCheck && velocityByte > 0){//note on int brightness = map(velocityByte, 0, 127, 0, 255);//map velocity to a number between 0 and 255 analogWrite(ledPin,brightness);//turn on led w brightness relative to velocity } if (noteByte == noteToCheck && velocityByte == 0){//note off digitalWrite(ledPin,LOW);//turn off led } } } } }
There are a few new commands used here:
Serial.available() returns the number of bytes available to read from the Arduino's serial port.
Serial.read() is the opposite of Serial.print(), it returns the next available byte of data from the Arduino's serial port.
In this code, the Arduino reads first available byte and checks to see if it is a command byte (remember, command bytes are always > 127)
if (incomingByte>127)
Then, since we're only looking for Note On commands, the Arduino checks to see if the command is a Note On command:
if (incomingByte == noteOn)
Then the Arduino reads in the next two bytes and stores them as noteByte and velocityByte:
byte noteByte = Serial.read();
byte velocityByte = Serial.read();
Then the Arduino checks to see if the note we have is the note we're interested in, and if the velocity is greater than 0 (note on or note off). If it is Note On, the Arduino uses map() to scale the velocity (0-127) to a brightness between 0 and 255, and sends this brightness to analogWrite(). If it's a Note Off message, the Arduino turns the LED off.
The one sneaky thing I added to this code is the delay(10) in the loop(). I found that the Arduino was able to read in the MIDI messages more reliably with this delay in there. You might also mess around with the baud rate to ensure that you never miss a MIDI message.