0

I am creating a keyboard and the PC I use it with is configured for UK so the keyboard layout it expects is this

UK keyboard with UK - Us differences highlighted

I've highlighted the ISO-UK vs ANSI-US differences but the only key I am having real problems with is the backslash key.

My physical layout is actually this (ortholinear "Planck") but it's essentially the same except with two logical layers and variation in positioning.

Planck style keyboard

The above is a fully functional UK-layout keyboard with the exception of the "\" key.

The sketch I am using is too large and distracting to post here but the following test sketch

#include "Keyboard.h"

byte keylist[] = { '`', '2', '3', '\'', '#', '\\' }; 

void setup() {
  Keyboard.begin();
  delay(1000);

  // unshifted
  for (byte i = 0; i < sizeof(keylist); i++) {
    Keyboard.press(keylist[i]);      delay(100);
    Keyboard.release(keylist[i]);    delay(100);
  }

  Keyboard.press(KEY_RETURN);
  Keyboard.releaseAll();

  // shifted
  for (byte i = 0; i < sizeof(keylist); i++) {
    Keyboard.press(KEY_LEFT_SHIFT);
    Keyboard.press(keylist[i]);
    delay(100);
    Keyboard.releaseAll();
    delay(100);
  }
}

void loop() {
  delay(1000);
}

Creates this output (in a Notepad window)

`23'£#
¬"£@£~ 

I did try switching focus to a keyboard diagnostic tool - KeyHitter by Elite Keyboards which showed different results

12:31.0675 ` (0xDF, BIOS 0x29) DOWN
12:31.0771 ` (0xDF, BIOS 0x29) UP -> 100ms
12:32.0772 2 (0x32, BIOS 0x03) DOWN
12:32.0871 2 (0x32, BIOS 0x03) UP -> 100ms
12:33.0872 3 (0x33, BIOS 0x04) DOWN
12:33.0972 3 (0x33, BIOS 0x04) UP -> 100ms
12:34.0972 ' (0xC0, BIOS 0x28) DOWN
12:35.0072 ' (0xC0, BIOS 0x28) UP -> 100ms
12:36.0072 LShift (0x10, BIOS 0x2A) DOWN
12:36.0082 3 (0x33, BIOS 0x04) DOWN
12:36.0172 LShift (0x10, BIOS 0x2A) UP -> 100ms
12:36.0182 3 (0x33, BIOS 0x04) UP -> 100ms
12:37.0172 \ (0xDE, BIOS 0x2B) DOWN
12:37.0272 \ (0xDE, BIOS 0x2B) UP -> 100ms
12:38.0271 Enter (0x0D, BIOS 0x1C) DOWN
12:38.0273 Enter (0x0D, BIOS 0x1C) UP -> 2ms

...

So the Arduino Keyboard library translates '#' into shift+3 which is unhelpful for UK users but I'm mystified why Keyhitter sees that '\\' produces the expected \ but other programs see it producing #! I guess Keyhitter intercepts keystrokes at a lower level than normal applications - for example it correctly shows Win being pressed but prevents Windows opening a start-menu.

The result in Notepad is typical of the effect produced in normal applications. So that's probably more relevant.

I can work around the '#' producing a shift+3 since '\\' produces # but how then do I generate a \?

4
  • try for (byte i = 0; i <= 255; i++) { Keyboard.press(i) ....... .... one of the values should generate the desired character ... then use that value instead of '\\' – jsotola Jan 3 at 19:51
  • @jsotola: I'm a bit wary of inadvertently hitting sequences of special keys like Win followed by say F1 - so I tried an app that I suspect captures keystrokes at a low level - but that wasn't too helpful. I may try your suggestion if I can work in some safeguards so that I have time to work out what's happening before the system descends into a frenzied chaos. – RedGrittyBrick Jan 3 at 21:24
  • figure out the byte value that represents the Win key and exclude it ... if ( i != Win ) Keyboard.press(i); – jsotola Jan 4 at 0:25
  • @jsotola: I was sceptical but that approach worked. I had to use a button press to increment my index because you encounter a lot of function keys etc causing help windows and other weird things to happen that you need time to recover from with a real keyboard. Keyboard.press(0xEC) produces "\" in Notepad and when used in conjunction with shift produces the expected "|". – RedGrittyBrick Jan 5 at 21:04
2

The USB keyboard protocol has no concept of characters. It doesn't know and doesn't care what character a key represents. Instead each code represents the location of the key on the keyboard, and it's down to the software on the computer to map those locations into characters through its own keyboard map.

So the Arduino has to do the opposite when you want to "print" text through the keyboard - convert that text into the locations of the keys on the keyboard. And it does that through the _asciimap array in Keyboard.cpp.

To use any other keyboard layout than US you need to modify that map so that each ASCII value points to the correct location for that character as defined in your computer's keyboard map.

You can find the list of keyboard location codes (named in this document after the US keycap decals) here (page 53) which you can then use to map any changes.

For example in _asciimap a double quote is listed as:

     0x34|SHIFT,    // "

Which maps to Keyboard ' and " in USB. But for UK you want that to be shift-2, which is Keyboard 2 and @ in a US layout. That entry is hex 1F.

So changing the quote to be a shifted 0x1F should give you the quote you desire:

    0x1F|SHIFT, // "

You would need to do the same to all other characters that need re-mapping (including ). Some may take a little trial and error to work out what the equivalent key on a UK keyboard is, since some keys are in physically different locations (most notably there is no key to the left of Z on a US keyboard, but there is an extra one to the right of ]).

Unfortunately the Arduino Keyboard library doesn't provide a way for a user to give their own keyboard map. Instead you have to modify the source code of the library itself.

1
  • This makes sense but there is more going on because, on my Arduino-based keyboard, shift+2 already produces " rather than @ - so there's some additional mapping in Windows that's already doing most of the work of making my keyboard behave like an ISO-UK keyboard. (If interested Keyboard.press(0xEC) is an effective UK "\" key. This whole area seems a lot more complex than I remember back in the days of PC-XT keyboard scancodes and TSRs – RedGrittyBrick Jan 5 at 21:08
1

For completeness I am posting this answer. You can produce a UK backslash keystroke event by using something like

#define KEY_BSLASH 0xEC

This was found rather more empirically than analytically so is not an especially informative result, but it might be of some use to others creating non-ANSI keyboard layouts using Arduinos.

1
  • If it works on ISO-UK, it is likely to work on every ISO layout (e.g. give “<” in AZERTY). And this makes your answer more generally useful. – Edgar Bonet Jan 5 at 21:48

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.