Arduino UNO R3 USB Keyboard

Hello everyone,

I am trying to make a simple "keyboard" out of Arduino UNO R3 to control videogames on PC (I just need up-down-left-right arrows and WASD controls).

I know that other boards provide native HID support but I discovered it only while working with UNO.

Fortunately I found a solution: flashing Arduino UNO with HID Firmware to make it recognised as a keyboard... but I still have some issues.

Here is the code that I found on the internet and that I'm trying to use (this is just for the "W" key):

/* Arduino USB Keyboard HID demo

uint8_t buf[8] = {
0 }; /* Keyboard report buffer */

#define PIN_UP 5

int state = 1;

void setup()
{
Serial.begin(9600);
pinMode(PIN_UP, INPUT);
// enable internal pull-ups
digitalWrite(PIN_UP, 1);

delay(200);
}

void loop()
{
state = digitalRead(PIN_UP);
if (state != 1) {
buf[2] = 26; // W key
Serial.write(buf, 8 ); // Send keypress
releaseKey();
}
}

void releaseKey()
{
buf[0] = 0;
buf[2] = 0;
Serial.write(buf, 8 ); // Release key
}

With this code, when I connect pin 5 directly to ground, I simulate the press of "W" on my keyboard.

The only problem is that it is not detected as a "continuous press".
In fact when I try this in any simple game I see the character going step by step instead of running, just like when pressing "W" multiple times instead of keeping it pressed.

Could you help me understanding what is causing this behaviour?

Thanks in advance!

I know nothing about the HID firmware that you are using, but the program that you posted seems to call the releaseKey() function every time through loop if state is not equal to 1. Try adding an else to the if statement and only call releaseKey() when state equals 1

Thanks for the reply UKHeliBob. The firmware that I am using is the one that I found here:

I also tried the unojoy firmware as suggested here in the arduino website (which also has a more clear coding going with it in my opinion) but I can't get that one to work so I used this second one.

Could you tell me more exactly what I should write in the code? Sorry, I am just starting out with this!

I have no way of testing it but this is what I had in mind

// Arduino USB Keyboard HID demo

uint8_t buf[8] =
{
  0
};    /* Keyboard report buffer */

#define PIN_UP 5

int state = 1;

void setup()
{
  Serial.begin(9600);
  pinMode(PIN_UP, INPUT_PULLUP);  //a minor change to make the code more readable
  delay(200);
}

void loop()
{
  state = digitalRead(PIN_UP);
  if (state == LOW)
  {
    buf[2] = 26;     // W key
    Serial.write(buf, 8 );   // Send keypress
  }
  else
  {
    releaseKey();
  }
}

void releaseKey()
{
  buf[0] = 0;
  buf[2] = 0;
  Serial.write(buf, 8 );   // Release key
}

Thank you so much! It works as intended now! :slight_smile:

Two quick questions: the informations sent to identify the W key are universal across all USB HID devices? Should I expect the same correct behaviour if I now connect the Arduino to a Mac or gaming console?

My second question is about the code: I see that if the state is LOW the "26" is written in buf[2] ELSE the key is released.

I do not understand why is not the other way around - if pin 5 is connected to ground (HIGH) then the information is written in declared unit8_t.

Thank you very much for the help!

I do not understand why is not the other way around - if pin 5 is connected to ground (HIGH) then the information is written in declared unit8_t.

If pin 5 is connected to ground, the pin will read LOW, which, because the pullup resistors are enabled, means that the switch is pressed, so sending the key pressed data makes perfect sense to me. When the pin reads HIGH, it means that the switch is not pressed, so sending the key released data makes perfect sense to me.

What does NOT make sense is sending garbage in position 1 in the array.

the informations sent to identify the W key are universal across all USB HID devices?

I don't know, but it may well be the case. Why not try it and see ?

I do not understand why is not the other way around

The input pin's pinMode() is set as INPUT_PULLUP which keeps the input HIGH unless you connect it to GND which causes it to go LOW

So

  state = digitalRead(PIN_UP);
  if (state == LOW)

tests whether the pin has been connected to GND, ie has been pressed, assuming that the switch is wired to take the pin to GND when the button is pressed.

Thank you for the explanation! I clearly understand now :slight_smile:

A little update:

I tried to expand the code and also have other keys with differet pins by adding them like this:

// Arduino USB Keyboard HID demo

uint8_t buf[8] =
{
  0
};    /* Keyboard report buffer */

#define PIN_LEFT 4
#define PIN_RIGHT 5
#define PIN_UP 6
#define PIN_DOWN 7

int state = 1;

void setup()
{
  Serial.begin(9600);
  pinMode(PIN_LEFT, INPUT_PULLUP);
  pinMode(PIN_RIGHT, INPUT_PULLUP);
  pinMode(PIN_UP, INPUT_PULLUP);
  pinMode(PIN_DOWN, INPUT_PULLUP);  
  delay(200);
}

void loop()
{
  state = digitalRead(PIN_LEFT);
  if (state == LOW)
  {
    buf[2] = 4;     // A key
    Serial.write(buf, 8 );   // Send keypress
  }
  state = digitalRead(PIN_RIGHT);
  if (state == LOW)
  {
    buf[2] = 7;     // D key
    Serial.write(buf, 8 );   // Send keypress
  }
  state = digitalRead(PIN_UP);
  if (state == LOW)
  {
    buf[2] = 26;     // W key
    Serial.write(buf, 8 );   // Send keypress
  }
  state = digitalRead(PIN_DOWN);
  if (state == LOW)
  {
    buf[2] = 22;     // S key
    Serial.write(buf, 8 );   // Send keypress
  }
  else
  {
    releaseKey();
  }
}

void releaseKey()
{
  buf[0] = 0;
  buf[2] = 0;
  Serial.write(buf, 8 );   // Release key
}

Unfortunately I only have the last key (F) working this way. I do not understand how I could insert additional keys correctly!

Every time you do a digitalRead(), you overwrite the result of the last digitalRead()

Use a different variable for the state of each key.

  stateA = digitalRead(PIN_LEFT);
  if (stateA == LOW)
  {
    buf[2] = 4;     // A key
    Serial.write(buf, 8 );   // Send keypress
  }

Then call releaseKey() only when none of the states are TRUE

Unfortunately I only have the last key (F) working this way.

Look carefully at what the code does. If any key except PIN_DOWN is LOW the code will get to the final else clause and call releaseKey()

Change the tests into if/else if/else if and so on so that first one to return true sets the value in buf[] and writes it. Then only if no keys are pressed will the final else be entered and call releaseKey()

If you are going to have more than 4 key inputs then there are better ways to manage it using arrays but get the current version working first.

UKHeliBob:
Look carefully at what the code does. If any key except PIN_DOWN is LOW the code will get to the final else clause and call releaseKey()

Change the tests into if/else if/else if and so on so that first one to return true sets the value in buf[] and writes it. Then only if no keys are pressed will the final else be entered and call releaseKey()

If you are going to have more than 4 key inputs then there are better ways to manage it using arrays but get the current version working first.

I understand what you mean. I have tried changing the conditionals to if/else if/else if/else if/else but, having the state variable in between the conditional statements gave me the error "else if without if before".

Hence I followed evanmars suggestion and declared different variables for each key, getting a more compact code:

// Arduino USB Keyboard HID demo

uint8_t buf[8] =
{
  0
};    /* Keyboard report buffer */

#define PIN_LEFT 4
#define PIN_RIGHT 5
#define PIN_UP 6
#define PIN_JUMP 7

int stateA = 1;
int stateD = 1;
int stateW = 1;
int stateF = 1;

void setup()
{
  Serial.begin(9600);
  pinMode(PIN_LEFT, INPUT_PULLUP);
  pinMode(PIN_RIGHT, INPUT_PULLUP);
  pinMode(PIN_UP, INPUT_PULLUP);
  pinMode(PIN_JUMP, INPUT_PULLUP);  
  delay(200);
}

void loop()
{
  if (stateA = digitalRead(PIN_LEFT) == LOW)
  {
    buf[2] = 4;     // A key
    Serial.write(buf, 8 );   // Send keypress
  }
  else if (stateD = digitalRead(PIN_RIGHT) == LOW)
  {
    buf[2] = 7;     // D key
    Serial.write(buf, 8 );   // Send keypress
  }
  else if (stateW = digitalRead(PIN_UP) == LOW)
  {
    buf[2] = 26;     // W key
    Serial.write(buf, 8 );   // Send keypress
  }
  else if (stateF = digitalRead(PIN_JUMP) == LOW)
  {
    buf[2] = 9;     // F key
    Serial.write(buf, 8 );   // Send keypress
  }
  else
  {
    releaseKey();
  }
}

