Tuesday, June 24, 2014

CUSTOM DIY MIDI FOOT CONTROLLER for ABLETON LIVE - Arduino

I'm a geek. I know it. I like DIY'ing things. I bought a 3d printer (which is sitting in my office not working right now)... and like building drones. I usually bite off more than I can chew and often have to go to others for help. This time around, it was to build a custom Midi foot controller to use with Ableton Live for our click/loop tracks.


I have a Keith MacMillen Softstep... but its really complicated to program, and the buttons are rubber. I never knew if the thing was ever pressed or not. I wanted the "click" of a regular guitar pedal. And I don't need something that complicated. There are some other pedals you can purchase... but their footprint wasn't correct for my pedal board. And I didn't want to spend another $300-$400.

So for around $70 I built my own.

Physical Construction: 
I used luan ply and just made it in the wood shop.
SKETCHUP MODEL

Switches:
You need momentary SPST switches. I got the "silent" ones so it doesn't sound like a firecracker going off in the middle of the worship service. Something like this:

Microcontroller: 
I used an arduino teensy that I bought from sparkfun. It offers a couple of important things:
• It has USB connectivity
•It's powered via USB
•It supports a native MIDI control without need for some intermediary software.
•It has a ton of digital pins

LCD Display
I bought it HERE from sparkfun. I wanted a serial rather than parallel connection so I didn't have to chew up lots of extra pins. (pins= possible switches or leds, or connectors)

Connections. 
I suppose if I were really thorough I'd have some sort of fancy connection diagram. Basically,
I hooked pin 1 up to the serial LCD pin. And then the switches have all the connections from there. You could in theory have 24 or so physical buttons/switches. Or if you were really good with programming could figure out a way to use multiple banks. But that's too complicated for me.

Code:
This was the hard part. I first got it working with the MIDI buttons sketch. Then got the serial LCD sketch to work. As I'm not a programmer, I had to learn to combine the two together.
Here's the Arduino Code




/* Buttons to USB MIDI Example
   You must select MIDI from the "Tools > USB Type" menu
   To view the raw MIDI data on Linux: aseqdump -p "Teensy MIDI"
   This example code is in the public domain.
 
   You'll have to change the pin assignments below if you use different pins than I did.
*/

#include
#include
#define txPin 1

// the MIDI channel number to send messages
const int channel = 1;
SoftwareSerial LCD = SoftwareSerial(0, txPin);
// since the LCD does not send data back to the Arduino, we should only define the txPin
const int LCDdelay=10;  // conservative, 2 actually works


// Create Bounce objects for each button.  The Bounce object
// automatically deals with contact chatter or "bounce", and
// it makes detecting changes very simple.
Bounce button14 = Bounce(14, 100);
Bounce button15 = Bounce(15, 100);  // 5 = 5 ms debounce time
Bounce button2 = Bounce(2, 10);  // which is appropriate for good
Bounce button3 = Bounce(3, 10);  // quality mechanical pushbuttons
Bounce button4 = Bounce(4, 10);
Bounce button5 = Bounce(5, 10);  // if a button is too "sensitive"
Bounce button6 = Bounce(6, 200);  // to rapid touch, you can
Bounce button7 = Bounce(7, 10);  // increase this time.
Bounce button8 = Bounce(8, 10);
Bounce button9 = Bounce(9, 10);
Bounce button10 = Bounce(10, 10);
Bounce button11 = Bounce(11, 10);
Bounce button12= Bounce(12, 10);



// wbp: goto with row & column
void lcdPosition(int row, int col) {
  LCD.write(0xFE);   //command flag
  LCD.write((col + row*64 + 128));    //position
  delay(LCDdelay);
}
void clearLCD(){
  LCD.write(0xFE);   //command flag
  LCD.write(0x01);   //clear command.
  delay(LCDdelay);
}
void backlightOn() {  //turns on the backlight
  LCD.write(0x7C);   //command flag for backlight stuff
  LCD.write(157);    //light level.
  delay(LCDdelay);
}
void backlightOff(){  //turns off the backlight
  LCD.write(0x7C);   //command flag for backlight stuff
  LCD.write(128);     //light level for off.
   delay(LCDdelay);
}
void serCommand(){   //a general function to call the command flag for issuing all other commands
  LCD.write(0xFE);
}


