Footsy - Teensy 2 based MIDI foot pedal controller

oddson

Well-known member
Teensy-based MIDI foot-controller system -- just add sustain and expression pedals.
footsy.jpg
Physical build consists of nothing but a Teensy 2, some 1/4" sockets, a USB cable and a polystyrene enclosure.

Here's a screenshot of the SysEx CC assign system (built with FlowStone [forum])
sreeny.png
It sends a small (16 byte) sysex message to configure the box -- only nine bytes are program data, the rest is to make sure it's the intended message.
(I used the private-use (non-commercial) sysex ID '7D' with an added four bytes as a 'key'.)
The code also sends back three bytes as an acknowledge message.



Code - fragments and ideas stolen liberally from all over but especially Knobber
Code:
#include <Bounce.h> 
#include <EEPROM.h>
const int thrshld = 7; // threshold for when change in voltage updates CC on analog pins
const int delayL = 5; // > delay can reduce number of MIDI msgs - can be good or bad??
// higher delay likely advantage if using lower threshold to limit repeats at same CC

int chnl; // MIDI channel 
int v0,v1;// analog values for volage from dividers
int v0L, v1L;// lagged voltage values - last time cc was recalc'd
int v0D = 10,v1D = 10; // delta values - current - lag; force cc to calc on init
int toggled[3] = {false,false,false}; // toggle indicates when toggle behavior in effect
int mem[14]={1,4,7,64,0,65,0,66,0};// an array of sysEx bytes, + defaults until EEPROM is loaded
Bounce button[3] = {Bounce(0, 5),Bounce(1, 5),Bounce(2, 5)}; // init 'bounce' for 3 buttons

void setup() {
  Serial.begin(31250);
  pinMode(0, INPUT_PULLUP); // set up three digital pins
  pinMode(1, INPUT_PULLUP); // with PULLUPs
  pinMode(2, INPUT_PULLUP); //
  if (EEPROM.read(0)>0 && EEPROM.read(0)<17){ // EEPROM @ 0 is non-zer0 or not a viable MIDI channel
    for (int i = 0; i <= 8; i++) { // read EEPROM for sysEx data
      mem[i] = EEPROM.read(i);
      delay(5); // give time for EEPROM read??
    }
    int chnl = mem[0];
  }
}


// *************THE MAIN LOOP************
void loop() { 
  if(usbMIDI.read() && usbMIDI.getType() == 7) { //if MIDI is sysEx
    doSysEx(); // look for CC change msg
  }
  getAnalogData(); // get/process analog pins
  getDigitalData(); // get/process digital pins
 delay(delayL);
}

//************ANALOG SECTION**************
void getAnalogData(){
  v0 = analogRead(0);
  v1 = analogRead(1);
  if (v0D > thrshld) // if delta variable shows change past threshold - recalulate
  {
    usbMIDI.sendControlChange(mem[1], v0/8, chnl); // calculate CC for analog 1 and send
    v0L = v0; // lag only on update
  }
  v0D = abs(v0L-v0);
  if (v1D > thrshld)
  {
    usbMIDI.sendControlChange(mem[2], v1/8, chnl); // calculate CC for analog 2 and send
    v1L = v1; // lag only on update
  }
  v1D = abs(v1L-v1);
}

//************DIGITAL SECTION**************
void getDigitalData(){
  for (int i = 0; i < 3; i++){
    button[i].update(); //update bounce object for each button
    if (button[i].fallingEdge()) { // button press to ground
      if (toggled[i] && mem[i * 2 + 4]==1){ // if toggled state and toggle behavoiour both true...
        usbMIDI.sendControlChange(mem[i * 2 + 3], 0, chnl);  // unlatch to OFF
        toggled[i] = false      ;  // toggled to false for next time
      }
      else { // either latched and toggled==false or non-latched... 
        usbMIDI.sendControlChange(mem[i * 2 + 3],127, chnl); //...either way to to ON state
        if(mem[i * 2 + 4]==1){
          toggled[i] = true      ;  // but only change toggled state if in latched mode
        }
      }
    }
    if (button[i].risingEdge()) { // button release - pullup to HIGH
      if (not(mem[i * 2 + 4]==1)){ // if non-latched 
          usbMIDI.sendControlChange(mem[i * 2 + 3], 0, chnl);  // to to OFF
      }
    }
  }
}

