PDA

View Full Version : Observations on Teensy USB keyboard support



Nantonos
11-28-2012, 08:19 AM
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

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

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

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 (http://forum.pjrc.com/threads/1085-Pound-sterling-%28currency%29-missing-from-Teensyduino-British-%28UK%29-keyboard?p=2410&viewfull=1#post2410). But this layout-based naming comes from the USB keyboard spec.

Or, on Teensyduino,

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

usb_keyboard_press(KEY_LEFT_BRACE, MODIFIER_KEY_SHIFT);
usb_keyboard_press(KEY_E, 0);
or on Teensyduino

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.

Nantonos
11-28-2012, 08:20 AM
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.

#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

0x7F Keyboard Mute
0x80 Keyboard Volume Up
0x81 Keyboard Volume Down

Nantonos
11-28-2012, 08:21 AM
There are missing codes: 1-3, 100-103, 116-164, 176-221, 224-231. These should be added.


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

Nantonos
11-28-2012, 08:09 PM
Teensy beta 8
Teensy 3.0
Win 7/64 SP1
USB Type:K+M+J
Examples > Teensy > USB_Keyboard > Simple

will not compile.


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


the offending bit of usb_keyboard.c is

#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

Experimentalist
11-28-2012, 08:15 PM
[QUOTE=Nantonos;2439]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_TY PE 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 ?

Nantonos
11-28-2012, 08:21 PM
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.

Nantonos
11-28-2012, 08:30 PM
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 (http://www.pjrc.com/teensy/usb_keyboard.html) and for Teensyduino (http://www.pjrc.com/teensy/td_keyboard.html). 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_TY PE 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.

Nantonos
11-28-2012, 08:36 PM
Teensy beta 8
Teensy 2.0
Win 7/64 SP1
USB Type:K+M+J
USB French keyboard test code posted here (http://forum.pjrc.com/threads/1085-Pound-sterling-%28currency%29-missing-from-Teensyduino-British-%28UK%29-keyboard?p=2410&viewfull=1#post2410).

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


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

Nantonos
11-30-2012, 07:12 PM
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).



/* Implement simple keyboard sending, from the C binding.

You must select Keyboard from the "Tools > USB Type" menu
Select the the correct layout from "Tools > Keyboard Layout"
*/

// Teensy 2.0 = Pin 11, Teensy++ 2.0 = Pin 6; Teensy 3.0 = Pin 13
const int ledPin = 11;

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

void loop () {
//Qq repeatedly
usb_keyboard_press(KEY_Q, MODIFIERKEY_SHIFT);
delay(100);
usb_keyboard_press(KEY_Q, 0);
delay(100);
}

void usb_keyboard_press(int key, int modifier) {
// press one key with up to one modifier, then release
Keyboard.set_modifier(modifier);
Keyboard.set_key1(key);
Keyboard.set_key2(0);
Keyboard.set_key3(0);
Keyboard.set_key4(0);
Keyboard.set_key5(0);
Keyboard.set_key6(0);
Keyboard.send_now();
delay(1);
Keyboard.set_key1(0);
Keyboard.send_now();
}

Nantonos
12-30-2012, 09:19 AM
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:



#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:


#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.

PaulStoffregen
01-02-2013, 10:33 AM
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.

Paul
04-11-2016, 11:05 PM
If anyone's still watching this old thread, new & improved media key support is available.

https://forum.pjrc.com/threads/34074-Keyboard-Media-Keys-now-%28hopefully%29-Windows-compatible

I'm closing this old thread. Please comment on the new thread.