void setup() {
  // Configure the pins for input mode with pullup resistors.
  // The pushbuttons connect from each pin to ground.  When
  // the button is pressed, the pin reads LOW because the button
  // shorts it to ground.  When released, the pin reads HIGH
  // because the pullup resistor connects to +5 volts inside
  // the chip.  LOW for "on", and HIGH for "off" may seem
  // backwards, but using the on-chip pullup resistors is very
  // convenient.  The scheme is called "active low", and it's
  // very commonly used in electronics... so much that the chip
  // has built-in pullup resistors!

  pinMode(txPin, OUTPUT);
  pinMode(2, INPUT_PULLUP);
  pinMode(3, INPUT_PULLUP);
  pinMode(4, INPUT_PULLUP);
  pinMode(5, INPUT_PULLUP);
  pinMode(6, INPUT_PULLUP);  // Teensy++ 2.0 LED, may need 1k resistor pullup
  pinMode(7, INPUT_PULLUP);
  pinMode(8, INPUT_PULLUP);
  pinMode(9, INPUT_PULLUP);
  pinMode(10, INPUT_PULLUP);
  pinMode(11, INPUT_PULLUP);
  pinMode(12, INPUT_PULLUP);
  pinMode(13, INPUT_PULLUP);
  pinMode(14, INPUT_PULLUP); //channel 1
  pinMode(15, INPUT_PULLUP); // channel 2 Teensy 2.0 LED, may need 1k resistor pullup
  LCD.begin(9600);
  clearLCD();
  lcdPosition(0,0);
  LCD.print("  Scott's MIDI      Dominator");
}

