On this page:
  • Built a USB foot pedal using the Arduino Uno
  • Code and circuit for a six-function Arduino-based USB footswitch
  • Converted my Arduino foot pedal into a Teensy foot pedal!

Built a USB foot pedal using the Arduino Uno

(W- and I are planning to go to the hacklab.to open house today. Hope to see folks there!)

This weekend was definitely an electronics hacking weekend. Whee!

It started when I found a three-way foot pedal at Active Surplus for the grand total of CAD 7.50. It’s the kind of foot pedal musicians use to control their equipment: metal, sturdy, and with an odd-shaped connector at the end. I’d been meaning to try foot switches as a way to control my computer (handy for transcription and for using multiple computers). I wanted to get into electronics, as W- enjoyed it so much. I was curious about the Arduino. I figured that making my own foot pedal would mean lots of learning, fun, and relationship-building time. Who knows, I might even get a productivity boost out of it.

USB foot pedal, then! I used my Kindle to look up which model of Arduino could be convinced to act as a USB keyboard. The Arduino Uno was the best bet, so I picked one up at Creatron, and W- got one too. With foot pedal and Arduino in hand, it was off to the house to see what I could make from it. First: figure out the switches. I’d forgotten to write down the labels from Active Surplus and the box had no information. No problem. I snipped the ends off the connector, stripped the wires, opened up the foot pedal, and started figuring out the circuit with the help of the 7-segment LED from the lab kit.

Programming the Arduino was straightforward. I’d already played around with debouncing buttons based on the sample code. I extended my code to debounce three buttons. I looked up Arduino USB keyboard resources (http://hunt.net.nz/users/darran/), reflashed the firmware on the USB chip (atmega8u2), looked up the USB keycodes to send, and wrote the code. I set it up so that left sends PageUp, right sends PageDown, and forward sends F13.

To clean up the circuit further, W- and I went to Active Surplus for the second time that weekend. I bought some headers so that I could solder the wires to them and slot them neatly into the Arduino Uno. I ran into people I know, and I found myself directing them towards the components they were looking for. I think we might be spending too much time at Active Surplus. ;) (But it’s fun!)

During the bike ride back, I was thinking about how to make the foot pedal even more awesome. W- had suggested differentiating between short and long presses, so I worked on that. I started getting confused with the different flags and variables I was using. I redid it as a finite state machine, and that was so much easier to write. (By golly, I did get to use that after all.)

So now I have a foot pedal that cost me around CAD 36.50, plus the time I spent learning how to make it. It doesn’t require a driver. It pretends to be a normal keyboard. Because I’m using function keys that don’t conflict with anything on my keyboard, I can use AutoHotkey to translate them to whatever I want: other keys, sequences of keys, commands, even context-sensitive shortcuts. I don’t have to reflash my firmware to change my keyboard settings – I just reload my AutoHotkey script.

It’s awesome. I’ve used it to flip through an e-book while eating lunch. I’m looking forward to using it while transcribing my presentations. I might remap it to other functions while drawing or programming.

One of the limitations is that the long presses can trigger key repeats. This is handy if you’re mapping it to something like down-arrow, but not so handy if you’re trying to use it as a keyboard shortcut prefix. I’d like to figure out how to control key repeat and key delay on a per-keyboard basis. If I can’t, I might either figure out how to selectively debounce the keys in AutoHotkey, or have a toggle that controls whether keys repeat.

Not at all bad for a weekend hack and my first electronic creation! I’ll ask the Powers that Be at IBM for permission to release schematics and code. (Ah, paperwork.) The circuit is basic (umm, switches), and I’m sure people will point out lots of ways it can be improved. In the meantime, here’s a picture:

C360_2011-08-21 22-16-23

Left: Foot pedal (it can rock forward or to either side), middle: really, really long USB printer cable (will replace with shorter one soon), right: Arduino Uno with the cable wired in and the program loaded.

I’m looking forward to catproofing it in my new project box. Not that anything can really be catproof in this house, but at least I can remove the temptation of wire strands and LEDs.

Code and circuit for a six-function Arduino-based USB footswitch

image

I’d been thinking about footswitches for a while, but I held off on buying one because they were expensive. Turns out that building your own is easy, even for someone with limited electronics experience. I do have the unfair advantage of having a spouse who’s an electrical engineer, but I figured out this circuit and code by myself!

