Turn Your Raspberry Pi Zero into a USB Keyboard (HID)

In this project you’ll learn how to turn a Raspberry Pi Zero board into a USB keyboard or HID (Human Interface Device). After following some simple steps, you can write a Python script to make your Pi act as a USB keyboard.

First, watch the video demonstration


If you like home automation and you want to learn more about Node-RED, Raspberry Pi, ESP8266 and Arduino. I recommend that you download my course: Build a Home Automation System for $100.

Parts Required

For this project you’ll need a Raspberry Pi Zero board. Important: this tutorial doesn’t work with a Raspberry Pi 3 board.

1. Enabling Modules and Drivers

These next steps to prepare the Pi Zero board are based on the instructions from iSticktoit. First, you need to run these three commands to enable the necessary modules and drivers:

pi@raspberrypi:~ $ echo "dtoverlay=dwc2" | sudo tee -a /boot/config.txt
pi@raspberrypi:~ $ echo "dwc2" | sudo tee -a /etc/modules
pi@raspberrypi:~ $ sudo echo "libcomposite" | sudo tee -a /etc/modules

2. Configuring the Gadget

Now, you have to define your Pi Zero (HID gadget) as a USB keyboard. The configuration is done via ConfigFS, a virtual file system located in /sys/.

Creating the config script

The configuration is volatile, so it must run on each startup. Create a new file called isticktoit_usb in /usr/bin/ and make it executable:

pi@raspberrypi:~ $ sudo touch /usr/bin/isticktoit_usb
pi@raspberrypi:~ $ sudo chmod +x /usr/bin/isticktoit_usb

Then, you need to run this script automatically at startup. Open /etc/rc.local with this command:

pi@raspberrypi:~ $ sudo nano /etc/rc.local

Add the following before the line containing exit 0:

/usr/bin/isticktoit_usb # libcomposite configuration

Here’s how your file should look like (to save the file, press Ctrl+X followed by Y and Enter):

3. Creating the gadget

For this project, we will turn the Raspberry Pi into a USB keyboard, but you could make it work as a Serial adapter, Ethernet adapter, and Mass Storage. Open the file with:

pi@raspberrypi:~ $ sudo nano /usr/bin/isticktoit_usb

Leave the default values, but you could even change the serial number, manufacturer and product name to fit your specific needs.

cd /sys/kernel/config/usb_gadget/
mkdir -p isticktoit
cd isticktoit
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 "Tobias Girstmair" > strings/0x409/manufacturer
echo "iSticktoit.net USB Device" > 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

Here’s how your file should look like in the end (to save the file, press Ctrl+X followed by Y and Enter):

4. Python Script

After preparing your Raspberry Pi Zero, connect it to a laptop or desktop computer through the micro USB port that is used for data and peripherals. That micro USB will both power the Pi Zero and act as a keyboard to the connected computer.

Establish an SSH connection with your Pi and use the next command to create a new Python script:

pi@raspberrypi:~ $ nano RPi_Keyboard_Example.py

Copy and paste the next Python script to your Raspberry Pi.

#!/usr/bin/env python3
NULL_CHAR = chr(0)

def write_report(report):
    with open('/dev/hidg0', 'rb+') as fd:

# Press a
# Release keys
# Press SHIFT + a = A

# Press b
# Release keys
# Press SHIFT + b = B

# Press SPACE key

# Press c key
# Press d key

# Press RETURN/ENTER key

# Press e key
# Press f key

# Release all keys

View raw code


Let’s test it, if you plug the Pi Zero to Computer #1, after a few seconds you’ll see an alert message or sound that indicates that a keyboard was connected successfully.

Sometimes you might see this warning message saying “USB device not recognized”. Throughout my tests, I found that you can ignore this warning message and your Pi Zero works as a keyboard without any additional configuration or drivers installation. So, you can continue and it will work just fine.

Computer #1

Open any text editor program and leave your cursor in the new file:

Computer #2

Establish an SSH connection with your Pi Zero and run the Python script created earlier:

pi@raspberrypi:~ $ sudo python3 RPi_Keyboard_Example.py


The script will press these keys in that order: a – A – b – B – Space key – c – D – Enter/Return key – e – f.

You can customize the Python script to act as a keyboard and press any other character sequence.

Note: the Pi Zero also acts as a keyboard when connected to a Mac or Linux machine without any additional changes.

Taking It Further

You can use Table 12: Keyboard/Keypad Page from this USB HID PDF to find the ID of each key that you would assign in the Python script.

Here’s a section of Table 12. The Usage ID (Dec) column contains the number that you need to use in your Python script to refer to a key press:

