[SOLVED] Very strange Seeeduino XIAO as keyboard-emulator code does not type the "3"

Hi everybody I encountered a very strange behaviour:

I modified the keyboard-library for the german keyboard-layout.
This means the translation table which ASCII-code is what USB-report-value is different.

while testing I recognized that the "3" is not typed.
I did quite some debuging to narrow down the problem.
I added debug-output if the corresponding USB-report-value is set properly
YES it is set properly

So finally I modified the correct value which is 0#20 with the value for character "+" which is 0x30
Then I sended the keystrokes "01234567890"
and received "012 + 4567890"

this confirms the ASCII-code-table is used correctly. If ASCCI-code of character "3" is handed over which is ASCII-code decimal 51 my code looks up the table at position decimal 51
If at this position 51 is the USB-report-value for "+" the character "+" is sended.

If at this position 51 is the USB-report-value for "3" NO character is sended.

I confirmed this behaviour on a second computer.
Huh ?!?!?! what's that ??

here is the ASCII-Code table in the german version

/* German keyboard (as HID standard) */
#define KEY_MOD_TABLE_SIZE (127)

const KEY_MOD_VALUE key_mod_values[KEY_MOD_TABLE_SIZE] = {
   {0x00, 0 }, /* 0  */
   {0x00, 0 }, /* 1  */
   {0x00, 0 }, /* 2  */
   {0x00, 0 }, /* 3  */
   {0x00, 0 }, /* 4  */
   {0x00, 0 }, /* 5  */
   {0x00, 0 }, /* 6  */
   {0x00, 0 }, /* 7  */
   {0x00, 0 }, /* 8  */
   {0x00, 0 }, /* 9  */
   {0x00, 0 }, /* 10  */
   {0x00, 0 }, /* 11  */
   {0x00, 0 }, /* 12  */
   {0x00, 0 }, /* 13  */
   {0x00, 0 }, /* 14  */
   {0x00, 0 }, /* 15  */
   {0x00, 0 }, /* 16  */
   {0x00, 0 }, /* 17  */
   {0x00, 0 }, /* 18  */
   {0x00, 0 }, /* 19  */
   {0x00, 0 }, /* 20  */
   {0x00, 0 }, /* 21  */
   {0x00, 0 }, /* 22  */
   {0x00, 0 }, /* 23  */
   {0x00, 0 }, /* 24  */
   {0x00, 0 }, /* 25  */
   {0x00, 0 }, /* 26  */
   {0x00, 0 }, /* 27  */
   {0x00, 0 }, /* 28  */
   {0x00, 0 }, /* 29  */
   {0x00, 0 }, /* 30  */
   {0x00, 0 }, /* 31  */
   {0x00, 0 }, /* 32  */
   {0x1E, KEY_SHIFT }, /* 33   !  */
   {0x0 , 0 }, /* 34  */
   {0x31, 0 }, /* 35   #  */
   {0x21, KEY_SHIFT }, /* 36   $  */
   {0x22, KEY_SHIFT }, /* 37   %  */
   {0x23, KEY_SHIFT }, /* 38   &  */
   {0x31, KEY_SHIFT }, /* 39   '  */
   {0x25, KEY_SHIFT }, /* 40   (  */
   {0x26, KEY_SHIFT }, /* 41   )  */
   {0x30, KEY_SHIFT }, /* 42   * */
   {0x30, 0 }, /* 43   +  */
   {0x36, 0 }, /* 44   ,  */
   {0x38, 0 }, /* 45   -  */
   {0x37, 0 }, /* 46   .  */
   {0x24, KEY_SHIFT }, /* 47   / */
   {0x27, 0 }, /* 48 0 */
   {0x1E, 0 }, /* 49 1 */
   {0x1F, 0 }, /* 50 2 */
   {0x20, 0 }, /* 51 3 */     // {0x20, 0 }, /* 51 3 */
   {0x21, 0 }, /* 52 4 */
   {0x22, 0 }, /* 53 5 */
   {0x23, 0 }, /* 54 6 */
   {0x24, 0 }, /* 55 7 */
   {0x25, 0 }, /* 56 8 */
   {0x26, 0 }, /* 57 9 */
   {0x37, KEY_SHIFT }, /* 58   :  */
   {0x36, KEY_SHIFT }, /* 59   ; */
   {0x64, 0 }, /* 60   <  */
   {0x27, KEY_SHIFT }, /* 61   =  */
   {0x64, KEY_SHIFT }, /* 62   >  */
   {0x2D, KEY_SHIFT }, /* 63   ?  */
   {0x14, KEY_CTRL_ALT }, /* 64   @ */
   {0x04, KEY_SHIFT }, /* 65   A  */
   {0x05, KEY_SHIFT }, /* 66   B  */
   {0x06, KEY_SHIFT }, /* 67   C  */
   {0x07, KEY_SHIFT }, /* 68   D  */
   {0x08, KEY_SHIFT }, /* 69   E */
   {0x09, KEY_SHIFT }, /* 70   F  */
   {0x0A, KEY_SHIFT }, /* 71   G  */
   {0x0B, KEY_SHIFT }, /* 72   H  */
   {0x0C, KEY_SHIFT }, /* 73   I */
   {0x0D, KEY_SHIFT }, /* 74   J  */
   {0x0E, KEY_SHIFT }, /* 75   K  */
   {0x0F, KEY_SHIFT }, /* 76   L  */
   {0x10, KEY_SHIFT }, /* 77   M  */
   {0x11, KEY_SHIFT }, /* 78   N  */
   {0x12, KEY_SHIFT }, /* 79   O */
   {0x13, KEY_SHIFT }, /* 80   P */
   {0x14, KEY_SHIFT }, /* 81   Q */
   {0x15, KEY_SHIFT }, /* 82   R */
   {0x16, KEY_SHIFT }, /* 83   S  */
   {0x17, KEY_SHIFT }, /* 84   T */
   {0x18, KEY_SHIFT }, /* 85   U */
   {0x19, KEY_SHIFT }, /* 86   V  */
   {0x1A, KEY_SHIFT }, /* 87   W */
   {0x1B, KEY_SHIFT }, /* 88   X  */
   {0x1D, KEY_SHIFT }, /* 89   Y  */
   {0x1C, KEY_SHIFT }, /* 90   Z */
   {0x25, KEY_CTRL_ALT }, /* 91   [ */
   {0x2D, KEY_CTRL_ALT }, /* 92   \ */
   {0x26, KEY_CTRL_ALT }, /* 93   ] */
   {0x35, 0 }, /* 94   ^  */
   {0x38, KEY_SHIFT }, /* 95   _  */
   {0x2E, KEY_SHIFT }, /* 96   `  */
   {0x04, 0 }, /* 97   a  */
   {0x05, 0 }, /* 98   b  */
   {0x06, 0 }, /* 99   c  */
   {0x07, 0 }, /* 100   d  */
   {0x08, 0 }, /* 101   e  */
   {0x09, 0 }, /* 102   f  */
   {0x0A, 0 }, /* 103   g  */
   {0x0B, 0 }, /* 104   h  */
   {0x0C, 0 }, /* 105   i  */
   {0x0D, 0 }, /* 106   j  */
   {0x0E, 0 }, /* 107   k  */
   {0x0F, 0 }, /* 108   l  */
   {0x10, 0 }, /* 109   m  */
   {0x11, 0 }, /* 110   n  */
   {0x12, 0 }, /* 111   o  */
   {0x13, 0 }, /* 112   p  */
   {0x14, 0 }, /* 113   q  */
   {0x15, 0 }, /* 114   r  */
   {0x16, 0 }, /* 115   s  */
   {0x17, 0 }, /* 116   t  */
   {0x18, 0 }, /* 117   u  */
   {0x19, 0 }, /* 118   v  */
   {0x1A, 0 }, /* 119   w  */
   {0x1B, 0 }, /* 120   x  */
   {0x1D, 0 }, /* 121   y  */
   {0x1C, 0 }, /* 122   z  */
   {0x0 , 0 }, /* 123   { */
   {0x64, KEY_CTRL_ALT }, /* 124   | */
   {0x0 , 0 }, /* 125   } */
   {0x30, KEY_CTRL_ALT }, /* 126   ~ */
};

