USB HID Keyboard returning unknown bytes

ctadlock

Active member
We have connected a USB HID keyboard device (think barbode scanner) to a Teensy 4 via the underside D-/D+ pins and are using PaulStoffregen/USBHost_t36. We have configured the device so it returns the following characters when a button is pressed. This works as expected when connected to Windows. However when we read the data from code on the Teensy we get unexpected data. We are in contact with the device manufacturer and they say they are using a standard keyboard.

So whats the issue? Is the code wrong? Are we not processing the data correctly?

EXPECTED / Windows
Code:
-045DD30AA84880;

ACTUAL / Teensy
Code:
Byte received (hex): 0
Byte received (hex): 5F
Byte received (hex): 29
Byte received (hex): 40
Byte received (hex): 24
Byte received (hex): 44
Byte received (hex): 5E
Byte received (hex): 42
Byte received (hex): 21
Byte received (hex): 41
Byte received (hex): 21
Byte received (hex): 24
Byte received (hex): 45
Byte received (hex): 29
Byte received (hex): 29
Byte received (hex): 29
Byte received (hex): 3A
Byte received (hex): 0

Teensy Code
C:
#include <USBHost_t36.h>

// Create USB host object
USBHost myusb;
USBHIDParser hid1(myusb);
KeyboardController keyboard(hid1);

// Callback function to handle when a key is pressed
void keyPressed() {
  uint8_t rawKey = keyboard.getKey();  // Get the raw byte from the device
 
  // Print the byte as hexadecimal
  Serial.print("Byte received (hex): ");
  Serial.print(rawKey, HEX);
}

void setup() {
  // Initialize Serial communication
  Serial.begin(9600);
  while (!Serial);  // Wait for Serial to connect

  // Initialize USB
  myusb.begin();

  // Attach the key press callback
  keyboard.attachPress(keyPressed);
}

void loop() {
  // Process USB events
  myusb.Task();
}
 
If you have not done so, I always suggest that you setup your Arduino preferences to show all warnings... Although not sure if Teensy actually looks
at this setting or not, but, if you do a build of your sketch you will see:
Code:
"C:\\Users\\kurte\\AppData\\Local\\Arduino15\\packages\\teensy\\tools\\teensy-compile\\11.3.1/arm/bin/arm-none-eabi-g++" -c -O2 -g -Wall -ffunction-sections -fdata-sections -nostdlib -MMD -std=gnu++17 -fno-exceptions -fpermissive -fno-rtti -fno-threadsafe-statics -felide-constructors -Wno-error=narrowing -Wno-psabi -mthumb -mcpu=cortex-m7 -mfloat-abi=hard -mfpu=fpv5-d16 -D__IMXRT1062__ -DTEENSYDUINO=160 -DARDUINO=10607 -DARDUINO_TEENSY41 -DF_CPU=600000000 -DUSB_SERIAL -DLAYOUT_US_ENGLISH "-IC:\\Users\\kurte\\AppData\\Local\\Temp\\arduino\\sketches\\521B66E656FE1FEC325D9391AE7469FA/pch" "-IC:\\Users\\kurte\\AppData\\Local\\Arduino15\\packages\\teensy\\hardware\\avr\\0.60.2\\cores\\teensy4" "-Ic:\\Users\\kurte\\Documents\\Arduino\\libraries\\USBHost_T36" "-IC:\\Users\\kurte\\AppData\\Local\\Arduino15\\packages\\teensy\\hardware\\avr\\0.60.2\\libraries\\SdFat\\src" "-IC:\\Users\\kurte\\AppData\\Local\\Arduino15\\packages\\teensy\\hardware\\avr\\0.60.2\\libraries\\SPI" "-IC:\\Users\\kurte\\AppData\\Local\\Arduino15\\packages\\teensy\\hardware\\avr\\0.60.2\\libraries\\EEPROM" "C:\\Users\\kurte\\AppData\\Local\\Temp\\arduino\\sketches\\521B66E656FE1FEC325D9391AE7469FA\\sketch\\sketch_oct7a.ino.cpp" -o "C:\\Users\\kurte\\AppData\\Local\\Temp\\arduino\\sketches\\521B66E656FE1FEC325D9391AE7469FA\\sketch\\sketch_oct7a.ino.cpp.o"
C:\Users\kurte\AppData\Local\Temp\.arduinoIDE-unsaved202497-18692-1cm6r2g.65ik\sketch_oct7a\sketch_oct7a.ino: In function 'void setup()':
C:\Users\kurte\AppData\Local\Temp\.arduinoIDE-unsaved202497-18692-1cm6r2g.65ik\sketch_oct7a\sketch_oct7a.ino:26:24: warning: invalid conversion from 'void (*)()' to 'void (*)(int)' [-fpermissive]
   26 |   keyboard.attachPress(keyPressed);
      |                        ^~~~~~~~~~
      |                        |
      |                        void (*)()
In file included from C:\Users\kurte\AppData\Local\Temp\.arduinoIDE-unsaved202497-18692-1cm6r2g.65ik\sketch_oct7a\sketch_oct7a.ino:1:
c:\Users\kurte\Documents\Arduino\libraries\USBHost_T36/USBHost_t36.h:798:33: note:   initializing argument 1 of 'void KeyboardController::attachPress(void (*)(int))'
  798 |     void     attachPress(void (*f)(int unicode)) { keyPressedFunction = f; }
      |                          ~~~~~~~^~~~~~~~~~~~~~~