The hardware for this circuit is really simple. If you’re lucky, you might find a three-way foot switch at your local audio-equipment-carrying surplus shop. If not, you could make your own, but I haven’t tried doing that yet. =)

The fun part is in the code that makes this a six-function USB keyboard. The code below maps left, center, and right short presses to F13, F14, and F15, while left, center, and right long presses send F16, F17, and F18. Here’s the code:

const int redPin = 9;
const int tanPin = 10;
const int bluePin = 11;
const int orangePin = 12;
const int debounceDelay = 150;
const int longPressThreshold = 650;

int currentState;
int lastSwitch;
long lastDebounce;
long lastPressed;
int lastSwitchDebounced;

uint8_t buf[8] = { 0 };	/* Keyboard report buffer */

#define SWITCH_NONE 0
#define SWITCH_LEFT 1
#define SWITCH_CENTER 2
#define SWITCH_RIGHT 3

#define STATE_WAITING 0
#define STATE_SHORT_PRESSED 1
#define STATE_LONG_PRESSED 2

#define KEY_F13	0x68
#define KEY_F14	0x69
#define KEY_F15	0x6A
#define KEY_F16	0x6B
#define KEY_F17	0x6C
#define KEY_F18	0x6D
#define KEY_F1D	0x6E
#define KEY_PAGEUP 0x4b
#define KEY_PAGEDOWN 0x4e

void setup() {
  pinMode(redPin, INPUT); digitalWrite(redPin, HIGH);
  pinMode(tanPin, INPUT); digitalWrite(tanPin, HIGH);
  pinMode(orangePin, INPUT); digitalWrite(orangePin, HIGH);
  pinMode(bluePin, INPUT); digitalWrite(bluePin, HIGH);
  Serial.begin(9600);
  delay(200);
  lastSwitch = 0;
  lastDebounce = millis();
  currentState = 0;
}

int getCurrentSwitch() {
  if (!digitalRead(orangePin)) { return SWITCH_LEFT; }
  if (!digitalRead(tanPin)) { return SWITCH_CENTER; }
  if (!digitalRead(redPin)) { return SWITCH_RIGHT; }
  return SWITCH_NONE;
}

void sendKey(int currentSwitch, boolean isShort, boolean keyDown) {
  buf[0] = 0;
  buf[1] = 0;
  int debug = 0;
  if (keyDown) {
    switch (currentSwitch) {
        case SWITCH_LEFT: buf[2] = isShort ? KEY_F13 : KEY_F16; break;
        case SWITCH_CENTER: buf[2] = isShort ? KEY_F14 : KEY_F17; break;
        case SWITCH_RIGHT: buf[2] = isShort ? KEY_F15 : KEY_F18; break;
    }
    if (debug) { 
      Serial.println(currentSwitch); 
      Serial.println(((int) buf[2]) - KEY_F13); 
      Serial.println("Down"); 
      Serial.println(isShort ? "Short" : "Long"); 
    } 
  } else {
    buf[2] = 0;
    if (debug) { Serial.println("Up"); Serial.println(isShort ? "Short" : "Long"); }
  }
  if (!debug) { Serial.write(buf, 8); }
}

void loop() {
  int currentSwitch = getCurrentSwitch();
  if (currentSwitch != lastSwitch) {
    lastDebounce = millis();
  }
//  Serial.println(currentSwitch);
  // Debounce it
  if (millis() - lastDebounce > debounceDelay) {
    switch (currentState) {
      case STATE_WAITING:
        // No keys pressed yet
        if (currentSwitch != SWITCH_NONE) {
          lastPressed = millis();
          currentState = STATE_SHORT_PRESSED;
        }
        break;
      case STATE_SHORT_PRESSED:
        // Wait to see if this counts as a long press
        if (currentSwitch == SWITCH_NONE) {
          // Send the keystroke
          sendKey(lastSwitchDebounced, true, true);          
          sendKey(lastSwitchDebounced, true, false);          
          currentState = STATE_WAITING;
        } else if (currentSwitch != lastSwitch) {
          // Shouldn't happen, but just in case you're using a different footpedal...
          sendKey(lastSwitchDebounced, true, true);          
          sendKey(lastSwitchDebounced, true, false);          
          lastPressed = millis();
        } else if (millis() - lastPressed > longPressThreshold) {
          currentState = STATE_LONG_PRESSED;
          sendKey(lastSwitch, false, true);
        }
        break;
      case STATE_LONG_PRESSED:
        // Wait for the transition
        if (currentSwitch == SWITCH_NONE) {
          currentState = STATE_WAITING;
          sendKey(lastSwitch, false, false);
        } else if (currentSwitch != lastSwitch) {
          // Likewise, switching between inputs shouldn't happen with this footpedal, 
          // but just in case...
          sendKey(lastSwitch, false, false);          
          currentState = STATE_SHORT_PRESSED;
          lastPressed = millis();          
        }
    }
    lastSwitchDebounced = currentSwitch;
  }
  lastSwitch = currentSwitch;
}

