Skip to content

Instantly share code, notes, and snippets.

@rmed
Last active July 26, 2023 17:04
Show Gist options
  • Star 14 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save rmed/0d11b7225b3b772bb0dd89108ee93df0 to your computer and use it in GitHub Desktop.
Save rmed/0d11b7225b3b772bb0dd89108ee93df0 to your computer and use it in GitHub Desktop.
RPi Zero keyboard test
#!/bin/bash
sleep 15
# Create gadget
mkdir /sys/kernel/config/usb_gadget/mykeyboard
cd /sys/kernel/config/usb_gadget/mykeyboard
# Add basic information
echo 0x0100 > bcdDevice # Version 1.0.0
echo 0x0200 > bcdUSB # USB 2.0
echo 0x00 > bDeviceClass
echo 0x00 > bDeviceProtocol
echo 0x00 > bDeviceSubClass
echo 0x08 > bMaxPacketSize0
echo 0x0104 > idProduct # Multifunction Composite Gadget
echo 0x1d6b > idVendor # Linux Foundation
# Create English locale
mkdir strings/0x409
echo "My manufacturer" > strings/0x409/manufacturer
echo "My virtual keyboard" > strings/0x409/product
echo "0123456789" > strings/0x409/serialnumber
# Create HID function
mkdir functions/hid.usb0
echo 1 > functions/hid.usb0/protocol
echo 8 > functions/hid.usb0/report_length # 8-byte reports
echo 1 > functions/hid.usb0/subclass
# Write report descriptor
echo "05010906a101050719e029e71500250175019508810275089501810175019503050819012903910275019505910175089506150026ff00050719002aff008100c0" | xxd -r -ps > functions/hid.usb0/report_desc
# Create configuration
mkdir configs/c.1
mkdir configs/c.1/strings/0x409
echo 0x80 > configs/c.1/bmAttributes
echo 200 > configs/c.1/MaxPower # 200 mA
echo "Example configuration" > configs/c.1/strings/0x409/configuration
# Link HID function to configuration
ln -s functions/hid.usb0 configs/c.1
# Enable gadget
ls /sys/class/udc > UDC
sleep 15
/usr/local/bin/tester.sh &
# /usr/local/bin/tester.py &
#!/usr/bin/env python3
#
# Python file to test the gadget by writing "Hello World!"
# If used with the configfs_test.sh, place this file under /usr/local/bin/tester.py
# and uncomment the appropriate line
NULL_CHAR = chr(0)
def write_report(report):
with open('/dev/hidg0', 'rb+') as fd:
fd.write(report.encode())
# H (press shift and H)
write_report(chr(32)+NULL_CHAR+chr(11)+NULL_CHAR*5)
# e
write_report(NULL_CHAR*2+chr(8)+NULL_CHAR*5)
# ll
write_report(NULL_CHAR*2+chr(15)+NULL_CHAR*5)
write_report(NULL_CHAR*8)
write_report(NULL_CHAR*2+chr(15)+NULL_CHAR*5)
# o
write_report(NULL_CHAR*2+chr(18)+NULL_CHAR*5)
# SPACE
write_report(NULL_CHAR*2+chr(44)+NULL_CHAR*5)
# W (press shift and W)
write_report(chr(32)+NULL_CHAR+chr(26)+NULL_CHAR*5)
# o
write_report(NULL_CHAR*2+chr(18)+NULL_CHAR*5)
# r
write_report(NULL_CHAR*2+chr(21)+NULL_CHAR*5)
# l
write_report(NULL_CHAR*2+chr(15)+NULL_CHAR*5)
# d
write_report(NULL_CHAR*2+chr(7)+NULL_CHAR*5)
# ! (press shift and 1)
write_report(chr(32)+NULL_CHAR+chr(30)+NULL_CHAR*5)
# Release all keys
write_report(NULL_CHAR*8)
#!/bin/bash
#
# Bash file to test the gadget by writing "Hello World!"
# If used with the configfs_test.sh, place this file under /usr/local/bin/tester.sh
# and uncomment the appropriate line
function write_report {
echo -ne $1 > /dev/hidg0
}
# H (press shift and H)
write_report "\x20\0\xb\0\0\0\0\0"
# e
write_report "\0\0\x8\0\0\0\0\0"
# ll
write_report "\0\0\xf\0\0\0\0\0"
write_report "\0\0\0\0\0\0\0\0"
write_report "\0\0\xf\0\0\0\0\0"
# o
write_report "\0\0\x12\0\0\0\0\0"
# SPACE
write_report "\0\0\x2c\0\0\0\0\0"
# W (press shift and W)
write_report "\x20\0\x1a\0\0\0\0\0"
# o
write_report "\0\0\x12\0\0\0\0\0"
# r
write_report "\0\0\x21\0\0\0\0\0"
# l
write_report "\0\0\xf\0\0\0\0\0"
# d
write_report "\0\0\x7\0\0\0\0\0"
# ! (press shift and 1)
write_report "\x20\0\x1e\0\0\0\0\0"
# Release al keys
write_report "\0\0\0\0\0\0\0\0"
@salamcast
Copy link

