TinyPilot: Build a KVM Over IP for Under $100

Michael Lynch

TinyPilot:不到 100 美元自製 KVM over IP

TinyPilot 是我打造的平價、開源裝置,用於遠端控制電腦。它在作業系統開機前就能運作,因此我用 TinyPilot 在我的裸機家用實驗室伺服器上安裝新的作業系統並除錯開機故障。

本文將詳細分享我打造 TinyPilot 的經驗,並示範如何使用 Raspberry Pi 以不到 100 美元的成本自製一台。

TinyPilot 連接兩台電腦的照片

透過 Microsoft Surface 上的 Chrome 使用 TinyPilot 控制我的 Ubuntu 筆電

不想聽你的長篇故事;直接告訴我怎麼做

如果你是想跳過我精彩的奮鬥與挫折故事的掃興鬼,請直接跳至「如何打造你專屬的 TinyPilot」一節。

示範

為什麼要做 TinyPilot?

幾年前,我為了測試軟體自行組裝了一台家用伺服器。這是一項很有價值的投資,我每天都在使用它。

我的家用實驗室虛擬機伺服器照片

我在 2017 年打造、用來代管虛擬機的家用實驗室伺服器

這台伺服器沒有接鍵盤或螢幕,因為我是透過 ssh 或網頁介面存取。這樣的設定很方便,但也會讓小問題變得非常棘手。

每隔幾個月,我總會搞砸一些設定,導致伺服器無法開機或連上網路,等於把自己鎖在門外。為了讓它重新運作,我得拔掉所有線材,把伺服器搬到桌邊,手忙腳亂地把伺服器接到桌機的鍵盤和螢幕上。

市售解決方案

朋友們對他們使用 iDRAC 的經驗讚不絕口。這是 Dell 伺服器內建的晶片,能在系統通電後立即提供虛擬主控台。我曾考慮為下一台家用伺服器採用 iDRAC,但高昂的價格很快就讓我打消念頭。光是授權就要 300 美元,還需要昂貴的專屬硬體。

300 美元的 iDRAC 9 Enterprise 授權價格截圖

Dell iDRAC 技術的授權每台機器要價 300 美元,另需加上硬體費用

接著,我研究了市售的 KVM over IP(網路 KVM)解決方案。它們的功能類似 Dell 的 iDRAC,但屬於外接裝置,需連接至電腦的鍵盤、影像與滑鼠埠(因此稱為 KVM)。可惜價格更高,每台要價 500 至 1000 美元。

Raritan Dominion KVM over IP 購買頁面截圖

市售 KVM over IP 裝置的價格介於 500 至 1,000 美元之間。

雖然我懶得搬伺服器,但也無法說服自己花 500 美元,只為了每年省下幾次插拔線材的麻煩。

於是,我做了任何一個夠不理性的程式設計師都會做的事:花上數百小時自製一台 KVM over IP。

使用 Raspberry Pi 打造 KVM over IP

Raspberry Pi 是一款體積小巧、價格便宜的單板電腦。這些裝置的效能足以執行完整的桌面作業系統,價格僅 30 至 60 美元,因此深受嗜好者與程式設計師的喜愛。

我手掌上的 Raspberry Pi

Raspberry Pi 是一台功能完整的電腦,體積僅有一張晶片大小,價格只要 30 至 60 美元。

新版的 Pi 支援 USB on-the-go(USB 隨身模式),可讓 Pi 模擬鍵盤、隨身碟和麥克風等 USB 裝置。

作為 Pi-as-KVM 概念的驗證,我建立了一個名為 Key Mime Pi 的簡易網頁應用程式。

Key Mime Pi 網頁介面截圖

Key Mime Pi,我早期開發、僅支援鍵盤轉發的 TinyPilot 前身。

Key Mime Pi 透過 USB 連接至另一台電腦,並註冊為 USB 鍵盤。它同時提供一個網頁並監聽 JavaScript 按鍵事件。當使用者輸入時,Key Mime Pi 會擷取按鍵事件,並透過其虛擬 USB 鍵盤轉譯為按鍵輸入。這會讓按鍵輸入出現在目標電腦上。我在先前的文章中詳細介紹了這個運作方式。

擷取影像的挑戰

如果看不到螢幕上的畫面,鍵盤轉發就沒什麼用。下一步很明顯,就是要找到方法在 Pi 上擷取伺服器的顯示輸出,並在瀏覽器中呈現。

我第一次嘗試擷取影像時,使用了Lenkeng LKV373A HDMI 延長器。Daniel Kučera(丹尼爾·庫切拉)(又名 danman)在逆向工程這款裝置方面做得非常出色。它在 eBay 上由中國賣家販售,價格約 40 美元,因此看起來是我的最佳選擇。

Lenkeng LKV373A HDMI 延長器照片