//************SYSEX SECTION**************
void doSysEx(){
  byte *sysExBytes = usbMIDI.getSysExArray();
  if (sysExBytes[0] == 0xf0 
  && sysExBytes[15] == 0xf7 
  && sysExBytes[1] == 0x7D // 7D is private use (non-commercial)
  && sysExBytes[2] == 0x4C // 4-byte 'key' - not really needed if via USB but why not!
  && sysExBytes[3] == 0x65
  && sysExBytes[4] == 0x69
  && sysExBytes[5] == 0x66){ // read and compare static bytes to ensure valid msg
      for (int i = 0; i < 10; i++) {
      EEPROM.write(i, sysExBytes[i+6]);
      mem[i] = sysExBytes[i+6];
    }  
    byte data[] = { 0xF0, 0x7D, 0xF7 }; // ACK msg - should be safe for any device even if listening for 7D
    usbMIDI.sendSysEx(3, data);         // SEND
    for (int i = 0; i < 3; i++) {
      toggled[i] = false; // for consistant behaviour, start in OFF position
    }
  }
}
 
Last edited:
Nice stuff! I always wanted to make my very own MIDI foot controller w/ a Teensy. I also love the idea of using sysex messages to configure the thing ;)

I don't know what do you use this for, but I'd love to have something like 10 heavy-duty footswitches on mine, I don't like the idea of plugging things together to actually use something. You know, you could easily hack a few footswitches and expression pedal inside a bigger box and you'll have a ready-to-use all-in-one foot controller ;) Keep up the good work :)
 
I don't know what do you use this for, but I'd love to have something like 10 heavy-duty footswitches on mine, I don't like the idea of plugging things together to actually use something. You know, you could easily hack a few footswitches and expression pedal inside a bigger box and you'll have a ready-to-use all-in-one foot controller ;) Keep up the good work :)
The plan is to make one with many momentary switches built in and with proper MIDI in/out/through. But it would still have external expression connections and at least two external switches as sustain pedals are so nice to 'play' -- rather than when you want to latch on an effect.

This one was to get a build done quickly and cheaply. I love Teensy!:D
 
Nice looking build.

I'd like to show this on the Teensy Projects page. But an image at least 300 pixels wide is needed. Any chance you could post a higher res image?
 
Nice looking build.

I'd like to show this on the Teensy Projects page. But an image at least 300 pixels wide is needed. Any chance you could post a higher res image?

Thanks... :eek: Would look better if I could get three holes to line up! ;)

I'll post better photos shortly (took that one after midnight last night as I wanted to post something here in a hurry)...

Can you hold off for a week or so with adding it to your projects page until I can polish things a bit and to show the FlowStone part on their site?

And would you update with a link to my site (oddsonic.com) once I have something on it there? (It's just a placeholder site for the domain name with some of my music on in right now.)
 
photos

here are the photos for the project page and a link to a thread on the CC software on the FlowStone site.
 

Attachments

  • footsy_w_pedals.JPG
    footsy_w_pedals.JPG
    70.5 KB · Views: 925
  • footsy.JPG
    footsy.JPG
    54.7 KB · Views: 610
  • footsy_wire.jpg
    footsy_wire.jpg
    41.6 KB · Views: 983
Last edited:
Hi!
I am going to build one of theese. I have a Teensy 3.1 on its way but I have never programmed. So is it possible to get some help?
How do I start?
Can I download the program/ code?
What program do I need?
Is it possible to do this with a Mac? (I have both)

Best Regards/
Mårten
 
How do I start?

This tutorial is a pretty good starting point.

http://www.pjrc.com/teensy/tutorial.html

After you get Arduino + Teensyduino set up and you're able to upload the LED blink, perhaps open some of the examples, from File > Examples > Teensy > USB_MIDI. Even if those examples aren't exactly what you need, I'd highly recommend at least connecting a pushbutton and get it to send note-on & note-off messages to software on your Mac. It's always easier to begin a new project when you've got a solid working example as starting point.
 
The enclosure in the images posted earlier in this thread look like the small enclosures from Hammond MfG.
Just in case they are not, Hammond makes these nice aluminum Die Cast boxes spcifically for Stomp Boxes.
Avaiable from Digikey and other sources. The smaller boxes cost about $10.
 
Hi!
I am going to build one of these...
The physical build is very simple. (@Headroom - It is a Hammond 1590B project box but --as mentioned in the initial post-- it's just Polystyrene as die-cast aluminum for EM shielding isn't needed for and non-audio project like this.)

I have a Teensy 3.1 on its way...
I don't know if any changes to my code above are required for a teensy 3.x as I built mine with a 2.x.
...but I have never programmed.
Hopefully you have the left-brain skills that makes programming make sense for you. Arduino programming is fairly straightforward but it's still programming. Take the time with Paul's tutorials before trying to move on.

So is it possible to get some help?
There's a lot of help available here if you put in some effort. If there's something about my code you need help with I'd be happy to assist.

Is it possible to do this with a Mac?
The Teensyduino software is available for the Mac but the Flowstone 'CC Assign Tool' applet referenced above works only on Windows. But you can skip it altogether and just use hard-coded CC parameters into your device or find another program to send MIDI Sysex to the device. It's just 16 bytes of Sysex data and there are lots of Mac programs that could do this part, although likely without a graphical user-interface for picking the parameters.
 
Thanks for your answers. I have done the blink programming so I know it is working. I think I'll skip the sysex for now. I want to have only 2 foot pedals. One for changing patch in MainStage (cc04 or 03) and one for sustain pedal on cc64 can I do a program like these?

Is this totally wrong?

#include <Bounce.h>

const int channel = 1;

Bounce button0 = Bounce(0, 5);
Bounce button1 = Bounce(1, 5);


void setup() {

pinMode(0, INPUT_PULLUP);
pinMode(1, INPUT_PULLUP);

}

void loop() {

button0.update();
button1.update();



if (button0.fallingEdge()) {
usbMIDI.setHandleControlChange(03, 127, channel); //cc03
}
if (button1.fallingEdge()) {
usbMIDI.setHandleControlChange(64, 127, channel); //cc64
}


while (usbMIDI.read()) {
// ignore incoming messages
}
}
 
Last edited:
Your going 'on' when switch goes off and you're not dealing with the rising edge... but you're not too far off from what I can see.
 
I don't understand. Do I need the rising edge too? Like this:

if (button0.risingEdge()) {
usbMIDI.setHandleControlChange(03, 0, channel); //cc03
}
if (button1.risingEdge()) {
usbMIDI.setHandleControlChange(64, 0, channel); //cc64
}

...Or should i just change falling to rising?


Sorry for hi-jacking your thread.
 
Last edited:
Honestly I'm not too sure... I was barely competent at this when I made it a year or more ago but (unless I'm badly mistaken) you need something to send back an 'off' state when you let up on the pedal. That's assuming you're making momentary switch like a sustain pedal and not a toggle switch. If a toggle switch then you can ignore the falling state and have it change on the rising but you have to keep track of what state it was in last.

