DEV Community

Junaid
Junaid

Posted on

USB HID in RPI PICO W Solved!

WAIT! Before proceeding—if you don't know how to set up CircuitPython, check out: What is RPI PICO W and how to SETUP it?


Index


Why Rubber Ducky, PICO Ducky, etc. Don't Work on RPI PICO W?

The answer is simple: Everyone tries to add a fail-safe or trigger to enable/disable storage in boot.py.

But according to CircuitPython documentation, the storage state can only be triggered once.

boot.py – This runs only once before the main code executes.


Investigation and Explanation

Here, I’m using boot.py to manipulate the USB protocol and declare the device as a USB HID to the OS.

boot.py – USB HID Init

import usb_hid, storage
storage.disable_usb_drive()
usb_hid.enable(usb_hid.Devices.KEYBOARD)
Enter fullscreen mode Exit fullscreen mode

code.py – The main keystroke or ducky script

import time
import usb_hid
from adafruit_hid.keyboard import Keyboard
from adafruit_hid.keycode import Keycode
from adafruit_hid.keyboard_layout_us import KeyboardLayoutUS

# Setup keyboard
kyb = Keyboard(usb_hid.devices)
layout = KeyboardLayoutUS(kyb)

# Open Notepad (Windows: Win + R → type 'notepad' → Enter)
kyb.press(Keycode.WINDOWS)
kyb.send(Keycode.R)
kyb.release_all()
time.sleep(0.5)

layout.write("notepad")
kyb.send(Keycode.ENTER)
time.sleep(1)

for i in range(10):
    layout.write("Hello World From Pico HID\n")
Enter fullscreen mode Exit fullscreen mode

OLAA! Your own ducky script.


WAIT WAIT!

Like everyone else, you may think:
"I can just add a trigger to switch between HID and USB Mass Storage!"

But here's the twist, my friend!

boot.py doesn't wait (or delay) for GPIO-related tasks.
Even if you add a delay, it skips it.
If anything goes wrong in boot.py, it automatically falls back to USB Mass Storage mode by default.


How to Fix It?

There are two ways to fix this:


Internal Communication (RECOMMENDED)

Here you have more reliable options to communicate between boot.py and code.py.
I recommend using microcontroller.nvm which helps toggle state between the two scripts.

microcontroller.nvm – Persistent Non-Volatile Memory.

Example:

boot.py

import microcontroller
microcontroller.nvm[0] = 1  # Use 1 or 0 depending on mode
Enter fullscreen mode Exit fullscreen mode

code.py

import microcontroller

if microcontroller.nvm[0] == 1:
    print("USB drive was enabled")
else:
    print("USB drive was disabled")
Enter fullscreen mode Exit fullscreen mode

External Communication

You can also use a simple state.txt file to read/write the current state.
This allows both scripts to enable or disable USB Mass Storage based on that value.

Example:

Get state from file

def getState():
    with open("state", "r") as f:
        return f.read().replace("\n", "")
Enter fullscreen mode Exit fullscreen mode

Set state to file

def setState(bool_val):
    with open("state", "w") as f:
        f.write(str(bool_val))
Enter fullscreen mode Exit fullscreen mode

What is RPI PICO W and How to SETUP it?

Hello Beginners!

  • RPI – Raspberry Pi

There are two ways to use Python on your RPI PICO W:

  1. CircuitPython – Easy, stable, and beginner-friendly.
  2. MicroPython – Gives full control. A bit tricky at first but easy once understood.

In simple terms:

  • CircuitPython is like Ubuntu for RPI-based microcontrollers.
  • MicroPython is like Arch Linux for RPI-based microcontrollers.

🚀 Speedrun: Setup Process

  1. Download the CircuitPython .uf2 file from the CircuitPython website.
  2. Download nuke.uf2 from CircuitPython or the Raspberry Pi website.
  3. Download the latest CircuitPython RPI PICO W bundle and unzip it.
  4. While plugging in your PICO to your PC, hold the BOOTSEL button until RPI-RP2 shows up in File Explorer.
  5. Copy nuke.uf2 to the RPI-RP2 drive.
  6. WAIT 2 seconds. It will eject and reconnect automatically.
  7. Now, copy the CircuitPython .uf2 file to RPI-RP2.
  8. WAIT again—this time it will show as CIRCUITPY.
  9. From the CircuitPython bundle, find the usb_hid folder and copy it to your PICO.
  10. Copy your code.py and boot.py into the CIRCUITPY drive.
  11. OLAA! DONE!

NOTE: I'm still exploring better solutions to improve this further.
If you have any suggestions or ideas, please comment down below!


Author: Junaid

Sources:

Top comments (0)