Lenkeng LKV373A HDMI 延長器是我首次嘗試的 HDMI 影像擷取方案。

擷取影像之所以棘手,是因為 LKV373A 發射器本身並非影像擷取裝置。它的原本用途是與 LKV373A 接收器配對,將網路串流轉回 HDMI 輸出。在 danman 的研究中,他發現了一種攔截並擷取影像串流的方法,但 LKV373A 使用的是非標準的 RTP 協定變體,很少有影音工具能理解。

幸好,danman 向 ffmpeg 貢獻了一個修補程式來處理 LKV377A 古怪的行為,因此我得以使用 ffmpeg 的播放器來呈現串流:

ffplay -i udp://239.255.42.42:5004
使用 ffplay 呈現來自 LKV373A 的影像串流截圖

使用 ffplay 呈現來自 LKV373A 的影像串流

就在這裡,我第一次嚐到了貫穿整個專案的難題:延遲。目標電腦與我桌機上的影像播放之間有將近一秒的延遲。

Lenkeng LKV373A HDMI 延長器照片

在任何重新編碼之前,LKV373A 就引入了 838 毫秒的延遲。

我嘗試調整 ffplay 的眾多命令列參數來加速串流,但始終無法低於 800 毫秒。而且這還是在配備高階 GPU 與 CPU 的桌機上。對於我那台小巧的 Raspberry Pi 來說,效能前景並不樂觀。

幸好,我完全是因緣際會找到了更好的解決方案。

HDMI 轉 USB 轉接器

在無意識地滑 Twitter 時,我偶然看到一則 Arsenio Dev(阿塞尼奧·戴夫)發的推文,提到他剛買了一個低價的 HDMI 轉 USB 轉接器:

Rufus 截圖

阿塞尼奧·戴夫的一則推文讓我發現了更好的影像擷取方案。

以 1080p 解析度、每秒 30 幀擷取影像聽起來有點好得不像真的,所以我從 eBay 下單了一個。含運費只要 11 美元。我甚至不知道該怎麼稱呼它——它沒有品牌名稱,所以我就稱它為「HDMI 轉接器」。雖然有好幾種外觀變體,但其實都是同一個 MacroSilicon MS2109 晶片換上不同外殼而已。

eBay 上售價 11.20 美元的 HDMI 截圖

eBay 上售價 11.20 美元、免運費的 HDMI 轉 USB 轉接器

幾天後裝置到貨,讓我大為驚艷。完全不需任何調整,只要插上 Raspberry Pi,它立刻就顯示為 UVC 影像擷取裝置。

$ sudo v4l2-ctl --list-devices
bcm2835-codec-decode (platform:bcm2835-codec):
        /dev/video10
        /dev/video11
        /dev/video12

UVC Camera (534d:2109): USB Vid (usb-0000:01:00.0-1.4): <<< HDMI capture dongle
        /dev/video0
        /dev/video1

幾分鐘內,我就能擷取並重新串流 HDMI 影像:

# On the Pi
ffmpeg \
  -re \
  -f v4l2 \
  -i /dev/video0 \
  -vcodec libx264 \
  -f mpegts udp://10.0.0.100:1234/stream

# On my Windows desktop
ffplay.exe -i udp://@10.0.0.100:1234/stream

它也實在太方便了。LKV373A 體積幾乎像塊磚,還需要獨立的電源和網路線。HDMI 轉接器則只有隨身碟大小,只需要一個 USB 埠就行。

Lenkeng LKV373A 與 HDMI 轉接器的對比

Lenkeng LKV373A HDMI 延長器(左)比 HDMI 轉接器(右)更大,且需要更多連接線。

唯一的問題還是延遲。Pi 重新廣播的影像串流落後來源電腦 7 至 10 秒。

Lenkeng LKV373A 與 HDMI 轉接器的對比

使用 ffmpeg 從我的 Pi 串流影像時,影像延遲高達 10 秒。

我不確定這個延遲是來自轉接器本身、Pi 上的 ffmpeg,還是桌機上的 ffplay。阿塞尼奧·戴夫回報的延遲只有 20 毫秒,因此看來如果深入研究 ffmpeg 那些艱澀又神秘的命令列參數,應該能達到更快的效能。

又一次幸運讓我免於那個痛苦的任務。

借鏡類似的專案

當我發表上一篇關於 Key Mime Pi 的部落格文章後,收到了來自 Max Devaev(馬克斯·德瓦耶夫)的留言,他鼓勵我去看看他的專案 Pi-KVM

馬克斯的留言:Hi:) Take a look at this project: https://github.com/pikvm/pikvm We have already done and debugged many things

馬克斯·德瓦耶夫向我推薦了他現有的 Pi-KVM 專案。

GPIO 接腳

我先前使用麵包板的經驗,就是不小心把它們熔掉了

我曾短暫看過 Pi-KVM,但它需要麵包板和焊接的要求把我嚇跑了。

在馬克斯的建議下,我重新仔細研究了 Pi-KVM,特別想了解他如何解決影像延遲問題。我注意到他是透過一款名為 uStreamer 的工具來擷取影像。

:經與馬克斯進一步討論後,我得知 Pi-KVM 其實也支援不需焊接或麵包板的組裝方式。

uStreamer:超快的影像串流工具

你是否曾找到一款好用到連你沒預料到的問題都一併解決的工具?

uStreamer 一開箱就將我的延遲從 8 秒降至 500 至 600 毫秒。但它還省去了整串額外的工作。

使用 uStreamer 與 HDMI 轉接器時的 500 毫秒延遲

uStreamer 將我的延遲降低了 15 倍。

在使用 uStreamer 之前,我不確定該如何將 ffmpeg 的影像送入使用者的瀏覽器,但我知道某種程度上是可行的。我測試了這份大致正確的教學,內容是透過 HLS 將影像從 ffmpeg 導向 nginx,但它反而增加了更多延遲。而且仍有未解決的問題,例如如何在 HDMI 線材連接與拔除時啟動與停止串流,以及如何將影像轉為瀏覽器友善的格式。

uStreamer 解決了所有這些問題。它運行自己的極簡 HTTP 伺服器,以 Motion JPEG(動態 JPEG) 提供影像,這是瀏覽器原生支援的格式。我不再需要處理 HLS 串流,也不必讓 ffmpeg 和 nginx 互相溝通。

這個工具功能如此完整,讓我以為馬克斯只是從更成熟的專案分支出來,但我錯了。這個狂人為了從 Pi 硬體中榨出最大效能,竟然用 C 語言自己寫了一套影像編碼器。我立刻向馬克斯捐款,也邀請所有使用他軟體的人一同響應。

進一步降低影像延遲

uStreamer 將我的延遲從 10 秒降至約 600 毫秒。這已是巨大的進步,但仍有明顯的延遲。我告訴馬克斯,如果他能找到提升效能的方法,我有意願進一步資助 uStreamer,於是我們開始聊了起來。

馬克斯對我使用的 HDMI 轉接器很感興趣,因為他從未見過這款裝置。他邀請我使用 tmate 建立一個共享的 shell 連線,讓他能遠端存取我的 Pi。

馬克斯透過 tmate 主動提供協助的對話截圖

馬克斯表示要嘛幫我改善延遲,要嘛栽贓讓我犯下聯邦重罪。幸好,他最後選擇了前者。

在我的硬體上測試 uStreamer 的運作幾分鐘後,馬克斯執行了 v4l2-ctl 公用程式,看到一行讓他眼睛一亮、卻讓我完全摸不著頭緒的資訊:

$ sudo v4l2-ctl --all
Driver Info:
        Driver name      : uvcvideo
        Card type        : UVC Camera (534d:2109): USB Vid
...
Format Video Capture:
        Width/Height      : 1280/720
        Pixel Format      : 'MJPG' (Motion-JPEG)
...
Streaming Parameters Video Capture:
        Capabilities     : timeperframe
        Frames per second: 30.000 (30/1)

HDMI 轉接器竟是以 Motion JPEG 格式傳送影像串流!uStreamer 的硬體加速編碼雖然很快,但完全是多餘的,因為現代瀏覽器本來就能原生播放 Motion JPEG。

我們將 uStreamer 設定為跳過重新編碼,直接原樣轉送影像串流。

省去重新編碼步驟後呈現 200 毫秒延遲的照片

在 Pi 上省去額外的重新編碼步驟後,延遲從 600 毫秒降至 200 毫秒。

延遲從 600 毫秒一路降至 200 毫秒。雖然不是即時,但低到使用幾分鐘後就會忘記它的存在。

TinyPilot 實際運作

還記得本文一開始我說想要 TinyPilot,以便在無螢幕的虛擬機伺服器開機前就能存取它嗎?它真的做到了,而且我現在就在這麼用!

我在 Key Mime Pi 的基礎上改良,打造了整合影像擷取功能的新網頁介面:

今年我組裝了一台新的無螢幕虛擬機伺服器,並使用 TinyPilot 安裝了 Proxmox,這是一套用於管理虛擬機的開源 Hypervisor 與網頁介面。

TinyPilot 讓我能直接在瀏覽器中完成整個安裝過程。這絕對比我過去搬電腦、來回插拔線材的方式愉快多了。

如何打造你專屬的 TinyPilot

零件清單

安裝 Raspberry Pi OS Lite

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

Rufus 截圖

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

在 microSD 的 boot 分割區放置一個名為 ssh 的檔案即可啟用 SSH 存取。若要透過無線網路連線,還需要一個 wpa_supplicant.conf 檔案

準備好 microSD 記憶卡後,將其插入 Pi 裝置。

安裝外殼(選配)

Raspberry Pi 4 以發熱量大而聞名。沒有散熱也能運作,但時間一久很可能會遇到穩定性問題。

我喜歡這款極簡外殼,因為它價格便宜,且能被動為 Pi 散熱,不需要額外供電風扇的複雜設計:

Raspberry Pi 極簡鋁製外殼

這款極簡鋁製外殼能在不需風扇的情況下為 Pi 提供良好的散熱。

透過 USB 連接至目標機器

為了讓 TinyPilot 能作為虛擬鍵盤運作,請將 Pi 的 USB-C 埠連接至目標機器上的 USB-A 埠:

連接至 Raspberry Pi 的 USB 連接線連接至目標電腦的 USB 連接線

使用 USB-C 對 USB-A 傳輸線,將 USB-C 端接至 Pi 的 USB-C 埠,USB-A 端接至目標電腦。

:請優先使用 USB 3.0 埠,因為它們能為 Pi 提供更多電力。

安裝 HDMI 擷取轉接器

要完成硬體組裝,請將 HDMI 轉接器插入 Pi 的其中一個 USB 埠。接著將 HDMI 傳輸線接上轉接器,另一端插入目標電腦的顯示輸出埠。

連接至 Raspberry Pi 的 HDMI 輸入連接來自目標電腦的 HDMI 輸出連接

將目標電腦的顯示輸出連接至 HDMI 轉接器,並將其插入 Pi 的 USB 埠。

:若要連接的電腦沒有 HDMI 輸出,應該可以使用 DisplayPort 轉 HDMI 傳輸線DVI 轉 HDMI 傳輸線,不過我本人尚未測試過。

連接網路線

若要透過有線區域網路連接 Pi,請將網路線接至 Pi 的乙太網路埠:

連接至 Pi 裝置的網路線照片

將網路線連接至你的 Pi。

:若你已如上所述透過新增 wpa_supplicant.conf 檔案設定無線存取,則可略過此步驟。

安裝 TinyPilot 軟體

以 SSH 連線至你的 Pi 裝置(Raspberry Pi OS 的預設帳號密碼為 pi / raspberry),並執行以下指令:

curl -sS https://raw.githubusercontent.com/tiny-pilot/tinypilot/master/quick-install \
  | bash -
sudo reboot

如果你對將網路上的隨機腳本直接導向 shell 抱持合理的懷疑,我鼓勵你先檢視原始碼

這個腳本會以我的 TinyPilot Ansible 角色啟動一個自給自足的 Ansible 環境。它會安裝四個在每次開機時自動執行的服務:

  • nginx:一款熱門的開源網頁伺服器
  • ustreamer:一套輕量的 HTTP 影像串流伺服器
  • usb-gadget:一個啟用 Pi「USB gadget 模式」的腳本,可讓 Pi 模擬 USB 裝置
  • tinypilot:我為 TinyPilot 建立的網頁介面

使用 TinyPilot

執行安裝腳本後,你可以透過以下位址存取 TinyPilot:

TinyPilot 網頁介面截圖

設定完成後,你可以在區域網路上透過 http://raspberrypi/ 存取 TinyPilot 的網頁介面。

供電問題

這個設定最大的限制是供電。依賴目標電腦供電意味著當目標關機時,Pi 會遭遇非預期的斷電。

此外,Pi 4 需要 3 安培才能穩定運作,雖然在較低功率下也能跑。電腦的 USB 3.0 埠僅提供 0.9 安培,USB 2.0 更只有 0.5 安培,這就是為什麼你可能會在 Pi 的系統日誌中看到這些警告:

 $ sudo journalctl -xe | grep "Under-voltage"
Jun 28 06:23:15 tinypilot kernel: Under-voltage detected! (0x00050005)

為了解決這個問題,我與一家工程公司合作打造了一塊客製化電路板,將 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

原始碼

TinyPilot 的軟體以寬鬆的 MIT 授權開源:

  • tinypilot:TinyPilot 的網頁介面與後端。
  • ansible-role-tinypilot:用於將 TinyPilot 及其相依套件安裝為 systemd 服務的 Ansible 角色。

現成的 TinyPilot 裝置

現成的 TinyPilot 裝置可在 TinyPilot 網站購買。我是 TinyPilot 這家公司的原始創辦人,但在 2024 年 4 月已將公司出售,現在與該公司已無任何關係,僅是一名熱情的使用者。


特別感謝馬克斯·德瓦耶夫在 uStreamer 上的傑出貢獻以及他對 TinyPilot 的付出。

原文由 Michael Lynch 發布

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