in the original keyboard.cpp for US-keyboard-layout it is the same USB-report-value 0x20. The original lookup-table of the keyboard.cpp has
different encoding for the modifier-key (it is "OR-ing" the value for the Shift-key) but the value for character "3" is the same

#define SHIFT 0x80
const uint8_t _asciimap[128] =
{
	0x00,             // NUL
	0x00,             // SOH
	0x00,             // STX
	0x00,             // ETX
	0x00,             // EOT
	0x00,             // ENQ
	0x00,             // ACK  
	0x00,             // BEL
	0x2a,			// BS	Backspace
	0x2b,			// TAB	Tab
	0x28,			// LF	Enter
	0x00,             // VT 
	0x00,             // FF 
	0x00,             // CR 
	0x00,             // SO 
	0x00,             // SI 
	0x00,             // DEL
	0x00,             // DC1
	0x00,             // DC2
	0x00,             // DC3
	0x00,             // DC4
	0x00,             // NAK
	0x00,             // SYN
	0x00,             // ETB
	0x00,             // CAN
	0x00,             // EM 
	0x00,             // SUB
	0x00,             // ESC
	0x00,             // FS 
	0x00,             // GS 
	0x00,             // RS 
	0x00,             // US 
	0x2c,		   //  ' '
	0x1e|SHIFT,	   // !
	0x34|SHIFT,	   // "
	0x20|SHIFT,    // #
	0x21|SHIFT,    // $
	0x22|SHIFT,    // %
	0x24|SHIFT,    // &
	0x34,          // '
	0x26|SHIFT,    // (
	0x27|SHIFT,    // )
	0x25|SHIFT,    // *
	0x2e|SHIFT,    // +
	0x36,          // ,
	0x2d,          // -
	0x37,          // .
	0x38,          // /
	0x27,          // 0
	0x1e,          // 1
	0x1f,          // 2
	0x20,          // 3   <<<===== same value 0x20 for character "3"
	0x21,          // 4
	0x22,          // 5
	0x23,          // 6
	0x24,          // 7
	0x25,          // 8
	0x26,          // 9
	0x33|SHIFT,      // :
	0x33,          // ;
	0x36|SHIFT,      // <
	0x2e,          // =
	0x37|SHIFT,      // >
	0x38|SHIFT,      // ?
	0x1f|SHIFT,      // @
	0x04|SHIFT,      // A
	0x05|SHIFT,      // B
	0x06|SHIFT,      // C
	0x07|SHIFT,      // D
	0x08|SHIFT,      // E
	0x09|SHIFT,      // F
	0x0a|SHIFT,      // G
	0x0b|SHIFT,      // H
	0x0c|SHIFT,      // I
	0x0d|SHIFT,      // J
	0x0e|SHIFT,      // K
	0x0f|SHIFT,      // L
	0x10|SHIFT,      // M
	0x11|SHIFT,      // N
	0x12|SHIFT,      // O
	0x13|SHIFT,      // P
	0x14|SHIFT,      // Q
	0x15|SHIFT,      // R
	0x16|SHIFT,      // S
	0x17|SHIFT,      // T
	0x18|SHIFT,      // U
	0x19|SHIFT,      // V
	0x1a|SHIFT,      // W
	0x1b|SHIFT,      // X
	0x1c|SHIFT,      // Y
	0x1d|SHIFT,      // Z
	0x2f,          // [
	0x31,          // bslash
	0x30,          // ]
	0x23|SHIFT,    // ^
	0x2d|SHIFT,    // _
	0x35,          // `
	0x04,          // a
	0x05,          // b
	0x06,          // c
	0x07,          // d
	0x08,          // e
	0x09,          // f
	0x0a,          // g
	0x0b,          // h
	0x0c,          // i
	0x0d,          // j
	0x0e,          // k
	0x0f,          // l
	0x10,          // m
	0x11,          // n
	0x12,          // o
	0x13,          // p
	0x14,          // q
	0x15,          // r
	0x16,          // s
	0x17,          // t
	0x18,          // u
	0x19,          // v
	0x1a,          // w
	0x1b,          // x
	0x1c,          // y
	0x1d,          // z
	0x2f|SHIFT,    // {
	0x31|SHIFT,    // |
	0x30|SHIFT,    // }
	0x35|SHIFT,    // ~
	0				// DEL
};

