Introduction: EAL - Arduino MIDI Controller
Made by Søren Østergaard Petersen, OEAAM16EDA
This instructable describes an arduino based MIDI controller. This is a school project. By using your hand you can play simple melodies via the MIDI connection and a connected MIDI instrument (or as in this case a notebook running a softsynth software). You can play notes from a C major scale, c-d-e-f-g-a-b-c. To be able to connect the MIDI controller to a notebook, you will need a MIDI to USB interface like m-audio Uno .
Step 1: Demonstration Video
Turn up the volume and enjoy !
How it works:
The MIDI controller uses an Arduino MEGA 2560 board. Two light (LDR) sensors built into 16mm electrical tube form a double sensor system and are used for creating a stable trigger without any false double triggering. A flashlight is creating a light beam, when the beam is interrupted by the hand playing the controller, the lower light sensor senses the missing beam, and a HC-SR04 ultrasonic sensor measures the distance from sensor to hand.
The distance measured is used in the Arduino program for calculating and setting up the appropriate note number value to be packed into a MIDI Note On message and transmittet on the MIDI interface. The MIDI output interface uses a 74HC14 hex inverter and is pretty much a standard circuit. MIDI communication uses serial1, the standard serial port is used for debugging.
When the hand is moved straight up and away from the light beam, the upper light sensor senses the light beam again and a MIDI Note Off message is packed and transmittet on the MIDI output.
The playing area between the sensors is around 63cm, and the total length of the MIDI controller is around 75cm.
Step 2: Details of the Light Sensors
The two light sensors are mounted on top of each other to form a double sensor system. It prevents false triggering when used correct in the software. Each light sensor consists of a photo resistor module built into a 16 mm standard electrical tube. A slot is made in each tube with a hacksaw and the photo resistor PCB can be pressed into the slot. The sensors are taped together with duct tape and also fixed to one end of a piece of wood. No light must be able to reach the sensors from behind.The light sensors has built in 10k pull-up resistors.
Step 3: Details of the HC-SR04 Ultrasonic Sensor
The HC-SR04 ultra sonic sensor is fixed in the other end of the MIDI controller. A bright flashlight is placed here too, it creates the necessary light beam.
Step 4: The Aduino Circuit
The MIDI output circuit is basically a standard 74HC14 hex inverter and a few resistors plus a 5 pin DIN female connector. The 74HC14 circuit drives the MIDI output and at the same time provide some means of protection for the Arduino board against the "real world" connected to MIDI out. An extra practical feature is the MIDI activity LED that signals when data is sent.
I have used a proper prototype PCB for my hardware because I had a lot of problems with bad connections on my breadboard. The schematic is made in Fritzing, a high resolution pdf copy can be downloaded by pressing the link below. I prefer using a proper schematics program like Kicad, I think Fritzing is to limited for anything but the simplest experiments.
Materials used:
1 pcs Arduino MEGA 2560
2 pcs Photo resistor (LDR) with built-in pull up resistor (from 37 sensor kit)
1 pcs HC-SR04 ultrasonic sensor
1 pcs 74HC14 hex inverting Schmitt trigger
2 pcs resistor 220 Ohm 0.25W
1 pcs resistor 1k Ohm 0.25W
1 pcs LED low current 2mA
1 pcs 100nF ceramic capacitor (for power supply decoupling, directly at the 74HC14's power pins)
Breadboard or prototype PCB
2 pcs 16mm electrical tube, length 65mm
1 pcs of wood, length 75cm
Duct tape
Wires
Attachments
Step 5: I/O Listing
Step 6: The Aduino Code
The sketch test_Midi6 uses the NewPing library which you must include in your Arduino programming enviroment, to use the HC-SC04 ultrasonic sensor. The sketch is commented in danish, sorry.. To keep the sketch well-structured, seperate functions are made up for different logic parts of the sketch and global variables are mostly avoided. The programflow is visualized in the MIDI controller flowchart pdf.
// 15-05-2017 version : test_Midi6 // Søren Østergaard Petesen // Arduino MEGA 2560 // Dette program udgør en simpel MIDI controller som kan styre en ekstern MIDI enhed, f.eks en softsynt på en PC. // MIDI controlleren kan sende toneanslag (note on kommando) hhv. (note off kommando) for en oktav C-C, C dur skala. // Der spilles med en "karate hånd" på et brædt // hvor sensorerne er monteret. MIDI kommandoerne trigges af en dobbelt LDR sensor, da der skal laves en sikker // detektering af både når hånden lander på brættet (note on), samt når hånden fjernes igen (note off). // MIDI kommandoerne "note on" og "note off" består hver af 3 bytes som sendes på serial1 porten // vha det i hardware opbyggede MIDI interface. // Tonehøjden bestemmes vha ultralydssensor HC-SR04 #include <NewPing.h> // bibliotek til den anvendte ultralydssensor HC-SR04 #define TRIGGER_PIN 3 // Arduino pin til trigger pin på ultrasonic sensor #define ECHO_PIN 2 // Arduino pin til echo pin på ultrasonic sensor #define MAX_DISTANCE 100 // Maximum afstand for Ping #define Median 5 // Antal målinger der beregnes gennemsnit af for at få en sikker afstandsbestemmelse NewPing sonar(TRIGGER_PIN, ECHO_PIN, MAX_DISTANCE); // Creating the NewPing Object. int Senspin1 = 53; //Underste LDR1 føler int Senspin2 = 52; //Øverste LDR2 føler byte MIDIByte2; //Variabel deklaration for MIDIByte2 bool klar_note_on = 1; //Variabel deklaration for klar_note_on, styrer afsendelse af note on kommando.Første kommando er en note on kommando bool klar_note_off = 0; //Variabel deklaration for klar_note_off, styrer afsendelse af note off kommando void setup() { pinMode(Senspin1, INPUT); // sæt sensor input pinMode(Senspin2, INPUT); // sæt sensor input Serial1.begin(31250); // Serial1 bruges til MIDI kommunikation: 31250 bit/sekundt Serial.begin(9600); //serial monitor, til test } void loop() { bool Sensor1 = digitalRead(Senspin1); //Læs LDR1 - underte LDR bool Sensor2 = digitalRead(Senspin2); //læs LDR2 - øverste LDR if (Sensor1 && klar_note_on) //hvis LDR1 aktiveret og klar til note on { byte Note_Byte = Hent_tonehojde(); //Hent tone højde via ultralyds sensor MIDIByte2 = Hent_MidiByte2(Note_Byte); // Hent MidByte2, MIDI note number, værdien 0xFF er out of range Send_Note_On(MIDIByte2); // kald Send_Note_On funktion klar_note_on = 0; // der skal kun sendes en note on kommando klar_note_off = 1; // næste kommando er note off } if (Sensor2 && !Sensor1 && klar_note_off) // Hvis der skal sendes note off kommando gøres det her.. { Send_Note_Off(MIDIByte2); // send note off kommando klar_note_off = 0; // der skal kun sendes en note off kommando } if (!Sensor1 && !Sensor2 ) // her gøres klar til ny note on kommando, hånd er væk fra brædt { klar_note_on = 1; } } byte Hent_MidiByte2(byte NoteByte) { // Denne funktion returnerer MIDI note number, valgt ud fra NoteByte byte MIDIB2; switch (NoteByte) // her defineres hvilken værdi MIDIByte2 skal have ud fra værdien af Note_Byte { case 0: { MIDIB2 = 0x3C; // tonen 'C' } break; case 1: { MIDIB2 = 0x3E; // tonen 'D' } break; case 2: { MIDIB2 = 0x40; // tonen 'E' } break; case 3: { MIDIB2 = 0x41; // tonen 'F' } break; case 4: { MIDIB2 = 0x43; // tonen 'G' } break; case 5: { MIDIB2 = 0x45; // tonen 'A' } break; case 6: { MIDIB2 = 0x47; // tonen 'B' } break; case 7: { MIDIB2 = 0x48; // tonen 'C' } break; default: { MIDIB2 = 0xFF; // out of range } } return MIDIB2; //returner MIDI note number } byte Hent_tonehojde() { //Denne funktion henter resultatet af ultralydsmålingen unsigned int Tid_uS; // målt tid i uS byte Afstand; // beregnet afstand i cm byte resultat; // inddeling af spille område const float Omregningsfaktor = 58.3; // 2*(1/343 m/s)/100 = 58,3uS/cm, der ganges med 2 da tiden er summen af tiden frem og tilbage. Tid_uS = sonar.ping_median(Median); // Send ping, få tid retur i uS, gennemsint af Median målinger Afstand = Tid_uS / Omregningsfaktor; // Omregn tid til afstand i cm (0 = outside distance range) resultat = Afstand / 8; //Beregn resultat return resultat; //Returner resultat } void Send_Note_On(byte tonenr) { //Denne funktion sender en note on kommando på MIDI interfacet const byte kommando = 0x90; //Note on kommando på MIDI kanal 1 const byte volumen = 0xFF; // volumen / Velocity = 127 Serial1.write(kommando); //send note on kommando Serial1.write(tonenr); //send tone nummer Serial1.write(volumen); //send volumen (velocity) } void Send_Note_Off(byte tonenr) {//Denne funktion sender note off kommando på MIDI interfacet const byte kommando = 0x80; //Note off kommando på MIDI kanal 1 const byte volumen = 0xFF; // volumen / Velocity = 127 Serial1.write(kommando); //send note off kommando Serial1.write(tonenr); //send tone nummer Serial1.write(volumen); //send volumen (velocity) }
Step 7: The Basics of MIDI Communication
MIDI (Musical Instrument Digital Interface) is a universal serial communication protocol for interfacing electronic musical instruments and other devices. Serial communication is used (31250 bit/s, transmission medium is a current loop, opto-isolated at the receiver end. 5pin DIN connectors are used. 16 logical communication channels are possible in one physical MIDI connection. Many commands are defined in the MIDI standard, I use two commands in this project, these commands consists of 3 bytes:
a) Note On command:
1. byte send = 0x90 meaning note on command on MIDI channel 1
2. byte send = 0xZZ ZZ is note number, I use the range 0x3C to 0x48
3. byte send =0xFF FF = 255 meaning max volume, range 0x00 to 0xFF
b) Note Off command:
1. byte send = 0x80 meaning note off command on MIDI channel 1
2. byte send = 0xZZ ZZ is note number, I use the range 0x3C to 0x48
3. byte send =0xFF FF = 255 meaning max volume, range 0x00 to 0xFF