No description
Find a file
sjat c783f59288 feat: Successfully deploy F13-F17 keyboard HID firmware to Pico
MAJOR BREAKTHROUGH: Discovered correct deployment method for CircuitPython

Critical Discovery:
- BOOTSEL mode (RPI-RP2) is ONLY for flashing .UF2 firmware files
- Python files MUST be copied to CIRCUITPY drive, not RPI-RP2
- Files copied to RPI-RP2 appear to succeed but don't persist to runtime

Deployment Success:
- Pico running CircuitPython 9.2.1
- HID Keyboard mode with F13-F17 support confirmed
- Buttons 4 and 5 tested successfully (F16, F17)
- boot.py: readonly=True (enables auto-execution)
- code.py: 70 lines, no _pin bug, proper debouncing

Files Added:
- DEPLOYMENT_GUIDE.md: Comprehensive guide with best practices from Adafruit docs
- deploy_pico_robust.sh: Automated deployment script with verification
- DEPLOYMENT_SUCCESS.md: Complete troubleshooting report and lessons learned

Button Mappings:
- Button 1 (GP28) → F13 → Volume Up
- Button 2 (GP27) → F14 → Volume Down
- Button 3 (GP22) → F15 → Next Tab
- Button 4 (GP21) → F16 → Toggle Display ✓ TESTED
- Button 5 (GP19) → F17 → Keyboard Toggle ✓ TESTED

Next Steps:
- Test buttons 1-3 for F13-F15 events
- Update baobab-button-handler on tembo to catch F13-F17 keys
- Configure actions for each button

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-02-01 08:51:39 +01:00
pico_firmware Initial commit: Keyboard HID mode with F13-F17 keys 2026-01-31 15:10:03 +01:00
testKioskHID Initial commit: Keyboard HID mode with F13-F17 keys 2026-01-31 15:10:03 +01:00
.claude_session_notes.md Initial commit: Keyboard HID mode with F13-F17 keys 2026-01-31 15:10:03 +01:00
.gitignore Initial commit: Keyboard HID mode with F13-F17 keys 2026-01-31 15:10:03 +01:00
code.py Initial commit: Keyboard HID mode with F13-F17 keys 2026-01-31 15:10:03 +01:00
code_multibutton_test.py Initial commit: Keyboard HID mode with F13-F17 keys 2026-01-31 15:10:03 +01:00
deploy_pico_robust.sh feat: Successfully deploy F13-F17 keyboard HID firmware to Pico 2026-02-01 08:51:39 +01:00
deploy_to_pico.py Initial commit: Keyboard HID mode with F13-F17 keys 2026-01-31 15:10:03 +01:00
DEPLOYMENT.md Initial commit: Keyboard HID mode with F13-F17 keys 2026-01-31 15:10:03 +01:00
DEPLOYMENT_GUIDE.md feat: Successfully deploy F13-F17 keyboard HID firmware to Pico 2026-02-01 08:51:39 +01:00
DEPLOYMENT_SUCCESS.md feat: Successfully deploy F13-F17 keyboard HID firmware to Pico 2026-02-01 08:51:39 +01:00
diagnose_pico.py Initial commit: Keyboard HID mode with F13-F17 keys 2026-01-31 15:10:03 +01:00
HARDWARE.md Initial commit: Keyboard HID mode with F13-F17 keys 2026-01-31 15:10:03 +01:00
INTEGRATION_TESTING.md Initial commit: Keyboard HID mode with F13-F17 keys 2026-01-31 15:10:03 +01:00
MANUAL_INSTALL.md Initial commit: Keyboard HID mode with F13-F17 keys 2026-01-31 15:10:03 +01:00
QUICKSTART.md Add QUICKSTART guide for 5-minute deployment 2026-01-31 15:10:54 +01:00
README.md Initial commit: Keyboard HID mode with F13-F17 keys 2026-01-31 15:10:03 +01:00
SERVER_HANDOFF.md Initial commit: Keyboard HID mode with F13-F17 keys 2026-01-31 15:10:03 +01:00
TEMBO_INTEGRATION.md Initial commit: Keyboard HID mode with F13-F17 keys 2026-01-31 15:10:03 +01:00
test_deployment.py Initial commit: Keyboard HID mode with F13-F17 keys 2026-01-31 15:10:03 +01:00
TESTING.md Initial commit: Keyboard HID mode with F13-F17 keys 2026-01-31 15:10:03 +01:00
TROUBLESHOOTING_NOTES.md Initial commit: Keyboard HID mode with F13-F17 keys 2026-01-31 15:10:03 +01:00
wiring-diagram.md Initial commit: Keyboard HID mode with F13-F17 keys 2026-01-31 15:10:03 +01:00

kioskHID - Raspberry Pi Pico HID Keyboard Controller

5-button USB HID keyboard controller for kiosk applications using F13-F17 function keys.

