Observations on Teensy USB keyboard support

Status
Not open for further replies.

Nantonos

Well-known member
I made these notes while investigating Teensy keyboard support, then thought they would be useful to share. There are specific bug reports and suggestions for enhancement, in bold.

The key constants are derived from labelling of the US ANSI no-altgr keyboard layout
http://en.wikipedia.org/wiki/File:KB_United_States-NoAltGr.svg

The modifiers (four on Teensyduino, eight in C) are combined with the base keys.

So to send ctrl-C you do
Code:
usb_keyboard_press(KEY_C, MODIFIER_KEY_CTRL);
if you are using C and makefiles. Apparently though Teensyduino users don't have things so simple. They need instead to do
Code:
Keyboard.set_modifier(MODIFIERKEY_CTRL);
Keyboard.set_key1(KEY_C);
Keyboard.set_key2(0);
Keyboard.set_key3(0);
Keyboard.set_key4(0);
Keyboard.set_key5(0);
Keyboard.set_key6(0);
Keyboard.send_now();
Keyboard.set_key1(0); 
Keyboard.send_now();
Keyboard.set_key1(0); 
Keyboard.send_now();

Three comments, firstly why do Teensyduino users need to use so many more lines of code? It would be better to add the same function as in the C binding, for the cases where only a single key plus a modifier is pressed, then released.
Secondly, why is there only access to left-hand modifiers? It would be better to support the right-hand modifier keys in Teensyduino as well as the left hand ones.
And thirdly (minor), why the MOFIFIER_KEY_foo vs MODIFIERKEY_foo difference? It would be better to be consistent (although a reason to not be consistent would be that a change might break existing code).

As required by the USB HID Keyboard spec, the key names however refer to layout, not labelling. So for example, to produce ctrl-A on a French AZERTY keyboard
http://en.wikipedia.org/wiki/File:KB_France.svg
requires
Code:
usb_keyboard_press(KEY_Q, MODIFIER_KEY_CTRL);
because KEY_Q means the leftmost top row key, which is labelled A. This is confusing, and is probably the reason for the A-grave becoming Q-grave bug I reported earlier. But this layout-based naming comes from the USB keyboard spec.

Or, on Teensyduino,
Code:
Keyboard.set_modifier(MODIFIERKEY_CTRL);
Keyboard.set_key1(KEY_Q);
Keyboard.set_key2(0);
Keyboard.set_key3(0);
Keyboard.set_key4(0);
Keyboard.set_key5(0);
Keyboard.set_key6(0);
Keyboard.send_now();
Keyboard.set_key1(0); 
Keyboard.send_now();
Keyboard.set_key1(0); 
Keyboard.send_now();
There is no direct support for dead keys, but again by reference to the ANSI keyboard layout they can be produced. So to output ë which, on a French AZERTY keyboard, is typed with the diaresis dead key followed by e, one would do
Code:
usb_keyboard_press(KEY_LEFT_BRACE, MODIFIER_KEY_SHIFT);
usb_keyboard_press(KEY_E, 0);
or on Teensyduino
Code:
Keyboard.set_modifier(MODIFIERKEY_SHIFT);
Keyboard.set_key1(KEY_LEFT_BRACE);
Keyboard.set_key2(0);
Keyboard.set_key3(0);
Keyboard.set_key4(0);
Keyboard.set_key5(0);
Keyboard.set_key6(0);
Keyboard.send_now();
Keyboard.set_modifier(0);
Keyboard.set_key1(KEY_E);
Keyboard.send_now();
Keyboard.set_key1(0); 
Keyboard.send_now();