void loop() {
  // Update all the buttons.  There should not be any long
  // delays in loop(), so this runs repetitively at a rate
  // faster than the buttons could be pressed and released.
  button14.update();
  button15.update();
  button2.update();
  button3.update();
  button4.update();
  button5.update();
  button6.update();
  button7.update();
  button8.update();
  button9.update();
  button10.update();
  button11.update();
  button12.update();


  // Check each button for "falling" edge.
  // Send a MIDI Note On message when each button presses
  // Update the Joystick buttons only upon changes.
  // falling = high (not pressed - voltage from pullup resistor)
  //           to low (pressed - button connects pin to ground)
  if (button14.fallingEdge()) {
    usbMIDI.sendNoteOn(60, 99, channel);  // 60 = C4
    if (digitalRead(14) == LOW) {
    LCD.print ("SONG 1");
    LCD.print (0xFE, BYTE);
    LCD.print (0x01, BYTE);
    LCD.print ("     SONG 1     ");}
  }
  if (button15.fallingEdge()) {
    usbMIDI.sendNoteOn(61, 99, channel);  // 61 = C#4
        if (digitalRead(15) == LOW) {
    LCD.print ("SONG 2");
    LCD.print (0xFE, BYTE);
    LCD.print (0x01, BYTE);
    LCD.print ("     SONG 2     ");}
  }
    if (button2.fallingEdge()) {
    usbMIDI.sendNoteOn(62, 99, channel);  // 62 = D4
    if (digitalRead(2) == LOW) {
    LCD.print ("SONG 3");
    LCD.print (0xFE, BYTE);
    LCD.print (0x01, BYTE);
    LCD.print ("     SONG 3     ");}
  }
  if (button3.fallingEdge()) {
    usbMIDI.sendNoteOn(63, 99, channel);  // 63 = D#4
    if (digitalRead(3) == LOW) {
    LCD.print ("SONG 4");
    LCD.print (0xFE, BYTE);
    LCD.print (0x01, BYTE);
    LCD.print ("     SONG 4     ");}
  }
  if (button4.fallingEdge()) {
    usbMIDI.sendNoteOn(64, 99, channel);  // 64 = E4
    if (digitalRead(4) == LOW) {
    LCD.print ("SONG 5");
    LCD.print (0xFE, BYTE);
    LCD.print (0x01, BYTE);
    LCD.print ("     SONG 5     "); }
  }
  if (button5.fallingEdge()) {
    usbMIDI.sendNoteOn(65, 99, channel);  // 65 = F4
    if (digitalRead(5) == LOW) {
    LCD.print ("SONG 6");
    LCD.print (0xFE, BYTE);
    LCD.print (0x01, BYTE);
    LCD.print ("     SONG 6     ");}
  }
  if (button6.fallingEdge()) {
    usbMIDI.sendNoteOn(66, 99, channel);  // 66 = F#4
    if (digitalRead(6) == LOW) {
    LCD.print ("PLAY");
    LCD.print (0xFE, BYTE);
    LCD.print (0x01, BYTE);
    LCD.print (">>>>>PLAY<<<<<");}
  }
  if (button7.fallingEdge()) {
    usbMIDI.sendNoteOn(67, 99, channel);  // 67 = G4
    if (digitalRead(7) == LOW) {
    LCD.print ("STOP");
    LCD.print (0xFE, BYTE);
    LCD.print (0x01, BYTE);
    LCD.print ("      STOP      XXXXXXXXXXXXXXXX"); }
  }
  if (button8.fallingEdge()) {
    usbMIDI.sendNoteOn(68, 99, channel);  // 68 = G#4
    if (digitalRead(8) == LOW) {
    LCD.print ("NEXT");
    LCD.print (0xFE, BYTE);
    LCD.print (0x01, BYTE);
    LCD.print ("      NEXT      >>>>>>>>>>>>>>>>");}
  }
  if (button9.fallingEdge()) {
    usbMIDI.sendNoteOn(69, 99, channel);  // 69 = A5
    if (digitalRead(9) == LOW) {
    LCD.print ("PREVIOUS");
    LCD.print (0xFE, BYTE);
    LCD.print (0x01, BYTE);
    LCD.print ("    PREVIOUS    <<<<<<<<<<<<<<<<"); }
  }
  if (button10.fallingEdge()) {
    usbMIDI.sendNoteOn(70, 99, channel);  // 70 = A#5
    if (digitalRead(10) == LOW) {
    LCD.print ("EXTRA BUTTON 11");
    LCD.print (0xFE, BYTE);
    LCD.print (0x01, BYTE);
    LCD.print ("EXTRA BUTTON 11"); }
  }
  if (button11.fallingEdge()) {
    usbMIDI.sendNoteOn(71, 99, channel);  // 71 = B5
    if (digitalRead(11) == LOW) {
    LCD.print ("EXTRA BUTTON 12");
    LCD.print (0xFE, BYTE);
    LCD.print (0x01, BYTE);
    LCD.print ("EXTRA BUTTON 12"); }
  }
    if (button12.fallingEdge()) {
    usbMIDI.sendNoteOn(72, 99, channel);  // 72 = C6
    if (digitalRead(12) == LOW) {
    LCD.print ("EXTRA BUTTON 13");
    LCD.print (0xFE, BYTE);
    LCD.print (0x01, BYTE);
    LCD.print ("EXTRA BUTTON 13");
          }
    delay(50);
  }

  // Check each button for "rising" edge
  // Send a MIDI Note Off message when each button releases
  // For many types of projects, you only care when the button
  // is pressed and the release isn't needed.
  // rising = low (pressed - button connects pin to ground)
  //          to high (not pressed - voltage from pullup resistor)
  if (button14.risingEdge()) {
    usbMIDI.sendNoteOff(60, 0, channel);  // 60 = C4
  }
  if (button15.risingEdge()) {
    usbMIDI.sendNoteOff(61, 0, channel);  // 61 = C#4
  }
  if (button2.risingEdge()) {
    usbMIDI.sendNoteOff(62, 0, channel);  // 62 = D4
  }
  if (button3.risingEdge()) {
    usbMIDI.sendNoteOff(63, 0, channel);  // 63 = D#4
  }
  if (button4.risingEdge()) {
    usbMIDI.sendNoteOff(64, 0, channel);  // 64 = E4
  }
  if (button5.risingEdge()) {
    usbMIDI.sendNoteOff(65, 0, channel);  // 65 = F4
  }
  if (button6.risingEdge()) {
    usbMIDI.sendNoteOff(66, 0, channel);  // 66 = F#4
  }
  if (button7.risingEdge()) {
    usbMIDI.sendNoteOff(67, 0, channel);  // 67 = G4
  }
  if (button8.risingEdge()) {
    usbMIDI.sendNoteOff(68, 0, channel);  // 68 = G#4
  }
  if (button9.risingEdge()) {
    usbMIDI.sendNoteOff(69, 0, channel);  // 69 = A5
  }
  if (button10.risingEdge()) {
    usbMIDI.sendNoteOff(70, 0, channel);  // 70 = A#5
  }
  if (button11.risingEdge()) {
    usbMIDI.sendNoteOff(71, 0, channel);  // 71 = B5
  }
  if (button12.risingEdge()) {
    usbMIDI.sendNoteOff(72, 0, channel);  // 71 = C6
  }
}










2 comments:

  1. Hi, I tried to download your Sketchup and Arduino code but your dropbox is not active. Can you reupload please? Thank you.

    ReplyDelete
  2. Hi, I enjoyed your project, but I'm just a simple guitarist. I wonder how you connected the wires between the buttons and the teensy. could provide an illustrated diagram of these connections? thank you for attention. Hugs

    ReplyDelete