TL;DR
PiKey is a Raspberry Pi project that spoofs a Logitech K380 Bluetooth keyboard and mouse. It jiggles the mouse to prevent idle detection and auto-types LLM-generated text to simulate human activity. The device appears as a standard Bluetooth HID peripheral – no drivers or software needed on the target machine. Three full implementations exist: Python (primary), Rust (static binary), and C (minimal dependencies). The whole thing was inspired by a Reddit thread on r/overemployed where someone asked for exactly this device.
Source: GitHub - ZoltyMat/pi-key
The Reddit Thread That Started It
A user on r/overemployed posted a request with 62 upvotes:
Create a jetson nano or raspberry pi type of microcontroller that will use its Bluetooth to present as a keyboard + mouse. Run a local LLM that can auto type any type of text (novels, code, random notes, etc). And then sell this.
I read that and thought: this is a weekend project. A Pi Zero 2W is $15, BlueZ has HID profile support, and any OpenAI-compatible API can generate typed content. The hard part is not the LLM – it is making the Bluetooth HID emulation reliable and the typing look human.
What It Does
PiKey operates in three modes:
| Mode | Description | LLM Required |
|---|---|---|
jiggle | Mouse cursor moves in small random patterns | No |
type | Auto-types LLM-generated text with realistic timing | Yes |
both | Jiggling + typing combined | Yes |
The mouse jiggler is the simple use case – small random movements every few seconds to prevent screen lock or idle status in chat apps. No LLM needed.
The typer is more interesting. It queries an LLM for context-appropriate text (Python code, Slack messages, commit messages, meeting notes) and types it out character by character with realistic timing:
- Variable speed: 220-360 CPM (configurable)
- Realistic typos: 2% error rate with backspace correction
- Think pauses: 1.5-4 second pauses at 5% chance (simulating reading/thinking)
- Content variety: Rotates through different content types so the output is not monotonous
Device Spoofing
This is the part that makes it work seamlessly. PiKey does not appear as “Raspberry Pi” or some unknown device. It identifies itself as a Logitech K380:
Name: Logitech K380 Multi-Device Keyboard
Vendor ID: 0x046d (Logitech)
Product ID: 0xb342 (K380)
Class: 0x002540 (Peripheral, Keyboard)
When you pair it with a laptop, it shows up in Bluetooth settings as a K380. The target machine uses its built-in HID drivers. No software installation, no admin rights, no USB ports consumed.
Two HID Transports
PiKey supports two ways to connect to a target machine:
Bluetooth (all Pi models)
Uses BlueZ D-Bus with raw L2CAP sockets – PSM 17 for the control channel, PSM 19 for the interrupt channel. This is the standard Bluetooth HID protocol. Pairing works the same way as any Bluetooth keyboard: put PiKey in discoverable mode, pair from the target, and it is ready.
USB Gadget (Pi Zero 2W and Pi 4 only)
Uses Linux ConfigFS with the dwc2 overlay to present as a USB HID device. No Bluetooth needed – just plug in a USB cable. The Pi appears as a wired keyboard/mouse combo. This is useful when Bluetooth is restricted or unreliable.
An --transport auto flag tries USB first and falls back to Bluetooth.
LLM Integration
PiKey does not run an LLM locally (a Pi Zero 2W has 512MB of RAM). Instead, it calls any OpenAI-compatible API endpoint:
llm:
provider: "litellm" # or "ollama", "openai"
base_url: "http://10.0.0.5:4000"
model: "gpt-4o-mini"
api_key: "sk-..."
It works with LiteLLM (which is what my homelab runs), Ollama, or any direct OpenAI-compatible endpoint. The prompt asks for content in a specific format – the LLM generates a block of text, and PiKey types it out over the next several minutes before requesting more.
For fully offline operation, you could run Ollama on a Pi 4 with a small model like Phi-3, but that is pushing the hardware pretty hard.
Three Implementations
I built three complete versions because different deployment scenarios have different constraints:
Python (Primary)
The reference implementation. Async architecture with asyncio, 11 modules, 82 tests. This is what most people should use – it is the most feature-complete and easiest to modify.
python3 -m src.main --mode both --transport auto
Rust
Single static binary, no runtime dependencies. Uses Tokio for async and trait-based transport abstraction. Ideal if you want to cross-compile once and drop a binary onto a Pi without installing Python.
C
Minimal dependencies: libbluetooth-dev, libcurl, libyaml. POSIX threads for concurrency, function pointer vtable for transport abstraction, vendored cJSON parser. For when you want the smallest possible footprint.
All three share the same config format (config.yaml) and produce the same HID output.
Hardware Compatibility
| Board | Bluetooth | USB Gadget | Recommended |
|---|---|---|---|
| Pi Zero 2W | Yes | Yes | Best choice – $15, tiny |
| Pi 4 | Yes | Yes | Works, overkill |
| Pi 3B/3B+ | Yes | No | Bluetooth only |
| Pi 5 | Yes | No | Bluetooth only |
The Pi Zero 2W is the sweet spot. It is small enough to Velcro to the back of a monitor, draws minimal power from any USB port, and supports both transport modes.
Setup
A one-liner handles system setup:
./setup.sh # Bluetooth mode
./setup.sh --usb # Also configure USB gadget
This installs BlueZ, sets up the HID profile, optionally configures libcomposite for USB gadget mode, and creates a systemd service so PiKey starts on boot.
REST API
Added in v0.1.0 – a lightweight REST API for remote control:
python3 -m src.main --mode both --api
This lets you start/stop jiggling or typing, change modes, and check status without SSH-ing into the Pi. Useful if you have several PiKey devices and want to manage them from a single dashboard.
Lessons Learned
Bluetooth HID is finicky. The L2CAP socket setup has to happen in exactly the right order, and some operating systems handle reconnection differently. macOS is the most forgiving; Windows occasionally drops the pairing and needs re-pairing.
Typing speed matters more than content. Idle detection systems care about keyboard activity intervals, not what is being typed. The variable speed and think pauses are more important than the quality of the generated text.
USB gadget mode is more reliable than Bluetooth. No pairing issues, no reconnection problems, no range limitations. If you can use a USB cable, do it.
The Logitech spoofing is essential. Early prototypes that identified as “Raspberry Pi” triggered security warnings on corporate machines. Identifying as a K380 – a device that millions of people actually use – eliminates that friction.
Three implementations was overkill but educational. The Python version is what people use. The Rust version proved the architecture could port cleanly. The C version was a fun exercise in minimalism. If I were starting over, I would just do Python and Rust.
What is Next
- macOS Bluetooth pairing reliability improvements
- Web UI for configuration (replacing raw YAML editing)
- Pre-built SD card images for zero-config deployment