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

Michael Lynch

Key Mime Pi:把你的 Raspberry Pi 變成遠端鍵盤

原文由 Michael Lynch 發布,訂閱此部落格

近期版本的 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 裝置:

# 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 服務的形式安裝到你的裝置上:

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 驅動程式。它讓使用者模式的應用程式可以像 USB 裝置一樣與作業系統互動。

key-mime-pi 設定腳本會在 /dev/hidg0 建立一個檔案路徑。任何程式都可以對這個路徑進行讀寫,而作業系統會將資料轉譯為鍵盤訊號。

要模擬鍵盤,Pi 必須依照USB HID 規格與作業系統溝通。那份文件長達 97 頁,充滿按鍵代碼與表格,讀起來有點吃力,但實際上鍵盤用的協定非常簡單。

每按一次鍵,鍵盤就會送出一個 8 位元組的訊息,稱為「report」。

位元組索引用途
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 個位元組全為 0 的區塊表示目前沒有任何按鍵被按下。

上面的範例一次只送出一個按鍵,但 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. 透過 USB 傳輸線與 Pi 連接的電腦會將其視為鍵盤輸入,進而在螢幕上顯示字元。

供電問題

在我的測試中,電腦的 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 的網頁介面中。這樣我就能把 Pi 接到無螢幕的伺服器上,並在瀏覽器中擁有一個虛擬主控台。本質上,這會是一台低成本、可自行改造的KVM over IP 裝置

我已經用ffplay和一個HDMI 延長器做出了一個可運作的原型,但我仍在研究如何將所有功能整合到單一瀏覽器視窗中,並保持低延遲。

顯示遠端機器畫面的 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 服務的 Ansible role。

致謝

  • raspberrypisig/pizero-usb-hid-keyboard是我找到的第一個能在我的 Pi 上成功安裝虛擬 USB HID 裝置的範例程式碼。
  • Fmstrat/diy-ipmi是這個專案的靈感來源,也證明了讓 Pi 作為 KVM over IP 來運作是可行的。
  • Rafael Medina提供了我找到最易讀的 HID 協定說明。
  • 感謝讓 USB gadget 功能得以實現的 Linux 與 Raspberry Pi OS 開發者。

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

留言