Compiling libraries...

Which is a hint, in that the callback function has a parameter where you are called with the actual processed character code for the
different keystrokes. Which is what you are probably expecting.

As the comment in your sketch mentions:
Code:
 uint8_t rawKey = keyboard.getKey();  // Get the raw byte from the device
returns to you the raw data (scan code) that was returned from your logical keyboard. Which the windows device driver and the USB Host
code converts into the expected data, using the keyboard layout settings.

There are several places on the internet where you can look up what these key codes are:
 
If you have not done so, I always suggest that you setup your Arduino preferences to show all warnings... Although not sure if Teensy actually looks
at this setting or not, but, if you do a build of your sketch you will see:
Code:
"C:\\Users\\kurte\\AppData\\Local\\Arduino15\\packages\\teensy\\tools\\teensy-compile\\11.3.1/arm/bin/arm-none-eabi-g++" -c -O2 -g -Wall -ffunction-sections -fdata-sections -nostdlib -MMD -std=gnu++17 -fno-exceptions -fpermissive -fno-rtti -fno-threadsafe-statics -felide-constructors -Wno-error=narrowing -Wno-psabi -mthumb -mcpu=cortex-m7 -mfloat-abi=hard -mfpu=fpv5-d16 -D__IMXRT1062__ -DTEENSYDUINO=160 -DARDUINO=10607 -DARDUINO_TEENSY41 -DF_CPU=600000000 -DUSB_SERIAL -DLAYOUT_US_ENGLISH "-IC:\\Users\\kurte\\AppData\\Local\\Temp\\arduino\\sketches\\521B66E656FE1FEC325D9391AE7469FA/pch" "-IC:\\Users\\kurte\\AppData\\Local\\Arduino15\\packages\\teensy\\hardware\\avr\\0.60.2\\cores\\teensy4" "-Ic:\\Users\\kurte\\Documents\\Arduino\\libraries\\USBHost_T36" "-IC:\\Users\\kurte\\AppData\\Local\\Arduino15\\packages\\teensy\\hardware\\avr\\0.60.2\\libraries\\SdFat\\src" "-IC:\\Users\\kurte\\AppData\\Local\\Arduino15\\packages\\teensy\\hardware\\avr\\0.60.2\\libraries\\SPI" "-IC:\\Users\\kurte\\AppData\\Local\\Arduino15\\packages\\teensy\\hardware\\avr\\0.60.2\\libraries\\EEPROM" "C:\\Users\\kurte\\AppData\\Local\\Temp\\arduino\\sketches\\521B66E656FE1FEC325D9391AE7469FA\\sketch\\sketch_oct7a.ino.cpp" -o "C:\\Users\\kurte\\AppData\\Local\\Temp\\arduino\\sketches\\521B66E656FE1FEC325D9391AE7469FA\\sketch\\sketch_oct7a.ino.cpp.o"
C:\Users\kurte\AppData\Local\Temp\.arduinoIDE-unsaved202497-18692-1cm6r2g.65ik\sketch_oct7a\sketch_oct7a.ino: In function 'void setup()':
C:\Users\kurte\AppData\Local\Temp\.arduinoIDE-unsaved202497-18692-1cm6r2g.65ik\sketch_oct7a\sketch_oct7a.ino:26:24: warning: invalid conversion from 'void (*)()' to 'void (*)(int)' [-fpermissive]
   26 |   keyboard.attachPress(keyPressed);
      |                        ^~~~~~~~~~
      |                        |
      |                        void (*)()
In file included from C:\Users\kurte\AppData\Local\Temp\.arduinoIDE-unsaved202497-18692-1cm6r2g.65ik\sketch_oct7a\sketch_oct7a.ino:1:
c:\Users\kurte\Documents\Arduino\libraries\USBHost_T36/USBHost_t36.h:798:33: note:   initializing argument 1 of 'void KeyboardController::attachPress(void (*)(int))'
  798 |     void     attachPress(void (*f)(int unicode)) { keyPressedFunction = f; }
      |                          ~~~~~~~^~~~~~~~~~~~~~~
Compiling libraries...

Which is a hint, in that the callback function has a parameter where you are called with the actual processed character code for the
different keystrokes. Which is what you are probably expecting.

As the comment in your sketch mentions:
Code:
 uint8_t rawKey = keyboard.getKey();  // Get the raw byte from the device
returns to you the raw data (scan code) that was returned from your logical keyboard. Which the windows device driver and the USB Host
code converts into the expected data, using the keyboard layout settings.

There are several places on the internet where you can look up what these key codes are:
I agree that code is not correct, but it is not causing the issue. If I make the change to have keyPressed take the key, it produces the exact same result.
 
What happens if you change the code like:
Code:
void keyPressed(int key) {
  // Print the byte as hexadecimal
  Serial.print("Byte received (hex): ");
  Serial.print(key, HEX);
}
 
So we figured it out... one of my developers noticed that the device is sending data as if the shift key is being pressed. When we checked `keyboard.getModifiers()` it returns 0. We eneded up just making a lookup table for the chars we wanted to "unshift" and it processes correctly now. Im assuming the first and last 0 are start and terminators. We wanted the alpha characters to remain shifted hence the default case below.

Is this some standard and/or is there some other better way to handle this?

image.png
 
Last edited:
That's a different string of "byte received" characters than what you posted in the first post, so not surprising nobody was able to recognize the issue.
 
Back
Top