Key Mime Pi: Turn Your Raspberry Pi into a Remote Keyboard

Michael Lynch

Key Mime Pi:把你的 Raspberry Pi 变成远程键盘

近期版本的 Raspberry Pi 支持 USB on-the-go(USB OTG,USB 即时通信功能),因此可以模拟键盘、U 盘和麦克风等 USB 设备。为了利用这一功能,我制作了一个开源 Web 应用,把我的 Pi 变成一块虚拟键盘。我把它称为 Key Mime Pi

本文将演示 Key Mime Pi 的工作方式,以及如何亲手制作一个。

演示

你需要准备的东西

安装 Raspberry Pi OS Lite

首先,将Raspberry Pi OS Lite(原名 Raspbian)安装到 microSD 卡上。

Rufus 截图

我使用 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 端口。

数据线插入 USB-C 端口的 Pi 4数据线插入 Micro-USB 数据端口的 Pi Zero W

对于 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 界面截图

等待输入的 Key Mime Pi Web 界面

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

Key Mime 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/hidg0
whatup

将 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,  # d

Key Mime Pi 服务器监听浏览器发来的 JavaScript keycode 事件,将其转换为 HID code,然后发送到 Pi 的 HID 接口 /dev/hidg0

整个过程如下:

  1. 用户在浏览器中按下一个键。
  2. 页面中的 JavaScript 将 JavaScript keycode 发送到 Pi 上的 Key Mime Pi 服务器。
  3. Key Mime Pi 服务器将 JavaScript keycode 转换为对应的 HID code。
  4. Key Mime Pi 服务器将 HID code 发送到 /dev/hidg0 中的 USB gadget 接口。
  5. 连接到 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 W5 V / 1.2 A
Pi 45 V / 3.0 A

标准 USB 端口达不到这一要求:

USB 端口类型输出功率
USB 2.05 V / 0.5 A
USB 3.05 V / 0.9 A

我目前仍在寻找这个问题的解决方案。以下是一些我尚未测试的可能方案:

  • 使用 PoE HAT 从以太网端口取电
  • 使用 Zero2Go Power Adaptor 连接 AC 转 microUSB 适配器。

更新:为了解决这个问题,我与一家工程公司合作,制作了一块定制电路板,将 Pi 的 USB-C 端口一分为二。第一个端口接收 USB 电源,因此仍然可以向 Pi 提供完整的 3 安培电流。第二个端口接收 USB 数据输出,因此 Pi 仍然可以模拟 USB 键盘。

电源连接器特写连接到 Raspberry Pi 和 microUSB 数据线的电源连接器

TinyPilot Power Connector 允许 Pi 通过 USB-C 端口获得 3 安培电力,同时不丧失 USB OTG 功能。

重要的是,电源连接器的数据端口不包含 USB 电源线。这样可以确保计算机电源与 Pi 电源之间的电压差不会造成不必要的电流回流。

注意:如果没有合适的连接器,在 Pi 连接到计算机的情况下从外部电源为其供电,可能会损坏硬件。更多详情请参阅 TinyPilot wiki

故障排除

确认驱动是否正常工作

如果 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 做出了一个可运行的原型,但仍在研究如何以低延迟的方式将所有内容放进同一个浏览器窗口。

显示远程机器屏幕的 Key Mime Pi 截图

我可以使用 HDMI extender 查看远程机器的显示器输出,但仍在研究如何将所有内容集成到浏览器中。

想要预配置套件吗?

更新:2020 年 6 月 29 日

我现在已经有了一个可运行的解决方案,可以在浏览器窗口中同时捕获目标设备的视频输出并发送按键。

kvmpi-bios.gif

详细的后续文章很快就会发布,但与此同时,你可以预订预配置的 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。

致谢

原文由 Michael Lynch 发布

本文章由 openai/gpt-5.6-luna 进行翻译