Does anybody have a clue what could cause this bug?

best regards Stefan

I want to add one detail:

the original keyboard-library does some checking with the 6-element array

	// Add k to the key report only if it's not already present
	// and if there is an empty slot.
	if (_keyReport.keys[0] != k && _keyReport.keys[1] != k && 
		_keyReport.keys[2] != k && _keyReport.keys[3] != k &&
		_keyReport.keys[4] != k && _keyReport.keys[5] != k) {
		
		for (i=0; i<6; i++) {
			if (_keyReport.keys[i] == 0x00) {
				_keyReport.keys[i] = k;
				break;
			}
		}
		if (i == 6) {
			setWriteError();
			return 0;
		}	
	}
	sendReport(&_keyReport);

which I do not in my german keyboard-layout-version.
But why the heck should such a modification only affect a single character?
If this would be a problem it should occur on all characters

I added the six-key-logic to my code and now it works.
I haven't analysed what exactly caused this strange behaviour.

So the problem is solved now.
My KeyboardGER library is still a work in progress.
The current state is that the libary itself does not have a function for sending keypresses inside the library. At my actual development state (early alpha-state) this function called typetext is inside the *.ino-file

This function iterates through all characters of the string and does the translation to the according key-code for the USB-report

Again I want to emphasise this is an early alpha state in developing.
The code is not cleaned up. Just a snapshot in the middle of the development process. Just reached the state of
"it works now to type all characters correctly"

With this hint I attach the code of the function and the library here

used constants

const byte Ctrl  = 1;
const byte Shift = 2;
const byte Alt   = 4;

const byte CtrlAlt   = Ctrl + Alt;
const byte AltCtrl   = CtrlAlt;

const byte aeoeue_1stByte = 195;
const byte ae_2ndByte = 164;
const byte oe_2ndByte = 182;
const byte ue_2ndByte = 188;

const byte Ae_2ndByte = 132;
const byte Oe_2ndByte = 150;
const byte Ue_2ndByte = 156;
const byte SharpS_2ndByte = 159;

const byte mue23_1stByte = 194;
const byte mue_2ndByte   = 181;
const byte mue2_2ndByte  = 178;
const byte mue3_2ndByte  = 179;

const byte degree_2ndByte = 176;
const byte paragraph_2ndByte = 167;

const byte euro_1stByte = 226;
const byte euro_2ndByte = 130;
const byte euro_3rdByte = 172;

const int SendDelay = 5;

