Arduino as a HID Keyboard

I just received my first Arduino (an Arduino Uno to be specific) in the mail yesterday and have been working on setting it up to look like a HID keyboard when it is plugged into the computer. I followed ant.b's topic to get started and expanded it to fit my needs. For specific steps in the process of making the Arduino appear to be an HID device, please see his post: http://www.arduino.cc/cgi-bin/yabb2/YaBB.pl?num=1285962838.

When I first came up with the idea for this project, I tried to find someone who had already done what I wanted to do. I wanted to build off of someone's experiences with turning an Arduino into a keyboard but the only information I could find that was even remotely helpful was ant.b's post. I hope that someone out there finds what I have done useful for their purposes.

Here's the Arduino sketch:

/*

Keyboard sketch
by Andrew McDaniel

Copyright (c) 2010 Andrew McDaniel

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
*/




/*
When we send data to the keyboard driver, it has to be
scancodes, not ascii characters. Refer to the AVR keyboard
source code to obtain the scancodes. 
Notes:
 -Modifiers (Shift, Alt, Ctrl) only modify the next character.
 -When sending modifiers, the byte that is sent must be the amount
   of shift (referenced in the source code) plus 0xf0.
 -Only one modifier can be used at a time.
 -Once a modifier is sent, it will remain on until one of two things
   happen: another byte is sent, or the same modifier is pressed on 
   another keyboard connected to the system.
 -A delay of at least 25 milliseconds is required after each byte in
   order to gice the keyboard driver time to respond. A function to 
   take care of this might be needed.
*/

// meaningful constants
byte SC_A = 0x04;              byte SC_SPACE = 0x2c;            byte MOD_SHIFT = 0xf1;
byte SC_B = 0x05;              byte SC_ENTER = 0x28;            byte MOD_ALT = 0xf2;
byte SC_C = 0x06;              byte SC_BCKSPC = 0x2a;           byte MOD_CTRL = 0xf0;
byte SC_D = 0x07;              byte SC_TAB = 0x2b;
byte SC_E = 0x08;              byte SC_ESCAPE = 0x29;
byte SC_F = 0x09;              byte SC_ESC = 0x29;
byte SC_G = 0x0a;              byte SC_RIGHT_ARROW = 0xef;
byte SC_H = 0x0b;              byte SC_LEFT_ARROW = 0x50;
byte SC_I = 0x0c;              byte SC_DOWN_ARROW = 0x51;
byte SC_J = 0x0d;              byte SC_UP_ARROW = 0x52;
byte SC_K = 0x0e;              
byte SC_L = 0x0f;              
byte SC_M = 0x10;              
byte SC_N = 0x11;              
byte SC_O = 0x12;              
byte SC_P = 0x13;              
byte SC_Q = 0x14;              
byte SC_R = 0x15;              
byte SC_S = 0x16;              
byte SC_T = 0x17;              
byte SC_U = 0x18;              
byte SC_V = 0x19;              
byte SC_W = 0x1a;              
byte SC_X = 0x1b;              
byte SC_Y = 0x1c;              
byte SC_Z = 0x1d;              

void setup()
{
  delay(6000); //  Give the keyboard driver time to boot
  Serial.begin(9600);
}

void loop()
{
  sendSCByte(MOD_SHIFT);
  sendSCByte(SC_A);
  sendSCByte(SC_R);
  sendSCByte(SC_D);
  sendSCByte(SC_U);
  sendSCByte(SC_I);
  sendSCByte(SC_N);
  sendSCByte(SC_O);
  sendSCByte(SC_SPACE);
  
  sendSCString("This is being sent FROM THE ");
}

void sendSCByte(byte byteToSend)
{
  // This function sends single scancode bytes.
  // If using this function to send MOD bytes,
  // keep in mind the above notes.
  Serial.print(byteToSend);
  delay(25);
}