void releaseKey()
{
  buf[0] = 0;
  buf[2] = 0;
  Serial.write(buf, 8 );   // Release key
}

But I still have the same issue of the last key (F) being the only one detected (I changed the S - PIN_DOWN - to F - PIN_JUMP).

Also, I am not sure if this way the code is less efficient or not!

Thank you all for the precious advice :slight_smile:

You don't actually need any state variables

void loop()
{
  if (digitalRead(PIN_LEFT) == LOW)
  {
    buf[2] = 4;     // A key
    Serial.write(buf, 8 );   // Send keypress
  }
  else if (digitalRead(PIN_RIGHT) == LOW)
  {
    buf[2] = 7;     // D key
    Serial.write(buf, 8 );   // Send keypress
  }
  else if (digitalRead(PIN_UP) == LOW)
  {
    buf[2] = 26;     // W key
    Serial.write(buf, 8 );   // Send keypress
  }
  else if (digitalRead(PIN_JUMP) == LOW)
  {
    buf[2] = 9;     // F key
    Serial.write(buf, 8 );   // Send keypress
  }
  else
  {
    releaseKey();
  }
}

Hello guys, the device is working as intended!

The one thing that I am looking for is a way for the computer to detect multiple keystrokes at the same time because right now only one is detected and I cannot send a new one without releasing the previous.

Any idea on how this could be implemented?

Please post the complete working program

Thanks for the reply UKHeliBob.

Following the code in this thread (for UNO) I have done something similar for the Micro. Here is the code:

#include "Keyboard.h"

void setup() 
{ 
  Serial.begin(9600);
  pinMode(2, INPUT_PULLUP);
  pinMode(3, INPUT_PULLUP);
  pinMode(4, INPUT_PULLUP);
  pinMode(5, INPUT_PULLUP);
  Keyboard.begin();
}

void loop() 
{
  if (digitalRead(2) == LOW) {
    Keyboard.press('w');
  }
  else if (digitalRead(3) == LOW) {
    Keyboard.press('s');
  }
  else if (digitalRead(4) == LOW) {
    Keyboard.press('a');
  }
  else if (digitalRead(5) == LOW) {
    Keyboard.press('d');
  }
  else {
    Keyboard.releaseAll();
  }
}

It works pretty much the same as far as I have tested it.

I'm just wondering how to implement the above mentioned functionality of sending detecting multiple keypress at the same time.

Thank you for the help!

I'm just wondering how to implement the above mentioned functionality of sending detecting multiple keypress at the same time.

Can you please be more explicit about what you want to do. Perhaps give some examples of the multiple simultaneous key presses that you want to detect and what you want to send as a result of them being pressed

Detecting the states of multiple digital inputs is easy, if that is what you have in mind. I would start by reading all of the inputs using a for loop at the beginning of loop() and putting the values in an array for use in the rest of the program.

Thanks for the reply UKHeliBob.

Being more explicit: right now if I use arduino with this code as a game controller my character can only go in one direction (up, left, down, right - WASD) and cannot go in diagonal as if I were pressing the W+A (or D) keys. I suppose this is because with this code I can only send one key press at a given time?

That's what I thought you were probably trying to do.

There are two things to get working.

First detect whether 2 buttons are pressed.
First I would give the input pins sensible names to make the code easier to read. Something like

const byte wKey = 2;
const byte sKey = 3;
const byte aKey = 4;
const byte dKey = 5;

Now to detect whether two of them are currently pressed
Read all of them at the start of loop()

byte aKeyState = digitalRead(aKey);
etc
etc

Then test the states

if (aKeyState == LOW && sKeyState == LOW)  //a and s pressed
  {
     //code here to send 'a' and 's'
  }

The second thing to get working is sending the two keys.

I suspect, but don't know and can't test, that sending them one after the other like this will work

    Keyboard.press('a');
    Keyboard.press('s');

Over to you to try it

Thanks for the very clear explanation!

If I understand correctly, in case I need to send more than 2 keypress at the same time I then would have to clearly state them in the code?