function typetext

void typeText(const char* text) {

  KeyReport_t myKeyMessage;

  myKeyMessage.modifiers = 0;
  myKeyMessage.reserved  = 0;
  for (int j = 0; j < 6; j++) {
    myKeyMessage.pressedKeys[j] = 0;
  }

  uint8_t val;
  uint8_t SecondByte;
  uint8_t ThirdByte;
  uint8_t k;

  int len = strlen(text);
  for (int i = 0; i < len; i++) {

    // translate character to key combination
    val = (uint8_t)text[i];

    if (val > KEY_MOD_TABLE_SIZE) { // character has a multibyte representation
      switch (val) {
        case aeoeue_1stByte: // if it's an äöü
          i++;
          SecondByte = text[i];

          if (SecondByte == ae_2ndByte) { // if it's an ä
            k = 52; // myKeyMessage.pressedKeys[0] = 52;
            myKeyMessage.modifiers   = 0;
            break;
          }

          if (SecondByte == oe_2ndByte) { // if it's an ü
            k = 51; // myKeyMessage.pressedKeys[0] = 51;
            myKeyMessage.modifiers   = 0;
            break;
          }

          if (SecondByte == ue_2ndByte) { // if it's an ü
            k = 47; // myKeyMessage.pressedKeys[0] = 47;
            myKeyMessage.modifiers   = 0;
            break;
          }

          if (SecondByte == Ae_2ndByte) { // if it's an ä
            k = 52; // myKeyMessage.pressedKeys[0] = 52;
            myKeyMessage.modifiers   = Shift;
            break;
          }

          if (SecondByte == Oe_2ndByte) { // if it's an ü
            k = 51; // myKeyMessage.pressedKeys[0] = 51;
            myKeyMessage.modifiers   = Shift;
            break;
          }

          if (SecondByte == Ue_2ndByte) { // if it's an ü
            k = 47; // myKeyMessage.pressedKeys[0] = 47;
            myKeyMessage.modifiers   = Shift;
            break;
          }

          if (SecondByte == SharpS_2ndByte) { // if it's an ü
            k = 45; // myKeyMessage.pressedKeys[0] = 45;
            myKeyMessage.modifiers   = 0;
            break;
          }
        case mue23_1stByte: // if it's a µ²³
          i++;
          SecondByte = text[i];

          if (SecondByte == mue_2ndByte) { // if it's an µ
            k = 16; // myKeyMessage.pressedKeys[0] = 16;
            myKeyMessage.modifiers   = CtrlAlt;
            break;
          }

          if (SecondByte == mue2_2ndByte) { // if it's an ²
            k = 31; // myKeyMessage.pressedKeys[0] = 31;
            myKeyMessage.modifiers   = CtrlAlt;
            break;
          }

          if (SecondByte == mue3_2ndByte) { // if it's an ³
            k = 32; // myKeyMessage.pressedKeys[0] = 32;
            myKeyMessage.modifiers   = CtrlAlt;
            break;
          }

          if (SecondByte == degree_2ndByte) { // if it's an °
            k = 53; // myKeyMessage.pressedKeys[0] = 53;
            myKeyMessage.modifiers   = Shift;
            break;
          }

          if (SecondByte == paragraph_2ndByte) { // if it's an §
            k = 32; // myKeyMessage.pressedKeys[0] = 32;
            myKeyMessage.modifiers   = Shift;
            break;
          }

        case euro_1stByte: // if it's a €-symbol
          i++;
          SecondByte = text[i];
          if (SecondByte == euro_2ndByte) {
            i++;
            ThirdByte = text[i];
            if (ThirdByte == euro_3rdByte) {
              k = 8; // myKeyMessage.pressedKeys[0] = 8;
              myKeyMessage.modifiers   = CtrlAlt;
              break;
            }
          }
        default:
          k = 0; // myKeyMessage.pressedKeys[0] = 0;
          myKeyMessage.modifiers      = 0;
          dbg("default", val);
          break;
      }
      //Serial.println("val > KEYMAP_SIZE special treatment");
    } //if (val > KEY_MOD_TABLE_SIZE) multibyte representation

    else { // character has a single-byte representation
      KEY_MOD_VALUE myKeyAndMod = key_mod_values[val];
      myKeyMessage.modifiers = myKeyAndMod.modifier;

      k = myKeyAndMod.key; // myKeyMessage.pressedKeys[0] = myKeyAndMod.key;
    } //  else { // character has a single-byte representation

    // Add k to the key report only if it's not already present
    // and if there is an empty slot.
    if (myKeyMessage.pressedKeys[0] != k && myKeyMessage.pressedKeys[1] != k &&
        myKeyMessage.pressedKeys[2] != k && myKeyMessage.pressedKeys[3] != k &&
        myKeyMessage.pressedKeys[4] != k && myKeyMessage.pressedKeys[5] != k) {

      for (int l = 0; l < 6; l++) {
        if (myKeyMessage.pressedKeys[l] == 0x00) {
          myKeyMessage.pressedKeys[l] = k;
          break;
        }
      }

      KeyboardGER.sendReport(&myKeyMessage);
      delay(SendDelay);

      // Test the key report to see if k is present.  Clear it if it exists.
      // Check all positions in case the key is present more than once (which it shouldn't be)
      for (int m = 0; m < 6; m++) {
        if (0 != k && myKeyMessage.pressedKeys[m] == k) {
          myKeyMessage.pressedKeys[m] = 0x00;
        }
      }

      myKeyMessage.modifiers = 0;
      KeyboardGER.sendReport(&myKeyMessage);
      delay(SendDelay);

    } // if (myKeyMessage.pressedKeys[0] != k && myKeyMessage.pressedKeys[1] != k &&

  } //for (int i = 0; i < len; i++) {
} // void typeText(const char* text)