void sendSCString(String stringToSend)
{
  byte charByte;
  int lengthOfString = stringToSend.length();
  for ( int i = 0; i < lengthOfString; i++)
  {
    charByte = stringToSend.charAt(i);
    if(charByte >= 0x61 && charByte <= 0x7a)
      sendSCByte(charByte - 0x5d);
    else if(charByte >= 0x41 && charByte <= 0x5a)
    {
      sendSCByte(MOD_SHIFT);
      sendSCByte(charByte - 0x3d);
    }
    else if(charByte == 0x20)
      sendSCByte(SC_SPACE);
  }
}

This code only demonstrates sending characters and strings to the computer, but it wouldn't take much to expand it to send keystrokes on button press events.

For the atmega8u2, I used the LUFA USB stack, modified the keyboard example, and started to optimize the code for use with my Arduino sketch. Here's a .zip with all of the source needed to make a hex file (using WinAVR) for the atmega8u2:

If you load the sketch up and then reflash the 8u2 with the keyboard.hex file, it will begin typing 6 seconds after being reset. With that being said, be sure to have notepad or something in focus on your computer so that you don't accidentally screw up a sketch.

Please keep in mind that LUFA and ant.b's code and contributions are copyright their respective owners.

Arduino This is being sent FROM THE Arduino This is being sent FROM THE Arduino This is being sent FROM THE Arduino This is being sent FROM THE Arduino This is being sent FROM THE Arduino This is being sent FROM THE Arduino This is being sent FROM THE Arduino ;D ;D ;D

Nice work :slight_smile:

Very nice work, i will try it!

Hi, I have a little problem with that. The keyboard works fine, but only in program like notepad, word, or text input box in internet browser, but if I want it to work for egz in movie player (like a shortcut- i press spacebar, and movie start, pause) or in winamp, the pc just dont see my virtual arduino keyboard and nothing happend. Anyone has an idea? or mayby can you test this keyboard in many aplications too ? thx

@ayen: I'll see if i can reproduce the problem and get back.

@ayen:
I was unable to reproduce the problem you're having. I tested the functionality in Winamp, Windows Media Player, and VLC. It works using the sketch below and with the Arduino Keyboard HEX file (in the zip file) reference in my first post.

/*

Keyboard sketch
by Andrew McDaniel

Copyright (c) 2010 Andrew McDaniel

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
*/
/*
When we send data to the keyboard driver, it has to be
scancodes, not ascii characters. Refer to the AVR keyboard
source code to obtain the scancodes. 
Notes:
 -Modifiers (Shift, Alt, Ctrl) only modify the next character.
 -When sending modifiers, the byte that is sent must be the amount
   of shift (referenced in the source code) plus 0xf0.
 -Only one modifier can be used at a time.
 -Once a modifier is sent, it will remain on until one of two things
   happen: another byte is sent, or the same modifier is pressed on 
   another keyboard connected to the system.
 -A delay of at least 25 milliseconds is required after each byte in
   order to gice the keyboard driver time to respond. A function to 
   take care of this might be needed.
*/

// meaningful names for constants used in this sketch
const byte SC_SPACE = 0x2c;            const byte MOD_SHIFT = 0xf1;
const byte SC_ENTER = 0x28;            const byte MOD_ALT = 0xf2;
const byte SC_BCKSPC = 0x2a;           const byte MOD_CTRL = 0xf0;
const byte SC_TAB = 0x2b;              const byte SC_PERIOD = 0x37;
const byte SC_ESCAPE = 0x29;           const byte SC_COMMA = 0x36;
const byte SC_ESC = 0x29;              const byte SC_SEMI_AND_COLON = 0x33;
const byte SC_RIGHT_ARROW = 0xef;      const byte SC_SLASH_AND_QUESTION = 0x38;
const byte SC_LEFT_ARROW = 0x50;       const byte SC_VOL_UP = 0x80; //not working
const byte SC_DOWN_ARROW = 0x51;       const byte SC_VOL_DOWN = 0x81; //not working
const byte SC_UP_ARROW = 0x52;         const byte SC_MUTE = 0x7f; //not working
const byte SC_F1 = 0x3a;
const byte SC_F2 = 0x3b;
const byte SC_F3 = 0x3c;
const byte SC_F4 = 0x3d;
const byte SC_F5 = 0x3e;
const byte SC_F6 = 0x3f;
const byte SC_F7 = 0x40;
const byte SC_F8 = 0x41;
const byte SC_F9 = 0x42;
const byte SC_F10 = 0x43;
const byte SC_F11 = 0x44;
const byte SC_F12 = 0x45;


void setup()
{
  delay(6000); //  Give the keyboard driver time to boot
  Serial.begin(9600);
}

void loop()
{
  /*
  sendSCStringLine("This is being sent FROM THE Arduino.");
  sendSCString("This should be a new line. ");
  sendSCString("This should be the same line. ");
  upArrow();
  downArrow();
  */
  
  // windows media player pause play every five seconds
  /*
  sendSCByte(MOD_CTRL);
  sendSCString("p");
  delay(5000);
  */
  
  // winamp play pause every 5
  sendSCString("x");
  delay(5000);
  sendSCString("c");
  delay(5000);
  
  
  // most every other media player pause play every 5
  /*
  sendSCByte(SC_SPACE);
  delay(5000);
  */
  
}



void sendSCByte(byte byteToSend)
{
  // This function sends single scancode bytes.
  // If using this function to send MOD bytes,
  // keep in mind the above notes.
  Serial.print(byteToSend);
  delay(20);
}

void sendSCString(String stringToSend) // send a string of characters with no carriage return
{
  // for everyn character in the string, convert to scancode and send
  for ( int i = 0; i < stringToSend.length(); i++)
  {
    sendSCByte(convertAsciiToScanCode(stringToSend.charAt(i)));
  }
}

void sendSCStringLine(String stringToSend)  // send a string of chars with a carriage return at the end
{
  // concatenate a carraige return onto the end of the string and then send like a normal string
  stringToSend.concat(byte(0x0d));
  sendSCString(stringToSend);
}

byte convertAsciiToScanCode(byte asciiByte)  // self explanatory name
{
  
  if(asciiByte >= 0x61 && asciiByte <= 0x7a) // lower case letters
    return(asciiByte - 0x5d);
  else if(asciiByte >= 0x41 && asciiByte <= 0x5a) // upper case letters
  {
    sendSCByte(MOD_SHIFT);
    return(asciiByte - 0x3d);
  }
  else if(asciiByte >= 0x31 && asciiByte <= 0x39) // numbers 1 to 9
    return(asciiByte - 0x13);  // this actually sends the numbers across the top of the keyboard
  else if(asciiByte == 0x30)  // number 0
    return(0x27);
  else if(asciiByte == 0x20)  // space (duh)
    return(SC_SPACE);
  else if(asciiByte == 0x2e)  // is a comment really necesary for this one?
    return(SC_PERIOD);
  else if(asciiByte == 0x0a || asciiByte == 0x0d)  // line feed/new line and carriage return
    return(SC_ENTER);
  else if(asciiByte == 0x2c)
    return(SC_COMMA);
  else if(asciiByte == 0x3b)
    return(SC_SEMI_AND_COLON);
  else if(asciiByte == 0x3a)
  {
    sendSCByte(MOD_SHIFT);
    return(SC_SEMI_AND_COLON);
  }
  return(0x67);
}

// Arrow key functions shouldn't need axplanations of functionality

void upArrow()
{
  sendSCByte(SC_UP_ARROW);
}

void downArrow()
{
  sendSCByte(SC_DOWN_ARROW);
}

void leftArrow()
{
  sendSCByte(SC_LEFT_ARROW);
}

void rightArrow()
{
  sendSCByte(SC_RIGHT_ARROW);
}

ok. very sorry, everything is now working fine. at first i used only a few line of your code, and the rest was my solution. If i copy all your code, virtual keyboard works perfect. My bad. respect for your job :slight_smile: