Flashing the ClawTouch HID Firmware
June 1, 2026 · View on GitHub
English | 简体中文
Flashing the ClawTouch HID Firmware
Last verified: 2026-03-14 — flash procedure on CircuitPython 10.1.4
- a Raspberry Pi Pico 2. The same procedure flashes every firmware in this repo through v1.1.2: the v1.1.0 drag opcodes, the v1.1.1 keyboard byte-order unification, and the v1.1.2 panic-stop fix are all
code.py-only changes — noboot.pyor bootloader change — so the flash path is identical.
What you need
- A Raspberry Pi Pico 2 (RP2350 board)
- A USB-C data cable (not a charge-only cable)
- A PC running Windows / macOS / Linux
Step 1: Install CircuitPython
- Download the latest CircuitPython 10.x UF2 for the Pico 2 from the official site: https://circuitpython.org/board/raspberry_pi_pico2/ (last verified version: 10.1.4)
- Hold down the BOOTSEL button on the Pico 2 (the only push-button on the board; on a ClawTouch cased unit it's the button on the left when the USB-C port faces up), then plug in the USB cable while still holding it.
- A USB drive named
RPI-RP2will appear (it containsINFO_UF2.TXT). - Drag the downloaded
.uf2file onto that drive. - The Pico 2 reboots automatically; the drive renames itself to
CIRCUITPY.
Step 2: Install the adafruit_hid library
The firmware imports the Adafruit CircuitPython HID library for the keyboard / mouse classes. You have two options:
Option A — use the bundled copy (recommended):
This repository ships a known-good copy under
firmware/lib/adafruit_hid/. Copy it to the Pico's lib/ folder.
The bundled
.mpyfiles are CircuitPythonmpyformat v6 (ABI 6), which CircuitPython 10.x loads as-is — the.mpyformat did not change across the 10.x series. If you ever see anincompatible .mpy fileerror at boot, use Option B to fetch the matching 10.x bundle.
Option B — fetch a fresh copy from Adafruit:
Download the 10.x bundle from
https://circuitpython.org/libraries, then copy the adafruit_hid
folder from lib/ inside the bundle to CIRCUITPY/lib/adafruit_hid/.
Step 3: Copy the firmware
The deployable files live in this repository's firmware/ folder. On
Windows PowerShell (substitute your actual CIRCUITPY drive letter):
$pico = "G:"
Copy-Item "firmware\boot.py" "$pico\boot.py" -Force
Copy-Item "firmware\code.py" "$pico\code.py" -Force
Copy-Item "firmware\lib\adafruit_hid" "$pico\lib\adafruit_hid" -Recurse -Force
On macOS / Linux (assuming the drive mounts at /Volumes/CIRCUITPY):
PICO=/Volumes/CIRCUITPY # or /media/$USER/CIRCUITPY on Linux
cp firmware/boot.py "$PICO/boot.py"
cp firmware/code.py "$PICO/code.py"
cp -R firmware/lib/adafruit_hid "$PICO/lib/adafruit_hid"
boot.pyonly runs once at power-on. After copyingboot.pyyou must unplug and re-plug the Pico (or pop CIRCUITPY's eject menu and reconnect). Changes tocode.pyalone auto-reload — no replug needed.
Expected layout on CIRCUITPY
CIRCUITPY/
├── boot.py ← USB descriptor setup
├── code.py ← Firmware main loop
├── boot_out.txt ← Auto-generated by CircuitPython
├── settings.toml ← CircuitPython config
└── lib/
└── adafruit_hid/ ← HID driver library (.mpy files)
├── __init__.mpy
├── keyboard.mpy
├── keycode.mpy
├── keyboard_layout_base.mpy
├── keyboard_layout_us.mpy
├── mouse.mpy
├── consumer_control.mpy
└── consumer_control_code.mpy
Step 4: Smoke test
After unplugging and replugging:
- LED blink: the onboard LED should blink once per second. Solid-on or off both mean the firmware did not start.
- COM ports: two new serial ports appear (one console + one
data). On Windows check Device Manager; on macOS / Linux they
appear as
/dev/tty.usbmodem*or/dev/ttyACM*. - PING: run
examples/ping_test.pypointed at the data port. You should seePONG received. CIRCUITPYdrive: with this (development) firmware theCIRCUITPYUSB mass-storage drive stays mounted on the host — intentional, so the firmware stays editable in place. On a locked-down / enterprise host where a removable drive is unwanted (USB-storage policies, scans, audit entries), flash the production firmware instead — see Production (locked-down) firmware below.
Production (locked-down) firmware
The default boot.py is the development variant: it keeps the
CircuitPython REPL console and the CIRCUITPY USB mass-storage drive
exposed to the host so the board stays debuggable and the firmware editable
in place. On a deployed device you usually want the controlled host to see
only the HID keyboard/mouse and the CDC data channel — no removable
drive, no REPL.
To harden it, copy firmware/boot_production.py
onto the CIRCUITPY drive renamed to boot.py (overwriting the dev
boot.py), then eject and replug:
# from the CIRCUITPY drive root
cp boot_production.py boot.py # or copy + rename in your file manager
CircuitPython only runs the file literally named
boot.py, soboot_production.pyis inert until you rename it. After this the device exposes only HID + CDC data — theCIRCUITPYdrive and the REPL console disappear.Caveat: with the drive hidden you can no longer edit files over USB. To make further changes, re-enter BOOTSEL and re-flash CircuitPython (Step 1), which restores the editable
CIRCUITPYdrive, then re-copy the firmware.
Upgrading the firmware
Once CircuitPython is on the board, upgrading the firmware does not
require BOOTSEL — just overwrite code.py:
cp firmware/code.py "$PICO/code.py"
CircuitPython auto-reloads on file change. To upgrade CircuitPython
itself, hold BOOTSEL again and drop a new .uf2.
Troubleshooting
| Symptom | Fix |
|---|---|
No CIRCUITPY drive after first boot | Hold BOOTSEL, replug, re-flash the UF2 |
boot.py error keeps board from starting | Press the reset button twice quickly to enter safe mode, then fix boot.py |
| USB HID device not recognised by OS | Check boot.py enabled the HID descriptors; replug |
| No data on the serial port | Confirm usb_cdc.enable(data=True) is set; verify you are using the data port, not the console |
| LED never blinks | code.py has a syntax error or crashed — connect to the console port with Thonny / screen / minicom to see the traceback |
| Only one COM port appears | boot.py did not run — replug the board |