code of KeyboardGER.h which contains the modified ASCII-code to USB-key-value table

/*
  KeyboardGER.h

  Copyright (c) 2015, Arduino LLC
  Original code (pre-library): Copyright (c) 2011, Peter Barrett

  This library is free software; you can redistribute it and/or
  modify it under the terms of the GNU Lesser General Public
  License as published by the Free Software Foundation; either
  version 2.1 of the License, or (at your option) any later version.

  This library is distributed in the hope that it will be useful,
  but WITHOUT ANY WARRANTY; without even the implied warranty of
  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  Lesser General Public License for more details.

  You should have received a copy of the GNU Lesser General Public
  License along with this library; if not, write to the Free Software
  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
*/

//#ifndef KEYBOARDGER_h
//#define KEYBOARDGER_h
//#endif

#include "HID.h"

//#if !defined(_USING_HID)

//#warning "Using legacy HID core (non pluggable)"

//#else

//================================================================================
//================================================================================
//  Keyboard

#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

#define KEY_UP_ARROW    0xDA
#define KEY_DOWN_ARROW    0xD9
#define KEY_LEFT_ARROW    0xD8
#define KEY_RIGHT_ARROW   0xD7
#define KEY_BACKSPACE   0xB2
#define KEY_TAB       0xB3
#define KEY_RETURN      0xB0
#define KEY_ESC       0xB1
#define KEY_INSERT      0xD1
#define KEY_DELETE      0xD4
#define KEY_PAGE_UP     0xD3
#define KEY_PAGE_DOWN   0xD6
#define KEY_HOME      0xD2
#define KEY_END       0xD5
#define KEY_CAPS_LOCK   0xC1
#define KEY_F1        0xC2
#define KEY_F2        0xC3
#define KEY_F3        0xC4
#define KEY_F4        0xC5
#define KEY_F5        0xC6
#define KEY_F6        0xC7
#define KEY_F7        0xC8
#define KEY_F8        0xC9
#define KEY_F9        0xCA
#define KEY_F10       0xCB
#define KEY_F11       0xCC
#define KEY_F12       0xCD
#define KEY_F13       0xF0
#define KEY_F14       0xF1
#define KEY_F15       0xF2
#define KEY_F16       0xF3
#define KEY_F17       0xF4
#define KEY_F18       0xF5
#define KEY_F19       0xF6
#define KEY_F20       0xF7
#define KEY_F21       0xF8
#define KEY_F22       0xF9
#define KEY_F23       0xFA
#define KEY_F24       0xFB

//#endif


/* Modifiers */

enum MODIFIER_KEY {
	KEY_CTRL     = 1,
	KEY_SHIFT    = 2,
	KEY_ALT      = 4,
    KEY_CTRL_ALT = 5
};

// START of modifications taken from the ESP32 HIDKeyboardTypes.h-file
//  Low level key report: up to 6 keys and shift, ctrl etc at once

typedef struct {
	unsigned char key;
	unsigned char modifier;
	} KEY_MOD_VALUE;


typedef struct
{
  uint8_t modifiers;
  uint8_t reserved;
  uint8_t pressedKeys[6];
} KeyReport_t;



/* German keyboard (as HID standard) */
#define KEY_MOD_TABLE_SIZE (127)