But assuming you do want a sustain-type momentary switch then you need to send a zero value when the switch is 'off'. And if so I think the what you have above is correct but possibly backward...

Because the next complicating factor is that some switches are normally open and some normally closed. If it's normally closed then you want the falling state to represent the 'on' state (MIDI=127) and the rising 'off' (MIDI=0) which is what you appear to have above.

Anyone else care to help with this... I'd hate to be mis-remembering how this works. :confused:
 
Last edited:
You were close

You were really close... although you did change the MIDI function name (setHandleControlChange?) which I didn't notice and caused me a bit of confusion when I got a compiler error with your code...

This worked for me:
Code:
#include <Bounce.h>

 const int channel = 1;

 Bounce button0 = Bounce(0, 5);
 Bounce button1 = Bounce(1, 5); 

  
 void setup() {
   Serial.begin(31250);
   pinMode(0, INPUT_PULLUP);
   pinMode(1, INPUT_PULLUP);
 }

 void loop() {

 button0.update();
 button1.update();



 if (button0.fallingEdge()) {
 usbMIDI.sendControlChange(3, 127, channel); //cc03
 }
 if (button1.fallingEdge()) {
 usbMIDI.sendControlChange(64, 127, channel); //cc64
 }
 if (button0.risingEdge()) {
 usbMIDI.sendControlChange(3, 0, channel); //cc03
 }
 if (button1.risingEdge()) {
 usbMIDI.sendControlChange(64, 0, channel); //cc64
 }

 while (usbMIDI.read()) {
 // ignore incoming messages
 }
 }

If your switches are normally closed just swap the zero and the 127 values.
 
If anyone is trying to run the code from the initial post a change is required for the current version of Teensyduino where getType() return values have been changed to match those from the main Arduino midi libaray:

Code:
  if(usbMIDI.read() && usbMIDI.getType() == [COLOR="#FF0000"]7[/COLOR]) { //if MIDI is sysEx

The number '7' needs to be replaced with 0xF7 or (preferably):

Code:
  if(usbMIDI.read() && usbMIDI.getType() == [COLOR="#FF0000"]usbMIDI.SystemExclusive [/COLOR]) {
 
Hello,

For my first post on the forum, I only want to thanks a lot Oddson for sharing his project. I was very inspired on it, to realize two midifoot projects.I made a program on Max/msp to flash CC config via sysex.
I used Teensy LC with both.
I made one box with 12 expression pedals inputs and 4 footswitchs inputs and another one with 10 footswitchs and 7 expression pedals inputs.

I could provide teensy code and max/msp path if someone is interested.
Here a photo of one box:

IMG_2986-e1536152488577.jpg
 
Great idea. Working with Bluetooth pedals, Airturn bt200s and Blueboard. Will this project provide a softthru midi message?
 
Back
Top