Nederlands | English

Footswitch

I wanted to make a footswitch which was very simple to make.
As a start I used polyurethane foam instead of a hinge with a spring. After that I only needed a few boards and a few switches.

This is the footswitch.
footswitch
The heel of my shoe or foot rests on the floor and the front of my foot is able to press one of the three switches.

To make a footswitch is simple.
material footswitch
I used board of 1/3" (9 mm) thick. The bottom board is 5" x 12" (13 cm x 31 cm). The upper boards are 5" x 3" (13 cm x 8 cm).
The polyurethane foam is 1/3" (1 cm) thick and is the most firm foam I could find. It is glued with contact glue on the back half of the board.
The switches could be switches from an old mouse. I used switches with the most firm click. The switches are glued on the the bottom board.
If the switches make contact too easy, some extra polyurethane foam can be glued on the left side and right side of the switch.

This is a side view.
side view footswitch
Wires were soldered to the switched. They were connected to a serial a serial RS232-port.

The best way to connect extra buttons to a computer is via the USB-bus. At that moment, using a joystick or gamepad seems a good idea.
So I bought a few old gamepads with a USB-connector from the "Logitech WingMan" series.
gamepads
The most simple gamepad turned out to be the "Action Pad". This one has nine buttons. It is not hard to solder extra wires for the footswitches. For safety extra resisters of 100 Ohm can be placed in series with the switches.
A wireless gamepad could be used, for more safety because there is no longer an electric connection between the footswitches and the computer and the footswitches can be uses without clumsy wires.
In Windows the buttons can be programmed with the "Profiler" of the Logitech driver. There are similar applications in linux that do the same thing, such as "QJoyPad".

The connection to the computer can be even more easier.
A very cheap "numpad" with a USB connection makes it very simple to connect to my foot switch.
footswitch with numpad

The ideal would be programmable footswitches.
The Arduino Leonardo is a small electronic device of about 15 euros. It can be used to simulate a keyboard and mouse. I used the Arduino Leonardo to make programmable footswitches.

foot switches foot switches

The three footswitches can be programmed with any keyboard key or mouse click.
The Arduino Leonardo also acts as a virtual serial port. I use that virtual port, to assign a keyboard key or a mouse click to a footswitch. This is possible with a common serial terminal program, but it is also possible via the command line, as explained in the file.
Therefor no configuration software is needed to assign keyboard codes to a foot switch.

Assigning a key or mouse click to a footswitch can be done once, if that is sufficient.
It is also possible to have a few scripts, and make the switches emulate different keys for different applications.

At this moment the (simulated) keyboard key is pressed as long as the foot switch is pressed. And it is possible to assign both a keyboard key and a mouse click to a footswitch (for example Ctrl and right mouse click).
These posibilities are fine for me right now. But I might add new features in the future, since it is not hard to devise many extra features.

The software program for the Arduino Leonardo is called a "sketch". The sketch is shown below, or download the sketch: footswitches.ino.


// 
// Foot Switches
// -------------
//
// Programmable foot switches.
//
// Public Domain
// February 2013
// 
// Using:
//    Arduino Leonardo R3
//    Arduino 1.0.3
//
// Each switch is programmable with the Arduino codes for keyboard and mouse.
// The serial port (via USB) of the Arduino Leonardo is used to program the switches.
// The codes for the switches are stored in EEPROM.
//
// The simulated key is pressed as long as the foot switch is pressed.
// This is useful if a key needs to be pressed continuesly, like in games and virtual worlds.
//
// The switches are connected to the Arduino Leonardo pins, and to ground.
// All digital pins 0 to 13 can be used, and also A0 to A5.
// Set the used pins in the code in the "pinSwitch" array.
// The internal pull-up resistor is used, but external pull-up resistors of 10k would be better.
//
// To initialize the codes, send the command 'INIT'.
// Do this only once.
//
// A serial monitor or terminal program can be used to program the switches.
// Set it to 9600,8,N,1
// In a serial terminal, type '?' followed by Enter for info.
// The commands must be transmitted without delay between the characters,
// due to a very simple timeout.
//
// The command line can be used to program the switches.
// With a few command line scripts, the foot switches can be programmed for a specific task.
//
// Foot switch programming command: 
//    #xKKMM\r\n
//       #, The '#' is used as a start character
//       x, The switch number, starting from 0
//       KK, The keyboard code as 2 hexadecimal numbers.
//             A code from the ASCII table, 
//             or a special key, see USBAPI.h
//             Set to 00 if not used"
//       MM, The mouse code as 2 hexadecimal numbers.
//             01 is left mouse button
//             02 is right mouse button
//             04 is middle mouse button
//             Set to 00 if not used"
//       \r\n, Any trailing \r\n is ignored.
//
// Examples: 
//        #0DA00   set switch 0 to cursor up
//        #10001   set switch 1 to left mouse click
//
//
// Windows, PowerShell
//    Get the serial port names:
//      [System.IO.Ports.SerialPort]::getportnames()
//
//    Write the 'INIT' command (using COM9)
//      $port= new-Object System.IO.Ports.SerialPort COM9,9600,None,8,one; $port.open(); $port.WriteLine("INIT"); $port.Close()
//
//    Program a switch (using COM9)
//      $port= new-Object System.IO.Ports.SerialPort COM9,9600,None,8,one; $port.open(); $port.WriteLine("#0DA00"); $port.Close()
//
// Linux
//    Set the serial port baudrate (using ttyACM0)
//      stty -F /dev/ttyACM0 speed 9600
//    
//    Write the 'INIT' command (using ttyACM0)
//      echo "INIT" > /dev/ttyACM0
//
//    Program a switch (using ttyACM0)
//      echo "#0DA00" > /dev/ttyACM0
//
//
// Possible improvements:
//    - Better debounce.
//    - Better timeout for serial commands.
//    - A sequence of key presses and releases per switch.
//    - Repetitive keystrokes or mouse clicks while the switch is active.
//
//
//
// EEPROM usage:
//     address 0 : two bytes for foot switch 0
//                 first byte : keyboard code, 0 if not used.
//                 second byte : mouse code, 0 if not used.
//     address 2 : two bytes for foot switch 1
//     ... and so on
//
//


