USB(Keyboard) Output not as expected

Hello I would like to use my ESP32S2 as virtual Keyboard.
And it kind of works but not as intendet. The Input (String) does not match the Output(PC). I am not sure if the problem is caused by keyboardlayouts or a different problem (like do I need to set a special property to enable other layouts?)
.

This is the code which I use:

#include <Arduino.h>

#include "USB.h"
#include "USBHIDMouse.h"
#include "USBHIDKeyboard.h"
USBHIDKeyboard keyboard;
void setup() {
  keyboard.begin();
  USB.begin();
  delay(2000); 

    String inputString = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz!\"#$%&'()*+,-./:;<=>?@[\\]^_``{|}~";
  keyboard.print(inputString);
}

void loop()
{}

By using different layouts I got this results:
USA: ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz!"#$%&'()*+,-./:;<=>?@[]^``{|}~
German: ABCDEFGHIJKLMNOPQRSTUVWXZYabcdefghijklmnopqrstuvwxzy!ħ$%/ä)=(,ß.-Öö;:
"ü#+&?^Ü'°
Italy: ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz!°£$%/à)=(^,'.-çò;ì:_"èù+&?\é§
|

Is there a easy way to fix that problem?
I first thought I could create an char array to map the keys but thats not really efficient if I need to translate every char by hand. Does someone may got an idea?

Many thanks in advance

Yes, the problem is caused by keyboard layouts.

The key-presses you send with print() represent real fingers pushing physical keys.

Imagine you have a US keyboard connected to your computer. You then change your computer's keyboard layout to German.

You press: Shift +" (two keys left of L):

Your computer sees that you pressed the button marked ", but interprets this to mean Ä, as your layout is set to German, like in the diagram.


(like do I need to set a special property to enable other layouts?)

The official Arduino keyboard library does have this feature. Unfortunately, it does not yet seem to have been added to ESP version.

Looking at their github issues page, you are not the only one to have struggled with this.


If you have a specific target language in mind, it could be quite reasonable to handle the swapped keys one by one, a lot of them are probably still in the same place (?)

If you're up for the challenge, you may be able to "shoehorn" the updated official Arduino library into the ESP version yourself. At a glance, I suspect they have originally come from the same codebase.

Have a look at the official library and see if it gives you any inspiration on how to tackle the problem.

Thank you for you answer so it is the problem which I was expecting.
Do you know if there are maybe tables which have the translation of the keys in it? So I just could transfer them in to my code (e.g. as "lookups")? That would save alot of time since I don´t have to check every key.

The information about the library is a good hint maybe I can use that to convert the different layouts too.

If it is not to complicated I could try to rewrite it in the esp-Lib too :smiley: .

Do you know if there are maybe tables which have the translation of the keys in it?

I guess the keyboard layouts files used in the official Arduino library are pretty much what you need. I looked into it a bit more and I don't think it should give you too much of a headache to use them.

See here, in the ESP library.
const uint8_t _asciimap[128] is defining a US keyboard layout.
This array is consulted in press() and release().

In the official Arduino library, _asciimap is a pointer, instead.
The appropriate asciimap for the selected layout is passed to begin(), as seen in the official reference.
The data is then accessed slightly differently than with ESP, in press() and release()

Note: looking closer, the print() and release() methods are not exactly identical; you may have to fiddle there a bit more.


If it is not to complicated I could try to rewrite it in the esp-Lib too :smiley: .

If you do go for it, my suggestion would be to try and fit the newer code of the official Arduino keyboard library, into the ESP version, with as little modification as possible.

If you do get something that works really well, put in a pull request, and mention the issue linked above. If it's helpful, who knows? :+1:

It seems I can not use the Layoutfiles for my mapping since they do not change the input to the correct char. They are the raw keycode if I under stand that correct.
(This would be the solution with the mapping, I did not include the layout since that did not work for me)

#include <Arduino.h>
#include "USB.h"
#include "USBHIDKeyboard.h"
#include "KeyboardLayout_DE.h"

char layout_US[] = {
    '\0', '\x01', '\x02', '\x03', '\x04', '\x05', '\x06', '\a',
    '\b', '\t', '\n', '\v', '\f', '\r', '\x0e', '\x0f',
    '\x10', '\x11', '\x12', '\x13', '\x14', '\x15', '\x16', '\x17',
    '\x18', '\x19', '\x1a', '\x1b', '\x1c', '\x1d', '\x1e', '\x1f',
    ' ', '!', '\"', '#', '$', '%', '&', '\'',
    '(', ')', '*', '+', ',', '-', '.', '/',
    '0', '1', '2', '3', '4', '5', '6', '7',
    '8', '9', ':', ';', '<', '=', '>', '?',
    '@', 'A', 'B', 'C', 'D', 'E', 'F', 'G',
    'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O',
    'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W',
    'X', 'Y', 'Z', '[', '\\', ']', '^', '_',
    '`', 'a', 'b', 'c', 'd', 'e', 'f', 'g',
    'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o',
    'p', 'q', 'r', 's', 't', 'u', 'v', 'w',
    'x', 'y', 'z', '{', '|', '}', '~', '\x7f'};

char layout_DE[] = {
    '\0', '\x01', '\x02', '\x03', '\x04', '\x05', '\x06', '\a',
    '\b', '\t', '\n', '\v', '\f', '\r', '\x0e', '\x0f',
    '\x10', '\x11', '\x12', '\x13', '\x14', '\x15', '\x16', '\x17',
    '\x18', '\x19', '\x1a', '\x1b', '\x1c', '\x1d', '\x1e', '\x1f',
    ' ', '!', '@', '\\', '$', '%', '&', '|',
    '*', '(', '{', ']', ',', '/', '.', '/',
    '0', '1', '2', '3', '4', '5', '6', '7',
    '8', '9', ':', '<', '<', '=', '>', '^',
    '@', 'A', 'B', 'C', 'D', 'E', 'F', 'G',
    'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O',
    'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W',
    'X', 'Z', 'Y', '[', '\\', ']', '`', '_',
    '+', 'a', 'b', 'c', 'd', 'e', 'f', 'g',
    'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o',
    'p', 'q', 'r', 's', 't', 'u', 'v', 'w',
    'x', 'z', 'y', '{', '|', '}', '~', '\x7f'};



class EasyKeyboard
{
public:
  EasyKeyboard()
  {
    keyboard.begin();
    USB.begin();
  }
  ~EasyKeyboard()
  {
  }
  void typeDE(uint8_t *buffer, size_t buffersize)
  {
    for (int i = 0; i < buffersize; i++)
    {
      keyboard.print(layout_DE[buffer[i]]);
    }
  }
  void newLine()
  {
    keyboard.println("");
  }
  void type(uint8_t *buffer, size_t buffersize)
  {

    for (int i = 0; i < buffersize; i++)
    {
      keyboard.print(layout_US[buffer[i]]);
    }
  }

private:
  USBHIDKeyboard keyboard;
};

For the secound method changing the files. That did not work for me either.
USBHIDKeyboard.h

/*
  Keyboard.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
*/

#pragma once
#include "Print.h"
#include "USBHID.h"
#if CONFIG_TINYUSB_HID_ENABLED

#include "esp_event.h"
// Default keyboardlayout
// #include "KeyboardLayout.h"
#include "KeyboardLayout_US.h"

ESP_EVENT_DECLARE_BASE(ARDUINO_USB_HID_KEYBOARD_EVENTS);

typedef enum {
    ARDUINO_USB_HID_KEYBOARD_ANY_EVENT = ESP_EVENT_ANY_ID,
    ARDUINO_USB_HID_KEYBOARD_LED_EVENT = 0,
    ARDUINO_USB_HID_KEYBOARD_MAX_EVENT,
} arduino_usb_hid_keyboard_event_t;

typedef union {
    struct {
            uint8_t numlock:1;
            uint8_t capslock:1;
            uint8_t scrolllock:1;
            uint8_t compose:1;
            uint8_t kana:1;
            uint8_t reserved:3;
    };
    uint8_t leds;
} arduino_usb_hid_keyboard_event_data_t;

#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

#define LED_NUMLOCK     0x01
#define LED_CAPSLOCK    0x02
#define LED_SCROLLLOCK  0x04
#define LED_COMPOSE     0x08
#define LED_KANA        0x10

//  Low level key report: up to 6 keys and shift, ctrl etc at once
typedef struct
{
  uint8_t modifiers;
  uint8_t reserved;
  uint8_t keys[6];
} KeyReport;

class USBHIDKeyboard: public USBHIDDevice, public Print
{
private:
    USBHID hid;
    KeyReport _keyReport;
    // contains the used keymap
    const uint8_t *_asciimap;
public:
    USBHIDKeyboard(void);
    USBHIDKeyboard(const uint8_t *keymap);
    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);
    void sendReport(KeyReport* keys);

    //raw functions work with TinyUSB's HID_KEY_* macros
    size_t pressRaw(uint8_t k);
    size_t releaseRaw(uint8_t k);

    void onEvent(esp_event_handler_t callback);
    void onEvent(arduino_usb_hid_keyboard_event_t event, esp_event_handler_t callback);

    // internal use
    uint16_t _onGetDescriptor(uint8_t* buffer);
    void _onOutput(uint8_t report_id, const uint8_t* buffer, uint16_t len);
};



#endif

USBHIDKeyboard.cpp:

/*
  Keyboard.cpp

  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 "USBHID.h"

#if CONFIG_TINYUSB_HID_ENABLED

#include "USBHIDKeyboard.h"


ESP_EVENT_DEFINE_BASE(ARDUINO_USB_HID_KEYBOARD_EVENTS);
esp_err_t arduino_usb_event_post(esp_event_base_t event_base, int32_t event_id, void *event_data, size_t event_data_size, TickType_t ticks_to_wait);
esp_err_t arduino_usb_event_handler_register_with(esp_event_base_t event_base, int32_t event_id, esp_event_handler_t event_handler, void *event_handler_arg);

static const uint8_t report_descriptor[] = {
    TUD_HID_REPORT_DESC_KEYBOARD(HID_REPORT_ID(HID_REPORT_ID_KEYBOARD))
};

USBHIDKeyboard::USBHIDKeyboard(): hid(){
    // Use US Keyboard as default
    _asciimap = KeyboardLayout_en_US;
    static bool initialized = false;
    if(!initialized){
        initialized = true;
        hid.addDevice(this, sizeof(report_descriptor));
    }
}

USBHIDKeyboard::USBHIDKeyboard(const uint8_t *keymap): hid(){
    _asciimap = keymap;
    static bool initialized = false;
    if(!initialized){
        initialized = true;
        hid.addDevice(this, sizeof(report_descriptor));
    }
}


uint16_t USBHIDKeyboard::_onGetDescriptor(uint8_t* dst){
    memcpy(dst, report_descriptor, sizeof(report_descriptor));
    return sizeof(report_descriptor);
}

void USBHIDKeyboard::begin(){
    hid.begin();
}

void USBHIDKeyboard::end(){
}

void USBHIDKeyboard::onEvent(esp_event_handler_t callback){
    onEvent(ARDUINO_USB_HID_KEYBOARD_ANY_EVENT, callback);
}
void USBHIDKeyboard::onEvent(arduino_usb_hid_keyboard_event_t event, esp_event_handler_t callback){
    arduino_usb_event_handler_register_with(ARDUINO_USB_HID_KEYBOARD_EVENTS, event, callback, this);
}

void USBHIDKeyboard::_onOutput(uint8_t report_id, const uint8_t* buffer, uint16_t len){
    if(report_id == HID_REPORT_ID_KEYBOARD){
        arduino_usb_hid_keyboard_event_data_t p;
        p.leds = buffer[0];
        arduino_usb_event_post(ARDUINO_USB_HID_KEYBOARD_EVENTS, ARDUINO_USB_HID_KEYBOARD_LED_EVENT, &p, sizeof(arduino_usb_hid_keyboard_event_data_t), portMAX_DELAY);
    }
}

void USBHIDKeyboard::sendReport(KeyReport* keys)
{
    hid_keyboard_report_t report;
    report.reserved = 0;
    report.modifier = keys->modifiers;
    if (keys->keys) {
        memcpy(report.keycode, keys->keys, 6);
    } else {
        memset(report.keycode, 0, 6);
    }
    hid.SendReport(HID_REPORT_ID_KEYBOARD, &report, sizeof(report));
}



size_t USBHIDKeyboard::pressRaw(uint8_t k) 
{
    uint8_t i;
    if (k >= 0xE0 && k < 0xE8) {
        // it's a modifier key
        _keyReport.modifiers |= (1<<(k-0x80));
    } else if (k && k < 0xA5) {
        // 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) {
                return 0;
            }   
        }
    } else {
        //not a modifier and not a key
        return 0;
    }
    sendReport(&_keyReport);
    return 1;
}

size_t USBHIDKeyboard::releaseRaw(uint8_t k) 
{
    uint8_t i;
    if (k >= 0xE0 && k < 0xE8) {
        // it's a modifier key
        _keyReport.modifiers &= ~(1<<(k-0x80));
    } else if (k && k < 0xA5) {
        // 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;
            }
        }
    } else {
        //not a modifier and not a key
        return 0;
    }

    sendReport(&_keyReport);
    return 1;
}

// 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 USBHIDKeyboard::press(uint8_t k) 
{
    if (k >= 0x88) {         // it's a non-printing key (not a modifier)
        k = k - 0x88;
    } else if (k >= 0x80) {  // it's a modifier key
        _keyReport.modifiers |= (1<<(k-0x80));
        k = 0;
    } else {                // it's a printing key
        k = _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;
        }
    }
    return pressRaw(k);
}

// 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 USBHIDKeyboard::release(uint8_t k) 
{
    if (k >= 0x88) {         // it's a non-printing key (not a modifier)
        k = k - 0x88;
    } else if (k >= 0x80) {  // it's a modifier key
        _keyReport.modifiers &= ~(1<<(k-0x80));
        k = 0;
    } else {                // it's a printing key
        k = _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;
        }
    }
    return releaseRaw(k);
}

void USBHIDKeyboard::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);
}

size_t USBHIDKeyboard::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 USBHIDKeyboard::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;
}

#endif /* CONFIG_TINYUSB_HID_ENABLED */

KeyboardLayout_US.h

#include "KeyboardLayout.h"
#pragma once

const uint8_t KeyboardLayout_en_US[128] PROGMEM
{
    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
    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
};

KeyboardLayout_DE.h:

/*
 * German keyboard layout.
 */

#include "KeyboardLayout.h"
#pragma once

extern const uint8_t KeyboardLayout_de_DE[128] PROGMEM =
{
	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,    // !
	0x1f|SHIFT,    // "
	0x31,          // #
	0x21|SHIFT,    // $
	0x22|SHIFT,    // %
	0x23|SHIFT,    // &
	0x31|SHIFT,    // '
	0x25|SHIFT,    // (
	0x26|SHIFT,    // )
	0x30|SHIFT,    // *
	0x30,          // +
	0x36,          // ,
	0x38,          // -
	0x37,          // .
	0x24|SHIFT,    // /
	0x27,          // 0
	0x1e,          // 1
	0x1f,          // 2
	0x20,          // 3
	0x21,          // 4
	0x22,          // 5
	0x23,          // 6
	0x24,          // 7
	0x25,          // 8
	0x26,          // 9
	0x37|SHIFT,    // :
	0x36|SHIFT,    // ;
	0x32,          // <
	0x27|SHIFT,    // =
	0x32|SHIFT,    // >
	0x2d|SHIFT,    // ?
	0x14|ALT_GR,   // @
	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
	0x1d|SHIFT,    // Y
	0x1c|SHIFT,    // Z
	0x25|ALT_GR,   // [
	0x2d|ALT_GR,   // bslash
	0x26|ALT_GR,   // ]
	0x00,          // ^  not supported (requires dead key + space)
	0x38|SHIFT,    // _
	0x00,          // `  not supported (requires dead key + space)
	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
	0x1d,          // y
	0x1c,          // z
	0x24|ALT_GR,   // {
	0x32|ALT_GR,   // |
	0x27|ALT_GR,   // }
	0x30|ALT_GR,   // ~
	0x00           // DEL
};

KeyboardLayout.h:

/*
  KeyboardLayout.h

  This file is not part of the public API. It is meant to be included
  only in Keyboard.cpp and the keyboard layout files. Layout files map
  ASCII character codes to keyboard scan codes (technically, to USB HID
  Usage codes), possibly altered by the SHIFT or ALT_GR modifiers.
  Non-ACSII characters (anything outside the 7-bit range NUL..DEL) are
  not supported.

  == Creating your own layout ==

  In order to create your own layout file, copy an existing layout that
  is similar to yours, then modify it to use the correct keys. The
  layout is an array in ASCII order. Each entry contains a scan code,
  possibly modified by "|SHIFT" or "|ALT_GR", as in this excerpt from
  the Italian layout:

      0x35,          // bslash
      0x30|ALT_GR,   // ]
      0x2e|SHIFT,    // ^

  Do not change the control characters (those before scan code 0x2c,
  corresponding to space). Do not attempt to grow the table past DEL. Do
  not use both SHIFT and ALT_GR on the same character: this is not
  supported. Unsupported characters should have 0x00 as scan code.

  For a keyboard with an ISO physical layout, use the scan codes below:

      +---+---+---+---+---+---+---+---+---+---+---+---+---+-------+
      |35 |1e |1f |20 |21 |22 |23 |24 |25 |26 |27 |2d |2e |BackSp |
      +---+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-----+
      | Tab |14 |1a |08 |15 |17 |1c |18 |0c |12 |13 |2f |30 | Ret |
      +-----++--++--++--++--++--++--++--++--++--++--++--++--++    |
      |CapsL |04 |16 |07 |09 |0a |0b |0d |0e |0f |33 |34 |31 |    |
      +----+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+---+----+
      |Shi.|32 |1d |1b |06 |19 |05 |11 |10 |36 |37 |38 |  Shift   |
      +----+---++--+-+-+---+---+---+---+---+--++---+---++----+----+
      |Ctrl|Win |Alt |                        |AlGr|Win |Menu|Ctrl|
      +----+----+----+------------------------+----+----+----+----+

  The ANSI layout is identical except that key 0x31 is above (rather
  than next to) Return, and there is not key 0x32.

  Give a unique name to the layout array, then declare it in Keyboard.h
  with a line of the form:

    extern const uint8_t KeyboardLayout_xx_YY[];

  == Encoding details ==

  All scan codes are less than 0x80, which makes bit 7 available to
  signal that a modifier (Shift or AltGr) is needed to generate the
  character. With only one exception, keys that are used with modifiers
  have scan codes that are less than 0x40. This makes bit 6 available
  to signal whether the modifier is Shift or AltGr. The exception is
  0x64, the key next next to Left Shift on the ISO layout (and absent
  from the ANSI layout). We handle it by replacing its value by 0x32 in
  the layout arrays.
*/

#include <Arduino.h>
#pragma once

#define SHIFT 0x80
#define ALT_GR 0xc0
#define ISO_KEY 0x64
#define ISO_REPLACEMENT 0x32

main.cpp:


#include <Arduino.h>

#include "USB.h"
#include "USBHIDMouse.h"
#include "USBHIDKeyboardMod.h"
#include "KeyboardLayout_DE.h"
#include "KeyboardLayout_US.h"
USBHIDKeyboardMod keyboard(KeyboardLayout_de_DE);
void setup() {
  keyboard.begin();
  USB.begin();
  delay(2000); 

    String inputString = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz!\"#$%&'()*+,-./:;<=>?@[\\]^_``{|}~";
  // keyboard.printChars();
  keyboard.print(inputString);
}

void loop()
{}

The expected result would be:
ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz!"#$%&'()+,-./:;<=>?@[]^_``{|}~
But I get :
ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz!"#$%&'()
+,-./:;#='?/" + It locks my screen
So I guess there is still a small error since most of the output is correct except the last ones.

Do you may know why this problem occures? Does the lib work correct with an arduino?

Thank you for your help so far :smiley:

they do not change the input to the correct char. They are the raw keycode if I under stand that correct.

That is correct yes, they would have to used in a different way.

It locks my screen. Do you may know why this problem occures?

Without testing it myself, I'd just be guessing. I don't have any ESP boards to try it out on, but I'll see what I can manage.

Rewriting the library may have been a bit ambitious of a suggestion. If you really wanted to see if it was a potential solution, you could try replacing the contents of _asciimap[] with the the contents of KeyboardLayout_de_DE[] from the official Arduino keyboard library, making no other modifications.

