Keyboard pass-through project...

Figured I'd pick/ping the talent here to see if anybody knows of a project, before I re-invent the wheel here.

I am currently using a (more or less) Leonardo and a USB Host Shield, with a normal USB keyboard attached to it..

before I start on the other effects/behavior I need in this project.. I need to get a base state hashed out where the keyboard acts 'as normal', and passes on all key strokes (and behaves) as normal.

I was a bit flawed in my understanding of the USB Host Shield/Library, where I thought it (by default) passed on the keystrokes/presses onto the end device. However this is not the case, and only mirrors the output to the serial monitor.

So I have been using these example HIDBootKBD sketch here:

and hooking into the methods available to pass on the keypresses to the end device, using the keyboard.write()..etc functions:

https://www.arduino.cc/en/Reference/MouseKeyboard

that being said... I need to account for the modifiers, control keys, repeating keys...etc.. so the keyboard still functions as normal and without error/incident...

I figured someone may have been down this road before? And already done what I'd like to do?

anyone have any links? tuts?..etc to share to perhaps help me along on this?

I have only gotten single 'characters' to pass through to the end device, no carriage returns, no control keys, no modifiers...etc..

The only approach in my head is to get some sort of long switch/case statement to detect when a special key has been pressed?

(ie: how to know what was pressed, in order to know what data to pass along using the keyboard class..etc) doing it for basic 'characters' was easy as you just pass along the: "Serial.println((char)key);"

Looking for some inspiration (clues) on the proper and easiest way to tackle this.

thanks!
-xl

I don't have a Host shield.

If you have a program that allows you to type on your USB keyboard and have the characters appear on the serial monitor then you will need to extend that program to detect each key press and send the corresponding key press to the PC.

...R

Thanks.

Yeah thats what I am currently doing... but thought perhaps someone had done this type of thing before..

(and I could crib off the approach used for detecting the modifier/control keys..etc)

the goal is have it working 'as normal' even though its going through the USB Shield..etc.

found this in my searches: https://www.arduino.cc/en/Tutorial/KeyboardSerial

Hey xl97,

I am working on a similar project, did you manage to pass through all modifier and command keys?

Would you share your code?

Thanks

No.

I never really went back to things...

seemed like such a pain to manually handle things...

I'd have to dig up what I had so far.. I just went through and tried to map out all keys, modifiers and command keys..

I think I got a few keys in and quit if I recall.. looking for code from someone who may have been down the road before.

Share if you find anything. :slight_smile:

Hi,

I am working on a project that seems to have some code that might help you.
I got a bit tired of waiting for the Rasky project to finish their IP KVM board, so went looking for something similar and found someone who had written some basic code which almost worked. You can find the updated code at:

and it looks like it is the here.py (I must rename that file some day) file that you are after, which uses pygame to read the key codes, including modifiers, from the keyboard and then sends them to an Arduino over a serial port. You can then see how the codes are received by the .ino file that runs on an Arduino.

Thanks for the reply. (I havent played with this project in a long time!)

I guess I'm not 100% clear though...

Where/why does the Python come into play here?

Is this just to 'decode' some of the key presses to add to the sketch or something?

In the end.. there can be no Python used in the end product.

The Python is just an example of how someone decoded the keypresses and sent them to the Arduino.
The main things that I learned are that you have to send through the individual key press and release events using keyboard.press and keyboard.release, rather than keyboard.write, because the modifiers have to be pressed in the right order, rather than sent as a single character code. Also you need to send the key codes as a byte, not a char, because bytes are from 1 - 255, while chars are from -127 - 127, so the key codes over 127 will not get through if you use a char.
Hope this helps.

I experiemented with USB keyboard pass through using a Leonardo, a USB host board, and a computer running Linux. The keypresses work including control, alt, num lock, etc. I do not have time to try with Windows or Mac.

The pass through code is short because it operates on a low level USB data structure named the USB HID keyboard report. There is no reason to decode/parse the report on the Arduino since the keys are not used on the Arduino. Keyboard.sendReport function sends the report out to the computer. The report contains all the information about modifier keys, multiple key presses, etc.

Keyboard.sendReport is not public so the Keyboard.h file must be modified to expose it. Put the modified version in the same directory as the sketch.

A similar approach should work for USB mouse and USB joystick/gamepad pass throughs.

Suppose you want to have one USB keyboard broadcast to two computers. Add a second Leonardo. Send the 8 byte USB HID report from the first Leonardo to the second Leonardo via UART Tx -> Rx. When the second Leonardo receives the report via UART Rx, it sends the report out using Keyboard.sendReport. There is no need for a USB host board on the second (or third or fourth) Leonardo.

#include <hidboot.h>
#include <usbhub.h>

// Locally modified to make SendReport public.
#include "Keyboard.h"

// Satisfy the IDE, which needs to see the include statment in the ino too.
#ifdef dobogusinclude
#include <spi4teensy3.h>
#endif
#include <SPI.h>

class KbdRptParser : public KeyboardReportParser
{
  protected:
    void Parse(USBHID *hid, bool is_rpt_id, uint8_t len, uint8_t *buf);
};

