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 裝置。為了善用這項功能,我製作了一個開源網頁應用程式,能將我的 Pi 變成虛擬鍵盤。我稱它為 Key Mime Pi

這篇文章將示範 Key Mime Pi 的運作方式,以及如何自行打造一個。

示範

所需材料

安裝 Raspberry Pi OS Lite

首先,請在 microSD 卡上安裝 Raspberry Pi OS Lite(前身為 Raspbian)。

Rufus 截圖

我使用 Rufus 來燒錄 Pi 的 microSD 卡,但任何整碟映像工具皆可使用。

在 microSD 卡的 boot 分割區放置一個名為 ssh 的檔案以啟用 SSH 存取,然後將 microSD 卡插入 Pi 裝置。若要透過無線網路連線,還需在 boot 分割區建立 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 來安裝。

選項一:純 bash 方式

在 bash 終端機中輸入下列指令,以連線至你的 Pi 並將其設定為 USB device emulation(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 網頁伺服器:

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

選項二:Ansible 方式

如果你是 Ansible 使用者,可以使用我的 Key Mime Pi Ansible role 來達到更好的自動化。下列指令會將 Key Mime Pi 以 systemd service(系統服務) 的形式安裝至你的裝置:

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 網頁介面截圖

等待輸入的 Key Mime Pi 網頁介面

就像變魔術一樣,當你在瀏覽器中輸入時,按鍵就會出現在與 Pi 連接的電腦上。

Key Mime Pi 從瀏覽器傳送按鍵輸入

Key Mime Pi 可讓你透過瀏覽器將按鍵輸入傳送至遠端電腦。

運作原理

USB 裝置模擬

真正的關鍵在於 Linux 的 USB Human Interface Device (HID) gadget driver(人機介面裝置模擬驅動程式)。它讓使用者模式的應用程式能像 USB 裝置一樣與作業系統互動。

key-mime-pi 設定指令碼會在 /dev/hidg0 建立一個檔案路徑。任何程式都能讀寫此路徑,作業系統則會將資料轉換為鍵盤訊號。

為了模擬鍵盤,Pi 必須依據 USB HID spec(人機介面規格) 與作業系統溝通。這份文件長達 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 代碼,然後傳送至 Pi 位於 /dev/hidg0 的 HID 介面。

以下是完整的端對端運作流程:

  1. 使用者在瀏覽器中按下按鍵。
  2. 頁面上的 JavaScript 將 JavaScript keycode 傳送至 Pi 上的 Key Mime Pi 伺服器。
  3. Key Mime Pi 伺服器將 JavaScript keycode 轉譯為對應的 HID 代碼。
  4. Key Mime Pi 伺服器將 HID 代碼傳送至位於 /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 連接埠提供的電力不足以滿足其需求。

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

目前我仍在尋找此問題的解決方案。以下是我尚未測試的一些可能解法:

更新:為了解決此問題,我與一家工程公司合作打造了一塊客製化電路板,將 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 的網頁介面。如此一來,我就能將 Pi 插上無螢幕伺服器,並在瀏覽器中擁有虛擬主控台。這基本上就是一個低成本、可自行改造的 KVM over IP(KVM 遠端管理) 裝置。

我已使用 ffplayHDMI 延伸器做出可運作的原型,但仍在研究如何將所有功能整合至單一瀏覽器視窗中,並保持低延遲。

顯示遠端電腦畫面的 Key Mime Pi 截圖

我可以使用 HDMI 延伸器檢視遠端電腦的螢幕輸出,但仍在努力將所有功能整合至瀏覽器中。

想要預先設定好的套件嗎?

更新:2020 年 6 月 29 日

我現在已有能在單一瀏覽器視窗中同時擷取目標裝置影像輸出並傳送按鍵輸入的完整解決方案。

kvmpi-bios.gif

詳細的後續文章即將發布,在此期間,你可以預購已預先設定好的 KVM Pi 套件,其中包含打造專屬 KVM Pi 所需的一切:

原始碼

Key Mime Pi 的程式碼完全以寬鬆的 MIT 授權條款開源:

  • key-mime-pi:將按鍵輸入轉送至 Pi 虛擬鍵盤的網頁伺服器。
  • ansible-role-key-mime-pi:用於設定 Pi 的 USB gadget 功能並將網頁伺服器安裝為 systemd service 的 Ansible role。

致謝

原文由 Michael Lynch 發布

本文章由 muse-spark-1.2-contributor 進行翻譯