1

Greeting, first time Arduino user here.

So I am using the Arduino Micro as an HID keyboard device. It accepts arbitrary input from a Node-JavaScript program I am running (so I have full freedom in in the data transferred to the micro).

The goal is for the data transmitted to the micro to include information about a keypress: whether it was pressed or released and which key it is. The serial connections are all fine and dandy - I just have questions regarding the key pressing logic.

As far as the JavaScript program goes, a key down is detected based on the keyCode, not the character code so when tapping “a”, character code 97, the Arduino micro is receiving “A”, character code 65 since the JavaScript detected keyCode is 65. So that all sounds like problems with JavaScript (which it is), but remember that I have freedom in portraying the information sent to the micro.

That being said, instead of detecting by keyCodes and character codes in JavaScript, I could just transmit the letter e.g. “a” with some identifier for press/release such as “+a” for release and “-a” for press. My problem here is how would I handle modifier keys using the Keyboard library.

Should I handle character keys and modifiers keys separately i.e. Keyboard.write for characters and Keyboard.press/release for modifiers or are there cases where it would make sense to press and hold “a” (which would also be different than pressing and holding “A”)?

Below is my original code using keyCodes and “u/d” identifiers to denote up or down (which I am thinking now that “u/d” could be easily confused as a letter if I decide to transmit characters as characters:

/*
  Receives from the hardware serial, sends to software serial.
  Receives from software serial, sends to hardware serial.

  The circuit:
   RX is digital pin 10 (connect to TX of other device)
   TX is digital pin 11 (connect to RX of other device)

  Not all pins on the Leonardo and Micro support change interrupts,
  so only the following can be used for RX:
  8, 9, 10, 11, 14 (MISO), 15 (SCK), 16 (MOSI).
*/

#include <SoftwareSerial.h>
#include <Keyboard.h>

SoftwareSerial rpiSerial(10, 11); // RX, TX

void setup() {
  // Open serial communications and wait for port to open:
  Serial.begin(115200);
  // Set the data rate for the SoftwareSerial port
  rpiSerial.begin(115200);
  // initialize control over the keyboard:
  Keyboard.begin();
  while (!Serial) {
    ; // wait for serial port to connect. Needed for native USB port only
  }

  Serial.println("Keyboard Serial Connection Established");
}

String keyCode = "";
char state;

void loop() {
  while (rpiSerial.available() > 0) {
    // "Type" via keyCodes (recommended for modifiers)
    // https://www.arduino.cc/en/Reference/KeyboardWrite not behaving as expected

    int data = rpiSerial.read();
    if (isDigit(data)) {
      keyCode += (char)data;
    }

    if (isAlpha(data)) {
      state = (char)data;
    }

    if (data == '\n') {
      Serial.print(state);
      Serial.println(keyCode.toInt());

      if (state == 'd') {
        Keyboard.press(keyCode.toInt());
      }

      if (state == 'u') {
        Keyboard.release(keyCode.toInt());
      }

      keyCode = "";
      char state;
    }
  }
}

I have also noticed that the data from the serial connection parsed by the micro is sometimes incorrect i.e. a keyCode will be misidentified. For instance, by repeatedly pressing “a” through my JavaScript program, the JavaScript program will consistently identify keyCode 65 yet Arduino will identify the received data as keyCode 65 most of the time and then randomly keyCode 64. This happens with other keys as well and the keyCode doesn’t always happen to be just one less nor does it always occur after “n” presses.

  • SoftwareSerial is not reliable at 115200. use 9600 baud – Juraj Apr 30 at 15:30
  • Good to know! I will try that @Juraj – Sterling Butters Apr 30 at 15:32
  • ASCII code for 'A' is 65 and for 'a' it is 97. asciitable.com the key on keyboard is 'A', not 'a'. – Juraj Apr 30 at 15:33
  • So how would you recommend identifying with the micro that the key has been pressed/released? – Sterling Butters Apr 30 at 15:36
  • what do you want to do in Arduino sketch while the key is pressed? – Juraj Apr 30 at 17:14
1

Modifier keys are treated no different to normal keys.

The Arduino Keyboard library contains code to map different keys from the internal list of macros to specific keypresses:

  • If the key value is >= 138 it's a non-printing special key (subtract 138)
  • If the key value is >= 128 it is a modifier key (1 << (k - 128))
  • Otherwise look the code up in a mapping table.

That means you can use:

Keyboard.press(KEY_LEFT_SHIFT);

or

Keyboard.release(KEY_RIGHT_GUI);

The full list of modifier keys is:

#define KEY_LEFT_CTRL   0x80
#define KEY_LEFT_SHIFT    0x81
#define KEY_LEFT_ALT    0x82
#define KEY_LEFT_GUI    0x83
#define KEY_RIGHT_CTRL    0x84
#define KEY_RIGHT_SHIFT   0x85
#define KEY_RIGHT_ALT   0x86
#define KEY_RIGHT_GUI   0x87

So to do a capital A you can use:

Keyboard.press(KEY_LEFT_SHIFT);
Keyboard.press('a');
Keyboard.release('a');
Keyboard.release(KEY_LEFT_SHIFT);

There is also a shortcut, and that is:

Keyboard.press('A');
Keyboard.release('A');

The character 'A' is the same in the lookup table as character 'a' except the high bit of the result is set, which triggers a "Press or release the shift key" addition in the mapping code.

A USB keyboard report consists of 8 bytes:

  • Up to 8 modifier keys (bitmap)
  • One reserved byte
  • 6 bytes to indicate pressed keys

This means that you can have up to 6 keys pressed at once, plus modifier keys. Never any more than that. So you don't need to handle any more than that in your code.

The current list of pressed keys is maintained by the keyboard and is sent in every report. There's no concept as such of "pressed" or "released" - only the list of what is currently pressed. If a key is released it's just removed from that list of pressed keys.

So what you need is some way of transferring that data in such a way that the Arduino can interpret it properly.

Since the data is not high throughput you can afford to be quite wasteful in your protocol. I would suggest using a fairly verbose ASCII based protocol.

  • All the protocol data is using ASCII characters between 32 and 127 inclusive
  • This frees up control characters (<32) for controlling the protocol.

The simplest thing is to use a single control character (such as \n) to act as a line ending to break your data up into packets. Each packet can then contain a list of instructions.

For example, you might choose to have a protocol that looks like:

P,129\n
P,97\n
R,97\n
R,129\n

By treating each line as a complete entity you know that you want to Press key 129 (#define KEY_LEFT_SHIFT 0x81), press key 97 ('a'), release 97, then release 129.

There's no confusion, since commands are all a single upper-case character, and keys are all a decimal number.

For your application, though, a "raw" access interface would be of more use, since you don't really want the Arduino interpreting things for you. However, the API doesn't provide that kind of thing. So you just have to make the best of what you have and work around it.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.