alt-gr is the same as MODIFIER_KEY_RIGHT_ALT which is available in C but not in Teensyduino. So any keypress that needs alt-gr (€¦ on UK English; €#{[¦`\^@]}¤ on French AZERTY) can only be produced with Keyboard.print();. It would be better to allow the right-hand modifier keys in Teensyduino.

Keys not on the US ANSI layout can't be produced at all by the current Teensy implementation. The menu key, the Fn key, and (on Japanese keyboards) the three keys near the spacebar which control character input methods, and the underscore/backslash keypresses cannot be produced
http://en.wikipedia.org/wiki/File:KB_Japanese.svg

Keyboard Menu has HID code 118 (0x76) so could be supported
.
 
Last edited:
The keycode constants evaluate to integers which are USB HID Usage IDs for the Keyboard/Keypad Page.
see Chapter 10, page 53ff of
http://www.usb.org/developers/devclass_docs/Hut1_12v2.pdf

also
http://www.usb.org/developers/devclass_docs/HID1_11.pdf


in keylayouts.h there is support for media keys but these are not documented, and the codes conflict with Keyboard/Keypad Page codes.
Code:
#define KEY_MEDIA_VOLUME_INC    0x01
#define KEY_MEDIA_VOLUME_DEC    0x02
#define KEY_MEDIA_MUTE          0x04
#define KEY_MEDIA_PLAY_PAUSE    0x08
#define KEY_MEDIA_NEXT_TRACK    0x10
#define KEY_MEDIA_PREV_TRACK    0x20
#define KEY_MEDIA_STOP          0x40
#define KEY_MEDIA_EJECT         0x80

while the listed USB Keyboard/Keypad codes are
Code:
0x7F	Keyboard Mute
0x80	Keyboard Volume Up
0x81	Keyboard Volume Down
 
There are missing codes: 1-3, 100-103, 116-164, 176-221, 224-231. These should be added.

Code:
1	Keyboard ErrorRollOver
2	Keyboard POSTFail
3	Keyboard ErrorUndefined
100	Keyboard Non-US \ and |
101	Keyboard Application
102	Keyboard Power
103	Keypad =
116	Keyboard Execute
117	Keyboard Help
118	Keyboard Menu
119	Keyboard Select
120	Keyboard Stop
121	Keyboard Again
122	Keyboard Undo
123	Keyboard Cut
124	Keyboard Copy
125	Keyboard Paste
126	Keyboard Find
127	Keyboard Mute
128	Keyboard Volume Up
129	Keyboard Volume Down
130	Keyboard Locking Caps Lock
131	Keyboard Locking Num Lock
132	Keyboard Locking Scroll Lock
133	Keypad Comma
134	Keypad Equals Sign
135	Keyboard International1
136	Keyboard International2
137	Keyboard International3
138	Keyboard International4
139	Keyboard International5
140	Keyboard International6
141	Keyboard International7
142	Keyboard International8
143	Keyboard International9
144	Keyboard LANG1
145	Keyboard LANG2
146	Keyboard LANG3
147	Keyboard LANG4
148	Keyboard LANG5
149	Keyboard LANG6
150	Keyboard LANG7
151	Keyboard LANG8
152	Keyboard LANG9
153	Keyboard Alternate Erase
154	Keyboard SysReq/Attention
155	Keyboard Cancel
156	Keyboard Clear
157	Keyboard Prior
158	Keyboard Return
159	Keyboard Separator
160	Keyboard Out
161	Keyboard Oper
162	Keyboard Clear/Again
163	Keyboard CrSel/Props
164	Keyboard ExSel
176	Keypad 00
177	Keypad 000
178	Thousands Separator
179	Decimal Separator
180	Currency Unit
181	Currency Sub-unit
182	Keypad (
183	Keypad )
184	Keypad {
185	Keypad }
186	Keypad Tab
187	Keypad Backspace
188	Keypad A
189	Keypad B
190	Keypad C
191	Keypad D
192	Keypad E
193	Keypad F
194	Keypad XOR
195	Keypad ^
196	Keypad %
197	Keypad <
198	Keypad >
199	Keypad &
200	Kepad &&
201	Keypad |
202	Keypad ||
203	Keypad :
204	Keypad #
205	Keypad Space
206	Keypad @
207	Keypad !
208	Keypad Memory Store
209	Keypad Memory Recall
210	Keypad Memory Clear
211	Keypad Memory Add
212	Keypad Memory Subtract
213	Keypad Memory Multiply
214	Keypad Memory Divide
215	Keypad +/-
216	Keypad Clear
217	Keypad Clear Entry
218	Keypad Binary
219	Keypad Octal
220	Keypad Decimal
221	Keypad Hexadecimal
224	Keyboard LeftControl
225	Keyboard LeftShift
226	Keyboard LeftAlt
227	Keyboard Left GUI
228	Keyboard RightControl
229	Keyboard RightShift
230	Keyboard RightAlt
231	Keyboard Right GUI
 
Teensy 3.0 and USB keyboard

Teensy beta 8
Teensy 3.0
Win 7/64 SP1
USB Type:K+M+J
Examples > Teensy > USB_Keyboard > Simple

will not compile.
Code:
F:\Arduino\arduino-1.0.2-teensybeta8\hardware\teensy\cores\teensy3\usb_keyboard.c: In function 'usb_keyboard_press_keycode':
F:\Arduino\arduino-1.0.2-teensybeta8\hardware\teensy\cores\teensy3\usb_keyboard.c:237: error: 'keyboard_report_data' undeclared (first use in this function)
F:\Arduino\arduino-1.0.2-teensybeta8\hardware\teensy\cores\teensy3\usb_keyboard.c:237: error: (Each undeclared identifier is reported only once
F:\Arduino\arduino-1.0.2-teensybeta8\hardware\teensy\cores\teensy3\usb_keyboard.c:237: error: for each function it appears in.)

the offending bit of usb_keyboard.c is
Code:
	#ifdef DEADKEYS_MASK
	deadkeycode = deadkey_to_keycode(keycode);
	if (deadkeycode) {
		modrestore = keyboard_report_data[0];
		if (modrestore) {
			keyboard_report_data[0] = 0;
			send_now();
		}
		// TODO: test if operating systems recognize
		// deadkey sequences when other keys are held
		mod = keycode_to_modifier(deadkeycode);
		key = keycode_to_key(deadkeycode);
		usb_keyboard_press_key(key, mod);
		usb_keyboard_release_key(key, mod);
	}
	#endif
 
alt-gr is the same as MODIFIER_KEY_RIGHT_ALT which is available in C but not in Teensyduino. So any keypress that needs alt-gr (€¦ on UK English; €#{[¦`\^@]}¤ on French AZERTY) can only be produced with Keyboard.print();. It would be better to allow the right-hand modifier keys in Teensyduino.

Whilst I have been working with the Teensy I have discovered that AltGr is actually just CTRL-ALT anyway so not hard to achieve in practice.

Also both the left and right hand modifiers appear to be mapped:

\arduino-1.0.1\hardware\teensy\cores\teensy\Keylayouts.h:

#define MODIFIERKEY_CTRL ( 0x01 | 0x8000 )
#define MODIFIERKEY_SHIFT ( 0x02 | 0x8000 )
#define MODIFIERKEY_ALT ( 0x04 | 0x8000 )
#define MODIFIERKEY_GUI ( 0x08 | 0x8000 )
#define MODIFIERKEY_LEFT_CTRL ( 0x01 | 0x8000 )
#define MODIFIERKEY_LEFT_SHIFT ( 0x02 | 0x8000 )
#define MODIFIERKEY_LEFT_ALT ( 0x04 | 0x8000 )
#define MODIFIERKEY_LEFT_GUI ( 0x08 | 0x8000 )
#define MODIFIERKEY_RIGHT_CTRL ( 0x10 | 0x8000 )
#define MODIFIERKEY_RIGHT_SHIFT ( 0x20 | 0x8000 )
#define MODIFIERKEY_RIGHT_ALT ( 0x40 | 0x8000 )
#define MODIFIERKEY_RIGHT_GUI ( 0x80 | 0x8000 )

See \arduino-1.0.1\hardware\teensy\cores\usb_hid\usb_api.cpp

uint8_t usb_keyboard_class::keycode_to_modifier(KEYCODE_TYPE keycode)
{
uint8_t modifier=0;

#ifdef SHIFT_MASK
if (keycode & SHIFT_MASK) modifier |= MODIFIERKEY_SHIFT;
#endif
#ifdef ALTGR_MASK
if (keycode & ALTGR_MASK) modifier |= MODIFIERKEY_RIGHT_ALT;
#endif
#ifdef RCTRL_MASK
if (keycode & RCTRL_MASK) modifier |= MODIFIERKEY_RIGHT_CTRL;
#endif
return modifier;
}


Not sure if I am missing something ?
 
Teensy beta 8
Teensy 2.0
Win 7/64 SP1
USB Type:K+M+J
Examples > Teensy > USB_Keyboard > Simple

compiles and executes as expected.
 
alt-gr is the same as MODIFIER_KEY_RIGHT_ALT which is available in C but not in Teensyduino. So any keypress that needs alt-gr (€¦ on UK English; €#{[¦`\^@]}¤ on French AZERTY) can only be produced with Keyboard.print();. It would be better to allow the right-hand modifier keys in Teensyduino.

Whilst I have been working with the Teensy I have discovered that AltGr is actually just CTRL-ALT anyway so not hard to achieve in practice.

No, AltGr is the same as MODIFIERKEY_RIGHT_ALT.

Also both the left and right hand modifiers appear to be mapped:

\arduino-1.0.1\hardware\teensy\cores\teensy\Keylayouts.h:

#define MODIFIERKEY_CTRL ( 0x01 | 0x8000 )
#define MODIFIERKEY_SHIFT ( 0x02 | 0x8000 )
#define MODIFIERKEY_ALT ( 0x04 | 0x8000 )
#define MODIFIERKEY_GUI ( 0x08 | 0x8000 )
#define MODIFIERKEY_LEFT_CTRL ( 0x01 | 0x8000 )
#define MODIFIERKEY_LEFT_SHIFT ( 0x02 | 0x8000 )
#define MODIFIERKEY_LEFT_ALT ( 0x04 | 0x8000 )
#define MODIFIERKEY_LEFT_GUI ( 0x08 | 0x8000 )
#define MODIFIERKEY_RIGHT_CTRL ( 0x10 | 0x8000 )
#define MODIFIERKEY_RIGHT_SHIFT ( 0x20 | 0x8000 )
#define MODIFIERKEY_RIGHT_ALT ( 0x40 | 0x8000 )
#define MODIFIERKEY_RIGHT_GUI ( 0x80 | 0x8000 )

For use in C, yes. For use in Teensyduino, apparently not.

Compare the list of modifier keys for C and for Teensyduino. The former lists 12 modifiers (but the ones without left or right in the names map to the left hand ones). The latter lists only 4 (which, again, map to the left hand ones).

See \arduino-1.0.1\hardware\teensy\cores\usb_hid\usb_api.cpp

uint8_t usb_keyboard_class::keycode_to_modifier(KEYCODE_TYPE keycode)
{
uint8_t modifier=0;

#ifdef SHIFT_MASK
if (keycode & SHIFT_MASK) modifier |= MODIFIERKEY_SHIFT;
#endif
#ifdef ALTGR_MASK
if (keycode & ALTGR_MASK) modifier |= MODIFIERKEY_RIGHT_ALT;
#endif
#ifdef RCTRL_MASK
if (keycode & RCTRL_MASK) modifier |= MODIFIERKEY_RIGHT_CTRL;
#endif
return modifier;
}

Not sure if I am missing something ?

Notice that in the code you quoted, altgr is right_alt not ctrl-alt :)

Or maybe I am putting too much faith in the documentation rather than the code. Will try it and report back.
 
Last edited:
Teensy beta 8
Teensy 2.0
Win 7/64 SP1
USB Type:K+M+J
USB French keyboard test code posted here.

compiles and runs as expected (with the same output errors I reported in that thread).

Teensy beta 8
Teensy 3.0
Win 7/64 SP1
USB Type:K+M+J
same sketch

compilation error
Code:
LayoutTest_Fr_AZERTY.ino: In function 'void setup()':
LayoutTest_Fr_AZERTY:97: error: 'MODIFIERKEY_SHIFT' was not declared in this scope
LayoutTest_Fr_AZERTY:98: error: 'KEY_LEFT_BRACE' was not declared in this scope
LayoutTest_Fr_AZERTY:106: error: 'KEY_E' was not declared in this scope
 
It would be better to add the same function as in the C binding, for the cases where only a single key plus a modifier is pressed, then released.

This turns out to be simple (Tested on Teensy 2.0, beta8 Teensyduino).

Code:
[color=#7E7E7E]/* Implement simple keyboard sending, from the C binding.[/color]
[color=#7E7E7E] [/color]
[color=#7E7E7E] You must select Keyboard from the "Tools > USB Type" menu[/color]
[color=#7E7E7E] Select the the correct layout from "Tools > Keyboard Layout"[/color]
[color=#7E7E7E] */[/color]

 [color=#7E7E7E]// Teensy 2.0 = Pin 11, Teensy++ 2.0 = Pin 6; Teensy 3.0 = Pin 13[/color]
[color=#CC6600]const[/color] [color=#CC6600]int[/color] ledPin = 11;  

[color=#CC6600]void[/color] [color=#CC6600][b]setup[/b][/color]() {
  [color=#7E7E7E]// Blink the LED for 10 seconds, to give time to open [/color]
  [color=#7E7E7E]// a word processor or text editor to receive the test text[/color]
  [color=#CC6600]pinMode[/color](ledPin, [color=#006699]OUTPUT[/color]);
  [color=#CC6600]for[/color] ([color=#CC6600]int[/color] i=0; i < 10; i++) {
    [color=#CC6600]digitalWrite[/color](ledPin, [color=#006699]HIGH[/color]);
    [color=#CC6600]delay[/color](500);
    [color=#CC6600]digitalWrite[/color](ledPin, [color=#006699]LOW[/color]);
    [color=#CC6600]delay[/color](500);
  }
}

[color=#CC6600]void[/color] [color=#CC6600][b]loop[/b][/color] () {
  [color=#7E7E7E]//Qq repeatedly[/color]
  usb_keyboard_press([color=#006699]KEY_Q[/color], [color=#006699]MODIFIERKEY_SHIFT[/color]);
  [color=#CC6600]delay[/color](100);
  usb_keyboard_press([color=#006699]KEY_Q[/color], 0);
  [color=#CC6600]delay[/color](100);
}

[color=#CC6600]void[/color] usb_keyboard_press([color=#CC6600]int[/color] key, [color=#CC6600]int[/color] modifier) {
  [color=#7E7E7E]// press one key with up to one modifier, then release[/color]
  [color=#CC6600][b]Keyboard[/b][/color].[color=#CC6600]set_modifier[/color](modifier);
  [color=#CC6600][b]Keyboard[/b][/color].[color=#CC6600]set_key1[/color](key);
  [color=#CC6600][b]Keyboard[/b][/color].[color=#CC6600]set_key2[/color](0);
  [color=#CC6600][b]Keyboard[/b][/color].[color=#CC6600]set_key3[/color](0);
  [color=#CC6600][b]Keyboard[/b][/color].[color=#CC6600]set_key4[/color](0);
  [color=#CC6600][b]Keyboard[/b][/color].[color=#CC6600]set_key5[/color](0);
  [color=#CC6600][b]Keyboard[/b][/color].[color=#CC6600]set_key6[/color](0);
  [color=#CC6600][b]Keyboard[/b][/color].[color=#CC6600]send_now[/color]();
  [color=#CC6600]delay[/color](1);
  [color=#CC6600][b]Keyboard[/b][/color].[color=#CC6600]set_key1[/color](0); 
  [color=#CC6600][b]Keyboard[/b][/color].[color=#CC6600]send_now[/color]();
}
 
I made these notes while investigating Teensy keyboard support, then thought they would be useful to share. There are specific bug reports and suggestions for enhancement, in bold.

I'm updating my comments based on beta9.
Three comments, firstly why do Teensyduino users need to use so many more lines of code? It would be better to add the same function as in the C binding, for the cases where only a single key plus a modifier is pressed, then released.

I see this is fixed in beta9. The following code compils with Teensy 3 and Keyboard+Mouse+Joystick:

Code:
#include "keylayouts.h"

void setup (){
  delay(3000);
  usb_keyboard_press(KEY_Q, MODIFIERKEY_CTRL);
}

void loop (){
}
Secondly, why is there only access to left-hand modifiers? It would be better to support the right-hand modifier keys in Teensyduino as well as the left hand ones.
And thirdly (minor), why the MOFIFIER_KEY_foo vs MODIFIERKEY_foo difference? It would be better to be consistent (although a reason to not be consistent would be that a change might break existing code).

So any keypress that needs alt-gr (€¦ on UK English; €#{[¦`\^@]}¤ on French AZERTY) can only be produced with Keyboard.print();. It would be better to allow the right-hand modifier keys in Teensyduino.

I see that left and right hand modifier keys have been added in beta9:

Code:
#define MODIFIERKEY_CTRL        ( 0x01 | 0x8000 )
#define MODIFIERKEY_SHIFT       ( 0x02 | 0x8000 )
#define MODIFIERKEY_ALT         ( 0x04 | 0x8000 )
#define MODIFIERKEY_GUI         ( 0x08 | 0x8000 )
#define MODIFIERKEY_LEFT_CTRL   ( 0x01 | 0x8000 )
#define MODIFIERKEY_LEFT_SHIFT  ( 0x02 | 0x8000 )
#define MODIFIERKEY_LEFT_ALT    ( 0x04 | 0x8000 )
#define MODIFIERKEY_LEFT_GUI    ( 0x08 | 0x8000 )
#define MODIFIERKEY_RIGHT_CTRL  ( 0x10 | 0x8000 )
#define MODIFIERKEY_RIGHT_SHIFT ( 0x20 | 0x8000 )
#define MODIFIERKEY_RIGHT_ALT   ( 0x40 | 0x8000 )
#define MODIFIERKEY_RIGHT_GUI   ( 0x80 | 0x8000 )

Keys not on the US ANSI layout can't be produced at all by the current Teensy implementation. The menu key, the Fn key, and (on Japanese keyboards) the three keys near the spacebar which control character input methods, and the underscore/backslash keypresses cannot be produced
http://en.wikipedia.org/wiki/File:KB_Japanese.svg

Keyboard Menu has HID code 118 (0x76) so could be supported
.

The list of HID codes stops at 115; Keyboard Menu has not been added.
 
Last edited:
I'm preparing beta 10. If you'd like me to test any particular thing, please post a ready-to-run sample program.

Media keys will not be in beta 10, but I will do them soon. Windows requires an entirely separate interface and endpoint, due to some lame security implementation decisions on Microsoft's part.

There is actually an easy API like the one you requested... I have haven't documented it yet. You can use Keyboard.press(code) and Keyboard.release(code). It allows either the keyboard codes, or ascii, and a fair amount of unicode too.

With beta 10 so close, I'm only fixing bugs. New keycodes can be added after 10 is published. Before proposing more keycodes, please make sure they work with Windows. I'm willing to add them if they work on all 3 systems. If Windows doesn't work, it'll go on the wish-list for the media keys interface.
 
Status
Not open for further replies.
Back
Top