TinyPilot:100ドル以下で作るKVM over IP
TinyPilotは、コンピューターを遠隔操作するための安価なオープンソースデバイスです。OSが起動する前から動作するので、ベアメタルで運用している自宅ラボのサーバーで、新しいOSのインストールや起動トラブルのデバッグに使っています。
この記事では、TinyPilotを作った経験を詳しく紹介し、Raspberry Piを使って100ドル以下で自作する方法を解説します。

Microsoft SurfaceのChromeからTinyPilotでUbuntuノートPCを操作しているところ
前置きはいいから作り方だけ教えてくれ
私の波瀾万丈で面白い開発秘話を読み飛ばしたいというせっかちな方は、「How to Build Your Own TinyPilot(自作TinyPilotの作り方)」のセクションまで直接ジャンプしてください。
デモ
なぜTinyPilotなのか?
数年前、ソフトウェアのテスト用に自宅サーバーを自作しました。価値ある投資で、毎日活用しています。

2017年に仮想マシン用に自作した自宅ラボのサーバー
このサーバーにはキーボードもモニターもつないでいません。sshやWebインターフェース経由でアクセスしているからです。普段は便利な構成ですが、ちょっとしたトラブルが途方もない手間につながることもあります。
数か月に一度は何かやらかして、サーバーが起動しなくなったりネットワークに接続できなくなったりして、完全に締め出されてしまいます。復旧するには、配線をすべて外し、サーバーを机まで運び、デスクのキーボードやモニターにつなぎ替えるためにケーブルをあれこれ付け替える必要があります。
市販の解決策
友人たちがDellサーバーに搭載されているiDRACの素晴らしさを熱く語ってくれました。電源を入れた瞬間から仮想コンソールを提供するチップです。次の自宅サーバー用にiDRACも一瞬考えましたが、あまりの高価格にすぐに諦めました。ライセンスだけで1台あたり300ドル、さらに高価な専用ハードウェアも必要です。

DellのiDRAC技術のライセンスは1台あたり300ドル、ハードウェア代は別途かかります
次に市販のKVM over IP製品を調べました。DellのiDRACと似た機能を提供しますが、コンピューターのキーボード・映像・マウス端子に接続する外付けデバイスです(KVMという名前の由来です)。残念ながらさらに高価で、1台あたり500〜1,000ドルします。

市販のKVM over IPデバイスは500〜1,000ドルします。
サーバーを運ぶのが面倒とはいえ、年に数回ケーブルを繋ぎ替える手間を省くために500ドルも出す気にはなれませんでした。
そこで、筋金入りの非合理的なプログラマーなら誰もがすること、つまり数百時間をかけて自作のKVM over IPを作ることにしました。
Raspberry PiでKVM over IPを作る
Raspberry Piは、小型で安価なシングルボードコンピューターです。フル機能のデスクトップOSが動くほどの性能を持ちながら、30〜60ドルという価格で、愛好家やプログラマーの間で人気のツールとなっています。

Raspberry Piは1枚の基板に収まるフル機能のコンピューターで、価格はわずか30〜60ドルです。
最近のPiはUSB On-The-Go(USB OTG)に対応しており、キーボードやUSBメモリ、マイクなどのUSBデバイスになりすますことができます。
PiをKVMとして使うアイデアの概念実証として、Key Mime PiというシンプルなWebアプリを作りました。

Key Mime Piは、キーボード転送のみに対応したTinyPilotの初期の前身です。
Key Mime PiはUSB経由で別のコンピューターに接続し、USBキーボードとして認識されます。同時にWebページを表示し、JavaScriptのキーイベントを待ち受けます。ユーザーが入力すると、Key Mime Piがキーイベントを捕捉し、仮想USBキーボード経由でキーストロークに変換します。これにより、入力がターゲットコンピューターに現れます。この仕組みについては以前の記事で詳しく解説しています。
映像キャプチャの課題
画面に何が表示されているか見えなければ、キーボード転送だけではあまり役に立ちません。次に考えるべき明白なステップは、サーバーの映像出力をPiでキャプチャしてブラウザに表示する方法を見つけることでした。
映像キャプチャの最初の試みでは、Lenkeng LKV373A HDMIエクステンダーを使いました。Daniel Kučera氏(別名danman)がこのデバイスを素晴らしくリバースエンジニアリングしてくれていました。中国の業者がeBayで約40ドルで販売していたので、最良の選択肢に思えました。

Lenkeng LKV373A HDMIエクステンダーは、HDMI映像キャプチャの最初の試みで使いました。
映像のキャプチャは一筋縄ではいきませんでした。LKV373Aのトランスミッターは本来ビデオキャプチャデバイスではなく、ネットワークストリームを再びHDMI出力に変換するレシーバーとペアで使うことを想定した製品だからです。danman氏の調査により映像ストリームを傍受してキャプチャする方法が見つかりましたが、LKV373AはRTPプロトコルの非標準な亜種を喋るため、対応している映像ツールがほとんどありませんでした。
幸い、danman氏がffmpegにパッチを提供し、LKV373Aの風変わりな挙動に対応できるようになっていたので、ffmpegの動画プレーヤーでストリームを再生できました。
ffplay -i udp://239.255.42.42:5004
ffplayでLKV373Aからの映像ストリームを再生しているところ
ここで、このプロジェクトを通じてずっと付きまとうことになる問題、すなわち遅延を初めて経験しました。ターゲットコンピューターとデスクトップでの映像再生の間に、ほぼ1秒の遅延があったのです。

LKV373Aは再エンコードする前から838ミリ秒の遅延を発生させていました。
ffplayの多彩なコマンドラインフラグをあれこれ試してストリームを高速化しようとしましたが、800ミリ秒を切ることはできませんでした。しかもそれはハイエンドなGPUとCPUを積んだデスクトップでの話です。非力なRaspberry Piでは性能的に期待できそうにありませんでした。
幸い、まったくの偶然からより良い解決策を見つけました。
HDMI - USBドングル
ぼんやりTwitterをスクロールしていたとき、たまたまArsenio Dev氏によるツイートで、彼が購入したばかりの低価格なHDMI - USBドングルを目にしました。

より良い映像キャプチャの解決策を教えてくれたArsenio Dev氏のツイート。
1080p解像度で30フレーム/秒の映像キャプチャというのは出来すぎているように思えましたが、eBayで注文してみました。送料込みでわずか11ドルでした。正式な名前すら分かりません。ブランド名がないので、ここでは単に「HDMIドングル」と呼ぶことにします。いくつかバリエーションがありますが、中身はすべて同じMacroSilicon MS2109チップを異なる筐体に入れただけです。

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はレンガほどの大きさで、専用の電源とEthernetケーブルが必要でした。一方HDMIドングルはUSBメモリほどの小ささで、USBポートに挿すだけで済みます。

Lenkeng LKV373A HDMIエクステンダー(左)は、HDMIドングル(右)より大きく、より多くの配線が必要でした。
唯一の問題は、またしても遅延でした。Piが再配信する映像ストリームは、元のコンピューターより7〜10秒も遅れていました。

ffmpegを使ってPiから映像をストリーミングすると、最大10秒の遅延が発生しました。
この遅延がドングル自体に起因するのか、Pi上のffmpegなのか、デスクトップのffplayなのかは分かりませんでした。Arsenio Dev氏は遅延が20ミリ秒だと報告していたので、ffmpegの難解で謎めいたコマンドラインフラグを深掘りすれば高速化できる可能性はありそうでした。
またしても幸運が、その面倒な作業から私を救ってくれました。
類似プロジェクトからの借用
以前のKey Mime Piについてのブログ記事を公開したとき、Max Devaev氏からコメントをもらい、彼のプロジェクトであるPi-KVMをチェックするよう勧められました。

既存のPi-KVMプロジェクトを教えてくれたMax Devaev氏。

ブレッドボードに関する私のこれまでの経験といえば、うっかり溶かしてしまったことくらいです。
Pi-KVMは以前ちらっと見たことがありましたが、ブレッドボードやはんだ付けが必要という要件に尻込みしていました。
Max氏に勧められ、Pi-KVMをもう一度見てみました。特に映像遅延の問題をどう解決したかに興味がありました。そこで、uStreamerというツールで映像をキャプチャしていることに気づきました。
注:その後Max氏と話して、Pi-KVMははんだ付けやブレッドボードなしでも構築できることを知りました。
uStreamer:超高速な映像ストリーマー
まだ気づいてもいなかった問題まで解決してくれるほど優れたツールに出会ったことはありますか?
uStreamerは導入するだけで遅延を8秒から500〜600ミリ秒に短縮してくれました。しかも、一連の余計な作業も不要にしてくれたのです。

uStreamerによって遅延は15分の1になりました。
uStreamer以前は、ffmpegからの映像をどうやってユーザーのブラウザに届けるか分からず、なんとか可能だということだけは分かっていました。ほぼ正確なチュートリアルを試してffmpegからnginxへHLSで映像を流してみましたが、さらに遅延が増えました。しかも、HDMIケーブルの抜き差しに応じてストリーミングを開始・停止する方法や、映像をブラウザが扱える形式に変換する方法といった問題も残ったままでした。
uStreamerはこれらすべてを解決してくれました。独自の最小限のHTTPサーバーを動かし、ブラウザがネイティブに再生できるMotion JPEG形式で映像を配信するのです。HLSストリームや、ffmpegとnginxを連携させる手間も不要になりました。
このツールはあまりにも高機能だったので、Max氏が成熟した既存プロジェクトをフォークしたのだろうと思い込んでいましたが、間違いでした。この驚くべき人物は、Piのハードウェアから最大限の性能を引き出すために、なんとC言語で独自のビデオエンコーダーを書き上げたのです。私はすぐにMax氏に寄付しました。このソフトウェアを使うすべての人にも、ぜひ同様の支援をお願いします。
映像遅延の改善
uStreamerによって遅延は10秒から約600ミリ秒まで縮まりました。大きな前進でしたが、まだはっきり分かる遅延が残っていました。性能を改善する方法が見つかるならuStreamerにさらに資金を提供したいとMax氏に伝えたところ、話が弾みました。
私が使っているHDMIドングルは見たことがないものだったので、Max氏は興味を示しました。tmateを使って共有シェルセッションを作り、Piにリモートアクセスさせてほしいと頼まれました。

Max氏は遅延改善を手伝うか、私を連邦犯罪で陥れるかのどちらかを申し出てくれました。幸い、実際には前者をしてくれました。
私のハードウェアでuStreamerがどう動くかを数分間テストした後、Max氏が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を、再エンコードをスキップして映像ストリームをそのままパススルーするように設定しました。

Piでの余計な再エンコードを省いたことで、遅延は600ミリ秒から200ミリ秒まで縮まりました。
遅延は600ミリ秒から一気に200ミリ秒まで下がりました。瞬時というわけではありませんが、数分使えば遅延を忘れられる程度には低くなりました。
TinyPilotの動作
この記事の冒頭で、起動前のヘッドレスVMサーバーにアクセスするためにTinyPilotが欲しいと言ったのを覚えていますか?ちゃんと動いていますし、実際にそうやって使っています!
Key Mime Piを発展させ、映像キャプチャ機能を統合した新しいWebインターフェースを作りました。

今年、新しいヘッドレスVMサーバーを組み立て、TinyPilotを使ってProxmox(VMを管理するためのオープンソースのハイパーバイザー兼Webインターフェース)をインストールしました。
TinyPilotのおかげで、インストール作業のすべてをブラウザから管理できました。コンピューターを運び回してケーブルを繋ぎ替えていた以前のやり方より、はるかに快適でした。
自分でTinyPilotを作る方法
必要な部品
- Raspberry Pi 4(すべてのバリエーションで動作します)
- USB-C - USB-Aケーブル(オス/オス)
- HDMI - USBキャプチャドングル
- 不思議なことに、これらにはブランド名がありませんが、見た目で見分けられます。
- 通常eBayで11〜15ドルで入手できます。
- microSDカード(Class 10、8GB以上)
- HDMI - HDMIケーブル
- またはターゲットマシンの映像出力に応じて、[その他] - HDMIケーブル
- (任意)USB-C OTG分岐コネクタ
- 別途USB-A - microUSBケーブル2本と3アンペアの電源アダプタが必要です。
- (任意)冷却ケース、ヒートシンク、またはファン
- PiのGPIOピンにアクセスできるケースを選んでください。
- 私はこのミニマルなパッシブ冷却ケースを使っています。
Raspberry Pi OS Liteをインストールする
まず、microSDカードにRaspberry Pi OS Lite(旧称Raspbian)をインストールします。

Pi用のmicroSDカードの書き込みにはRufusを使っていますが、ディスク全体を書き込むツールなら何でも構いません。
microSDカードのbootパーティションにsshという名前のファイルを置いて、SSHアクセスを有効にします。無線で接続する場合は、wpa_supplicant.confファイルも必要です。
microSDカードの準備ができたら、Pi本体に挿入します。
ケースを取り付ける(任意)
Raspberry Pi 4は発熱が大きいことで知られています。冷却なしでも動作はしますが、時間が経つと安定性の問題が出る可能性があります。
私はこのミニマルなケースが気に入っています。安価で、電源を必要とするファンの複雑さなしにPiをパッシブ冷却できるからです。

このミニマルなアルミケースは、ファンの複雑さなしにPiをしっかり冷却します。
USBでマシンに接続する
TinyPilotを仮想キーボードとして機能させるため、PiのUSB-CポートをターゲットマシンのUSB-Aポートに接続します。


USB-C - USB-Aケーブルを使い、USB-C側をPiのUSB-Cポートに、USB-A側をターゲットコンピューターに接続します。
注:Piにより多くの電力を供給できるため、USB 3.0ポートの使用を推奨します。
HDMIキャプチャドングルを取り付ける
物理的な組み立てを完了するには、HDMIドングルをPiのUSBポートのいずれかに挿します。次に、HDMIケーブルをドングルに接続し、もう一方の端をターゲットコンピューターの映像出力に挿します。


ターゲットコンピューターの映像出力をHDMIドングルに接続し、PiのUSBポートに挿します。
注:接続するコンピューターにHDMI出力がない場合は、DisplayPort - HDMIケーブルやDVI - HDMIケーブルでも使えるはずですが、私自身は試していません。
Ethernetケーブルを接続する
Piに有線LANで接続する場合は、ネットワークケーブルをPiのEthernetポートに接続します。

EthernetケーブルをPiに接続します。
注:上記の手順でwpa_supplicant.confファイルを追加して無線アクセスを設定した場合は、この手順をスキップできます。
TinyPilotソフトウェアをインストールする
PiデバイスにSSHで接続し(Raspberry Pi OSのデフォルトの認証情報はpi / raspberryです)、次のコマンドを実行します。
curl -sS https://raw.githubusercontent.com/tiny-pilot/tinypilot/master/quick-install \
| bash -
sudo reboot得体の知れないWeb上のスクリプトをシェルにパイプすることに当然の疑念を抱く方は、ぜひソースを確認してください。
このスクリプトは、私のTinyPilot用Ansibleロールを使って自己完結型のAnsible環境をブートストラップします。起動のたびに実行される4つのサービスをインストールします。
- nginx:人気のオープンソースWebサーバー
- ustreamer:軽量なHTTP映像ストリーミングサーバー
- usb-gadget:Piの「USBガジェットモード」を有効にするスクリプトで、PiがUSBデバイスになりすますことを可能にします
- tinypilot:私がTinyPilot用に作成したWebインターフェース
TinyPilotの使い方
インストールスクリプトを実行すると、TinyPilotは次のアドレスで利用できるようになります。

セットアップが完了すると、ローカルネットワーク上のhttp://raspberrypi/でTinyPilotのWebインターフェースにアクセスできます。
電源の問題
この構成の最大の制限は電源です。ターゲットコンピューターからの給電に頼っているため、ターゲットがシャットダウンすると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ポートを2つに分岐させるカスタム基板を作りました。一方のポートはUSB電源を受け付けるので、Piにフルで3アンペアを供給できます。もう一方はUSBデータ出力を担うので、Piは引き続きUSBキーボードになりすますことができます。


TinyPilot Power Connectorを使えば、USB OTG機能を失うことなく、PiのUSB-Cポート経由で3アンペアの電源を供給できます。
重要なことに、この電源コネクタのデータポートにはUSB電源ラインが含まれていません。これにより、コンピューター側の電源とPi側の電源の電圧差によって望ましくない逆流が起こるのを防ぎます。
注:適切なコネクタなしでPiに外部電源から給電しながらコンピューターに接続すると、ハードウェアを損傷する恐れがあります。詳細はTinyPilotのWikiをご覧ください。
ソースコード
TinyPilotのソフトウェアは、寛容なMITライセンスのもとでオープンソースとして公開されています。
- tinypilot:TinyPilotのWebインターフェースとバックエンド。
- ansible-role-tinypilot:TinyPilotとその依存関係をsystemdサービスとしてインストールするためのAnsibleロール。
完成済みTinyPilotデバイス
完成済みのTinyPilotデバイスはTinyPilotのWebサイトで購入できます。私はTinyPilotという会社の当初の創業者ですが、2024年4月に事業を売却し、現在は熱心なユーザーである以外、会社との関係はありません。
素晴らしいuStreamerの開発とTinyPilotへの貢献をしてくれたMax Devaev氏に心から感謝します。
記事をランダムに読む