Programmable Footswitches DIY

This is for all folks, that want digital footswitches. Not everyone uses rudders and out of simulators rudders are not that useful. A programmable footswitch can be used for other things too. I use one pedal for push-to-talk for example. The four other pedal to steer a tank, sailship or a character in an MMORPG.

You can get footpedal switches for next to nothing on ebay. I opted for more solid ones, but you can get them in all shapes and sizes. I paid 10€ for a pedal.

My old programmable footswitches (stealthswich3) uses 1/8" jacks and plugs I chosen those for the new set too. That way I can switch pedals between both sets.

But to be honest: That was a stupid idea. The jacks are quite finicky to work with and a closed contact had cost me hours of debugging. I would advise to use 1/4". Those are used for music related stuff and you can buy pedals with a cable and a 1/4" plug. At least the needed 1/8" cables were quite cheap. I paid 1,25€ for a cable with two plugs and 35 cents for a jack.

To control everything I opted for an arduino micro compatible micro computer with a ATMega32U4. Tiny thing that can emulate a HID controller (mouse, keyboard, etc) on PC. I got mine for 13,90€.

As a case for the arduino and jacks I bought a small, sturdy one out of aluminium. It will live under the desk and should be sturdy enough.

The last part I needed was a cable with a micro USB cable with a standard USB A plug on the other hand. I guess most have some already around, but I wanted a new one and paid 3,39€.
gehaeuse_mit_anschluessen_896x896

For soldering, I had some old cables around. The soldering is extermely simple:

  • Close the tiny bridge near the jack, so the arduino will run over the USB power.
  • Connect the ground to the sleve solder pin of your first jack. Then connect from there all other jacks.
  • Connect the pin 2 to the tip pin of your first jack. Pin 3 goes to the tip pin of your second jack and so on. I used five pedals in my case. So pin 2 up to pin 6 are in use.


J1_bridge

Prepare the case of your choice. For the 1/8" jacks I could use a 6mm drill. I used the same drill and a dremel to add a slit for the USB cable.

I used velcro to install the arduino. The tiny pcb weights next to nothing and so I can easily take it out again.

To program your Arduino you need the software. It can be found here: https://www.arduino.cc/en/software

Now you select your type of Arduino: Tools > Board > Arduino AVR Boards > Arduino Micro

Finally you insert this code into your sketch. The sketch is the program you compile and upload.

// Including the keyboard library
#include <Keyboard.h>
// Including the mouse library
#include <Mouse.h>                                  

// The Values for my Keys
const char keyValueA = 'w';
const char keyValueB = 's';
const char keyValueC = 'q';
const char keyValueD = 'e';
const char keyValueE = KEY_SCROLL_LOCK;

// The Time for the delay. This script needs a delay, because the used 
// switches bounce. That means, they trigger multiple times in a very 
// short time when the switch is activated. The delay has to be set so 
// high, that after the first trigger, the following triggers are 
// ignored but low enough, that the switch can be activated in a very
// short order.
// This value is depending on the switches I used. With different 
// switches your needed value may vary. Here the values that worked 
// for my switches:
// 50 - OK
// 40 - OK
// 30 - OK
// 20 - OK
// 12 - OK
// 11 - triggers double extremly rarely
// 10 - triggers double rarely
//  9 - triggers double sometimes
//  8 - triggers double often
//  7 - triggers double
//  6 - triggers double
//  5 - triggers double
const int delayValue = 12;

// Declaring variables for the pins
const int pinA = 2;
const int pinB = 3;
const int pinC = 4;
const int pinD = 5;
const int pinE = 6;

// Variables for the previous status of the switch
int previousSwitchStateA = HIGH; 
int previousSwitchStateB = HIGH;
int previousSwitchStateC = HIGH;
int previousSwitchStateD = HIGH;
int previousSwitchStateE = HIGH;

void setup()
{
  // Setting up the internal pull-ups resistors and also setting the 
  // pins to inputs.
  pinMode(pinA, INPUT_PULLUP);
  pinMode(pinB, INPUT_PULLUP);                        
  pinMode(pinC, INPUT_PULLUP);
  pinMode(pinD, INPUT_PULLUP);
  pinMode(pinE, INPUT_PULLUP);
  // as seen in this example:
  // https://www.instructables.com/Arduino-Programmable-Button-Panel-As-Keyboard/
  digitalWrite(pinA, HIGH);
  digitalWrite(pinB, HIGH);
  digitalWrite(pinC, HIGH);
  digitalWrite(pinD, HIGH);
  digitalWrite(pinE, HIGH);
  // start the keyboard
  Keyboard.begin();
}

// the permanent running loop
void loop()
{
  //checking the states of the switches
  int switchStateA = digitalRead(pinA);
  int switchStateB = digitalRead(pinB);
  int switchStateC = digitalRead(pinC);
  int switchStateD = digitalRead(pinD);
  int switchStateE = digitalRead(pinE);

  // Checking if the first switch has been pressed and the variable for a
  // pressed key not already set
  if ( (switchStateA == LOW) && (switchStateA != previousSwitchStateA) )
  {
    // press the key
    Keyboard.press(keyValueA);
    delay(delayValue);
  }

  if ( (switchStateA == HIGH) && (switchStateA != previousSwitchStateA) )
  {
    // release the key
    Keyboard.release(keyValueA);
    delay(delayValue);
  }

  // Checking if the second switch has been pressed and the variable for a
  // pressed key not already set
  if ( (switchStateB == LOW) && (switchStateB != previousSwitchStateB) )
  {
    // and it's currently pressed:
    Keyboard.press(keyValueB);
    delay(delayValue);
  }

  if ( (switchStateB == HIGH) && (switchStateB != previousSwitchStateB) )
  {
    // and it's currently released:
    Keyboard.release(keyValueB);
    delay(delayValue);
  }

  // Checking if the third switch has been pressed and the variable for a
  // pressed key not already set
  if ( (switchStateC == LOW) && (switchStateC != previousSwitchStateC) )
  {
    // and it's currently pressed:
    Keyboard.press(keyValueC);
    delay(delayValue);
  }

  if ( (switchStateC == HIGH) && (switchStateC != previousSwitchStateC) )
  {
    // and it's currently released:
    Keyboard.release(keyValueC);
    delay(delayValue);
  }

  // Checking if the fourth switch has been pressed and the variable for a
  // pressed key not already set
  if ( (switchStateD == LOW) && (switchStateD != previousSwitchStateD) )
  {
    // and it's currently pressed:
    Keyboard.press(keyValueD);
    delay(delayValue);
  }

  if ( (switchStateD == HIGH) && (switchStateD != previousSwitchStateD) )
  {
    // and it's currently released:
    Keyboard.release(keyValueD);
    delay(delayValue);
  }

  // Checking if the fifth switch has been pressed and the variable for a
  // pressed key not already set
  if ( (switchStateE == LOW) && (switchStateE != previousSwitchStateE) )
  {
    // and it's currently pressed:
    Keyboard.press(keyValueE);
    delay(delayValue);
  }

  if ( (switchStateE == HIGH) && (switchStateE != previousSwitchStateE) )
  {
    // and it's currently released:
    Keyboard.release(keyValueE);
    delay(delayValue);
  }

  // save the states
  previousSwitchStateA = switchStateA;
  previousSwitchStateB = switchStateB;
  previousSwitchStateC = switchStateC;
  previousSwitchStateD = switchStateD;        
  previousSwitchStateE = switchStateE;
}

Depending on your preferences you should change the pressed keys. I use W, S, Q, E and Scroll Lock. I have put variables in the upper section, so you can easily change those.

Furthermore you should take a peek at the delay. I used a value of 12 because the switches in my pedals bounced a little bit. Depending on your pedals, you should change that value. You want to trigger only on keypress when stomped down. After some mili seconds windows will begin to repeat that key over and over again in something like an editor, but in any game the key is pressed as long the pedal is pressed. Should you tap the pedal only of a short moment and you see more then one character, you have to increase that value. Just compare it, with pressing a key on your keyboard.
HID_pedale.zip (1.4 KB)

3 Likes

Here is a better sketch without the delay.

It uses the LC_baseTools library that have to be installed, to compile the sketch.

The sketch has a debug define, that can be set to “true”. That way you can see any status changes on the serial console.

Before you can compile and upload this sketch, you need to download that Library. Under “Tools > Manage Libraries” search for “LC_baseTools” and install it.


02_LC_baseTools

And here the new sketch:

// A lot of mechanical buttons have the problem that they bounce. You can
// work around that with delay settings, to ignore the later bouncing 
// switch impulses. But it is problematic as switches age and a value that 
// worked fine might not do after some months or years. On the other hand, 
// you want to keep the value as low as possible.
//
// Thankfully there is a better way:
//
// The mechButton class, which is part of the LC_baseTools library.
// "This does NOT use hardcoded delay() for debouncing. It actually uses
//  a digital filter to sense when the button stops bouncing. And is 
//  totally non-blocking to boot." by jimLee
//
// Example:
// https://wokwi.com/projects/391133606159541249
// This Sketch uses a lot from the example. Thanks a lot to jimLee
// for his great library and example!
//
// You have to install the LC_baseTools library into your Arduino IDE to
// use the mechButton class.
//
// Including the mechButton library with a Class that creates a 
// debounced button.
#include <mechButton.h>

// Including the keyboard library for keyboard key defines
#include <Keyboard.h>

// Including the mouse library for mouse action defines
#include <Mouse.h>  

// Setting defines for the used pins to make it better readable
#define PIN_A	2
#define PIN_B	3
#define PIN_C	4
#define PIN_D	5
#define PIN_E	6

// Set define to true to enable debug
#define DEBUG_VALUE false

// The Values for my Keys
const char keyValueA = 'w';
const char keyValueB = 's';
const char keyValueC = 'q';
const char keyValueD = 'e';
const char keyValueE = KEY_SCROLL_LOCK;

// create the mechButtons objects for the callbacks later
mechButton aButton(PIN_A);
mechButton bButton(PIN_B);
mechButton cButton(PIN_C);
mechButton dButton(PIN_D);
mechButton eButton(PIN_E);


// Your standard sketch setup()
void setup()
{
  // start the serial should debug is enabled
  if(DEBUG_VALUE == true)
  {
    Serial.begin(9600); 
  }

  // Set up our callback. (Also calls hookup() for idling.)
  aButton.setCallback(aCallback);
  bButton.setCallback(bCallback);
  cButton.setCallback(cCallback);
  dButton.setCallback(dCallback);
  eButton.setCallback(eCallback);
}


// This is the function that's called when the A button changes state.
void aCallback(void)
{
  if(DEBUG_VALUE == true)
  {
    Serial.print("A Button just became ");
  }

  if (aButton.getState())
  {
    if(DEBUG_VALUE == true)
    {
      Serial.println("true!");
    }
    // released the pressed key
    Keyboard.release(keyValueA);
  }
  else
  {
    if(DEBUG_VALUE == true)
    {
      Serial.println("false!");
    }
    // press the key
    Keyboard.press(keyValueA);
  }
}


// This is the function that's called when the B button changes state.
void bCallback(void)
{
  if(DEBUG_VALUE == true)
  {
    Serial.print("B Button just became ");
  }

  if (bButton.getState())
  {
    if(DEBUG_VALUE == true)
    {
      Serial.println("true!");
    }
    // released the pressed key
    Keyboard.release(keyValueB);
  }
  else
  {
    if(DEBUG_VALUE == true)
    {
      Serial.println("false!");
    }
    // press the key
    Keyboard.press(keyValueB);
  }
}


// This is the function that's called when the C button changes state.
void cCallback(void)
{
  if(DEBUG_VALUE == true)
  {
    Serial.print("C Button just became ");
  }

  if (cButton.getState())
  {
    if(DEBUG_VALUE == true)
    {
      Serial.println("true!");
    }
    // released the pressed key
    Keyboard.release(keyValueC);
  }
  else
  {
    if(DEBUG_VALUE == true)
    {
      Serial.println("false!");
    }
    // press the key
    Keyboard.press(keyValueC);
  }
}


// This is the function that's called when the D button changes state.
void dCallback(void)
{
  if(DEBUG_VALUE == true)
  {
    Serial.print("D Button just became ");
  }

  if (dButton.getState())
  {
    if(DEBUG_VALUE == true)
    {
      Serial.println("true!");
    }
    // released the pressed key
    Keyboard.release(keyValueD);
  }
  else
  {
    if(DEBUG_VALUE == true)
    {
      Serial.println("false!");
    }
    // press the key
    Keyboard.press(keyValueD);
  }
}


// This is the function that's called when the E button changes state.
void eCallback(void)
{
  if(DEBUG_VALUE == true)
  {
    Serial.print("E Button just became ");
  }

  if (eButton.getState())
  {
    if(DEBUG_VALUE == true)
    {
      Serial.println("true!");
    }
    // released the pressed key
    Keyboard.release(keyValueE);
  }
  else
  {
    if(DEBUG_VALUE == true)
    {
      Serial.println("false!");
    }
    // press the key
    Keyboard.press(keyValueE);
  }
}


// the permanent running loop
void loop()
{
  // Let all the idlers have time to do their thing.  
  idle();
}

Here is the sketch as a zip file.
HID_pedale_mechButton.zip (1.4 KB)

This is cool, and I like the way you’ve made it with plugs/jacks.

You’ve learnt a fair bit from this and I think you should broaden the tutorial to include the rotary controllers and axis methods.

I’d wanna see the capabilities of the arduino in this sort of thing compared to my gaming keyboard with it’s ‘apps’. Whether or not it can do a few things at a time, or make a macro style ‘activation’ and still handle inputs.

I still have a trusty old G13 programmable keyboard so there is no urgent need for me, to expand that tutorial. But this is not that complicated and you can explore it yourself.

Other then most keyboards, this uses no matrix. Every foot switch is connected to it’s own pin. So you could press all five switches at once, should you be able to do the acrobatics. But you could use a bigger case and buttons instead.

Here is a tutorial with a matrix and encoders: https://www.youtube.com/watch?v=Z7Sc4MJ8RPM

1 Like

And here is a peek inside the case. The wiring is very simple, as you can see.

1 Like