If your board then works correctly, with your computer's keyboard layout set to German, it would suggest that slotting the Arduino keyboard layouts into the ESP library is reasonable.


This would be the solution with the mapping,

The code you've written there might be a much better way to go. Have you tested this out? What was the result?


I also want to ask, how are you hoping to use this keyboard device? This question might help in deciding how you want to handle the problem.

Yes the official Arduino library does work correctly. I need to say, that although the ESP version appears to have originally been made from the Arduino library, they are not identical.

I actually thougth that I did that. I replaced the _asciimap[] with an pointer and I pass the layout in the constructor (if there is no layout then use the us layout). I think the biggest difference between the code I posted and the arduino code is that pass the argument in the constructor not in the begin methode. Maybe I need to go deeper into the code to get that working.
It does work for the most of the "keys" but some do not work.

It works but I need to get the correct tranlations between the chars but I am stuck to find the missing chars so I got a complete set. (I also would like to support other keyboardlayouts too).
Maybe I even could create an arduino lib with that so others can use that too.

Multiple things currently a passwort manager with special functions and after that my own keyboard with makros and stuff like that.

Thank you, again for your help

Maybe I need to go deeper into the code to get that working.
It does work for the most of the "keys" but some do not work.

It might be a lost cause, but I notice that the actual code is different for press() and release(). It might be an idea to completely replace the ESP library's versions of these methods with the Arduino ones. You'd have to modify them slightly, as the arduino versions call sendReport(), but the ESP versions currently return pressRaw(k).

It works but I need to get the correct tranlations between the chars but I am stuck

You would only need to handle keys that can be physically pressed on the keyboard, I think.

One way I guess you could find them:

Set your computer's keyboard layout to en_US
Look at the keyboard layout you want to "translate".
See the Eszett, on the right side of the zero key.

On your physical keyboard, push the key where the Eszett would be located.
Your computer will type -.

This character , - , when passed to keyboard.press(), will give you your Eszett, if your computer's keyboard is set to de_DE.

Try it and see: set your computer's keyboard layout to German, then on a physical English keyboard, press the - key. This is what you will be mimicking.

Note: You may (?) run into difficulty handling special characters in strings. I don't imagine that 'ß' fits into a char.

To work around this, the official Arduino keyboard library uses a set of macros for each layout:

// de_DE keys
#define KEY_CIRCUMFLEX    (136+0x35)
#define KEY_ESZETT        (136+0x2d)
#define KEY_ACUTE         (136+0x2e)
#define KEY_U_UMLAUT      (136+0x2f)
#define KEY_O_UMLAUT      (136+0x33)
#define KEY_A_UMLAUT      (136+0x34)

These are passed down through the high level methods until they are ultimately handled in press() and release(). They are the same format as the _asciimap[] data.

There is an official page on this in the Arduino reference: Keyboard Modifiers and Special Keys .

I will look in to that.

Thats a good hint I didn´t pay attention to this in the quick solution

I will try that it sounds do able.

I just remembered that I also can use ESP-IDF code, maybe there are easy to use options or functions there. I could then keep the signature of the methodes but replace them with the other code. That could be another option when the others do not work as expected.

Let us know how you get on!

I did look in to the possibilitys the way with the IDF may could work but for that I need to add an IFF lib to use the TinyUSB Stack so I guess that is to complicated at the moment (but I could do alot of extra stuff with that too)

I think I found out why the @ does not work. The problem is that the ESP lib only got the US layout which mean that the pressing of alt-gr is implemented. But I am not sure if I can fix that somehow.

I could not fully test the solution with translating the characters in the correct output symbol but I think I may have similar issus since US keyboards don´t use alt-gr as far as I know.

I think I got it working. I was thinking to add more special chars but I just have not that much time at the moment (since this project didn´t really changed since last week. Now I still need to clean up and complete the missing chars, the logic should work. Also other keyboardlayouts should be able to get ported with this as well.
But it is still work in progress. I think it is to memory intensive for arduino for now I could add an more memory friendly alternative but that would make the code slower.

This topic was automatically closed 180 days after the last reply. New replies are no longer allowed.