After you upload the code to the Arduino, you’ll also need to reflash the ATMega8U2 chip so that it can act like a USB keyboard. This sounds scary, but the instructions on the Arduino site can help you. When you’ve gotten the hang of reflashing the ATMega8U2 with the standard firmware, reflash it with the Arduino-keyboard-0.3.hex (Uno) or Arduino-keyboard-0.3-mega2560.hex (Mega) firmware from Arduino Hacking. After you reflash, unplug, and re-plug your Arduino, it should now appear as a keyboard. If you made a mistake, don’t panic. Just reflash the standard firmware onto it, and you can upload new programs again.

The last step is to map the F13..F18 function keys to something useful on the computer. I do this in software instead of hardcoding it into sendKey so that I can easily change the keycodes without reflashing the device. I’m on Windows 7 for work and other reasons, so I use AutoHotkey to map the keys. For example, the following AutoHotkey code maps left and right to Page Up and Page Down, and the center to Alt-Tab.

F13::Send, {PgUp}
F14::Send, !{Tab}
F15::Send, {PgDn}

This is a great combination for, say, reading an e-book while eating noodles.

On Linux, you can use Xmodmap or XBindKeys to remap your keys. For the Mac, KeyRemap4MacBook might work – haven’t tried it, though.

Picture!

Making a USB footswitch turned out to be an easy and fun weekend Arduino project. Hope you can build on this idea for more awesomeness! =) I’m looking forward to finding my next project idea. Hmm…

Converted my Arduino foot pedal into a Teensy foot pedal!

Look at how neat my foot pedal looks now! It’s much better than my dangling-wires prototype with the Arduino. At just 0.7” by 1.2”, the Teensy USB board was small enough to tuck into the base of the foot pedal. Now it looks almost exactly like how it looked when I bought it from the store, except that the connector at the end of  the cable is USB instead of some funky plug.

Thanks to Teensyduino, I didn’t have to rewrite a lot of my code. The Teensy was much easier to turn into a keyboard, because I could use the standard bootloader instead of reflashing back and forth between the standard bootloader and a HID keyboard hex. The only wrinkle was that the Teensy library used keyboard scancodes instead of the USB keycodes I had used before. I couldn’t figure out how to send F13 using the Teensy library, so I changed it to send Shift+F1…Shift+F6, and I updated my AutoHotkey script to map the new keys.

If you happen to have the same foot pedal, you can solder brown and black to GND, orange to B0, blue to B1, tan to B2, and red to B3.

Here’s the new code:

const int redPin = 3;
const int tanPin = 2;
const int bluePin = 1;
const int orangePin = 0;
const int debounceDelay = 150;
const int longPressThreshold = 650;

int currentState;
int lastSwitch;
long lastDebounce;
long lastPressed;
int lastSwitchDebounced;
#define LED 11

uint8_t buf[8] = { 0 };	/* Keyboard report buffer */

#define SWITCH_NONE 0
#define SWITCH_LEFT 1
#define SWITCH_CENTER 2
#define SWITCH_RIGHT 3

#define STATE_WAITING 0
#define STATE_SHORT_PRESSED 1
#define STATE_LONG_PRESSED 2

void setup() {
  pinMode(redPin, INPUT); digitalWrite(redPin, HIGH);
  pinMode(tanPin, INPUT); digitalWrite(tanPin, HIGH);
  pinMode(orangePin, INPUT); digitalWrite(orangePin, HIGH);
  pinMode(bluePin, INPUT); digitalWrite(bluePin, HIGH);
  Serial.begin(9600);
  pinMode(LED, OUTPUT); digitalWrite(LED, HIGH);
  delay(200);
  lastSwitch = 0;
  lastDebounce = millis();
  currentState = 0;
  digitalWrite(LED, LOW);
}