For example, if you change the number highlighted in red, you can simulate a different key press:


The number 4 correspondes to keyboard key a. You can find in the Usage ID (Dec) column the numbers for your desired key press sequence. If you use number 5 it corresponds to b, and so on…

Wrapping Up

You can use this method to make the Raspberry Pi Zero act as password filler or use it as a keystroke injection tool. That way you can easily create programs that type hundreds of keystrokes per minute.

You may also like some of our most popular Raspberry Pi projects:

Thanks for reading.

Learn how to build a home automation system and we’ll cover the following main subjects: Node-RED, Node-RED Dashboard, Raspberry Pi, ESP32, ESP8266, MQTT, and InfluxDB database DOWNLOAD »
Learn how to build a home automation system and we’ll cover the following main subjects: Node-RED, Node-RED Dashboard, Raspberry Pi, ESP32, ESP8266, MQTT, and InfluxDB database DOWNLOAD »

Recommended Resources

Build a Home Automation System from Scratch » With Raspberry Pi, ESP8266, Arduino, and Node-RED.

Home Automation using ESP8266 eBook and video course » Build IoT and home automation projects.

Arduino Step-by-Step Projects » Build 25 Arduino projects with our course, even with no prior experience!

What to Read Next…

Enjoyed this project? Stay updated by subscribing our newsletter!

