Hi -
I have a sketch that turns my Arduino into a USB HID game controller so that I can intercept Joystick key presses in Windows. This all works fine to some degree, but to get more flexibility I would need to use keyboard commands.
Sending keyboard commands was easy with the tutorials that I found, unfortunately I do not want the keyboard to interfere with other keyboard devices and I would like to differenciate between keyboards when intercepting the presses in Windows, makes sense right?
To be able to do that I came up with the idea to use the F13 .. F24 keys instead of the Joystick reports. I created this sketch:
// SparkFun Pro Micro 5v/16mhz
// Board: "SparkFun Pro Micro 5v/16mhz"
// Programmer: AVRISP MkII
// Change this line in HID.cpp
// 0x29, 0x73, //USAGE_MAXIMUM (Keyboard Application)
#define KEY_F13 0x68
#define KEY_F14 0x69
#define KEY_F15 0x6A
#define KEY_F16 0x6B
#define KEY_F17 0x6C
#define KEY_F18 0x6D
const int buttonPins[] = {3, 4, 15, 14, 16, 10};
const int buttonKeys[] = {KEY_F13,KEY_F14,KEY_F15,KEY_F16,KEY_F17,KEY_F18};
bool buttonStates[] {false, false, false, false, false, false};
const int size = sizeof(buttonPins) / sizeof(int);
const int pollRate = 50;
long m = millis();
void setup() {
for (int i = 0; i < size; i++)
pinMode(buttonPins[i], INPUT_PULLUP);
m = millis();
}
void loop() {
if (millis() - m > pollRate)
{
m = millis();
for (int i = 0; i < size; i++)
{
int state = digitalRead(buttonPins[i]);
if (state == LOW && !buttonStates[i])
{
buttonStates[i] = true;
Keyboard.press(buttonKeys[i]);
}
else if (state == HIGH && buttonStates[i])
{
buttonStates[i] = false;
Keyboard.release(buttonKeys[i]);
}
}
}
}
The problem was that the Arduino keyboard code falsely translates the keycodes that are sent via Keyboard.press(). It all works of course, but not for these F13 .. F18 keys. I fixed it by creating two overloads for when you want to send USB HID key commands in RAW format. (not chars, not ascii, not VK_code).
The result
// Added to HID.cpp
size_t Keyboard_::rawpress(uint8_t k) {
uint8_t i;
// Add k to the key report only if it's not already present
// and if there is an empty slot.
if (_keyReport.keys[0] != k && _keyReport.keys[1] != k &&
_keyReport.keys[2] != k && _keyReport.keys[3] != k &&
_keyReport.keys[4] != k && _keyReport.keys[5] != k) {
for (i=0; i<6; i++) {
if (_keyReport.keys[i] == 0x00) {
_keyReport.keys[i] = k;
Serial.println(k);
break;
}
}
if (i == 6) {
setWriteError();
return 0;
}
}
sendReport(&_keyReport);
return 1;
}
size_t Keyboard_::rawrelease(uint8_t k) {
uint8_t i;
// Test the key report to see if k is present. Clear it if it exists.
// Check all positions in case the key is present more than once (which it shouldn't be)
for (i=0; i<6; i++) {
if (0 != k && _keyReport.keys[i] == k) {
_keyReport.keys[i] = 0x00;
}
}
sendReport(&_keyReport);
return 1;
}
When you inspect the code that this replaces you'll find that it is the same implementation with the exception of some code that translates the input code into whatever USB HID wants to have. You'll need to change the release and press calls in the sketch above.
Unfortunately one more thing was missing because the keys still weren't registered by windows. It was missing a proper HID descriptor:
This was changed:
0x29, 0x65, // USAGE_MAXIMUM (Keyboard Application)
To this:
0x29, 0x73, // USAGE_MAXIMUM (Keyboard Application)
And now it all works. Sometimes I do searches in these forums and not find anything, I hope this might be useful to someone. Please do post back.