// // 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 } } }