Key Mime Pi:把你的 Raspberry Pi 变成远程键盘
近期版本的 Raspberry Pi 支持 USB on-the-go(USB OTG,USB 即时通信功能),因此可以模拟键盘、U 盘和麦克风等 USB 设备。为了利用这一功能,我制作了一个开源 Web 应用,把我的 Pi 变成一块虚拟键盘。我把它称为 Key Mime Pi。
本文将演示 Key Mime Pi 的工作方式,以及如何亲手制作一个。
演示
你需要准备的东西
- 支持 USB OTG 的 Raspberry Pi:
- Raspberry Pi 4(所有型号)
- Raspberry Pi Zero W
- Raspberry Pi A 和 A+ (需要验证)
- 这个来源声称早期的 Pi 支持 USB OTG,但我个人没有测试过这些设备。
- Raspberry Pi OS(又称 Raspbian)
- Stretch 或更高版本
- 一根 USB 数据线
- Pi 4:USB-C 转 USB-A(公对公)
- Pi Zero W:Micro-USB 转 USB-A(公对公)
安装 Raspberry Pi OS Lite
首先,将Raspberry Pi OS Lite(原名 Raspbian)安装到 microSD 卡上。

我使用 Rufus 写入 Pi 的 microSD 卡,不过任何整盘镜像工具都可以。
在 microSD 卡的启动分区中放置一个名为 ssh 的文件,以启用 SSH 访问,然后将 microSD 卡插入 Pi 设备。如果通过无线网络连接,还需要在启动分区中创建 wpa_supplicant.conf 文件。
连接 Pi
将 USB 数据线连接到 Pi 的 USB OTG 端口。Pi 4 使用 USB-C 端口;Pi Zero 使用标有“USB”的 Micro-USB 端口。


对于 Raspberry Pi 4(左),连接 USB-C 端口;对于 Raspberry Pi Zero W(右),连接 Micro-USB 数据端口。
将 USB 数据线的另一端连接到你想让它连接成键盘的计算机。USB 3.0 端口效果更好,因为它们能输出更多电力,但我测试过的所有 USB 2.0 端口也都运行正常。
你的 Pi 应该会从计算机的 USB 端口取电并启动。
安装 Key Mime Pi
安装 Key Mime Pi 有两种方式。你可以使用无需任何外部工具的传统 bash;或者,想要更优雅一点,也可以通过 Ansible 安装,这是我最喜欢的开源配置管理工具。
选项 1:纯 bash 方式
在 bash shell 中输入以下命令,连接到 Pi 并将其配置为 USB 设备模拟器:
# SSH into your Pi
PI_HOSTNAME="raspberrypi" # Change to your pi's hostname
PI_SSH_USERNAME="pi" # Change to your Pi username
# Connect to the Pi (default password is "raspberry")
ssh "${PI_SSH_USERNAME}@${PI_HOSTNAME}"
# Install pre-requisites
sudo apt-get update && \
sudo apt-get install -y \
git \
python-pip \
python3-venv
# Install Key Mime Pi
git clone https://github.com/mtlynch/key-mime-pi.git
cd key-mime-pi
sudo ./enable-usb-hid
sudo reboot等待 Pi 重启,然后再次通过 SSH 登录并启动 Key Mime Pi Web 服务器:
ssh "${PI_SSH_USERNAME}@${PI_HOSTNAME}"
cd key-mime-pi
python3 -m venv venv
. venv/bin/activate
pip install --requirement requirements.txt
PORT=8000 ./app/main.py选项 2:Ansible 方式
如果你是 Ansible 用户,可以使用我的 Key Mime Pi Ansible role,实现更好的自动化。以下命令会将 Key Mime Pi 作为 systemd 服务安装到设备上:
PI_HOSTNAME="raspberrypi" # Change to your pi's hostname
PI_SSH_USERNAME="pi" # Change to your Pi username
# Install the Key Mime Pi Ansible role
ansible-galaxy install mtlynch.keymimepi
# Create a minimal Ansible playbook to configure your Pi
echo "- hosts: $PI_HOSTNAME
roles:
- role: mtlynch.keymimepi" > install.yml
# Install all software (default password is "raspberry")
ansible-playbook \
--inventory "$PI_HOSTNAME", \
--user "$PI_SSH_USERNAME" \
--become \
--become-method sudo \
--ask-pass \
install.yml
# Reboot the Pi
ansible \
"$PI_HOSTNAME" \
-m reboot \
--inventory "$PI_HOSTNAME", \
--user "$PI_SSH_USERNAME" \
--ask-pass \
--become \
--become-method sudo使用 Key Mime Pi
运行安装脚本后,可以通过以下地址访问 Key Mime Pi:
它的界面如下:

等待输入的 Key Mime Pi Web 界面
然后,神奇的事情发生了:当你在浏览器中输入内容时,按键就会出现在连接到 Pi 的那台机器上。

Key Mime Pi 允许你通过浏览器向远程计算机发送按键。
工作原理
USB 设备模拟(USB device emulation)
这里真正神奇的地方来自 Linux 的 USB Human Interface Device(HID,人机接口设备)gadget 驱动。它允许用户态应用与操作系统交互时表现得像 USB 设备一样。
key-mime-pi 配置脚本会创建一个位于 /dev/hidg0 的文件路径。任何程序都可以读取或写入这个路径,而操作系统会将其中的数据转换为键盘信号。
要模拟键盘,Pi 必须根据 USB HID 规范与操作系统通信。那份文档有 97 页的键码和表格,读起来相当枯燥,但事实证明,键盘使用的协议非常简单。
每次按键时,键盘都会发送一条称为“report”的 8 字节消息。
| 字节索引 | 用途 |
|---|---|
| 0 | 修饰键(Ctrl、Alt、Shift) |
| 1 | 保留给制造商 |
| 2 | 按键 #1 |
| 3 | 按键 #2 |
| 4 | 按键 #3 |
| 5 | 按键 #4 |
| 6 | 按键 #5 |
| 7 | 按键 #6 |
发送“Hi”的按键序列如下:
# H (Right shift + h)
echo -ne "\x20\0\xb\0\0\0\0\0" > /dev/hidg0
# i
echo -ne "\0\0\xc\0\0\0\0\0" > /dev/hidg0
# Release all keys
echo -ne "\0\0\0\0\0\0\0\0" > /dev/hidg0除了发送按键按下信号,键盘还必须指示按键释放。一个全为零的 8 字节数据块表示当前没有任何按键处于激活状态。
上面的示例一次发送一个按键,但 HID report 可以容纳六个按键。这意味着,只要按键各不相同,就可以在一条消息中最多发送六个按键:
echo -ne "\0\0\x1a\x0b\x04\x17\x18\x13" > /dev/hidg0 && \
echo -ne "\0\0\0\0\0\0\0\0" > /dev/hidg0whatup将 JavaScript 转换为 HID
当你在浏览器窗口中输入时,JavaScript 会为每次按键生成事件。网站 keycode.info 对这一功能进行了出色的演示。
JavaScript 按键事件包含 keycode,但它们与 HID keycode 不同。幸运的是,两者基本上存在一一对应的映射关系。为了将 JavaScript 转换为 HID,我创建了如下的查找表:
_JS_TO_HID_KEYCODES = {
3: 0x48, # Pause / Break
8: 0x2a, # Backspace / Delete
9: 0x2b, # Tab
...
65: 0x04, # a
66: 0x05, # b
67: 0x06, # c
68: 0x07, # dKey Mime Pi 服务器监听浏览器发来的 JavaScript keycode 事件,将其转换为 HID code,然后发送到 Pi 的 HID 接口 /dev/hidg0。
整个过程如下:
- 用户在浏览器中按下一个键。
- 页面中的 JavaScript 将 JavaScript keycode 发送到 Pi 上的 Key Mime Pi 服务器。
- Key Mime Pi 服务器将 JavaScript keycode 转换为对应的 HID code。
- Key Mime Pi 服务器将 HID code 发送到
/dev/hidg0中的 USB gadget 接口。 - 连接到 Pi USB 数据线的计算机会将其作为键盘输入接收,从而在屏幕上显示一个字符。
供电问题
在我的测试中,计算机的 USB 端口能够产生足够的电力为 Pi 供电,但系统日志中频繁出现欠压警告:
$ sudo journalctl -xe | grep "Under-voltage"
Jun 05 03:46:05 keymimepi kernel: Under-voltage detected! (0x00050005)
Jun 05 03:48:29 keymimepi kernel: Under-voltage detected! (0x00050005)
Jun 05 03:54:22 keymimepi kernel: Under-voltage detected! (0x00050005)Pi 正确地检测到,标准 USB 2.0 和 USB 3.0 端口提供的电力不足以满足 Pi 的需求。
| Raspberry Pi 型号 | 供电需求 |
|---|---|
| Pi Zero W | 5 V / 1.2 A |
| Pi 4 | 5 V / 3.0 A |
标准 USB 端口达不到这一要求:
| USB 端口类型 | 输出功率 |
|---|---|
| USB 2.0 | 5 V / 0.5 A |
| USB 3.0 | 5 V / 0.9 A |
我目前仍在寻找这个问题的解决方案。以下是一些我尚未测试的可能方案:
使用 PoE HAT 从以太网端口取电使用 Zero2Go Power Adaptor 连接 AC 转 microUSB 适配器。
更新:为了解决这个问题,我与一家工程公司合作,制作了一块定制电路板,将 Pi 的 USB-C 端口一分为二。第一个端口接收 USB 电源,因此仍然可以向 Pi 提供完整的 3 安培电流。第二个端口接收 USB 数据输出,因此 Pi 仍然可以模拟 USB 键盘。


TinyPilot Power Connector 允许 Pi 通过 USB-C 端口获得 3 安培电力,同时不丧失 USB OTG 功能。
重要的是,电源连接器的数据端口不包含 USB 电源线。这样可以确保计算机电源与 Pi 电源之间的电压差不会造成不必要的电流回流。
故障排除
确认驱动是否正常工作
如果 Key Mime Pi 无法将按键转发到目标机器,第一步是确定 USB gadget 是否正常工作。
尝试运行以下命令:
echo -ne "\0\0\xb\0\0\0\0\0" > /dev/hidg0 && \
echo -ne "\0\0\xc\0\0\0\0\0" > /dev/hidg0 && \
echo -ne "\0\0\0\0\0\0\0\0" > /dev/hidg0如果一切正常,你应该能在通过 USB 连接到 Pi 的那台机器上看到以下输出:
hi写入 HID 接口时挂起
我在 Pi Zero W 上测试 Key Mime Pi 时,遇到了向 /dev/hidg0 写入会无限期挂起的情况。我换了一根 Micro-USB 转 USB-A 数据线,问题就消失了。我怀疑第一根线要么已损坏,要么只支持供电而不支持数据传输。如果你遇到向 /dev/hidg0 写入时挂起的情况,请尝试使用支持数据传输的 USB 数据线(大多数 USB 数据线都支持)。
下一步:嵌入显示输出
远程输入很有趣,但不太实用。当你向一个系统输入内容时,通常也需要看到输出。
我的下一步是捕获目标计算机的 HDMI 输出,并将其嵌入 Key Mime Pi 的 Web 界面。这样,我就能把 Pi 插入一台无头服务器,并在浏览器中使用虚拟控制台。它本质上会成为一种低成本、可自行修改的 KVM over IP 设备。
我已经使用 ffplay 和一个 HDMI extender 做出了一个可运行的原型,但仍在研究如何以低延迟的方式将所有内容放进同一个浏览器窗口。

我可以使用 HDMI extender 查看远程机器的显示器输出,但仍在研究如何将所有内容集成到浏览器中。
想要预配置套件吗?
我现在已经有了一个可运行的解决方案,可以在浏览器窗口中同时捕获目标设备的视频输出并发送按键。

详细的后续文章很快就会发布,但与此同时,你可以预订预配置的 KVM Pi 套件,其中包含制作自己的 KVM Pi 所需的一切:
源代码
Key Mime Pi 的代码完全开源,采用宽松的 MIT license:
- key-mime-pi:将按键转发到 Pi 虚拟键盘的 Web 服务器。
- ansible-role-key-mime-pi:用于配置 Pi 的 USB gadget 功能,并将 Web 服务器安装为 systemd 服务的 Ansible role。
致谢
- raspberrypisig/pizero-usb-hid-keyboard 是我找到的第一份能够在我的 Pi 上成功安装虚拟 USB HID 设备的示例代码。
- Fmstrat/diy-ipmi 为这个项目提供了灵感,并证明了让 Pi 充当 KVM over IP 是可行的。
- Rafael Medina(拉斐尔·梅迪纳)提供了我找到的关于 HID 协议最易读的说明。
- 感谢 Linux 和 Raspberry Pi OS 开发者让 USB gadget 功能成为可能。
随机一篇博客