Overview

This project implements a simple, reliable HID keyboard device using a Raspberry Pi Pico and 5 tactile buttons. The Pico presents itself as a standard USB keyboard and sends F13-F17 key presses when buttons are pressed.

Why F13-F17?

  • F13-F17 are rarely bound by applications (unlike F1-F12)
  • Gives you complete control over custom key bindings
  • Simpler than Consumer Control codes - direct key press mapping
  • Works with any key binding system (xbindkeys, systemd, custom daemons)

Hardware

Components

  • Raspberry Pi Pico (RP2040)
  • 5x NINIGI TACT-64K-F tactile switches (6x6mm, SPST-NO)
  • Breadboard and jumper wires
  • USB cable (micro-USB to USB-A)

Wiring

Button 1 (Volume Up)      : GP28 ──┬─ [Button] ─┬── GND
Button 2 (Volume Down)    : GP27 ──┤            │
Button 3 (Next Tab)       : GP22 ──┤            │
Button 4 (Display Toggle) : GP21 ──┤            │
Button 5 (Keyboard Toggle): GP19 ──┴────────────┘

All buttons use internal pull-up resistors (no external resistors needed).

Button Mappings

Button GPIO Function Key Intended Action Linux Event Code
1 GP28 F13 Volume Up KEY_F13 (183)
2 GP27 F14 Volume Down KEY_F14 (184)
3 GP22 F15 Next Browser Tab KEY_F15 (185)
4 GP21 F16 Toggle Display On/Off KEY_F16 (186)
5 GP19 F17 Toggle On-Screen Keyboard KEY_F17 (187)

Firmware Architecture

Boot Modes

The firmware supports two boot modes for different use cases:

Production Mode (boot_production.py)

  • Filesystem: Read-only (readonly=True)
  • Auto-execution: YES - code.py runs automatically on boot
  • Use case: Deployed/production use
  • Updates: Must switch to development mode first

Development Mode (boot_development.py)

  • Filesystem: Writable (readonly=False)
  • Auto-execution: NO - boots to REPL, manual execution required
  • Use case: Development and testing
  • Updates: Can copy files directly via USB mass storage

Important: CircuitPython only auto-runs code.py when the filesystem is read-only for security reasons.

Code Structure

pico_firmware/
├── boot_production.py      # Production boot config (readonly=True)
├── boot_development.py     # Development boot config (readonly=False)
├── code.py                 # Main firmware (Keyboard HID with F13-F17)
├── adafruit_hid/           # HID library (precompiled .mpy modules)
│   ├── keyboard.mpy
│   ├── keycode.mpy
│   └── ...

Deployment

Quick Deploy (Production Mode)

# 1. Find the Pico device
lsblk | grep CIRCUITPY

# 2. Mount the Pico
sudo mount /dev/sdb1 /mnt/circuitpy  # Adjust device as needed

# 3. Copy files
sudo cp pico_firmware/boot_production.py /mnt/circuitpy/boot.py
sudo cp pico_firmware/code.py /mnt/circuitpy/code.py
sudo cp -r pico_firmware/adafruit_hid /mnt/circuitpy/lib/

# 4. Sync and unmount
sync
sudo umount /mnt/circuitpy

# 5. Unplug and replug the Pico
# The code should now run automatically!

Verify Deployment

# Check serial output for startup banner
timeout 3 cat /dev/ttyACM0

# Expected output:
# ============================================================
# KIOSK HID KEYBOARD CONTROLLER - READY
# ============================================================
# Button Configuration:
#   Button 1 (GP28) → F13 → Volume Up
#   Button 2 (GP27) → F14 → Volume Down
#   ...

Testing HID Events

# Find the Pico keyboard device
ls -l /dev/input/by-id/ | grep -i Pico

# Monitor key events with evtest
sudo evtest /dev/input/eventX

# Press buttons and verify F13-F17 events appear

Integration with tembo

On the computer side (tembo), you'll need to configure a key binding daemon or systemd service to catch F13-F17 events and execute the appropriate actions.

Example Event Mapping

# Pseudo-config for button handler on tembo
key_bindings:
  F13:  # Button 1
    command: "pactl set-sink-volume @DEFAULT_SINK@ +5%"
    description: "Volume Up"

  F14:  # Button 2
    command: "pactl set-sink-volume @DEFAULT_SINK@ -5%"
    description: "Volume Down"

  F15:  # Button 3
    command: "/usr/local/bin/chrome-next-tab.sh"
    description: "Next Browser Tab"

  F16:  # Button 4
    command: "/usr/local/bin/toggle-display.sh"
    description: "Toggle Display On/Off"

  F17:  # Button 5
    command: "/usr/local/bin/toggle-onscreen-keyboard.sh"
    description: "Toggle On-Screen Keyboard"

Python Event Handler Example

import evdev

# Find Pico keyboard device
device = None
for path in evdev.list_devices():
    dev = evdev.InputDevice(path)
    if 'Pico' in dev.name and 'Keyboard' in dev.name:
        device = dev
        break

# Key mappings (F13-F17 event codes)
KEY_ACTIONS = {
    183: lambda: os.system("pactl set-sink-volume @DEFAULT_SINK@ +5%"),      # F13
    184: lambda: os.system("pactl set-sink-volume @DEFAULT_SINK@ -5%"),      # F14
    185: lambda: os.system("/usr/local/bin/chrome-next-tab.sh"),             # F15
    186: lambda: os.system("/usr/local/bin/toggle-display.sh"),              # F16
    187: lambda: os.system("/usr/local/bin/toggle-onscreen-keyboard.sh"),    # F17
}

# Event loop
for event in device.read_loop():
    if event.type == evdev.ecodes.EV_KEY and event.value == 1:  # Key press
        action = KEY_ACTIONS.get(event.code)
        if action:
            action()

Testing

Layer 1: Hardware Test (Standalone)

Watch the onboard LED:

  • Startup: 3 quick blinks (ready)
  • Button press: LED turns on
  • Button release: LED turns off

Connect to serial console:

screen /dev/ttyACM0 115200

Press each button and verify serial output shows correct F-key.

Layer 2: HID Events (Linux Kernel)

Test with evtest (no daemon needed):

sudo evtest /dev/input/eventX

Verify each button generates correct event:

  • Button 1 → KEY_F13 (183)
  • Button 2 → KEY_F14 (184)
  • Button 3 → KEY_F15 (185)
  • Button 4 → KEY_F16 (186)
  • Button 5 → KEY_F17 (187)

Layer 3: Full Integration

Test with your button handler daemon/service on tembo to verify actions execute correctly.

Troubleshooting

Code doesn't auto-run after plugging in Pico

Cause: Likely using development boot.py (readonly=False)

Solution:

# Deploy production boot.py
sudo mount /dev/sdb1 /mnt/circuitpy
sudo cp pico_firmware/boot_production.py /mnt/circuitpy/boot.py
sync
sudo umount /mnt/circuitpy
# Unplug and replug Pico

No events appear in evtest

Possible causes:

  1. Code not running (check serial output)
  2. Wrong event device selected
  3. Permissions issue

Debugging:

# Check if Pico keyboard is detected
lsusb | grep -i "Raspberry Pi Pico"

# Find correct event device
ls -l /dev/input/by-id/ | grep -i Pico

# Check serial output
timeout 3 cat /dev/ttyACM0

Buttons don't respond

Check hardware:

  1. Verify wiring (GP28, GP27, GP22, GP21, GP19 to one side of buttons, GND to other)
  2. Check for loose connections
  3. Verify buttons work (multimeter continuity test)

Need to update firmware but can't modify files

Cause: Production mode has readonly filesystem

Solution: Temporarily switch to development mode:

sudo mount /dev/sdb1 /mnt/circuitpy
sudo cp pico_firmware/boot_development.py /mnt/circuitpy/boot.py
sync
sudo umount /mnt/circuitpy
# Now you can modify files
# After changes, switch back to production mode

Development

Making Changes

  1. Switch to development mode
  2. Make changes to code.py
  3. Copy to Pico
  4. Test via REPL or serial
  5. Switch back to production mode

Adding More Buttons

Edit code.py and add to the BUTTONS list:

BUTTONS = [
    (board.GP28, Keycode.F13, "Volume Up"),
    (board.GP27, Keycode.F14, "Volume Down"),
    (board.GP22, Keycode.F15, "Next Tab"),
    (board.GP21, Keycode.F16, "Toggle Display"),
    (board.GP19, Keycode.F17, "Keyboard Toggle"),
    (board.GP20, Keycode.F18, "New Function"),  # New button
]

Available F-keys: F13-F24 (Keycode.F13 through Keycode.F24)

Technical Details

Debouncing

Software debouncing with 20ms delay:

  1. Detect button press (HIGH → LOW transition)
  2. Wait 20ms
  3. Verify still pressed
  4. Send HID key press/release
  5. Wait for button release before next detection

HID Protocol

  • Device Type: USB HID Keyboard
  • Keys Used: F13-F17 (function keys 13-17)
  • Linux Keycodes: 183-187
  • Press/Release: Both press and immediate release sent (simulates key tap)

Serial Console

Debug output available on /dev/ttyACM0 at 115200 baud:

  • Startup banner
  • Button press/release logs
  • F-key sent confirmation

Repository

Git repository: ssh://git@git.baobab.band:7577/sjat/kioskHID.git

License

MIT License - Free for personal and commercial use

Credits

  • CircuitPython by Adafruit Industries
  • adafruit_hid library