74 thoughts on “Turn Your Raspberry Pi Zero into a USB Keyboard (HID)”

  1. Maybe I’m missing something, but why would you ever want to do this? (serious question)

    I never followed the thinking with the Arduino Leonardo either…

    • You can use this method to make the Raspberry Pi Zero act as password filler or use it as a keystroke injection tool. That way you can easily create programs that type hundreds of keystrokes per minute.

      • Yes, it said that in the notes – but, to me, it seems very impractical (a button for each password you use, or remembering a number to type on another keyboard). Personally, it seems to be an example of ‘because you can, that doesn’t mean you should’, although I can see that hackers might want it.

        • Imagine a headless remote computer with boot password, OPAL hardware encrypted drive, (inclusive-)or LUKS software encryption on your root drive and a power outage or other unplanned reboot. This situation requires a keyboard entering passwords.

          I came here looking for a possible solution to this kind of problem.

          A more complicated version of this could work. Will it compromise the security more than we’re willing to risk? Depends on how we build it. I am glad that this was posted.

        • Send key sequence to kvm to select different hosts / display arrangements. Many modern higher end keyboards don’t comply with some aspect of the EDID (ASUS I’m looking at you). From what I’ve read usb keyboards have a limited number of “inputs” and one way of getting around it is to make the keyboard a hub with multiple keyboards attached. This doesn’t work with kvms as they need to “see” a keyboard.

        • This is an interesting solution for a program I am going to work on.
          My dad has an old 400 cd player for his stereo system and you can program it with an IBM Keyboard. Using this as a base I might be able to automate most of the keystrokes needed to program the disk names into his system.

    • I have a work laptop on a domain, so I have to hit CTRL+ALT+DELETE, and type in my username and password. I’m using a fingerprint library so I can tell my Pi to emulate those keystrokes when the right fingerprint is used.

    • Came here looking for an easy way to emulate a Mute/Unmute button for my Microsoft Teams meetings. There is an example using an Arduino CPX, but not willing to spend $20 on that board. $5 for a PiZero is reasonable and I happen to have a few of those laying around.

      Need to wire to a momentary switch to activate the script.

      • I am also here for the MS Teams shortcut!
        But can you believe: The latest update removed the shortcut! Hopefully the next one will bring it back.
        There is also a push-to-talk on the roadmap with Ctrl spacebar 😀

        I am planning to write a REST API so I don’t need a button and can use a StreamDeck “macrokeyboard” to trigger shortcuts (and more).

  2. I’ve used this type of thing to build system test rigs – for instance to send a stream of keystrokes – with relevant delays – to automate regression testing of software.

    On a more fun note I also use with my CodeClub so that they can control their scratch games with strange controllers – a bit like the MakeyMakey. https://makeymakey.com/

  3. Thanks, for the amazing tutorial, helped heaps! I’m not particularly good at Python, and I was wondering if you could tell me how to get started in creating a repeater, so that when keys on the Pi’s keyboard are pressed, it sends them to the computer it is connected to?

  4. Great post, but there is a small error in the instructions, in section 3, the line:
    echo -ne \\x05\\x01\\x09\\x06\\xa1\\x01\\x05\\x07\\x19\\xe0\\x29\\xe7\\x15\\x00\\x25\\x01\\x75\\x01\\x95\\x08\\x81\\$

    should read:

    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

    (see the screenshot and the linked article from isticktoit).

  5. Great tutorial. Worked first time, well after I saw David’s comment re the typo, which by the way is not fixed in the instructions :). But again, great stuff and thanks so much.

    • Based on the above keyboard version, this worked for me connected as a USB mouse to Windows 7, 10 and a Raspberry Pi 3B+.
      It didn’t work as a mouse connected to a Pi Zero W.
      In all circumstances this would only work if the device was properly recognised in Control Panel or with lsusb.

      Note: I used a RPiZeroW with kernel 2017-02-16-raspbian-jessie-lite (which is version 4.4.48).
      Post 4.4.50 this doesn’t work.

      I tested this with sudo hid_gadget_test /dev/hidg0 mouse
      10, 10 eg moves the mouse right 10 and down 10
      –b1 eg simulates a left-button click

      cd /sys/kernel/config/usb_gadget/
      mkdir -p isticktoit
      cd isticktoit
      echo 0x1d6b > idVendor # Linux Foundation
      echo 0x1001 > idProduct # Mouse
      echo 0x0100 > bcdDevice # v1.0.0
      echo 0x0200 > bcdUSB # USB2
      mkdir -p strings/0x409
      echo “fedcba9876543210” > strings/0x409/serialnumber
      echo “Tobias Girstmair” > strings/0x409/manufacturer
      echo “iSticktoit.net USB Device” > 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 0 > functions/hid.usb0/protocol
      echo 0 > functions/hid.usb0/subclass
      echo 8 > functions/hid.usb0/report_length
      echo -ne \\x05\\x01\\x09\\x02\\xa1\\x01\\x09\\x01\\xa1\\x00\\x05\\x09\\x19\\x01\\x29\\x03\\x15\\x00\\x25\\x01\\x95\\x03\\x75\\x01\\x81\\x02\\x95\\x01\\x75\\x05\\x81\\x03\\x05\\x01\\x09\\x30\\x09\\x31\\x15\\x81\\x25\\x7f\\x75\\x08\\x95\\x02\\x81\\x06\\xc0\\xc0 > functions/hid.usb0/report_desc
      ln -s functions/hid.usb0 configs/c.1/
      # End functions

      ls /sys/class/udc > UDC

      • I am trying to emulate mouse movements with a raspberry pi. Could you make a video or give more instructions on the python code used to emulate mouse movements.

  6. Thank you for this awesome tutorial. I followed your steps on my pi zero w … works perfectly. (I did do a “chmod +x” on the script out of habit.)

  7. I’ve followed the guide several times – and used the 2017-04-10-raspbian-jessie-lite.
    I can’t get it to work.

    Dmsg says: (during startup)

    dwc2 20980000.usb: dwc2_hsotg_enqueue_setup: failed queue (-11)

    Any clue ???

  8. Hi, This works fine on my Raspberry Pi 3 A+ (with a male to male usb cable) but I cannot get the g key to work..

    This works: (typing f)
    echo -ne “\0\0\x9\0\0\0\0\0” > “/dev/hidg0”
    echo -ne “\0\0\0\0\0\0\0\0” > “/dev/hidg0”

    And this works: (typing h)
    echo -ne “\0\0\xb\0\0\0\0\0” > “/dev/hidg0”
    echo -ne “\0\0\0\0\0\0\0\0” > “/dev/hidg0”

    But this doesn’t:
    echo -ne “\0\0\xa\0\0\0\0\0” > “/dev/hidg0”
    echo -ne “\0\0\0\0\0\0\0\0” > “/dev/hidg0”

    Would be nice if someone could help.

  9. I FOUND OUT what is the issue and HOW TO GET IT WORKING!!!!
    This Works with Even the last versions of Raspberrypi Lite images.
    The only issue that prevents it from working is that some of the guides out there for enabling SSH say to add “modules-load=dwc2,g_ether” to cmdline.txt.
    This causes the RPI the identify to windows as an RNDIS USB Gadget which
    prevents it from being recognized as a USB Keyboard.
    When I removed the line above from cmdline.txt and rebooted the RPI it worked!!
    I think this is very important that should add it to the top of the guide, else people will spend a lot of time trying to make it work (like me).

  10. Just thought I’d share a non-ssh work around to let you run headless and be able to revert to normal mode + easily edit and manage your python script outside the Pi.

    In RPi:
    – move ‘isticktoit_usb’ file to the /boot/ directory
    – add your python script to the end of isticktoit_usb file “python /boot/myscript.py”
    – change the ‘/etc/rc.local’ line you entered above to “/boot/isticktoit_usb # libcomposite configuration”

    For raspbian the /boot/ partition is accessible to your computer when you plug the SD card into your computer. From there you can edit your python code + to boot normally just rename ‘isticktoit_usb’ to ‘anythingelseitdoesntmatter’.

  11. Got this working on a Pi Zero W this morning, and what I did may help others, and I’ll be tesitng it on a Pi3b+ later today as well.

    Output of uname -a:
    pi@touchpad02:~ $ uname -a
    Linux touchpad02 5.10.17+ #1403 Mon Feb 22 11:26:13 GMT 2021 armv6l GNU/Linux

    This is from a NOOB rasbain install. And it did require a couple of ‘tricks’ to get working correctly. I already had to create a .desktop file for the app I have using this setup, but even though I could run the app, it was complaining about not being authorized for /dev/hidg0. I took a look at the device via ls and found the following permissions:
    pi@touchpad02:~ $ ls -l /dev | grep hidg
    crw——- 1 root root 240, 0 Mar 5 04:05 hidg0

    After some trial and testing, found that changing the ownership from root:root to root:dialout didn’t help, even though it did change the permissions to:
    pi@touchpad02:~ $ ls -l /dev | grep hidg
    crw-rw—- 1 root dialout 240, 0 Mar 5 04:05 hidg0

    What ultimately worked was changing the permissions to
    pi@touchpad02:~ $ ls -l /dev | grep hidg
    crw-rw-rw- 1 root root 240, 0 Mar 5 04:05 hidg0

    However this isn’t ‘persistent’ as the device is created at each boot. Going through the options, I tried setting the permissions in an @reboot entry in the sudo crontab file, that didn’t work. What I ended up with is a ‘cleanup.desktop’ entry in ~/.config/autostart/ that looks like this:
    pi@touchpad02:~ $ cat .config/autostart/cleanup.desktop
    [Desktop Entry]
    Exec=lxterminal -e sudo chmod 666 /dev/hidg0
    pi@touchpad02:~ $

    Now the app, basically a poor-man’s elgato macro screen, is able to send keyboard combinations that OBS Studio or Streamlabs OBS can recognize as hotkeys to do things like start and stop streaming, recording, toggling mute state for microphone or audio inputs, w/o affecting the app that I’m streaming.

    As this is probably almost never going to see proper shutdown commands from the computer, I’m going to attach a shutdown script to gpio pin 6, but also have already set the system up with an fsoverlay to make the file system effectively read-only. (It’s not like I haven’t run into other pi’s that have ended up with corrupt file systems.) This will protect the system even if I don’t hit the shutdown button on the device before shutting down the system it’s acting as a macro board for.

    • I’m having trouble setting this up on my Raspberry Pi Zero W.

      The error I’m getting is “BrokenPipeError: [Errno 108] Cannot send after transport endpoint shutdown”. Is this the problem that you fixed above?

      • Same problem here, despite of being connected to the data usb port.
        The problem was caused by the cable : I replaced the power bank usb cable I was user by a micro usb that came with the Pi and it worked perfectly.
        I guess all usb cables are not equals.

        • I have checked cables, I am able to transfer data via that cable and port.

          my setup is like:
          i want this HID device to be keyboard of IPhone Device.
          Iphone data i can access in raspberri pi, and phone is also charging.

          Getting same error: Cannot send after transport endpoint shutdown.

    • Hello Rusty,

      I think that I have the same problem. Could you please explain a bit more in detail what you did? It looks for me like: -made this false, -made this false… – made something could workt.. etc. So your work is for me unfortunately not clear.

      Thank you!

  12. Could this same principle be used to make a macropad? Have a python program that says if I press this key run this program?

  13. Hi, This is a great project. I tried on My RPi Zero W and it worked perfectly.
    But I want to use the KEyboard functionality over the GPIO pins of Raspberry Pi because a USB device is already connected to my Micro USB port of the Raspberry Pi Zero, so I want to emulate the Keyboard from the GPIOs and connect the GPIO to another Computer to enter the data in the Active Field at the cursor. How Can I implement this is this method?

  14. Hello,

    I’ve got the problem that I get these message if I start the program:

    “File “/home/pi/RPi_Keyboard_Example.py”, line 14, in
    File “/home/pi/RPi_Keyboard_Example.py”, line 7, in write_report
    with open(‘/dev/hidg0’, ‘rb+’) as fd:
    FileNotFoundError: [Errno 2] No such file or directory: ‘/dev/hidg0′”

    • Hi Alexander. I’m running this in December 2021 as well and I’m getting the exact same error. Were you able to resolve this and get it working?

      FileNotFoundError: [Errno 2] No such file or directory: ‘/dev/hidg0’

      • Hi Tim,

        Now that I have finished all the other construction sites in my project, my head is a bit freer for the HID problem and I was able to find a solution.

        Rusty has already described a solution, which unfortunately was not understandable for me 2 months ago.

        For this reason I would like to tell you what you have to do:

        $sudo nano /boot/cmdline.txt
        …. rootwait modules-load=dwc2,ghid

        paste. This will allow the Pi to be recognised as a keyboard. By the way, it doesn’t matter which Pi. I currently have it running with a Pi Model 4 B.

        After that, you should use Rusty’s solution. Since it is unfortunately very incomprehensible, here is a short summary without that didn’t work, that did, etc.

        $cd .config/
        $mkdir autostart
        $cd autostart/
        $nano cleanup.desktop
        [Desktop Entry]
        Exec=lxterminal -e sudo chmod 666 /dev/hidg0

        Afterwards it should work. Feel free to contact me if it works or not!

        Translated with http://www.DeepL.com/Translator (free version)

        • It matters which Pi.

          The only ones that have the controller properly wired up, to my knowledge, are:

          zero 1/2 [w]
          4B (any memory)

  15. Great tutorial! One thing I don’t understand…in the python script you only send the “release keys” command after certain letters. Why doesn’t it have to be called after every key press? For example why doesn’t release keys have to be called right before sending “b”? Wouldn’t that mean the Shift + A is still being held down?

    • Send Key 1 (eg A)
      Send Key 2 (eg B)
      Send Key 3 (eg C)
      The send KeyUp (release)

      Its explained above – look at the Python code for exmaples.

      • so If i was using a joystick to emulate a keyboard, eg the joystick’s potentiometer would emulate the left/right on the keyboard. A tactile button will emulate “up” on a keyboard. Would the main look something like this?

        note: x represents the x-axis of the joystick.

        def main():
        if x150:
        if nc.buttons.Z:

  16. This works great only problem is using this example I cannot see any way to properly send certain keys like left control key or right control key.

    Left control is supposedly 224 dec and right control is 228

    These keys are not sent when using 224 or 228 like in the example python code when doing a, or b, etc..

    Anyone have any idea how to get left control key (224) sent from the PI?


  17. So if I wanted to use this as a mouse button remapper so my mouse can copy and paste using its keys, as there is no onboard memory, is it possible to that with this?

  18. Have a retro keyboard (i.e Tandy 102 computer) and would like to use it on a modern PC. I bet there’s enough GPIO pins to make a keyboard encoder. Need to come up with a wiring plan and software to encode key presses. Goal is to utilize keyboard with a new display hooked to a Raspberry Pi 3 or 4 and use as a serial terminal for retro computers.

  19. If you’re getting the “FileNotFoundError: [Errno 2] No such file or directory: ‘/dev/hidg0’” error, on a zero w, here’s what worked for me:

    ssh to your pi
    sudo nano /usr/bin/isticktoit_usb

    Once it opens, add the following below “ls /sys/class/udc > UDC”
    chmod 666 /dev/hidg0

  20. My Raspberry Pi Zero work on a Windows and Mac machine, however when I plug into my Mac Mini that is waiting for me to enter a FIRMWARE password, it will not recognize it (the keyboard) not the firmware password.

    When I try to run my script :

    sudo python3 RPi_Keyboard_Example2.py

    It does nothing. Tried changing cables, tried rebooting.

    Anyone else had that issue?

  21. Hey, I’m making a pixel bot for WoW, are sent keystrokes detected as coming from a real keyboard or as an injection? (LLMHF_INJECTED)

  22. Hello, when i try to make the rpi click the control key by doing ‘0xE0’, it clicks the shift key, im sure this isnt just the problem for this key, any solutions?

  23. Using this approach, how can I support the ALT keyboard codes? For example: “é” correspond to the keyboard keys “ALT + keypad_0 + keypad_2 + keypad_3 + keypad_3”. More specifically, that would be “press/hold ALT while pressing/releasing keypad_0, then pressing/releasing keypad_2, then pressing/releasing keypad_3, then pressing/releasing keypad_3 and finally releasing ALT.

  24. Hi, error with with line of code as raspbien has been updated. It longer uses boot/config.txt -> boot/firmware/config.txt or you will get error hidg0 not found. Took me. While to figure this one out


Leave a Comment

Download Our Free eBooks and Resources

Get instant access to our FREE eBooks, Resources, and Exclusive Electronics Projects by entering your email address below.