Turn a Raspberry Pi into a USB HID keyboard/mouse bridge with web-based control
Project description
Keybird v1.0.12
Transform your Raspberry Pi into a professional USB keyboard, mouse, and media controller with web-based management.
Control a computer from another computer, customize per-keyboard mappings, suppress keys or functions that auto-enter, and manage everything through a modern Bootstrap 5 dark theme web interface. But why does this exist BHB.buzz
๐ GitHub: github.com/mcyork/keybird
๐ฏ What Is This?
The Pi HID Bridge turns a Raspberry Pi into a composite USB HID device that emulates:
- โจ๏ธ Keyboard with 100+ mapped keys
- ๐ฑ๏ธ Mouse with 3 buttons + scroll wheel
- ๐ต Media keys (volume, play/pause, etc.)
Plus unique features:
- ๐น Multi-keyboard pass-through - Monitor YubiKey, Dell, Apple, Microsoft keyboards simultaneously
- ๐ง Per-keyboard custom mappings - YubiKey Enter suppression, key remapping, text macros
- ๐ Emulation profile switching - Pretend to be different keyboard models
- ๐ฑ๏ธ Web trackpad with automatic calibration for different displays
- ๐ Web UI - Control from laptop, phone, or tablet
โก Quick Start (2 Minutes)
Prerequisites
- Raspberry Pi 4 Model B (tested and validated)
- Full functionality including keyboard pass-through
- Raspberry Pi Zero 2 W (tested and validated)
- โ ๏ธ Keyboard functionality only - no pass-through capability due to single USB port hardware limitation
- โ ๏ธ NOT compatible with Pi 5 (USB gadget mode unresolved issues)
- USB-C data cable - Must support data, not just charging (Pi 4B)
- Micro-USB data cable - Must support data, not just charging (Pi Zero 2 W)
- Pi connected to network (WiFi or Ethernet)
- Pi has SSH enabled (default on Raspberry Pi OS)
Deploy from Your Computer (No SSH login needed!)
# 1. Install pi-shell and keybird on YOUR COMPUTER
pip install pi-shell keybird
# 2. Add your Pi (one-time setup with password-less SSH)
pi-shell add mypi --host 192.168.1.50 --user pi --password raspberry --push-key
# 3. Deploy to Pi (single command!)
keybird-deploy mypi
Done! ๐ Access web UI at http://192.168.1.50:8080
๐ฆ Note: Keybird not yet on PyPI. For now, install from source:
pip install pi-shell git clone https://github.com/mcyork/keybird.git cd keybird pip install . pi-shell add mypi --host 192.168.1.50 --user pi --password raspberry --push-key keybird-deploy mypi
What Gets Installed
โ
Flask web app with Bootstrap 5 dark theme UI
โ
USB HID gadget (keyboard + mouse + media keys)
โ
Systemd services (auto-start on boot)
โ
Boot configuration (enables USB gadget mode)
โ
Dependencies (flask, evdev)
Manual Deployment (See Under the Hood)
Want transparency? See manual-deploy/README.md for step-by-step SSH deployment.
Post-Installation
Connect Hardware
Connect your Pi's USB-C port to your target computer's USB port.
Access Web UI
open http://<pi-ip>:8080
Done! ๐
๐น Core Features
Multi-Keyboard Pass-Through
Note: This feature requires Raspberry Pi 4 Model B (Pi Zero 2 W lacks physical USB host ports for keyboards)
Monitor all keyboards at once:
- YubiKey NEO
- Dell WK636 Wireless Keyboard
- Apple Magic Keyboard
- Microsoft keyboards
- Any USB keyboard
Each keyboard can have its own custom key mappings!
Example: Suppress YubiKey's auto-Enter key while keeping Enter normal on other keyboards.
Emulation Profile Switching
Pretend to be different keyboards:
- Dell WK636 (VID: 046d, PID: c52b)
- Apple Magic Keyboard (VID: 05ac, PID: 029f)
- Logitech Unifying Receiver (VID: 046d, PID: 4049)
- Any keyboard you clone!
Why? Some software only works with specific keyboard models. Clone the required keyboard and switch profiles as needed.
Mouse Control
Two ways to control the mouse:
-
Web Trackpad - Beautiful 500x350px trackpad in browser
- Click on trackpad โ sends click to target PC
- Move in trackpad โ cursor moves
- Right-click โ context menu
- Scroll wheel โ page scrolling
- Auto-calibration for your display size
-
Physical Mouse Pass-Through - Plug real mice into Pi
- Forwards all movements and clicks
- Multi-mouse support
- Zero latency
Per-Keyboard Custom Mappings
Three mapping types:
-
HID Remap - Map key X to key Y
- Example: Map F13 to Print Screen
-
Text Replacement - Map key to type text
- Example: Map F14 to type "show ip interface brief"
-
Suppress - Ignore specific keys
- Example: Suppress Enter key on YubiKey only
Management UI:
- Select keyboard from dropdown
- View all mappings in table
- Add/Edit/Delete mappings
- Export/Import for backup
๐ฑ Web Interface
Access at http://<pi-ip>:8080
Modern Bootstrap 5 dark theme with tabbed interface:
Header Controls (Always Visible)
- Status Bar: USB connection and emulation profile status
- Mode Toggles: Keyboard and mouse pass-through switches
- Quick Actions: Reboot button
Tab 1: Control (Default)
- Send Text: Batch send text as keystrokes to connected computer
- Trackpad: Interactive mouse control with sensitivity and calibration
Tab 2: Learning
- Unmapped Keys: Auto-detected unknown keys with one-click mapping
- HID Reference: Quick lookup for common HID codes
Tab 3: Mappings
- Keyboard Management: Select any connected keyboard
- Custom Mappings: Suppress keys, remap, or send text macros
- Per-Keyboard Rules: Different mappings for each physical device
Tab 4: Settings
- Emulation Profiles: Switch which keyboard device the Pi pretends to be
- Detected Keyboards: See all physically connected keyboards
- Export/Import: Backup your profiles and mappings
๐ Advanced Usage
YubiKey Integration
Problem: YubiKey OTP mode sends Enter after password
Solution: Suppress Enter key on YubiKey only
1. Go to Mappings tab
2. Select "Yubico Yubikey NEO OTP+U2F+CCID"
3. Add Mapping:
- Key Code: 28 (Enter)
- Action Type: ๐ซ Suppress
4. Done! YubiKey outputs password without auto-submit
Trackpad Calibration
Problem: Different screens need different sensitivities
Solution: Calibrate for each display
1. Enable Mouse Pass-through toggle in header
2. Go to Control tab โ Click ๐ฏ Calibrate button
3. Use physical mouse to click 4 corners of screen
4. Save as "Windows Desktop" or "MacBook Pro"
5. Disable mouse pass-through toggle
6. Use web trackpad with perfect sensitivity!
Profile Switching
Problem: Some software requires specific keyboard models
Solution: Clone and switch profiles
1. Plug target keyboard into Pi
2. Go to Settings tab โ Detected Physical Keyboards
3. Click "Clone" button next to the keyboard
4. Pi reboots as that keyboard
5. Use Emulation Profile dropdown to switch between saved profiles anytime
Multi-Computer Setup
Best Practice with KVM:
[Powered USB Hub] โ Constant power
โ
[KVM] โ Switches USB data
โ
[Computer A] [Computer B] [Computer C]
Connected to hub:
- Raspberry Pi (via USB-C data cable)
- Your keyboards
- Your mice
- Microphone
- Other USB devices
Benefits:
- Pi stays powered (no reboot when switching)
- All devices switch together
- Web UI always accessible
- Different calibrations per computer
๐ฆ What Gets Installed
On the Pi
Application Files:
/home/pi/pi-hid-bridge/
โโโ app/pi_kb.py # Flask app (2,850+ lines)
โโโ scripts/
โ โโโ setup_gadget_composite.sh # Creates USB gadget
โ โโโ cleanup_gadget.sh # Removes USB gadget
โ โโโ gadget.conf # VID/PID configuration
โโโ systemd/
โ โโโ hid-gadget.service # Auto-start gadget
โ โโโ pi-hid-bridge.service # Auto-start Flask
โโโ requirements.txt # Python deps
Runtime Data Files:
/home/pi/
โโโ keyboard_profiles.json # Emulation profiles
โโโ keyboard_mappings.json # Per-keyboard custom mappings
โโโ trackpad_calibrations.json # Trackpad calibrations
System Configuration:
/boot/firmware/config.txt # dtoverlay=dwc2 added
/boot/firmware/cmdline.txt # modules-load=dwc2 added
/etc/systemd/system/ # Services installed
USB Devices Created:
/dev/hidg0 # Keyboard
/dev/hidg1 # Consumer Control (media keys)
/dev/hidg2 # Mouse
๐ ๏ธ Pi Shell CLI
This project uses Pi Shell - a pip-installable CLI tool for managing Raspberry Pis via SSH.
Install: pip install pi-shell
GitHub: github.com/mcyork/pi-shell
Essential Commands
After adding your Pi with pi-shell add mypi ..., you can use the symlink:
# Check status
pi-shell status mypi
# Execute commands (streaming output)
mypi run-stream "command"
# Send files
mypi send /local/file /remote/path
# Read files
mypi read /remote/file > local-file
# List all Pis
pi-shell list
Deployment Commands
# Deploy from scratch
keybird-deploy mypi
# Quick restart (already deployed)
cd keybird/manual-deploy
./quick-start.sh mypi
๐ Hardware Setup
Raspberry Pi 4 Model B
USB Ports:
- USB-C port
- USB gadget output (connects to target computer)
- 4ร USB-A ports - Connect keyboards, mice, YubiKey, etc.
- Ethernet - Management network
- Wi-Fi - Wireless management (optional)
Wiring:
Target Computer USB Port
โ
[USB-C Cable] โ Data + Power
โ
Pi USB-C Port
Pi USB-A Ports:
โโ Keyboard 1 (Dell)
โโ Keyboard 2 (YubiKey)
โโ Mouse 1 (Logitech)
โโ Mouse 2 (Microsoft)
Power Options:
- Via USB-C from target - If target provides enough power
- Via GPIO pins - 5V to pins 2/4, GND to pin 6
- PoE HAT - Power over Ethernet
๐ Documentation
Getting Started
- Manual Deployment README - Deploy without pip install
- Manual Deployment Guide - Step-by-step SSH deployment
Maintenance
- Uninstall Guide - Complete removal instructions
๐ Troubleshooting
USB Not Connecting
Check USB status:
mypi run-stream 'cat /sys/class/udc/*/state'
Should show: configured
If shows: not attached โ Check USB cable is data-capable
Manually recreate gadget:
mypi run-stream 'sudo systemctl restart hid-gadget.service'
Services Won't Start
Check logs:
mypi run-stream 'sudo journalctl -u pi-hid-bridge.service -n 50'
mypi run-stream 'sudo journalctl -u hid-gadget.service -n 50'
Manual restart:
mypi run-stream 'sudo systemctl restart hid-gadget.service'
mypi run-stream 'sudo systemctl restart pi-hid-bridge.service'
Keyboard Pass-Through Not Working
Check keyboards detected:
curl http://<pi-ip>:8080/detected_keyboards
Enable pass-through:
- Open web UI
- Toggle "Keyboard Pass-through" in header
Check logs:
mypi run-stream 'sudo journalctl -u pi-hid-bridge.service -f'
Mouse Not Working
Check mouse devices exist:
mypi run-stream 'ls -la /dev/hidg*'
Should see: /dev/hidg0, /dev/hidg1, /dev/hidg2
If missing hidg2:
mypi run-stream 'sudo systemctl restart hid-gadget.service'
Web UI Not Loading
Check Flask is running:
mypi run-stream 'sudo systemctl status pi-hid-bridge.service'
Check it's listening:
mypi run-stream 'sudo ss -tlnp | grep 8080'
View live logs:
mypi run-stream 'sudo journalctl -u pi-hid-bridge.service -f'
๐ Updating & Backup
Update Python Backend
mypi send app/pi_kb.py /home/pi/pi-hid-bridge/app/pi_kb.py
mypi run-stream 'sudo systemctl restart pi-hid-bridge.service'
Update Web UI (No Restart Needed)
mypi send templates/index.html /home/pi/pi-hid-bridge/templates/index.html
mypi send static/css/style.css /home/pi/pi-hid-bridge/static/css/style.css
mypi send static/js/app.js /home/pi/pi-hid-bridge/static/js/app.js
# Just refresh browser
Backup Profiles and Mappings
# Download from Pi
mypi read /home/pi/keyboard_profiles.json > backup_profiles.json
mypi read /home/pi/keyboard_mappings.json > backup_mappings.json
mypi read /home/pi/trackpad_calibrations.json > backup_calibrations.json
Or use Web UI โ Export buttons.
Restore Backups
# Upload to Pi
mypi send backup_profiles.json /home/pi/keyboard_profiles.json
mypi send backup_mappings.json /home/pi/keyboard_mappings.json
mypi send backup_calibrations.json /home/pi/trackpad_calibrations.json
# Restart service
mypi run-stream 'sudo systemctl restart pi-hid-bridge.service'
Full Re-Deploy
keybird-deploy mypi
๐ API Reference
Health Check
GET /health
USB Status
GET /usb_status
# Returns: {"state": "configured", "attached": true}
Keyboard Pass-Through
GET /passthrough
# Check status
POST /passthrough
# Body: {"enabled": true}
# Toggle on/off
Mouse Pass-Through
GET /mouse_passthrough
POST /mouse_passthrough
# Body: {"enabled": true}
Mouse Control
POST /mouse_move
# Body: {"dx": 10, "dy": -5, "buttons": 0, "wheel": 0}
POST /mouse_click
# Body: {"button": "left"} # or "right", "middle"
Send Text
POST /send_text
# Body: {"text": "Hello, world!"}
Detected Keyboards
GET /detected_keyboards
# Returns list of physical keyboards with VID/PID
Detected Mice
GET /detected_mice
# Returns list of physical mice
Emulation Profiles
GET /emulation_profiles
# List all saved profiles
POST /emulation_profiles/switch
# Body: {"profile_id": "logitech_4049"}
Keyboard Mappings
GET /keyboard_mappings/all
# List all keyboards with mappings
GET /keyboard_mappings/<kbd_id>
# Get specific keyboard's mappings
POST /keyboard_mappings/<kbd_id>/mapping
# Body: {"code": 28, "type": "suppress"}
DELETE /keyboard_mappings/<kbd_id>/mapping/<code>
Trackpad Calibration
POST /calibration/start
# Start calibration process
GET /calibration/status
# Check calibration progress
POST /calibration/save
# Body: {"name": "Windows Desktop", "sensitivity": 3.2, "points": [...]}
GET /calibrations
# List all saved calibrations
POST /calibrations/<cal_id>/activate
# Activate a calibration profile
System Control
POST /reboot
# Reboot the Pi
๐ Detailed Documentation
Getting Started
- Read DEPLOYMENT_QUICK_REFERENCE.md for deployment cheat sheet
- Read DEPLOYMENT_GUIDE.md for detailed manual deployment
Feature Guides
- LEARNING_MODE_GUIDE.md - Map unknown keys interactively
- YUBIKEY_SUPPRESS_EXAMPLE.md - Suppress YubiKey Enter key
- MOUSE_CONTROL_GUIDE.md - Use the web trackpad
- TRACKPAD_CALIBRATION_GUIDE.md - Calibrate for your display
- PROFILES_AND_MAPPINGS.md - Understand emulation vs mappings
Advanced Topics
- KEYBOARD_VENDOR_NOTES.md - Why Dell keyboards show as Logitech
- MOUSE_PASSTHROUGH_GUIDE.md - Physical mouse forwarding
- FINDINGS.md - Pi 5 USB gadget investigation
๐๏ธ Architecture
Components
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ Target Computer (Windows/Mac/Linux) โ
โ โฒ โ
โ โ USB HID โ
โโโโโโโโโโโโโโโโโโโโโโโโโโผโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ
USB-C Cable
โ
โโโโโโโโโโโโโโโโโโโโโโโโโโผโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ Raspberry Pi 4 Model B โ
โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ โ
โ โ Composite USB HID Gadget โ โ
โ โ - /dev/hidg0 (Keyboard) โ โ
โ โ - /dev/hidg1 (Consumer Control) โ โ
โ โ - /dev/hidg2 (Mouse) โ โ
โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ โ
โ โฒ โ
โ โ โ
โ โโโโโโโโโโโโโโโโโโโโโดโโโโโโโโโโโโโโโโโโโโโโโโ โ
โ โ Flask Web App (pi_kb.py) โ โ
โ โ - Keyboard pass-through โ โ
โ โ - Mouse pass-through โ โ
โ โ - Custom mappings engine โ โ
โ โ - Profile management โ โ
โ โ - Calibration system โ โ
โ โ - Web UI server โ โ
โ โโโโโโโโโโโโโโโโโโโโโฌโโโโโโโโโโโโโโโโโโโโโโโโ โ
โ โ โ
โ โ evdev โ
โ โผ โ
โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ โ
โ โ Physical Input Devices โ โ
โ โ - Keyboards (YubiKey, Dell, Apple...) โ โ
โ โ - Mice (Logitech, Microsoft...) โ โ
โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ โ
โ โ
โ Network: Ethernet + Wi-Fi โโโโ Web Browser โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
Data Flow
Keyboard Pass-Through:
Physical Keyboard โ evdev โ Pi โ Custom Mappings โ HID Report โ Target PC
Web Trackpad:
Browser โ HTTP โ Pi โ Calibration โ HID Report โ Target PC
Profile Switching:
User Selection โ Update gadget.conf โ Reboot โ New VID/PID โ Target PC sees different keyboard
๐ค Contributing
This is a personal project, but suggestions welcome!
Areas for improvement:
- Additional mapping types (delays, sequences)
- Drag-and-drop mouse support
- Encrypted macro implementation
- AI command generation
- GamePad emulation
๐ License
Personal project - use at your own risk!
๐ Acknowledgments
Built with:
- Flask - Web framework
- evdev - Linux input device interface
- Python - Glue holding it all together
- Pi Shell - Pi management CLI (github.com/mcyork/pi-shell)
Inspired by Black Hat Booze (https://bhb.buzz) and the deep obsession with the distilation process - be it for spirits or code.
๐๏ธ Uninstalling
Keybird makes system-level changes that need proper cleanup:
# Complete removal (cleans everything)
sudo keybird-uninstall
# Then remove Python package
sudo pip uninstall keybird
# Reboot to apply boot config changes
sudo reboot
The uninstall script removes:
- โ USB gadget configuration (boot files)
- โ Systemd services
- โ
Installed files in
/opt/keybird/
Preserves (in case you reinstall):
- ๐พ Saved profiles, mappings, calibrations in
/home/pi/
See UNINSTALL.md for manual uninstall steps.
๐ Support
Check logs:
sudo journalctl -u pi-hid-bridge.service -f
Reset everything (pip install users):
sudo systemctl restart hid-gadget.service
sudo systemctl restart pi-hid-bridge.service
Reset everything (manual deploy users):
cd keybird/manual-deploy
./quick-start.sh mypi
๐ Version History
-
v1.0.12 (Dec 2025) - Pass-Through Initialization Fix: Fixed pass-through flags initialization at module level (now properly enabled on boot), improved deployment script to use source files during development, ensures GUI correctly shows pass-through status on startup
-
v1.0.11 (Dec 2025) - Pass-Through Reliability & Key Mapping Fixes: Fixed pass-through always enabled on boot (flags set before threads start, removed retry limits), fixed key mapping modifier handling to preserve user modifier state, improved mouse polling rate (1ms timeout), fixed indentation error in mouse pass-through loop
-
v1.0.10 (Dec 2025) - Auto-Start & UI Fixes: Pass-through modes (keyboard and mouse) now auto-start on boot by default, UI toggles correctly reflect enabled state, restored missing LED indicators and controls on Control tab
-
v1.0.9 (Nov 2025) - Human-Readable HID Code Dropdown: Added dropdown with human-readable key names (Delete, Enter, F1-F24, etc.) instead of requiring hex codes, with Custom option for unknown keys
-
v1.0.8 (Nov 2025) - Revert to File-Copying Deployment: Restored original deployment approach for better workflow (edit locally, deploy to multiple Pis)
-
v1.0.7 (Nov 2025) - Editable Mappings & Modifier Support: Auto-inject captured keys into editable table, inline editing of all mappings, modifier support (Ctrl+Alt+Shift+Win) for key combinations like Ctrl+Alt+Del
-
v1.0.6 (Nov 2025) - LED Control & Robustness: Added LED forwarding (sync host lock keys to physical keyboards), real-time LED status indicators, clickable lock key toggles in GUI, and improved pass-through crash recovery
-
v1.0.5 (Nov 2025) - Listen Mode & Branding: Added key listening mode for easy keyboard mapping, auto-start pass-through on boot, keyboard deduplication, favicon integration, and rebranded to "Keybird"
-
v1.0.4 (Oct 2025) - PyPI Fix: Corrected package build to include touch support for iPhone/iPad trackpad
-
v1.0.3 (Oct 2025) - Mobile Support: Added touch support for iPhone/iPad trackpad - touch to move cursor, tap to click, two-finger right-click
-
v1.0.2 (Oct 2025) - Modernization: Updated to Python 3.10+, replaced deprecated pkg_resources with importlib.resources
-
v1.0.1 (Oct 2025) - Bug fix: Fixed silent crash in keyboard pass-through mode
-
v1.0 (Oct 2025) - Initial release with keyboard pass-through, web UI, mouse control, trackpad calibration, multi-keyboard support, profiles, per-keyboard mappings, and YubiKey support
-
v0.1 (Oct 2025) - Initial prototype
Ready to control your computers like a pro? Deploy and enjoy! ๐ฎ
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file keybird-1.0.12.tar.gz.
File metadata
- Download URL: keybird-1.0.12.tar.gz
- Upload date:
- Size: 416.9 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.10.2
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
b510e9ce23dafe89ab15d42e5d134e1e29bc942ae005cffb10b5ed7a9715f031
|
|
| MD5 |
460623b21f02d1fd1c65d77801c948ab
|
|
| BLAKE2b-256 |
dd799af2dcfb10d7158143e0a9288b62296cae3f70f1cccbf1b74d82725d09ff
|
File details
Details for the file keybird-1.0.12-py3-none-any.whl.
File metadata
- Download URL: keybird-1.0.12-py3-none-any.whl
- Upload date:
- Size: 418.8 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.10.2
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
ecc1d50799d034690a81364f16c5a7d0cfa0742dce42351fe436997981624620
|
|
| MD5 |
40b12c63ef145da71879ff681339e777
|
|
| BLAKE2b-256 |
5c2666492b99b676eca45a774236c7cc3a442ba91e04b2bae1a5d198df7061fc
|