Pound sterling (currency) missing from Teensyduino British (UK) keyboard

Nantonos

Well-known member
Tested with the LayoutTest example, Teensy beta 8, Teensy 2.0++ on Windows7. A British keyboard
http://en.wikipedia.org/wiki/Keyboard_layouts#United_Kingdom
is the system default and is correctly detected by Teensyduino in the keyboard layout menu.

The results were initially surprising to me; letters with acute accents are printed (although UK English does not use them). Grave accents are not printed. But I discovered that this is correct; the acute vowels are not printed on the keyboard but are typed with Alt-GR.

I then made a modified copy of the example, to add the three untested letters which are on a UK keyboard - the pound sterling (currency) symbol £, the EBCIDIC negation ¬ and the EBCIDIC broiken pipe ¦. Of those, the pound sterling was not generated. Here is the modified code (just additions, at the end):

Code:
/* USB Keyboard Layout Test

   You must select Keyboard from the "Tools > USB Type" menu

   Select the the correct layout from "Tools > Keyboard Layout"

   If you discover incorrect results for your country's layout,
   please email Paul Stoffregen <paul@pjrc.com> with the results
   of this test and an explanation of which keys are wrong.  If
   your layout is not available, please find the layout which
   is closest, and email Paul Stoffregen.
   
   Modified to add UK keyboard symbols, Chris Lilley.
*/

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

void setup() {
  Serial.begin(9600);

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

  // Type all possible characters.  Many countries do not use all
  // characters.  Unsupported characters will be skipped
  //
  Keyboard.println("Teensy USB UK Keyboard Layout Test");
  delay(100);
  Keyboard.println("Lowercase:  abcdefghijklmnopqrstuvwxyz");
  delay(100);
  Keyboard.println("Uppercase:  ABCDEFGHIJKLMNOPQRSTUVWXYZ");
  delay(100);
  Keyboard.println("Numbers:    0123456789");
  delay(100);
  Keyboard.println("Symbols1:   !\"#$%&'()*+,-./");
  delay(100);
  Keyboard.println("Symbols2:   :;<=>?[\\]^_`{|}~");
  delay(100);
  Keyboard.println("Symbols3:   ¡¢£¤¥¦§¨©ª«¬*®¯°±");
  delay(100);
  Keyboard.println("Symbols4:   ²³´µ¶·¸¹º»¼½¾¿×÷");
  delay(100);
  Keyboard.println("Grave:      ÀÈÌÒÙàèìòù");
  delay(100);
  Keyboard.println("Acute:      ÁÉÍÓÚÝáéíóúý");
  delay(100);
  Keyboard.println("Circumflex: ÂÊÎÔÛâêîôû");
  delay(100);
  Keyboard.println("Tilde:      ÃÑÕãñõ");
  delay(100);
  Keyboard.println("Diaeresis:  ÄËÏÖÜäëïöüÿ");
  delay(100);
  Keyboard.println("Cedilla:    Çç");
  delay(100);
  Keyboard.println("Ring Above: Åå");
  delay(100);
  Keyboard.println("AE:         Ææ");
  delay(100);
  Keyboard.println("Thorn:      Þþ");
  delay(100);
  Keyboard.println("Sharp S:    ß");
  delay(100);
  Keyboard.println("O-Stroke:   Øø");
  delay(100);
  Keyboard.println("Eth:        Ðð");
  delay(100);
  Keyboard.println("Euro:       €");
    delay(100);
    // UK-specific additions
  Keyboard.println("Sterling:       £");
      delay(100);
  Keyboard.println("EBCEDIC negation:       ¬");
        delay(100);
  Keyboard.println("EBCEDIC broken bar:       ¦");
}

void loop() {
  // Do nothing after the test
}

And the results:

Code:
Teensy USB UK Keyboard Layout Test
Lowercase:  abcdefghijklmnopqrstuvwxyz
Uppercase:  ABCDEFGHIJKLMNOPQRSTUVWXYZ
Numbers:    0123456789
Symbols1:   !$%&'()*+,-./
Symbols2:   :;<=>?[\]^_`{|}~
Symbols3:   ¦¬
Symbols4:   
Grave:      
Acute:      ÁÉÍÓÚáéíóú
Circumflex: 
Tilde:      
Diaeresis:  
Cedilla:    
Ring Above: 
AE:         
Thorn:      
Sharp S:    
O-Stroke:   
Eth:        
Euro:       €
Sterling:       
EBCEDIC negation:       ¬
EBCEDIC broken bar:       ¦
 
Last edited:
THe £ sign, an explanation and work around

The £ sign (GBP Sterling) lives in the high ANSI characters, 0x00A3, for reference and this is the root of the problem. One thing to note is that this makes it a good character to use in your passwords due to the issues you are having in outputting it.

http://www.unicode.org/charts/PDF/U0000.pdf
http://www.unicode.org/charts/PDF/U0080.pdf

A work around for this that I use is as follows:

Keyboard.set_modifier(MODIFIERKEY_SHIFT);
Keyboard.send_now();
delay(10);
Keyboard.set_key1(KEY_3);
Keyboard.send_now();
delay(10);
Keyboard.set_modifier(0);
Keyboard.set_key1(0);
Keyboard.send_now();

Also worth noting, on Windows not sure about others, to print any high ANSI character not available from the keyboard e.g. © hold the ALT key and use the numeric keypad to enter the Unicode in decimal © is 0x00A9, 0169 decimal:

Keyboard.set_modifier(MODIFIERKEY_ALT);
Keyboard.send_now();
Keyboard.set_key1(KEYPAD_0);
Keyboard.send_now();
Keyboard.set_key1(0);
Keyboard.send_now();
Keyboard.set_key1(KEYPAD_1);
Keyboard.send_now();
Keyboard.set_key1(0);
Keyboard.send_now();
Keyboard.set_key1(KEYPAD_6);
Keyboard.send_now();
Keyboard.set_key1(0);
Keyboard.send_now();
Keyboard.set_key1(KEYPAD_9);
Keyboard.send_now();
Keyboard.set_key1(0);
Keyboard.send_now();
Keyboard.set_modifier(0);
Keyboard.set_key1(0);
Keyboard.send_now();

Obviously not much effort to write a method to send any Unicode character.

Hope it helps
 
The £ sign (GBP Sterling) lives in the high ANSI characters, 0x00A3, for reference and this is the root of the problem.
I doubt it. Any modern system is working in Unicode (unfortunately modern system excludes Arduino and the like, which are resolutely stuck in the mid-1980s with 8-bit code pages, but I mean any version of Windows from NT/2000/XP on up, any MacOS X, any Linux of FreeBSD from the last decade).

Your comment would apply to something like Win95, which mostly used 8-bit 'code pages'. If 'high ansi' (8 bit characters with the high bit set) were the issue then € áéíóú would also be a problem along with £.

One thing to note is that this makes it a good character to use in your passwords due to the issues you are having in outputting it.
I have no trouble outputting it. It is printed right on my keyboard (above the '3' is '£', so shift-3) and I typed it in right there.

Thanks for your comments, but I don't think they are the root of the problem. Its just a small flaw in the Teensy keyboard emulation, which was probably not noticed because the example test program doesn't expose it.
 
Paul, yep that is a UK keyboard.

The UK mods in Keylayouts.h are wrong for the £ sign:

#define ISO_8859_1_A3 KEY_3 + ALTGR_MASK // 163 £ Pound Sign

Should be a SHIFT_MASK

Nantonos the solution above works then since Shift-3 was exactly what the code I posted does, I was just trying to help. We had a pen tester in recently and all the passwords with a £ sign took a little longer to crack than those of similar complexity without. Regarding the other characters € áéíóú, those are the only high chars you can enter directly using modifiers physically from the UK keyboard emulation and therefore are the only ones Paul has mapped in the UK keyboard layout. I simply provide a solution for people who want to send the others.

Take a look in "\arduino-1.0.x\hardware\teensy\cores\teensy\Keylayouts.h" to understand further.
 
Also, does this image match your keyboard's actual layout? I don't have a UK keyboard, so I depend on these images for making the layout mapping.

http://en.wikipedia.org/wiki/File:KB_United_Kingdom.svg

Please confirm and I'll look at the code....

Yes, exactly. Black letters in that diagram are printed on the keyboard; blue letters are not, but can be generated with alt-gr.

Pound stering £ is printed on the '3' key, where '#' is on US layout.
 
The UK mods in Keylayouts.h are wrong for the £ sign:

#define ISO_8859_1_A3 KEY_3 + ALTGR_MASK // 163 £ Pound Sign

Should be a SHIFT_MASK

Good catch, yes that looks like it.

Nantonos the solution above works then since Shift-3 was exactly what the code I posted does, I was just trying to help.
Thanks, appreciated. I was more interested in reporting a bug and having it fixed, rather than using a workaround.
 
OK, I will go test French keyboards now, I think we have one in the house unused somewhere.

Paul, would it help if I retest on Teensy 3.0 and beta 8? I assume the same bug will be copied over there.
 
I've put the fix in. It'll be in the next release.

Testing on 3.0 would be awesome. The keylayouts.h file is identical, so any layout definition bugs should be the same on 2.0 and 3.0. But 3.0's code international keyboard hasn't been tested much, because it's so new. I made that character test example because I only have US keyboards and I depend on people testing on actual machines configured for these other keyboards.
 
French AZERTY keyboards have some bugs too. I compared the layout on Wikipedia to an actual French keyboard, just to be sure it was correct.

The LayoutTest example gave incorrect results

I then modified the LayoutTest to

- check for additional characters
- check that French quotation marks aren't produced by a standard French keyboard (I kid you not)
- briefly check that the 'dead keys' used to create accents work.

Here is the modified code
Code:
/* USB French Keyboard Layout Test
 
 You must select Keyboard from the "Tools > USB Type" menu
 
 Select the the correct layout from "Tools > Keyboard Layout"
 
 If you discover incorrect results for your country's layout,
 please email Paul Stoffregen <paul@pjrc.com> with the results
 of this test and an explanation of which keys are wrong.  If
 your layout is not available, please find the layout which
 is closest, and email Paul Stoffregen.
 
 Modified by Chris Lilley to fully test French AZERTY keyboard
 */

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

void setup() {
  Serial.begin(9600);

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

  // Type all possible characters.  Many countries do not use all
  // characters.  Unsupported characters will be skipped
  //
  Keyboard.println("Teensy USB French AZERTY Keyboard Layout Test");
  delay(100);
  Keyboard.println("Lowercase:  abcdefghijklmnopqrstuvwxyz");
  delay(100);
  Keyboard.println("Uppercase:  ABCDEFGHIJKLMNOPQRSTUVWXYZ");
  delay(100);
  Keyboard.println("Numbers:    0123456789");
  delay(100);
  Keyboard.println("Symbols1:   !\"#$%&'()*+,-./");
  delay(100);
  Keyboard.println("Symbols2:   :;<=>?[\\]^_`{|}~");
  delay(100);
  Keyboard.println("Symbols3:   ¡¢£¤¥¦§¨©ª«¬*®¯°±");
  delay(100);
  Keyboard.println("Symbols4:   ²³´µ¶·¸¹º»¼½¾¿×÷");
  delay(100);
  Keyboard.println("Grave:      ÀÈÌÒÙàèìòù");
  delay(100);
  Keyboard.println("Acute:      ÁÉÍÓÚÝáéíóúý");
  delay(100);
  Keyboard.println("Circumflex: ÂÊÎÔÛâêîôû");
  delay(100);
  Keyboard.println("Tilde:      ÃÑÕãñõ");
  delay(100);
  Keyboard.println("Diaeresis:  ÄËÏÖÜäëïöüÿ");
  delay(100);
  Keyboard.println("Cedilla:    Çç");
  delay(100);
  Keyboard.println("Ring Above: Åå");
  delay(100);
  Keyboard.println("AE:         Ææ");
  delay(100);
  Keyboard.println("Thorn:      Þþ");
  delay(100);
  Keyboard.println("Sharp S:    ß");
  delay(100);
  Keyboard.println("O-Stroke:   Øø");
  delay(100);
  Keyboard.println("Eth:        Ðð");
  delay(100);
  Keyboard.println("Euro:       €");
  // additions for Azerty French layout, see
  // http://en.wikipedia.org/wiki/File:KB_France.svg
  // http://en.wikipedia.org/wiki/AZERTY
  delay(100);
  Keyboard.println("French additional characters");
  delay(100);
  Keyboard.println("Superscript 2:       ²");
  delay(100);
  Keyboard.println("Broken pipe:       ¦");
  delay(100);
  Keyboard.println("Pound sterling:       £");
  delay(100);
  Keyboard.println("Currency sign:       ¤");
  delay(100);
  Keyboard.println("French quotation marks NOT on French AZERTY keyboard!");
  delay(100);
  //These won't be output
  Keyboard.println("Guillemets:       «»");
  delay(100);
  Keyboard.print("Combining diaresis:       ");
  // Test 'dead key' combining diaresis plus vowels
  delay(100);
  Keyboard.set_modifier(MODIFIERKEY_SHIFT);
  Keyboard.set_key1(KEY_LEFT_BRACE); // This is actually the diaresis and circumflex key
  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();
}

void loop() {
  // Do nothing after the test
}

and here are the results which are not as expected:

Code:
Teensy USB French AZERTY Keyboard Layout Test
Lowercase:  abcdefghijklmnopqrstuvwxyz
Uppercase:  ABCDEFGHIJKLMNOPQRSTUVWXYZ
Numbers:    0123456789
Symbols1:   !"#$%&'()*+,-./
Symbols2:   :;<=>?[\]^_`{|}~
Symbols3:   £¤§¨°
Symbols4:   ²µ
Grave:      `QÈÌÒÙàèìò;
Acute:      é
Circumflex: ^QÊÎÔÛ^qêîôû
Tilde:      ~QÑÕ~qñõ
Diaeresis:  ¨QËÏÖü¨qëïöüÿ
Cedilla:    ç
Ring Above: 
AE:         
Thorn:      
Sharp S:    
O-Stroke:   
Eth:        
Euro:       €
French additional characters
Superscript 2:       ²
Broken pipe:       
Pound sterling:       £
Currency sign:       ¤
French quotation marks NOT on French AZERTY keyboard!
Guillemets:       
Combining diaresis:       ë
 
Last edited:
So the incorrect things are:

- while A and Q are correct (they are swapped on the French keyboard layout, compared to US) the accented forms of the letter A are mostly replaced by accented letter Q instead! (except à is correct) Edit: corrected by code posted below
- broken pipe symbol ¦ is missing (but in practice French keyboards seem to output | instead) Edit: known Windows bug, apparently.
- Edit: ù was missing Edit: corrected by code posted below
 
Last edited:
Corrected French AZERTY section

Code:
#ifdef LAYOUT_FRENCH

#define SHIFT_MASK		0x0040
#define ALTGR_MASK		0x0080
#define DEADKEYS_MASK		0x0700
#define	CIRCUMFLEX_BITS		0x0100
#define GRAVE_ACCENT_BITS	0x0200
#define DIAERESIS_BITS		0x0300
#define TILDE_BITS		0x0400
#define KEYCODE_TYPE		uint16_t
#define DEADKEY_CIRCUMFLEX	KEY_LEFT_BRACE
#define DEADKEY_GRAVE_ACCENT	KEY_7 + ALTGR_MASK
#define DEADKEY_DIAERESIS	KEY_LEFT_BRACE + SHIFT_MASK
#define DEADKEY_TILDE		KEY_2 + ALTGR_MASK
#define KEY_NON_US_100		63

#define ASCII_20	KEY_SPACE				// 32  
#define ASCII_21	KEY_SLASH				// 33 !
#define ASCII_22	KEY_3					// 34 "
#define ASCII_23	KEY_3 + ALTGR_MASK			// 35 #
#define ASCII_24	KEY_RIGHT_BRACE				// 36 $
#define ASCII_25	KEY_QUOTE + SHIFT_MASK			// 37 %
#define ASCII_26	KEY_1					// 38 &
#define ASCII_27	KEY_4					// 39 '  
#define ASCII_28	KEY_5					// 40 ( 
#define ASCII_29	KEY_MINUS				// 41 )
#define ASCII_2A	KEY_BACKSLASH				// 42 *
#define ASCII_2B	KEY_EQUAL + SHIFT_MASK			// 43 +
#define ASCII_2C	KEY_M					// 44 ,
#define ASCII_2D	KEY_6					// 45 -
#define ASCII_2E	KEY_COMMA + SHIFT_MASK			// 46 .
#define ASCII_2F	KEY_PERIOD + SHIFT_MASK			// 47 /
#define ASCII_30	KEY_0 + SHIFT_MASK			// 48 0
#define ASCII_31	KEY_1 + SHIFT_MASK			// 49 1
#define ASCII_32	KEY_2 + SHIFT_MASK			// 50 2
#define ASCII_33	KEY_3 + SHIFT_MASK			// 51 3
#define ASCII_34	KEY_4 + SHIFT_MASK			// 52 4
#define ASCII_35	KEY_5 + SHIFT_MASK			// 53 5
#define ASCII_36	KEY_6 + SHIFT_MASK			// 54 6
#define ASCII_37	KEY_7 + SHIFT_MASK			// 55 7
#define ASCII_38	KEY_8 + SHIFT_MASK			// 55 8
#define ASCII_39	KEY_9 + SHIFT_MASK			// 57 9
#define ASCII_3A	KEY_PERIOD				// 58 :
#define ASCII_3B	KEY_COMMA				// 59 ;
#define ASCII_3C	KEY_NON_US_100				// 60 <
#define ASCII_3D	KEY_EQUAL				// 61 =
#define ASCII_3E	KEY_NON_US_100 + SHIFT_MASK		// 62 >
#define ASCII_3F	KEY_M + SHIFT_MASK			// 63 ?
#define ASCII_40	KEY_0 + ALTGR_MASK			// 64 @
#define ASCII_41	KEY_Q + SHIFT_MASK			// 65 A
#define ASCII_42	KEY_B + SHIFT_MASK			// 66 B
#define ASCII_43	KEY_C + SHIFT_MASK			// 67 C
#define ASCII_44	KEY_D + SHIFT_MASK			// 68 D
#define ASCII_45	KEY_E + SHIFT_MASK			// 69 E
#define ASCII_46	KEY_F + SHIFT_MASK			// 70 F
#define ASCII_47	KEY_G + SHIFT_MASK			// 71 G
#define ASCII_48	KEY_H + SHIFT_MASK			// 72 H
#define ASCII_49	KEY_I + SHIFT_MASK			// 73 I
#define ASCII_4A	KEY_J + SHIFT_MASK			// 74 J
#define ASCII_4B	KEY_K + SHIFT_MASK			// 75 K
#define ASCII_4C	KEY_L + SHIFT_MASK			// 76 L
#define ASCII_4D	KEY_SEMICOLON + SHIFT_MASK		// 77 M
#define ASCII_4E	KEY_N + SHIFT_MASK			// 78 N
#define ASCII_4F	KEY_O + SHIFT_MASK			// 79 O
#define ASCII_50	KEY_P + SHIFT_MASK			// 80 P
#define ASCII_51	KEY_A + SHIFT_MASK			// 81 Q
#define ASCII_52	KEY_R + SHIFT_MASK			// 82 R
#define ASCII_53	KEY_S + SHIFT_MASK			// 83 S
#define ASCII_54	KEY_T + SHIFT_MASK			// 84 T
#define ASCII_55	KEY_U + SHIFT_MASK			// 85 U
#define ASCII_56	KEY_V + SHIFT_MASK			// 86 V
#define ASCII_57	KEY_Z + SHIFT_MASK			// 87 W
#define ASCII_58	KEY_X + SHIFT_MASK			// 88 X
#define ASCII_59	KEY_Y + SHIFT_MASK			// 89 Y
#define ASCII_5A	KEY_W + SHIFT_MASK			// 90 Z
#define ASCII_5B	KEY_5 + ALTGR_MASK			// 91 [
#define ASCII_5C	KEY_8 + ALTGR_MASK			// 92 
#define ASCII_5D	KEY_MINUS + ALTGR_MASK			// 93 ]
#define ASCII_5E	KEY_9 + ALTGR_MASK			// 94 ^
#define ASCII_5F	KEY_8					// 95 _
#define ASCII_60	GRAVE_ACCENT_BITS + KEY_SPACE		// 96 `
#define ASCII_61	KEY_Q					// 97 a
#define ASCII_62	KEY_B					// 98 b
#define ASCII_63	KEY_C					// 99 c
#define ASCII_64	KEY_D					// 100 d
#define ASCII_65	KEY_E					// 101 e
#define ASCII_66	KEY_F					// 102 f
#define ASCII_67	KEY_G					// 103 g
#define ASCII_68	KEY_H					// 104 h
#define ASCII_69	KEY_I					// 105 i
#define ASCII_6A	KEY_J					// 106 j
#define ASCII_6B	KEY_K					// 107 k
#define ASCII_6C	KEY_L					// 108 l
#define ASCII_6D	KEY_SEMICOLON				// 109 m
#define ASCII_6E	KEY_N					// 110 n
#define ASCII_6F	KEY_O					// 111 o
#define ASCII_70	KEY_P					// 112 p
#define ASCII_71	KEY_A					// 113 q
#define ASCII_72	KEY_R					// 114 r
#define ASCII_73	KEY_S					// 115 s
#define ASCII_74	KEY_T					// 116 t
#define ASCII_75	KEY_U					// 117 u
#define ASCII_76	KEY_V					// 118 v
#define ASCII_77	KEY_Z					// 119 w
#define ASCII_78	KEY_X					// 120 x
#define ASCII_79	KEY_Y					// 121 y
#define ASCII_7A	KEY_W					// 122 z
#define ASCII_7B	KEY_4 + ALTGR_MASK			// 123 {
#define ASCII_7C	KEY_6 + ALTGR_MASK			// 124 |
#define ASCII_7D	KEY_EQUAL + ALTGR_MASK			// 125 }
#define ASCII_7E	TILDE_BITS + KEY_SPACE			// 126 ~
#define ASCII_7F	KEY_BACKSPACE				// 127