const KEY_MOD_VALUE key_mod_values[KEY_MOD_TABLE_SIZE] = {
   {0x00, 0 }, /* 0  */
   {0x00, 0 }, /* 1  */
   {0x00, 0 }, /* 2  */
   {0x00, 0 }, /* 3  */
   {0x00, 0 }, /* 4  */
   {0x00, 0 }, /* 5  */
   {0x00, 0 }, /* 6  */
   {0x00, 0 }, /* 7  */
   {0x00, 0 }, /* 8  */
   {0x00, 0 }, /* 9  */
   {0x58, 0 }, /* 10  */  //translate newline to returnkey
   {0x00, 0 }, /* 11  */
   {0x00, 0 }, /* 12  */
   {0x58, 0 }, /* 13  */  // translate return to returnkey
   {0x00, 0 }, /* 14  */
   {0x00, 0 }, /* 15  */
   {0x00, 0 }, /* 16  */
   {0x00, 0 }, /* 17  */
   {0x00, 0 }, /* 18  */
   {0x00, 0 }, /* 19  */
   {0x00, 0 }, /* 20  */
   {0x00, 0 }, /* 21  */
   {0x00, 0 }, /* 22  */
   {0x00, 0 }, /* 23  */
   {0x00, 0 }, /* 24  */
   {0x00, 0 }, /* 25  */
   {0x00, 0 }, /* 26  */
   {0x00, 0 }, /* 27  */
   {0x00, 0 }, /* 28  */
   {0x00, 0 }, /* 29  */
   {0x00, 0 }, /* 30  */
   {0x00, 0 }, /* 31  */
   {0x00, 0 }, /* 32  */
   {0x1E, KEY_SHIFT }, /* 33   !  */
   {0x1F, KEY_SHIFT }, /* 34   "  */
   {0x31, 0 }, /* 35   #  */
   {0x21, KEY_SHIFT }, /* 36   $  */
   {0x22, KEY_SHIFT }, /* 37   %  */
   {0x23, KEY_SHIFT }, /* 38   &  */
   {0x31, KEY_SHIFT }, /* 39   '  */
   {0x25, KEY_SHIFT }, /* 40   (  */
   {0x26, KEY_SHIFT }, /* 41   )  */
   {0x30, KEY_SHIFT }, /* 42   * */
   {0x30, 0 }, /* 43   +  */
   {0x36, 0 }, /* 44   ,  */
   {0x38, 0 }, /* 45   -  */
   {0x37, 0 }, /* 46   .  */
   {0x24, KEY_SHIFT }, /* 47   / */
   {0x27, 0 }, /* 48 0 */
   {0x1E, 0 }, /* 49 1 */
   {0x1F, 0 }, /* 50 2 */
   {0x20, 0 }, /* 51 3 */     // {0x20, 0 }, /* 51 3 */
   {0x21, 0 }, /* 52 4 */
   {0x22, 0 }, /* 53 5 */
   {0x23, 0 }, /* 54 6 */
   {0x24, 0 }, /* 55 7 */
   {0x25, 0 }, /* 56 8 */
   {0x26, 0 }, /* 57 9 */
   {0x37, KEY_SHIFT }, /* 58   :  */
   {0x36, KEY_SHIFT }, /* 59   ; */
   {0x64, 0 }, /* 60   <  */
   {0x27, KEY_SHIFT }, /* 61   =  */
   {0x64, KEY_SHIFT }, /* 62   >  */
   {0x2D, KEY_SHIFT }, /* 63   ?  */
   {0x14, KEY_CTRL_ALT }, /* 64   @ */
   {0x04, KEY_SHIFT }, /* 65   A  */
   {0x05, KEY_SHIFT }, /* 66   B  */
   {0x06, KEY_SHIFT }, /* 67   C  */
   {0x07, KEY_SHIFT }, /* 68   D  */
   {0x08, KEY_SHIFT }, /* 69   E */
   {0x09, KEY_SHIFT }, /* 70   F  */
   {0x0A, KEY_SHIFT }, /* 71   G  */
   {0x0B, KEY_SHIFT }, /* 72   H  */
   {0x0C, KEY_SHIFT }, /* 73   I */
   {0x0D, KEY_SHIFT }, /* 74   J  */
   {0x0E, KEY_SHIFT }, /* 75   K  */
   {0x0F, KEY_SHIFT }, /* 76   L  */
   {0x10, KEY_SHIFT }, /* 77   M  */
   {0x11, KEY_SHIFT }, /* 78   N  */
   {0x12, KEY_SHIFT }, /* 79   O */
   {0x13, KEY_SHIFT }, /* 80   P */
   {0x14, KEY_SHIFT }, /* 81   Q */
   {0x15, KEY_SHIFT }, /* 82   R */
   {0x16, KEY_SHIFT }, /* 83   S  */
   {0x17, KEY_SHIFT }, /* 84   T */
   {0x18, KEY_SHIFT }, /* 85   U */
   {0x19, KEY_SHIFT }, /* 86   V  */
   {0x1A, KEY_SHIFT }, /* 87   W */
   {0x1B, KEY_SHIFT }, /* 88   X  */
   {0x1D, KEY_SHIFT }, /* 89   Y  */
   {0x1C, KEY_SHIFT }, /* 90   Z */
   {0x25, KEY_CTRL_ALT }, /* 91   [ */
   {0x2D, KEY_CTRL_ALT }, /* 92   \ */
   {0x26, KEY_CTRL_ALT }, /* 93   ] */
   {0x35, 0 }, /* 94   ^  */
   {0x38, KEY_SHIFT }, /* 95   _  */
   {0x2E, KEY_SHIFT }, /* 96   `  */
   {0x04, 0 }, /* 97   a  */
   {0x05, 0 }, /* 98   b  */
   {0x06, 0 }, /* 99   c  */
   {0x07, 0 }, /* 100   d  */
   {0x08, 0 }, /* 101   e  */
   {0x09, 0 }, /* 102   f  */
   {0x0A, 0 }, /* 103   g  */
   {0x0B, 0 }, /* 104   h  */
   {0x0C, 0 }, /* 105   i  */
   {0x0D, 0 }, /* 106   j  */
   {0x0E, 0 }, /* 107   k  */
   {0x0F, 0 }, /* 108   l  */
   {0x10, 0 }, /* 109   m  */
   {0x11, 0 }, /* 110   n  */
   {0x12, 0 }, /* 111   o  */
   {0x13, 0 }, /* 112   p  */
   {0x14, 0 }, /* 113   q  */
   {0x15, 0 }, /* 114   r  */
   {0x16, 0 }, /* 115   s  */
   {0x17, 0 }, /* 116   t  */
   {0x18, 0 }, /* 117   u  */
   {0x19, 0 }, /* 118   v  */
   {0x1A, 0 }, /* 119   w  */
   {0x1B, 0 }, /* 120   x  */
   {0x1D, 0 }, /* 121   y  */
   {0x1C, 0 }, /* 122   z  */
   {0x0 , 0 }, /* 123   { */
   {0x64, KEY_CTRL_ALT }, /* 124   | */
   {0x0 , 0 }, /* 125   } */
   {0x30, KEY_CTRL_ALT }, /* 126   ~ */
};