int getCurrentSwitch() {
  if (!digitalRead(orangePin)) { return SWITCH_LEFT; }
  if (!digitalRead(tanPin)) { return SWITCH_CENTER; }
  if (!digitalRead(redPin)) { return SWITCH_RIGHT; }
  return SWITCH_NONE;
}

void sendKey(int currentSwitch, boolean isShort, boolean keyDown) {
  Keyboard.set_key1(0);
  Keyboard.set_key2(0);
  Keyboard.set_key3(0);
  Keyboard.set_key4(0);
  Keyboard.set_key5(0);
  Keyboard.set_key6(0);
  int debug = 0;
  digitalWrite(LED, keyDown ? HIGH : LOW);
  if (keyDown) {
    Keyboard.set_modifier(MODIFIERKEY_SHIFT);
    switch (currentSwitch) {
        case SWITCH_LEFT:   if (isShort) { Keyboard.set_key1(KEY_F1); } else { Keyboard.set_key1(KEY_F4); } break;
        case SWITCH_CENTER: if (isShort) { Keyboard.set_key1(KEY_F2); } else { Keyboard.set_key1(KEY_F5); } break;
        case SWITCH_RIGHT:  if (isShort) { Keyboard.set_key1(KEY_F3); } else { Keyboard.set_key1(KEY_F6); } break;
    }
    if (debug) {
      Serial.println(currentSwitch);
      Serial.println("Down");
      Serial.println(isShort ? "Short" : "Long");
    }
  } else {
    Keyboard.set_modifier(0);
    if (debug) { Serial.println("Up"); Serial.println(isShort ? "Short" : "Long"); }
  }
  if (!debug) { Keyboard.send_now(); }
}

void loop() {
  int currentSwitch = getCurrentSwitch();
  if (currentSwitch != lastSwitch) {
    lastDebounce = millis();
  }
//  Serial.println(currentSwitch);
  // Debounce it
  if (millis() - lastDebounce > debounceDelay) {
    switch (currentState) {
      case STATE_WAITING:
        // No keys pressed yet
        if (currentSwitch != SWITCH_NONE) {
          lastPressed = millis();
          currentState = STATE_SHORT_PRESSED;
        }
        break;
      case STATE_SHORT_PRESSED:
        // Wait to see if this counts as a long press
        if (currentSwitch == SWITCH_NONE) {
          // Send the keystroke
          sendKey(lastSwitchDebounced, true, true);
          sendKey(lastSwitchDebounced, true, false);
          currentState = STATE_WAITING;
        } else if (currentSwitch != lastSwitch) {
          // Shouldn't happen, but just in case you're using a different footpedal...
          sendKey(lastSwitchDebounced, true, true);
          sendKey(lastSwitchDebounced, true, false);
          lastPressed = millis();
        } else if (millis() - lastPressed > longPressThreshold) {
          currentState = STATE_LONG_PRESSED;
          sendKey(lastSwitch, false, true);
        }
        break;
      case STATE_LONG_PRESSED:
        // Wait for the transition
        if (currentSwitch == SWITCH_NONE) {
          currentState = STATE_WAITING;
          sendKey(lastSwitch, false, false);
        } else if (currentSwitch != lastSwitch) {
          // Likewise, switching between inputs shouldn't happen with this footpedal,
          // but just in case...
          sendKey(lastSwitch, false, false);
          currentState = STATE_SHORT_PRESSED;
          lastPressed = millis();
        }
    }
    lastSwitchDebounced = currentSwitch;
  }
  lastSwitch = currentSwitch;
}

Here is the relevant AutoHotkey snippet I’m working with:

+F1::Send, {PgUp}
+F2::Send, !{Tab}
+F3::Send, {PgDn}
+F4::Send, {PgUp}
+F5::Send, !{Tab}
+F6::Send, {PgDn}

Wheeee! It looks so neat now. I’ve still got some flakiness to work out, but it looks awesome!

—- Another foot pedal story —-

One Saturday, W- and I were at Active Surplus to buy some electronic components and to browse their ever-interesting collections. I overheard a woman looking for a footswitch. As she asked one of the Active Surplus employees about the switch characteristics so that she could turn it into a USB keyboard-type device, I couldn’t help but tell her the footswitch was totally awesome and that I’d done something similar recently. She was also interested in building a footswitch for transcription. I told her to look for "Sacha Chua Arduino USB footswitch" as a way to get to my notes on building a 6-way USB footswitch using the very same footswitch she was thinking of buying. We had a short but great conversation about hacking stuff. I hope she ends up making something awesome out of the footswitch!