I was having problems with this, but I have a otg hub with a turn around on one of the of the USB ports. has anyone tried this before? i have build an arcade stick using RPi gpio pins for inputs. I might need to disconnect the hub and use a different cable. kinda annoying.

RetroPie, Linux 5.10.0 Raspberry Pi Zero W

my goal is to send keystrokes via the gpio to a windows pc when plugged in, otherwise it uses retrogame to to map gpio to keys for my attractmode setup. I have 25 inputs configured

https://photos.google.com/share/AF1QipNtCT9V-gHYDWtwuIJbDX8kp_Z1EIvZ9jndaGNso7ytg7nI2gDmXurwL9cJ4xTtfg?key=UHNFXzFVSUVHckxES3NZc0JFenU3Mm5DVmpJellB

just so you have an idea of what I'm talking about, the photos are above.

@rmed
Copy link
Author

rmed commented Jan 1, 2021

I was having problems with this, but I have a otg hub with a turn around on one of the of the USB ports. has anyone tried this before? i have build an arcade stick using RPi gpio pins for inputs. I might need to disconnect the hub and use a different cable. kinda annoying.

RetroPie, Linux 5.10.0 Raspberry Pi Zero W

my goal is to send keystrokes via the gpio to a windows pc when plugged in, otherwise it uses retrogame to to map gpio to keys for my attractmode setup. I have 25 inputs configured

https://photos.google.com/share/AF1QipNtCT9V-gHYDWtwuIJbDX8kp_Z1EIvZ9jndaGNso7ytg7nI2gDmXurwL9cJ4xTtfg?key=UHNFXzFVSUVHckxES3NZc0JFenU3Mm5DVmpJellB

just so you have an idea of what I'm talking about, the photos are above.

Last time I tried using a hub the Pi Zero was not able to configure the data port to act as a device rather than a host. Not sure it is possible to make use of a hub and send keystrokes at the same time.

@airtonzanon
Copy link

Hello @rmed, thanks for the blog post, it's the most clearer about HID with RPi Zero that I ever saw.

@flash76 Seems like the device already exists when you try to execute the script. Is there any gadget preconfigured in the RPi (e.g. ethernet over USB)?

Related to this, I'm trying to use a RPi Zero (not the W), but to have access to the terminal, I need to use the data micro USB, and I'm having the same problem that @flash76 had about the resource being busy, do you have any way that I could make it happen?

@dogwong
Copy link

dogwong commented Feb 20, 2021

not sure if this is my case only
tester.sh - line 34 ~ 35

# r
write_report "\0\0\x21\0\0\0\0\0"

this report typed "4" instead of "r"

so I changed that 21 to 15 and works fine for me

(Reference: https://usb.org/sites/default/files/hut1_21.pdf Chaprter 10)

@ylh888
Copy link

ylh888 commented Feb 24, 2021

Hello @rmed, thanks for the blog post, it's the most clearer about HID with RPi Zero that I ever saw.

@flash76 Seems like the device already exists when you try to execute the script. Is there any gadget preconfigured in the RPi (e.g. ethernet over USB)?

Related to this, I'm trying to use a RPi Zero (not the W), but to have access to the terminal, I need to use the data micro USB, and I'm having the same problem that @flash76 had about the resource being busy, do you have any way that I could make it happen?

I don't know if this is related. I added "chown pi /dev/hidg0" at the end of the startup script so that the device is available to the regular user "pi".

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment