🌐 English · 中文
Turn an M5Cardputer / M5StickC / M5StickC Plus into a tiny BLE keyboard whose every key is yours to remap.
My day with AI is now a constant back-and-forth — ask, wait, ask again. The bottleneck isn't typing speed, it's typing posture. I wanted to talk to the AI from the couch with a wireless mic, not be chained to the keyboard.
So: one button under my thumb starts dictation; press again to stop. Another key sends Enter. With the Cardputer I also get a row of digits and Esc / Backspace, enough to flip between several AI sessions or terminal tabs without touching the laptop. The thing is small, runs off USB-C, and pairs as a plain Bluetooth keyboard.
- A physical button for your voice-input app. Tap once to start dictation, tap again to stop. The default preset wires it up for LazyTyper; remap the key to whatever shortcut your dictation app uses.
- Switch terminal tabs from across the room.
1–8sendCmd+1–Cmd+8(iTerm2, Ghostty, Terminal, …). One key per tab, no head-down typing. - Four behaviours per modifier key. Single / double / triple-click and long-press each fire a different shortcut — get a lot of mileage out of one tiny keyboard.
- Edit your bindings in a browser, not in code. CardPuter joins your WiFi, serves a config page, and you re-map keys without re-flashing.
- Multiple devices coexist. Each unit auto-picks a unique name (
cardputer-kb-XXXX.local) and its own AP password. - USB-C only. One cable for flashing, debugging, charging.
Personal project, MIT-licensed. Tested on my own devices on macOS, but the BLE HID side is standard and pairs with any host.
# Cardputer (set the side switch to OFF for stable upload)
make flash-card
# StickC Plus / StickC
make flash-cp
make flash-c
# Serial monitor (115200)
make monitor-cardPair the resulting BLE device on your host (Cardputer-KB-XXXX / M5StickC-KB / M5StickCP-KB).
Defaults map common keys; see Default key map below.
Hold Fn for 5 seconds. The screen flashes yellow ("Config Mode") and the device reboots into WiFi setup.
- Stored creds → STA mode → web UI at
http://<ip>/andhttp://cardputer-kb-XXXX.local/(XXXX is the last 4 hex of the BLE MAC, so multiple devices coexist). - No creds, or STA fails → AP fallback. SSID
CardPuter-KB-XXXX(shown on the LCD); web UI athttp://192.168.4.1/.
In the web UI you can:
- Pick a preset (
LazyTyper + iTerm2,Passthrough). - Edit any binding: trigger key, modifier checkboxes (Cmd/Opt/Ctrl/Shift), event (single / double / triple / long-press with custom ms), target action key. Add or remove rows freely.
- Annotate each binding with a free-text note.
- Toggle UI language between English and 中文 (top-right; persisted in localStorage).
- Export / import the whole config as JSON.
- Switch from STA to AP mode (or back) with one click.
The LCD also shows live state and accepts two physical keys: [A] swap mode, [Q] exit to BLE.
config # reboot into config mode (no Fn-hold needed)
wifi <ssid> <pass> # save WiFi creds without entering AP mode
wifi-clear # clear saved creds
Held portrait, USB pointing up.
| Key | Sends | Use |
|---|---|---|
| Button A (front) | Opt+Tab |
voice-input hotkey |
| Power (side) | Enter |
confirm (~100-200 ms latency, AXP192 hardware) |
| Button B (side) | — | wake screen |
Held landscape so the column ` → Ctrl → Fn → Tab falls under the thumb.
| Key | Sends | Use |
|---|---|---|
1-8 |
Cmd+1 … Cmd+8 |
iTerm2 tab switch |
` |
Esc |
escape |
Ctrl |
Opt+Tab |
voice-input hotkey |
Fn |
Enter |
enter (5 s hold = config mode) |
- Arduino CLI
m5stack:esp32core (3.2.5 verified)- T-vK/ESP32-BLE-Keyboard — install from GitHub
- NimBLE-Arduino 1.4.3 — the 2.x API is incompatible
USE_NIMBLEmust be defined insideBleKeyboard.hitself (defining it in the sketch is too late)
The Cardputer FQBN sets PartitionScheme=huge_app,PSRAM=opi (the Makefile does this for you). PSRAM is required for the LCD double-buffer sprites.
- Trouble flashing or pairing? See
TROUBLESHOOTING.md.
| Path | Notes |
|---|---|
aikb.ino |
The whole firmware (single file, conditional compilation per device) |
Makefile |
make flash-{c,cp,card} and make monitor-{c,cp,card} |
scripts/trigger-config.sh |
macOS AppleScript that taps Caps Lock to remote-trigger config mode (CardPuter only; needs the host to have a working CapsLock keypath) |
ARCHITECTURE.md |
Module layout, data structures, dispatch flow |
REQUIREMENTS.md |
Hardware / library matrix |
CLAUDE.md |
Conventions for AI-assisted contributions |
MIT © Xie Yanbo, 2026.