Nederlands | English

Voetschakelaar

Om een voetschakelaar te maken zocht ik naar de eenvoudigste manier om zoiets te maken.
Om te beginnen gebruikte ik polyetherschuim en geen scharnier met veer. Daarna had ik alleen nog een paar plankjes en een paar schakelaars nodig.

Dit is de voetschakelaar.
voetschakelaar
De hak van mijn schoen of de hiel van mijn voet staat op de grond, en de voorkant van mijn voet kan op de drie schakelaars klikken.

Het maken van de voetschakelaar is eenvoudig.
materiaal voetschakelaar
Ik gebruikte een paar plankjes van 9 mm dik. De onderste plank is 13 cm x 31 cm. De bovenste plankjes zijn 13 cm x 8 cm.
Het polyetherschuim is 1 cm dik en is het stevigste dat ik kon vinden. Het zit met contactlijm gelijmd op de achterste helft.
De schakelaars kunnen bijvoorbeeld schakelaars uit een oude muis zijn. Ik gebruikte schakelaars met de meest harde klik. Die lijmde ik met montagelijm op de onderste plank.
Als de schakelaar te snel contact maakt, dan kan er extra polyetherschuim links en rechts van de schakelaar gelijmd worden.

Dit is een zijaanzicht.
zijaanzicht voetschakelaar
Vervolgens soldeerde ik draden aan de schakelaars. Deze waren eerst nog verbonden met een seriele RS232-poort.

De beste manier om knoppen met een computer te verbinden is via de USB-bus. I dacht toendertijd dat het gebruik van een joystick of gamepad een goed idee was.
Ik kocht een paar oude gamepads met USB-aansluiting uit de "Logitech WingMan" serie.
gamepads
De eenvoudigste bleek de "Action Pad". Die heeft negen knoppen. Daar kunnen vrij eenvoudig draden aan gesoldeerd worden voor de voetschakelaars. Voor de veiligheid kunnen weerstanden van 100 Ohm in serie met de schakelaars geplaatst worden.
Eventueel kan een draadloos gamepad gebruikt worden. Dat is nog veiliger omdat er dan geen elektrische verbinding tussen de voetschakelaars en de computer meer is en de voetschakelaars kunnen gebruikt worden zonder lastige draden.
In Windows kunnen die knoppen eenvoudig geprogrammeerd worden via de "Profiler" van de Logitech driver. Ook in linux bestaan er enkele programma's die dat doen zoals "QJoyPad".

Het kan nog eenvoudiger.
Er zijn hele goedkope "numpads" met een usb aansluiting. Daar is heel eenvoudig mijn voetschakelaar op aan te sluiten.
voetschakelaar met numpad

Een programmeerbare voetschakelaar zou helemaal mooi zijn.
De Arduino Leonardo is een klein electronisch apparaatje van ongeveer 15 euro. Daarmee kan een toetsenbord en muis gesimuleerd worden. Ik gebruikte de Arduino Leonardo om een programmeerbare voetschakelaar te maken.

voetschakelaars voetschakelaars

De drie voetschakelaars kunnen met iedere toets of muisklik geprogrammeerd worden.
De Arduino Leonardo fungeert ook als een extra virtuele seriele poort. Ik gebruik die virtuele seriele poort om een toets of muisklik aan een voetschakelaar toe te wijzen. Dat kan met een algemeen serial terminal programma, maar ook via de commandoregel. Dat staat beschreven in het bestand.
Er is dus geen configuratie programma nodig om toetsen aan de voetschakelaars toe te wijzen.

Het toekennen van een toets of muisklik aan een voetschakelaar kan eenmalig, als dat voldoende is.
Het is ook mogelijk om meerdere scripts te maken, zodat voor verschillende toepassingen de schakelaars verschillende dingen doen.

Op dit moment wordt een (gesimuleerde) toets ingedrukt zolang de voetschakelaar is ingedrukt. En het is mogelijk om zowel een toets en een muisklik aan een voetschakelaar toe te wijzen (bijvoorbeeld Ctrl en rechter muis klik).
Op dit moment zijn deze mogelijkheden voldoende voor mij. Maar ik zou in de toekomst nieuwe dingen toe kunnen voegen, want er zijn heel veel extra mogelijkheden te bedenken.

Het programma voor de Arduino Leonardo heet een "schets". De schets staat hieronder, of download de schets: 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 continuously, 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
    }
  }
}

Laatste wijziging van deze bladzijde: februari 2013