class Keyboard_ : public Print
{
//private: {}
public:
  KeyReport_t _keyReport;
  void sendReport(KeyReport_t* keys);
  Keyboard_(void);
  void begin(void);
  void end(void);
  size_t write(uint8_t k);
  size_t write(const uint8_t *buffer, size_t size);
  size_t press(uint8_t k);
  size_t release(uint8_t k);
  void releaseAll(void);
};
extern Keyboard_ KeyboardGER;

and KeyboardGER.cpp

/*
  KeyboardGER.cpp
  derived from Keyboard.cpp and modified by user StefanL38

  Copyright (c) 2015, Arduino LLC
  Original code (pre-library): Copyright (c) 2011, Peter Barrett

  This library is free software; you can redistribute it and/or
  modify it under the terms of the GNU Lesser General Public
  License as published by the Free Software Foundation; either
  version 2.1 of the License, or (at your option) any later version.

  This library is distributed in the hope that it will be useful,
  but WITHOUT ANY WARRANTY; without even the implied warranty of
  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  Lesser General Public License for more details.

  You should have received a copy of the GNU Lesser General Public
  License along with this library; if not, write to the Free Software
  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
*/

#include "KeyboardGER.h"

#if defined(_USING_HID)

//================================================================================
//================================================================================
//	Keyboard

static const uint8_t _hidReportDescriptor[] PROGMEM = {

  //  Keyboard
    0x05, 0x01,                    // USAGE_PAGE (Generic Desktop)  // 47
    0x09, 0x06,                    // USAGE (Keyboard)
    0xa1, 0x01,                    // COLLECTION (Application)
    0x85, 0x02,                    //   REPORT_ID (2)
    0x05, 0x07,                    //   USAGE_PAGE (Keyboard)
   
  0x19, 0xe0,                    //   USAGE_MINIMUM (Keyboard LeftControl)
    0x29, 0xe7,                    //   USAGE_MAXIMUM (Keyboard Right GUI)
    0x15, 0x00,                    //   LOGICAL_MINIMUM (0)
    0x25, 0x01,                    //   LOGICAL_MAXIMUM (1)
    0x75, 0x01,                    //   REPORT_SIZE (1)
    
  0x95, 0x08,                    //   REPORT_COUNT (8)
    0x81, 0x02,                    //   INPUT (Data,Var,Abs)
    0x95, 0x01,                    //   REPORT_COUNT (1)
    0x75, 0x08,                    //   REPORT_SIZE (8)
    0x81, 0x03,                    //   INPUT (Cnst,Var,Abs)
    
  0x95, 0x06,                    //   REPORT_COUNT (6)
    0x75, 0x08,                    //   REPORT_SIZE (8)
    0x15, 0x00,                    //   LOGICAL_MINIMUM (0)
    0x25, 0x73,                    //   LOGICAL_MAXIMUM (115)
    0x05, 0x07,                    //   USAGE_PAGE (Keyboard)
    
  0x19, 0x00,                    //   USAGE_MINIMUM (Reserved (no event indicated))
    0x29, 0x73,                    //   USAGE_MAXIMUM (Keyboard Application)
    0x81, 0x00,                    //   INPUT (Data,Ary,Abs)
    0xc0,                          // END_COLLECTION
};

Keyboard_::Keyboard_(void) {
	static HIDSubDescriptor node(_hidReportDescriptor, sizeof(_hidReportDescriptor));
	HID().AppendDescriptor(&node);
}

void Keyboard_::begin(void){ }

void Keyboard_::end(void) { }

void Keyboard_::sendReport(KeyReport_t* keys){
	HID().SendReport(2,keys,sizeof(KeyReport_t));
}


uint8_t USBPutChar(uint8_t c);