#include <EEPROM.h>

#define NUMBER_SWITCHES 3          // the total number of foot switches

// Set the used Arduino pins in the pinSwitch variable
// Write your own pin numbers here for the connected switches !
const int pinSwitch[NUMBER_SWITCHES] = {10, 9, 8};

// boolean variable to remember if switch is pressed at the moment.
boolean active[NUMBER_SWITCHES];

char buffer[20];                  // common buffer


void setup() 
{
  int i;
  
  Serial.begin(9600);
  
  // Set pins for switches to input.
  // Use internal pull-up resistor.
  // A external 10k pull-up resistor would be better.  
  for( i=0; i<NUMBER_SWITCHES; i++)
    pinMode(pinSwitch[i], INPUT_PULLUP);

  // Assume no switches are active.
  for( i=0; i<NUMBER_SWITCHES; i++)
    active[i] = false;

  // Initialize mouse and keyboard control.
  Keyboard.begin();
  Mouse.begin();
}


void loop()
{
  if( Serial.available() > 0)
    SerialTask();
  else
    ProcessSwitches();
}


int SerialTask( void)
{
  int i, j, keyboardCode, mouseCode;
  char inChar;

    
  inChar = Serial.read();
  
  switch( inChar)
  {
  case '#':
    // A command to program a code for a switch.
    // Wait for all characters to get in the buffer.
    // This is done with a delay, which is used as a timeout.
    delay( 100);
    
    // If not enough characters are received, ignore everything.
    if (Serial.available() < 5)
      return( -1);

    // The next character is the ascii number of the switch.    
    inChar = Serial.read();
      
    i = inChar - '0';
    
    // Test if the switch number is valid.
    if (i < 0 || i >= NUMBER_SWITCHES)
      return( -2);

    // Read two hexadecimal numbers for keyboard code.
    buffer[0] = Serial.read();
    buffer[1] = Serial.read();
    buffer[2] = '\0';
    // Convert ascii hex string to integer
    // No error checking for non-hexadecimal characters at the moment.
    keyboardCode = strtol( buffer, NULL, 16);
      
    // Read two hexadecimal numbers for mouse code
    buffer[0] = Serial.read();
    buffer[1] = Serial.read();
    buffer[2] = '\0';
    // Convert ascii hex string to integer
    // No error checking for non-hexadecimal characters at the moment.
    mouseCode = strtol( buffer, NULL, 16);
      
    // Test if the switch is pressed.
    // And release it first, before accepting the new codes.
    if( active[i])
    {
      j = EEPROM.read( (2*i) + 0);     // keyboard code
      if( j != 0)
        Keyboard.release( j);
        
      j = EEPROM.read( (2*i) + 1);     // mode code
      if( j != 0)
        Mouse.release( j);
        
      // Even though the switch is pressed, make it inactive.
      // The new code will be used the next time the switches are checked.
      active[i] = false;
    }

    // Write the new codes to EEPROM.
    EEPROM.write( (2*i) + 0, keyboardCode);
    EEPROM.write( (2*i) + 1, mouseCode);
    break;
    
  case 'I':
    // Test for command 'INIT'
    // The command is 'INIT' and not just 'I'.
    // This is to avoid accidential initializing
    // with bad serial data (for example if the baudrate was wrong).
    // Wait for the hole command to be in the receive buffer.
    delay(100);
    buffer[0] = 'I';
    buffer[1] = Serial.read();
    buffer[2] = Serial.read();
    buffer[3] = Serial.read();
    buffer[4] = '\0';
    if( strcmp( buffer, "INIT") == 0)
    {
      // reset the values for the switches
      // First switch will be 'A', second switch 'B' and so on.
      // The mouse code is not used, it is set to 0.
      for( i=0; i<NUMBER_SWITCHES; i++)
      {
        EEPROM.write( (2*i) + 0, 'A' + i);       // set keyboard code
        EEPROM.write( (2*i) + 1, 0);              // set mouse code
      }
        
      // Assume no switches are active
      for( i=0; i<NUMBER_SWITCHES; i++)
        active[i] = false;
        
      // Nothing is returned to the serial port.
      // Because I assume that it is mostly used with a command line script.
      // To know that the initialization has succeeded,
      // the foot switches can be pressed one by one.
    }
    break;
    
  case 'W':
    // Test for command 'WHO'
    // This can be used to identify that the footswitches
    // are connected to the serial COM port.
    // Wait for the whole command te be in the receive buffer.
    delay(100);
    buffer[0] = 'W';
    buffer[1] = Serial.read();
    buffer[2] = Serial.read();
    buffer[3] = '\0';
    if( strcmp( buffer, "WHO") == 0)
    {
      // return string for identification
      Serial.println( F("FOOTSWITCHES"));
    }
    break;

  case '?':
    // Command '?'
    // A single character which can be used in the serial monitor.
    // It shows information.
    Serial.println(F("Foot Switches"));
    Serial.println(F("February 2013"));
    Serial.println(F("Compilation date: " __DATE__));
    Serial.println(F("Compilation time: " __TIME__));
    Serial.println(F("9600,8,N,1"));
    Serial.println(F("Usage:"));
    Serial.println(F("  #xKKMM\\r\\n"));
    Serial.println(F("      #, The '#' is used as a start character"));
    Serial.println(F("      x, The switch number, starting from 0"));
    Serial.println(F("      KK, The keyboard code as 2 hexadecimal numbers."));
    Serial.println(F("          A code from the ASCII table,"));
    Serial.println(F("          or a special key, see USBAPI.h"));
    Serial.println(F("          Set to 00 if not used"));
    Serial.println(F("      MM, The mouse code as 2 hexadecimal numbers."));
    Serial.println(F("          01 is left mouse button"));
    Serial.println(F("          02 is right mouse button"));
    Serial.println(F("          04 is middle mouse button"));
    Serial.println(F("          Set to 00 if not used"));
    Serial.println(F("      \\r\\n, Any trailing \\r\\n is ignored."));
    Serial.println(F("    Examples:"));
    Serial.println(F("      #0DA00   set switch 0 to cursor up"));
    Serial.println(F("      #10001   set switch 1 to left mouse click"));
    Serial.println(F("  ?    : Show this info"));
    Serial.println(F("  INIT : Initialize codes to 'A', 'B', ..."));
    Serial.println(F("  WHO  : Returns \"FOOTSWITCHES\""));

  default:
    break;
  }

  return( 0);
}


void ProcessSwitches( void)
{
  int i, j, p;

  for( i=0; i<NUMBER_SWITCHES; i++)
  {
    // If the foot switch is pressed, the input is LOW.
    // So LOW is active.
    p = digitalRead( pinSwitch[i]);
    if (!active[i] && p == LOW)
    {
      // Switch has been pressed.
      // Send the codes from the EEPROM.
      j = EEPROM.read( (2*i) + 0);         // keyboard code
      if( j != 0)
        Keyboard.press( j);
    
      j = EEPROM.read( (2*i) + 1);         // mouse code
      if( j != 0)
        Mouse.press( j);

      active[i] = true;                    // remember switch is pressed
      delay(20);                           // for debounce
    }
    else if (active[i] && p == HIGH)
    {
      // Switch has been released.
      // First the mouse button is released, and after that the keyboard.
      // That is the best order for example for Ctrl + mouse click.

      j = EEPROM.read( (2*i) + 1);         // read mouse code
      if( j != 0)
        Mouse.release( j);

      j = EEPROM.read( (2*i) + 0);         // read keyboard code
      if( j != 0)
        Keyboard.release( j);

      active[i] = false;
      delay(20);                           // for debounce
    }
  }
}

Last change to this page: February 2013