USB Keyboard Scancode Injector
To automate the switching of KVM switches for my RetroRacks project, I needed a solution. First I thought it would be an idea to use serial connections of the switches, but unfortunately they are only meant for upgrading their firmware, and not to control them. The KVM switches are controlled by keyboard shortcuts, so my next idea was to send automated USB keyboard strokes (“scancodes”) using an additional device which acts like a USB keyboard. Fotunately, the main KVM switch in my setup allows to connect more than one keyboard by using a USB hub connected to the USB keyboard connector, so I could connect the injector this way. The injector, once it is finished, should be integrated into the multi switch, which should also operate it.
Selecting a Device for Sending USB Scancodes
The device needed must be able to work as a USB device (“USB Gadget Mode”) and behave like a standard keyboard via the USB connection. Searching for a solution on the net, my first attention turned to the Raspberry Pi Zero, which supports the USB Gadget Mode. I also found Arduino based solutions, but they seemed to be much more complicated, and I would have had to learn a new platform. I found a Raspberry Pi Zero for less than 6 € on the net, so I decided to give this a try. There were also the “H” and “WH” versions of the Raspberry Pi Zero, but the additional features didn’t help me. Wireless technologies like WLAN and Bluetooth were of no help, because I wanted to install the device within a metal casing, which would not allow it to work.
Setting Up the Raspberry Pi Zero as a Virtual Keyboard
I used this tutorial on randomnerdtutorials.com as a basis, which itself is based on another tutorial on isticktoit.net, and show you a slightly modified and stripped down version of it for my special case.
Install Raspbian Lite
First install Raspbian Lite on your device. I used the version from February 2020 (Release date: 2020-02-13).
Accessing the Raspberry Pi Zero via Serial Console
Because we will set the Raspberry Pi Zero to USB Gadget Mode, we won’t be able to access it via USB. Also it has got no network interfaces, so we will not be able to access it via SSH. To get console access, we can connect to its serial port via GPIO using GPIO 14 and 15 (pins 8 and 10). There are two options to do this:
- Use a USB-UART with a PL2303/CH340 or similar based chip:
- Configure it to 3.3 Volts
- Connect Ground/GND of UART to Ground (e. g. pin 6)
- Connect RX of UART to GPIO 14 (pin 8)
- Connect TX of UART to GPIO 15 (pin 10)
- Do NOT connect 3V3 power!
- Connnect USB port of UART to computer
- Device should be something like
- Use a RS-232 to 3.3 Volts converter with a MAX3232 or similar based chip:
- Connect Ground/GND of converter to Ground (e. g. pin 6)
- Connect RX of converter to GPIO 14 (pin 8)
- Connect TX of converter to GPIO 15 (pin 10)
- Connect VCC of converter to 3V3 power (e. g. pin 1)
- Connect RS-232 port of converter to computer
- Device should be something like
Then use a serial terminal client, e. g. PuTTY, configure it to
On Linux, use device name as described above and connect to the Raspberry Pi Zero.
<Enter> twice if you get no console.
Enabling Modules and Drivers
Boot up Raspbian, login as root and execute the following commands to configure the modules and drivers to be loaded at boot time:
# echo "dtoverlay=dwc2" >> /boot/config.txt # echo "dwc2" >> /etc/modules # echo "libcomposite" >> /etc/modules
Creating a USB Gadget Configuration Script
To configure the device as a USB Keyboard, a bootup script has to be written.
We call our script
usbkbscancodeinjector and place it in
Then it has to be filled with the following content:
#!/bin/bash cd /sys/kernel/config/usb_gadget/ mkdir -p usbkbscancodeinjector cd usbkbscancodeinjector echo 0x1d6b > idVendor # Linux Foundation echo 0x0104 > idProduct # Multifunction Composite Gadget echo 0x0100 > bcdDevice # v1.0.0 echo 0x0200 > bcdUSB # USB2 mkdir -p strings/0x409 echo "fedcba9876543210" > strings/0x409/serialnumber echo "KVM Nerd" > strings/0x409/manufacturer echo "USB Keyboard Scancode Injector" > strings/0x409/product mkdir -p configs/c.1/strings/0x409 echo "Config 1: ECM network" > configs/c.1/strings/0x409/configuration echo 250 > configs/c.1/MaxPower # Add functions here mkdir -p functions/hid.usb0 echo 1 > functions/hid.usb0/protocol echo 1 > functions/hid.usb0/subclass echo 8 > functions/hid.usb0/report_length echo -ne \\x05\\x01\\x09\\x06\\xa1\\x01\\x05\\x07\\x19\\xe0\\x29\\xe7\\x15\\x00\\x25\\x01\\x75\\x01\\x95\\x08\\x81\\x02\\x95\\x01\\x75\\x08\\x81\\x03\\x95\\x05\\x75\\x01\\x05\\x08\\x19\\x01\\x29\\x05\\x91\\x02\\x95\\x01\\x75\\x03\\x91\\x03\\x95\\x06\\x75\\x08\\x15\\x00\\x25\\x65\\x05\\x07\\x19\\x00\\x29\\x65\\x81\\x00\\xc0 > functions/hid.usb0/report_desc ln -s functions/hid.usb0 configs/c.1/ # End functions ls /sys/class/udc > UDC
Make it executable with this command:
# chmod a+x /usr/bin/usbkbscancodeinjector
Add the script to
/etc/rc.local to let it execute at each boot by adding the following line before the line which contains
exit 0 (which itself should be the last line):
/usr/bin/usbkbscancodeinjector # libcomposite configuration
After the next boot it should be ready for some testing!
Connect the USB port of the Raspberry Pi Zero to the USB port of a computer, it should detect a new USB hardware.
When running Linux, run
lsusb -d 1d6b:0104 and it should output an entry like the following:
Bus 007 Device 018: ID 1d6b:0104 Linux Foundation Multifunction Composite Gadget
This is our selfmade device!
- Replace the script with a Python script
- Make the Python script receive scancodes via I2C
- Write second Python script which sends the scancodes via I2C from another Raspberry Pi