second half in following post (forum limit)
 
Code:
#define ISO_8859_1_A0	KEY_SPACE				// 160       Nonbreakng Space
#define ISO_8859_1_A1	0					// 161 ¡     Inverted Exclamation
#define ISO_8859_1_A2	0					// 162 ¢     Cent SIGN
#define ISO_8859_1_A3	KEY_RIGHT_BRACE + SHIFT_MASK		// 163 £     Pound Sign
#define ISO_8859_1_A4	KEY_RIGHT_BRACE + ALTGR_MASK		// 164 ¤     Currency or Euro Sign
#define ISO_8859_1_A5	0					// 165 ¥     YEN SIGN
#define ISO_8859_1_A6	0					// 166 ¦     BROKEN BAR
#define ISO_8859_1_A7	KEY_SLASH + SHIFT_MASK			// 167 §     SECTION SIGN
#define ISO_8859_1_A8	DIAERESIS_BITS + KEY_SPACE		// 168 ¨     DIAERESIS
#define ISO_8859_1_A9	0					// 169 ©     COPYRIGHT SIGN
#define ISO_8859_1_AA	0					// 170 ª     FEMININE ORDINAL
#define ISO_8859_1_AB	0					// 171 «     LEFT DOUBLE ANGLE QUOTE
#define ISO_8859_1_AC	0					// 172 ¬     NOT SIGN
#define ISO_8859_1_AD	0					// 173       SOFT HYPHEN
#define ISO_8859_1_AE	0					// 174 ®     REGISTERED SIGN
#define ISO_8859_1_AF	0					// 175 ¯     MACRON
#define ISO_8859_1_B0	KEY_MINUS + SHIFT_MASK			// 176 °     DEGREE SIGN
#define ISO_8859_1_B1	0					// 177 ±     PLUS-MINUS SIGN
#define ISO_8859_1_B2	KEY_TILDE				// 178 ²     SUPERSCRIPT TWO
#define ISO_8859_1_B3	0					// 179 ³     SUPERSCRIPT THREE
#define ISO_8859_1_B4	0					// 180 ´     ACUTE ACCENT
#define ISO_8859_1_B5	KEY_BACKSLASH + SHIFT_MASK		// 181 µ     MICRO SIGN
#define ISO_8859_1_B6	0					// 182 ¶     PILCROW SIGN
#define ISO_8859_1_B7	0					// 183 ·     MIDDLE DOT
#define ISO_8859_1_B8	0					// 184 ¸     CEDILLA
#define ISO_8859_1_B9	0					// 185 ¹     SUPERSCRIPT ONE
#define ISO_8859_1_BA	0					// 186 º     MASCULINE ORDINAL
#define ISO_8859_1_BB	0					// 187 »     RIGHT DOUBLE ANGLE QUOTE
#define ISO_8859_1_BC	0					// 188 ¼     FRACTION ONE QUARTER
#define ISO_8859_1_BD	0					// 189 ½     FRACTION ONE HALF
#define ISO_8859_1_BE	0					// 190 ¾     FRACTION THREE QUARTERS
#define ISO_8859_1_BF	0					// 191 ¿     INVERTED QUESTION MARK
#define ISO_8859_1_C0	GRAVE_ACCENT_BITS + KEY_Q + SHIFT_MASK	// 192 À     A GRAVE
#define ISO_8859_1_C1	0					// 193 Á     A ACUTE
#define ISO_8859_1_C2	CIRCUMFLEX_BITS	+ KEY_Q + SHIFT_MASK	// 194 Â     A CIRCUMFLEX
#define ISO_8859_1_C3	TILDE_BITS + KEY_Q + SHIFT_MASK		// 195 Ã     A TILDE
#define ISO_8859_1_C4	DIAERESIS_BITS + KEY_Q + SHIFT_MASK	// 196 Ä     A DIAERESIS
#define ISO_8859_1_C5	0					// 197 Å     A RING ABOVE
#define ISO_8859_1_C6	0					// 198 Æ     AE
#define ISO_8859_1_C7	0					// 199 Ç     C CEDILLA
#define ISO_8859_1_C8	GRAVE_ACCENT_BITS + KEY_E + SHIFT_MASK	// 200 È     E GRAVE
#define ISO_8859_1_C9	0					// 201 É     E ACUTE
#define ISO_8859_1_CA	CIRCUMFLEX_BITS + KEY_E + SHIFT_MASK	// 202 Ê     E CIRCUMFLEX
#define ISO_8859_1_CB	DIAERESIS_BITS + KEY_E + SHIFT_MASK	// 203 Ë     E DIAERESIS
#define ISO_8859_1_CC	GRAVE_ACCENT_BITS + KEY_I + SHIFT_MASK	// 204 Ì     I GRAVE
#define ISO_8859_1_CD	0					// 205 Í     I ACUTE
#define ISO_8859_1_CE	CIRCUMFLEX_BITS + KEY_I + SHIFT_MASK	// 206 Î     I CIRCUMFLEX
#define ISO_8859_1_CF	DIAERESIS_BITS + KEY_I + SHIFT_MASK	// 207 Ï     I DIAERESIS
#define ISO_8859_1_D0	0					// 208 Ð     ETH
#define ISO_8859_1_D1	TILDE_BITS + KEY_N + SHIFT_MASK		// 209 Ñ     N TILDE
#define ISO_8859_1_D2	GRAVE_ACCENT_BITS + KEY_O + SHIFT_MASK	// 210 Ò     O GRAVE
#define ISO_8859_1_D3	0					// 211 Ó     O ACUTE
#define ISO_8859_1_D4	CIRCUMFLEX_BITS + KEY_O + SHIFT_MASK	// 212 Ô     O CIRCUMFLEX
#define ISO_8859_1_D5	TILDE_BITS + KEY_O + SHIFT_MASK		// 213 Õ     O TILDE
#define ISO_8859_1_D6	DIAERESIS_BITS + KEY_O + SHIFT_MASK	// 214 Ö     O DIAERESIS
#define ISO_8859_1_D7	0					// 215 ×     MULTIPLICATION
#define ISO_8859_1_D8	0					// 216 Ø     O STROKE
#define ISO_8859_1_D9	GRAVE_ACCENT_BITS + KEY_U + SHIFT_MASK	// 217 Ù     U GRAVE
#define ISO_8859_1_DA	0					// 218 Ú     U ACUTE
#define ISO_8859_1_DB	CIRCUMFLEX_BITS + KEY_U + SHIFT_MASK	// 219 Û     U CIRCUMFLEX
#define ISO_8859_1_DC	DIAERESIS_BITS + KEY_U			// 220 Ü     U DIAERESIS
#define ISO_8859_1_DD	0					// 221 Ý     Y ACUTE
#define ISO_8859_1_DE	0					// 222 Þ     THORN
#define ISO_8859_1_DF	0					// 223 ß     SHARP S
#define ISO_8859_1_E0	KEY_0					// 224 à     a GRAVE
#define ISO_8859_1_E1	0					// 225 á     a ACUTE
#define ISO_8859_1_E2	CIRCUMFLEX_BITS + KEY_Q			// 226 â     a CIRCUMFLEX
#define ISO_8859_1_E3	TILDE_BITS + KEY_Q			// 227 ã     a TILDE
#define ISO_8859_1_E4	DIAERESIS_BITS + KEY_Q			// 228 ä     a DIAERESIS
#define ISO_8859_1_E5	0					// 229 å     a RING ABOVE
#define ISO_8859_1_E6	0					// 230 æ     ae
#define ISO_8859_1_E7	KEY_9					// 231 ç     c CEDILLA
#define ISO_8859_1_E8	KEY_7					// 232 è     e GRAVE
#define ISO_8859_1_E9	KEY_2					// 233 é     e ACUTE
#define ISO_8859_1_EA	CIRCUMFLEX_BITS + KEY_E			// 234 ê     e CIRCUMFLEX
#define ISO_8859_1_EB	DIAERESIS_BITS + KEY_E			// 235 ë     e DIAERESIS
#define ISO_8859_1_EC	GRAVE_ACCENT_BITS + KEY_I		// 236 ì     i GRAVE
#define ISO_8859_1_ED	0					// 237 í     i ACUTE
#define ISO_8859_1_EE	CIRCUMFLEX_BITS + KEY_I			// 238 î     i CIRCUMFLEX
#define ISO_8859_1_EF	DIAERESIS_BITS + KEY_I			// 239 ï     i DIAERESIS
#define ISO_8859_1_F0	0					// 240 ð     ETH
#define ISO_8859_1_F1	TILDE_BITS + KEY_N			// 241 ñ     n TILDE
#define ISO_8859_1_F2	GRAVE_ACCENT_BITS + KEY_O		// 242 ò     o GRAVE
#define ISO_8859_1_F3	0					// 243 ó     o ACUTE
#define ISO_8859_1_F4	CIRCUMFLEX_BITS + KEY_O			// 244 ô     o CIRCUMFLEX
#define ISO_8859_1_F5	TILDE_BITS + KEY_O			// 245 õ     o TILDE
#define ISO_8859_1_F6	DIAERESIS_BITS + KEY_O			// 246 ö     o DIAERESIS
#define ISO_8859_1_F7	0					// 247 ÷     DIVISION
#define ISO_8859_1_F8	0					// 248 ø     o STROKE
#define ISO_8859_1_F9	KEY_SEMICOLON				// 249 ù     u GRAVE
#define ISO_8859_1_FA	0					// 250 ú     u ACUTE
#define ISO_8859_1_FB	CIRCUMFLEX_BITS + KEY_U			// 251 û     u CIRCUMFLEX
#define ISO_8859_1_FC	DIAERESIS_BITS + KEY_U			// 252 ü     u DIAERESIS
#define ISO_8859_1_FD	0					// 253 ý     y ACUTE
#define ISO_8859_1_FE	0					// 254 þ     THORN
#define ISO_8859_1_FF	DIAERESIS_BITS + KEY_Y			// 255 ÿ     y DIAERESIS
#define UNICODE_20AC	KEY_E + ALTGR_MASK			//     €     Euro Sign

#endif // LAYOUT_FRENCH
 
Correct output from test program

Code:
Teensy USB French AZERTY Keyboard Layout Test
Lowercase:  abcdefghijklmnopqrstuvwxyz
Uppercase:  ABCDEFGHIJKLMNOPQRSTUVWXYZ
Numbers:    0123456789
Symbols1:   !"#$%&'()*+,-./
Symbols2:   :;<=>?[\]^_`{|}~
Symbols3:   £¤§¨°
Symbols4:   ²µ
Grave:      ÀÈÌÒÙàèìòm
Acute:      é
Circumflex: ÂÊÎÔÛâêîôû
Tilde:      ÃÑÕãñõ
Diaeresis:  ÄËÏÖüäëïöüÿ
Cedilla:    ç
Ring Above: 
AE:         
Thorn:      
Sharp S:    
O-Stroke:   
Eth:        
Euro:       €
French additional characters
Superscript 2:       ²
Broken pipe:       
Pound sterling:       £
Currency sign:       ¤
French quotation marks NOT on French AZERTY keyboard!
Guillemets:       
Combining diaresis:       ë
 
Also worth noting, on Windows not sure about others, to print any high ANSI character not available from the keyboard e.g. © hold the ALT key and use the numeric keypad to enter the Unicode in decimal © is 0x00A9, 0169 decimal:

Keyboard.set_modifier(MODIFIERKEY_ALT);
. . .

You now have me wondering is there an easier/better way of outputting the characters in this http://www.unicode.org/charts/PDF/U0080.pdf code table ???
 
You now have me wondering is there an easier/better way of outputting the characters in this http://www.unicode.org/charts/PDF/U0080.pdf code table ???

The part of keylayouts.h that converts keycodes to characters seems to output to ASCII, ISO-8859-1, and Unicode. It is really not clear to me why it does that, since ASCII corresponds exactly to the codes 32..127 of Unicode and 8859-1 corresponds exactly to codes 32...127 and 160..255 of Unicode. In other words, while there are many and varied character encodings worldwide, the only two used in that header file are code for code exactly the same as Unicode, for the characters they cover.

Anyway it would seem both useful and easy to define some sort of 'virtual Unicode' keyboard which would be impossible to construct in practice but is straightforward to code; it can output any Unicode character. Perhaps the USB HID keyboard prevents that.

But Keyboard.println("any Unicode string you want"); has a certain attraction.
 
Last edited:
Thanks for testing the French layout. I'm incorporating your fixes right now!


The part of keylayouts.h that converts keycodes to characters seems to output to ASCII, ISO-8859-1, and Unicode. It is really not clear to me why it does that, since ASCII corresponds exactly to the codes 32..127 of Unicode and 8859-1 corresponds exactly to codes 32...127 and 160..255 of Unicode.

USB keyboards do not transmit in ascii or unicode. They use the codes defined in the HID usage tables. Here's the official document:

http://www.usb.org/developers/devclass_docs/Hut1_12v2.pdf

The codes for keyboard keys are in chapter 10, starting on page 53.

The Arduino IDE encodes all string constants as UTF8. So the keyboard support for Serial.print(string) has to decode UTF8 to Unicode. Then it uses those lookup tables to translate Unicode into specific HID usage table codes. The tables are populated with a 6 bit field for the HID usage code, and other bits indicate which modifier keys to use, and/or which dead key sequence to execute before sending the main hid code. I tried to make as much as possible compile-time configured, so it's efficient, but it still ends up as quite a bit of code to do the various translations and execute the dead key sequences.

It would have been awesome if the usb keyboards sent unicode. It also would have been nice if usb keyboard were required to provide the actual layout in their descriptor. Then the PC could know which keyboard layout is used and automatically translate the hid usage codes into they unicode they represent. That would have made it possible to simple plug in any keyboard with any layout and have it automatically work.

Unfortunately, that's not the way things were standardized. Apparently the keyboard manufacturers were involved in the early talks (early 90s) to create the usb standards. It was known that usb could never become adopted without support from keyboards. I've been told the keyboard manufacturers insisted on these codes, because they wanted to manufacture exactly the same electronics and simple populate international keyboards with different plastic keys (or different pad printing on the same plastic keys). They were not willing to even change 1 bit of actual electronic data between different layouts.

So the very unfortunate long-term result is all usb keybords send these very arbitrary codes that indicate which physical location was pressed, but encode absolutely nothing about the meaning of that location... not even a single fixed byte in the usb descriptors indicating the keyboard's layout which could be used. Every user then is responsible for configuring their PC for their keyboard's actual layout (Microsoft and Apple sell localized copies which default to that nation's most common layout), because it can't be automatically detected. And when emulating a keyboard, all this extra complexity is needed to support the many different international layouts, because the PC is configured for a specific layout and you can't tell anything else.

On the plus side, usb keyboards are very cheap!
 
Last edited:
Thanks for testing the French layout. I'm incorporating your fixes right now!

So the very unfortunate long-term result is all usb keybords send these very arbitrary codes that indicate which physical location was pressed, but encode absolutely nothing about the meaning of that location... not even a single fixed byte in the usb descriptors indicating the keyboard's layout which could be used. Every user then is responsible for configuring their PC for their keyboard's actual layout (Microsoft and Apple sell localized copies which default to that nation's most common layout), because it can't be automatically detected. And when emulating a keyboard, all this extra complexity is needed to support the many different international layouts, because the PC is configured for a specific layout and you can't tell anything else.

On the plus side, usb keyboards are very cheap!

Thanks for that bit of history. Wouldn't be surprised if it's a similar story behind the other USB peripherals.
 
Back
Top