The Raspberry Pi has become a favorite among tech enthusiasts, educators, and developers, thanks to its versatility and affordability. One of its standout features is the ability to emulate various USB devices, including Human Interface Devices (HID) like keyboards. This article will guide you through the process of setting up a HID keyboard over USB using the Raspberry Pi, unlocking exciting possibilities for your projects.
What is HID?
HID stands for Human Interface Device, a standard that allows devices like keyboards and mice to communicate with computers in a standardized way. By emulating an HID keyboard, your Raspberry Pi can send keystrokes to any compatible system, making it a powerful tool for automation, testing, or creating custom input solutions.
Use Cases for HID Keyboard Emulation
- Automating Tasks: You can use the Raspberry Pi to automate repetitive tasks, such as sending keystrokes to software for testing purposes.
- Custom Input Devices: Build specialized input devices for gaming or specific applications where standard keyboards don’t suffice.
- Accessibility Tools: Create custom keyboards for individuals with disabilities, allowing for tailored solutions to meet their needs.
Implementing Hid Keyboards
To get this working, we need to create the USB HID gadget. This process is quite similar to setting up the Ethernet over USB gadget as discussed in a previous article.
- First, connect the USB Type-C port of your Raspberry Pi 4 to your PC.
- Next , we will need to enable the dwc2 overlay by modifying the boot configuration. Open the config.txt file by :
sudo nano /boot/config.txt
# Add the following line at the end:
dtoverlay=dwc2
# Save and exit (CTRL+X, then Y).
Next , we will need to create usb hid gadget by executing following script :
#!/bin/bash
cd /sys/kernel/config/usb_gadget/
mkdir -p bhanotusb
cd bhanotusb
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 "fedcba9876544210" > strings/0x409/serialnumber
echo "random" > strings/0x409/manufacturer
echo "bhanotusb" > 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
After setting this up, your Raspberry Pi should be recognised as a HID device on your PC or laptop. You can verify this by checking the Device Manager if you’re using a Windows system, or by running the dmesg command in the terminal on a Linux system.
Sending keystrokes form pi to pc/laptop
For sending keystrokes i have written a simple python script :
import time
# Define keycodes (e.g., for 'V' key, Windows key, etc.)
KEYCODES = {
'a': 0x04, 'b': 0x05, 'c': 0x06, 'd': 0x07, 'e': 0x08, 'f': 0x09, 'g': 0x0A,
'h': 0x0B, 'i': 0x0C, 'j': 0x0D, 'k': 0x0E, 'l': 0x0F, 'm': 0x10, 'n': 0x11,
'o': 0x12, 'p': 0x13, 'q': 0x14, 'r': 0x15, 's': 0x16, 't': 0x17, 'u': 0x18,
'v': 0x19, 'w': 0x1A, 'x': 0x1B, 'y': 0x1C, 'z': 0x1D, '1': 0x1E, '2': 0x1F,
'3': 0x20, '4': 0x21, '5': 0x22, '6': 0x23, '7': 0x24, '8': 0x25, '9': 0x26,
'0': 0x27, 'Enter': 0x28, 'Esc': 0x29, 'Backspace': 0x2A, 'Tab': 0x2B, 'Space': 0x2C,
'Minus': 0x2D, 'Equal': 0x2E, 'LeftBracket': 0x2F, 'RightBracket': 0x30, 'Backslash': 0x31,
'Semicolon': 0x33, 'Quote': 0x34, 'Grave': 0x35, 'Comma': 0x36, 'Dot': 0x37, 'Slash': 0x38,
'CapsLock': 0x39, 'F1': 0x3A, 'F2': 0x3B, 'F3': 0x3C, 'F4': 0x3D, 'F5': 0x3E,
'F6': 0x3F, 'F7': 0x40, 'F8': 0x41, 'F9': 0x42, 'F10': 0x43, 'F11': 0x44, 'F12': 0x45,
'PrintScreen': 0x46, 'ScrollLock': 0x47, 'Pause': 0x48, 'Insert': 0x49, 'Home': 0x4A,
'PageUp': 0x4B, 'Delete': 0x4C, 'End': 0x4D, 'PageDown': 0x4E, 'RightArrow': 0x4F,
'LeftArrow': 0x50, 'DownArrow': 0x51, 'UpArrow': 0x52, 'NumLock': 0x53, 'KP_Divide': 0x54,
'KP_Multiply': 0x55, 'KP_Subtract': 0x56, 'KP_Add': 0x57, 'KP_Enter': 0x58, 'KP_1': 0x59,
'KP_2': 0x5A, 'KP_3': 0x5B, 'KP_4': 0x5C, 'KP_5': 0x5D, 'KP_6': 0x5E, 'KP_7': 0x5F,
'KP_8': 0x60, 'KP_9': 0x61, 'KP_0': 0x62, 'KP_Decimal': 0x63, 'NonUsBackslash': 0x64,
'Application': 0x65, 'Power': 0x66, 'KP_Equals': 0x67, 'F13': 0x68, 'F14': 0x69,
'F15': 0x6A, 'F16': 0x6B, 'F17': 0x6C, 'F18': 0x6D, 'F19': 0x6E, 'F20': 0x6F,
'F21': 0x70, 'F22': 0x71, 'F23': 0x72, 'F24': 0x73, 'Execute': 0x74, 'Help': 0x75,
'Menu': 0x76, 'Select': 0x77, 'Stop': 0x78, 'Again': 0x79, 'Undo': 0x7A, 'Cut': 0x7B,
'Copy': 0x7C, 'Paste': 0x7D, 'Find': 0x7E, 'Mute': 0x7F, 'VolumeUp': 0x80, 'VolumeDown': 0x81
}
MODIFIERS = {
'LeftCtrl': 0x01, 'LeftShift': 0x02, 'LeftAlt': 0x04, 'LeftGUI': 0x08,
'RightCtrl': 0x10, 'RightShift': 0x20, 'RightAlt': 0x40, 'RightGUI': 0x80
}
KEYCODES.update({
'LeftCtrl': 0xE0, # Assuming 0xE0 is the keycode for LeftCtrl
'LeftShift': 0x02 # Assuming 0xE0 is the keycode for LeftCtrl
})
class HIDKeyboard:
def __init__(self, device_path='/dev/hidg0'):
self.device_path = device_path
def write_report(self, report):
with open(self.device_path, 'rb+') as fd:
fd.write(report)
def press_key(self, keycode, modifiers=0):
report = bytearray(8)
report[0] = modifiers
report[2] = keycode
self.write_report(report)
def release_keys(self):
self.write_report(bytearray(8))
def press_key_combination(self, keycodes, modifiers=0):
report = bytearray(8)
report[0] = modifiers
for i, keycode in enumerate(keycodes):
if i < 6:
report[i + 2] = keycode
self.write_report(report)
time.sleep(0.1)
self.release_keys()
def press_key_with_modifiers(self, key, *modifiers):
modifier_byte = 0
for mod in modifiers:
modifier_byte |= MODIFIERS.get(mod, 0)
keycode = KEYCODES.get(key, 0)
# Send modifiers and key
self.press_key(keycode, modifier_byte)
# Release keys after a short delay
time.sleep(0.1)
self.release_keys()
# Test Super + V [in this v has to be sent first]
def press_windows_v(self):
# Press Windows key + V
self.press_key_with_modifiers('v', 'LeftGUI')
# Example usage
keyboard = HIDKeyboard()
keyboard.press_windows_v()
# Tested keycombinations
keyboard.press_key_with_modifiers('v','LeftGUI')
#keyboard.press_key_with_modifiers('Space', 'LeftGUI')
#keyboard.press_key_with_modifiers('KP_2','LeftAlt')
#keyboard.press_key_with_modifiers('KP_2','LeftGUI')
#keyboard.press_key_with_modifiers('F4','LeftAlt','LeftCtrl')
#keyboard.press_key_with_modifiers('a', 'LeftShift')
#keyboard.press_key_combination([KEYCODES['LeftCtrl'], KEYCODES['LeftShift'], KEYCODES['n']])
# just like before key has to be sent first
# keyboard.press_key_combination(
# [KEYCODES['n']], # Keycode for 'n'
# MODIFIERS['LeftCtrl'] | MODIFIERS['LeftShift'] # Modifier byte for Ctrl + Shift
# )
Explanation
This code defines a class, HIDKeyboard, which allows a Raspberry Pi to simulate keyboard input via USB HID (Human Interface Device). Here’s a breakdown of its key components:
Keycodes and Modifiers
- KEYCODES: A dictionary mapping character keys (like ‘a’, ‘b’, etc.) and special keys (like ‘Enter’, ‘Esc’) to their corresponding USB HID keycodes.
- MODIFIERS: A dictionary for modifier keys (like Ctrl, Shift, Alt) that combine with other keys for shortcuts.
HIDKeyboard Class
- Initialization: The class is initialized with a device path (/dev/hidg0), which represents the HID device.
- write_report: A method that sends a byte report to the HID device.
- press_key: Sends a key press event for a specified keycode, optionally including modifiers.
- release_keys: Releases all keys by sending an empty report.
- press_key_combination: Handles pressing multiple keys at once (up to 6) and releases them after a short delay.
- press_key_with_modifiers: Combines key presses with specified modifiers and includes a brief pause before releasing the keys
- press_windows_v: A specific method to simulate pressing the Windows key along with the ‘V’ key.
Conclusion
Setting up a HID keyboard over USB with a Raspberry Pi is an exciting project that showcases the flexibility of this tiny computer. This gadget can be even combined with ethernet over usb , uart over usb and mass-storage over usb. Dive into the world of USB gadgets and discover the endless possibilities at your fingertips!