void KbdRptParser::Parse(USBHID *hid, bool is_rpt_id, uint8_t len, uint8_t *buf)
{
  Serial.print("KbdRptParser::Parse");
  // Show USB HID keyboard report
  for (uint8_t i = 0; i < len ; i++) {
    Serial.print(' '); Serial.print(buf[i], HEX);
  }
  Serial.println();

  // On error - return
  if (buf[2] == 1)
    return;

  if (len == 8)
    Keyboard.sendReport((KeyReport *)buf);
}

USB     Usb;
//USBHub     Hub(&Usb);
HIDBoot<USB_HID_PROTOCOL_KEYBOARD>    HidKeyboard(&Usb);

KbdRptParser Prs;

void setup()
{
  Serial.begin( 115200 );
  while (!Serial) delay(1);
  // Make sure we have time to get control of the board before going into USB keyboard mode.
  Serial.println("Start in 5 seconds");
  delay(5000);
#if !defined(__MIPSEL__)
  while (!Serial); // Wait for serial port to connect - used on Leonardo, Teensy and other boards with built-in USB CDC serial connection
#endif
  Serial.println("Start");

  if (Usb.Init() == -1)
    Serial.println("OSC did not start.");

  delay( 200 );

  HidKeyboard.SetReportParser(0, &Prs);
  
  Keyboard.begin();
}

void loop()
{
  Usb.Task();
}

There is one odd thing. On Linux the Leonardo appears as a composite device consisting of USB CDC ACM (serial port) AND USB keyboard. The serial console works at the same time as the USB keyboard! This is quite suprising so I am not sure if this works on Windows or Mac. Windows may require an INF file. Perhaps the solution is the sketch should not use Serial.

The keyboard LEDs do not work.

gdsports:
I experiemented with USB keyboard pass through using a Leonardo, a USB host board, and a computer running Linux. The keypresses work including control, alt, num lock, etc. I do not have time to try with Windows or Mac.

The pass through code is short because it operates on a low level USB data structure named the USB HID keyboard report. There is no reason to decode/parse the report on the Arduino since the keys are not used on the Arduino. Keyboard.sendReport function sends the report out to the computer. The report contains all the information about modifier keys, multiple key presses, etc.

Keyboard.sendReport is not public so the Keyboard.h file must be modified to expose it. Put the modified version in the same directory as the sketch.

A similar approach should work for USB mouse and USB joystick/gamepad pass throughs.

Suppose you want to have one USB keyboard broadcast to two computers. Add a second Leonardo. Send the 8 byte USB HID report from the first Leonardo to the second Leonardo via UART Tx -> Rx. When the second Leonardo receives the report via UART Rx, it sends the report out using Keyboard.sendReport. There is no need for a USB host board on the second (or third or fourth) Leonardo.

#include <hidboot.h>

#include <usbhub.h>

// Locally modified to make SendReport public.
#include "Keyboard.h"

// Satisfy the IDE, which needs to see the include statment in the ino too.
#ifdef dobogusinclude
#include <spi4teensy3.h>
#endif
#include <SPI.h>

class KbdRptParser : public KeyboardReportParser
{
  protected:
    void Parse(USBHID *hid, bool is_rpt_id, uint8_t len, uint8_t *buf);
};

void KbdRptParser::Parse(USBHID *hid, bool is_rpt_id, uint8_t len, uint8_t *buf)
{
  Serial.print("KbdRptParser::Parse");
  // Show USB HID keyboard report
  for (uint8_t i = 0; i < len ; i++) {
    Serial.print(' '); Serial.print(buf[i], HEX);
  }
  Serial.println();

// On error - return
  if (buf[2] == 1)
    return;

if (len == 8)
    Keyboard.sendReport((KeyReport *)buf);
}

USB    Usb;
//USBHub    Hub(&Usb);
HIDBoot<USB_HID_PROTOCOL_KEYBOARD>    HidKeyboard(&Usb);

KbdRptParser Prs;

void setup()
{
  Serial.begin( 115200 );
  while (!Serial) delay(1);
  // Make sure we have time to get control of the board before going into USB keyboard mode.
  Serial.println("Start in 5 seconds");
  delay(5000);
#if !defined(MIPSEL)
  while (!Serial); // Wait for serial port to connect - used on Leonardo, Teensy and other boards with built-in USB CDC serial connection
#endif
  Serial.println("Start");

if (Usb.Init() == -1)
    Serial.println("OSC did not start.");

delay( 200 );

HidKeyboard.SetReportParser(0, &Prs);
 
  Keyboard.begin();
}

void loop()
{
  Usb.Task();
}




There is one odd thing. On Linux the Leonardo appears as a composite device consisting of USB CDC ACM (serial port) AND USB keyboard. The serial console works at the same time as the USB keyboard! This is quite suprising so I am not sure if this works on Windows or Mac. Windows may require an INF file. Perhaps the solution is the sketch should not use Serial.

The keyboard LEDs do not work.

I'm trying to understand how to use this library also with standard keys. How is the piece of code detecting keypress?
In the PS2KEYBOARD library there is like keyboard.read()

Here I can't find anything but this usb.task()