// press() adds the specified key (printing, non-printing, or modifier)
// to the persistent key report and sends the report.  Because of the way 
// USB HID works, the host acts like the key remains pressed until we 
// call release(), releaseAll(), or otherwise clear the report and resend.
size_t Keyboard_::press(uint8_t k) {
	uint8_t i;

	return 1;
}

// release() takes the specified key out of the persistent key report and
// sends the report.  This tells the OS the key is no longer pressed and that
// it shouldn't be repeated any more.
size_t Keyboard_::release(uint8_t k) {

    // reset all to zero
	return 1;
}


/* START  START  START  START  START  START  START  START  START  START  START  START  START  START  START  START  START  START 
###############################################################################################################################
original version of 
size_t Keyboard_::press(uint8_t k) 
and 
size_t Keyboard_::release(uint8_t k) 



// press() adds the specified key (printing, non-printing, or modifier)
// to the persistent key report and sends the report.  Because of the way 
// USB HID works, the host acts like the key remains pressed until we 
// call release(), releaseAll(), or otherwise clear the report and resend.
size_t Keyboard_::press(uint8_t k) 
{
	uint8_t i;
	if (k >= 136) {			// it's a non-printing key (not a modifier)
		k = k - 136;
	} else if (k >= 128) {	// it's a modifier key
		_keyReport.modifiers |= (1<<(k-128));
		k = 0;
	} else {				// it's a printing key
		k = pgm_read_byte(_asciimap + k);
		if (!k) {
			setWriteError();
			return 0;
		}
		if (k & 0x80) {						// it's a capital letter or other character reached with shift
			_keyReport.modifiers |= 0x02;	// the left shift modifier
			k &= 0x7F;
		}
	}
	
	// Add k to the key report only if it's not already present
	// and if there is an empty slot.
	if (_keyReport.keys[0] != k && _keyReport.keys[1] != k && 
		_keyReport.keys[2] != k && _keyReport.keys[3] != k &&
		_keyReport.keys[4] != k && _keyReport.keys[5] != k) {
		
		for (i=0; i<6; i++) {
			if (_keyReport.keys[i] == 0x00) {
				_keyReport.keys[i] = k;
				break;
			}
		}
		if (i == 6) {
			setWriteError();
			return 0;
		}	
	}
	sendReport(&_keyReport);
	return 1;
}

// release() takes the specified key out of the persistent key report and
// sends the report.  This tells the OS the key is no longer pressed and that
// it shouldn't be repeated any more.
size_t Keyboard_::release(uint8_t k) 
{
	uint8_t i;
	if (k >= 136) {			// it's a non-printing key (not a modifier)
		k = k - 136;
	} else if (k >= 128) {	// it's a modifier key
		_keyReport.modifiers &= ~(1<<(k-128));
		k = 0;
	} else {				// it's a printing key
		k = pgm_read_byte(_asciimap + k);
		if (!k) {
			return 0;
		}
		if (k & 0x80) {							// it's a capital letter or other character reached with shift
			_keyReport.modifiers &= ~(0x02);	// the left shift modifier
			k &= 0x7F;
		}
	}
	
	// Test the key report to see if k is present.  Clear it if it exists.
	// Check all positions in case the key is present more than once (which it shouldn't be)
	for (i=0; i<6; i++) {
		if (0 != k && _keyReport.keys[i] == k) {
			_keyReport.keys[i] = 0x00;
		}
	}

	sendReport(&_keyReport);
	return 1;
}

void Keyboard_::releaseAll(void)
{
	_keyReport.keys[0] = 0;
	_keyReport.keys[1] = 0;	
	_keyReport.keys[2] = 0;
	_keyReport.keys[3] = 0;	
	_keyReport.keys[4] = 0;
	_keyReport.keys[5] = 0;	
	_keyReport.modifiers = 0;
	sendReport(&_keyReport);
}
// ********************************************************************************************************************************
// END  END  END  END  END  END  END  END  END  END  END  END  END  END  END  END  END  END  END  END  END  END  END  END  END  END 
*/
size_t Keyboard_::write(uint8_t c)
{
	uint8_t p = press(c);  // Keydown
	release(c);            // Keyup
	return p;              // just return the result of press() since release() almost always returns 1
}


size_t Keyboard_::write(const uint8_t *buffer, size_t size) {
	size_t n = 0;
	while (size--) {
		if (*buffer != '\r') {
			if (write(*buffer)) {
			  n++;
			} else {
			  break;
			}
		}
		buffer++;
	}
	return n;
}

Keyboard_ KeyboardGER;

#endi

If somebody wants to give hints how to move the typetext-function into the library please do so.
I guess it is not that easy because the characters that are represented by two or three bytes require a different treatment.

A simple

  • take one byte
  • translate to key-report by using a simple write(char Ch) is not possible

best regards Stefan

It's fairly unconventional to modify the index of a 'for()' loop any place but the third field of the 'for' specification. IMO, that could cause confusion for people reading your code. I'd recommend a 'while()' loop instead. Make sure that 'i' is incremented at least once for every possible execution path through your function.