Key Mime Pi:Raspberry Piをリモートキーボードに変える
原文は Michael Lynch により に公開されました。 このブログを購読する
最近のRaspberry PiはUSB On-The-Go(USB OTG)に対応しており、キーボードやUSBメモリ、マイクなどのUSBデバイスになりすますことができます。これを活用するため、私はPiを仮想キーボードに変えるオープンソースのウェブアプリを作りました。名前はKey Mime Piです。
この記事では、Key Mime Piの仕組みと、自分で作る方法を紹介します。
デモ
必要なもの
- USB OTGに対応したRaspberry Pi:
- Raspberry Pi 4(全モデル)
- Raspberry Pi Zero W
- Raspberry Pi AおよびA+ (要検証)
- こちらの情報源では初期のPiもUSB OTGをサポートしているとされていますが、私自身はこれらの機種で検証していません。
- Raspberry Pi OS(旧称Raspbian)
- Stretch以降
- USBケーブル
- Pi 4用:USB-C - USB-A(オス/オス)
- Pi Zero W用:Micro-USB - USB-A(オス/オス)
Raspberry Pi OS Liteをインストールする
まずは、microSDカードにRaspberry Pi OS Lite(旧称Raspbian)をインストールします。

私はPi用のmicroSDカードの書き込みにRufusを使っていますが、ディスク全体を書き込めるツールなら何でも構いません。
microSDカードのbootパーティションにsshという名前のファイルを置くことでSSHアクセスを有効化し、microSDカードをPi本体に挿入します。無線で接続する場合は、bootパーティションにwpa_supplicant.confファイルを作成する必要もあります。
Piを接続する
USBケーブルをPiのUSB OTGポートに接続します。Pi 4ではUSB-Cポート、Pi Zeroでは「USB」と表示されたMicro-USBポートがそれにあたります。


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のインストール方法は2つあります。外部ツールを必要としない、昔ながらのbashを使う方法と、ちょっと気の利いた方法として、私のお気に入りのオープンソース構成管理ツールであるAnsibleを使う方法です。
方法1:純粋に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 rebootPiが再起動するのを待ってから、再度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方法2:Ansibleでインストールする
Ansibleを使っている場合は、私のKey Mime Pi Ansibleロールを使うとより自動化できます。以下のコマンドで、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 sudoKey Mime Piの使い方
インストールスクリプトを実行すると、Key Mime Piは以下で利用できるようになります:
インターフェースはこのように表示されます:

入力待ち状態のKey Mime Piウェブインターフェース
そして魔法のように、ブラウザで入力すると、そのキーがPiに接続されたマシンに表示されます。

Key Mime Piを使えば、ブラウザ経由でリモートコンピューターにキーストロークを送信できます。
仕組み
USBデバイスエミュレーション
ここでの本当の肝は、LinuxのUSB Human Interface Device(HID)ガジェットドライバです。これにより、ユーザーモードのアプリケーションがあたかもUSBデバイスであるかのようにOSとやり取りできるようになります。
key-mime-pi設定スクリプトは/dev/hidg0にファイルパスを作成します。どのプログラムでもこのパスに対して読み書きができ、OSがデータをキーボード信号に変換します。
キーボードになりすますために、PiはUSB HID仕様に従ってOSと通信する必要があります。97ページにわたるキーコードと表が並ぶその仕様書は少々骨が折れますが、キーボード用のプロトコル自体は驚くほどシンプルです。
キーストロークごとに、キーボードは「レポート」と呼ばれる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バイトすべてがゼロのブロックは、どのキーも押されていないことを示します。
上の例では1回に1キーずつ送信していましたが、HIDレポートには6キー分の領域があります。つまり、異なるキーであれば、1つのメッセージで最大6キーまで同時に送信できるということです:
echo -ne "\0\0\x1a\x0b\x04\x17\x18\x13" > /dev/hidg0 && \
echo -ne "\0\0\0\0\0\0\0\0" > /dev/hidg0whatupJavaScriptからHIDへの変換
ブラウザウィンドウで入力すると、JavaScriptがキーストロークごとにイベントを生成します。keycode.infoというサイトでは、この動きを実際に確認できる優れたデモが提供されています。
JavaScriptのキーイベントにはキーコードが含まれますが、これはHIDのキーコードとは別物です。幸い、両者はほぼ1対1で対応しています。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, # dKey Mime PiサーバーはブラウザからのJavaScriptキーコードイベントを待ち受け、それをHIDコードに変換してから、PiのHIDインターフェースである/dev/hidg0に送信します。
全体の流れは次のとおりです:
- ユーザーがブラウザでキーを押します。
- ページ上のJavaScriptがJavaScriptキーコードをPi上のKey Mime Piサーバーに送信します。
- Key Mime PiサーバーがJavaScriptキーコードを対応するHIDコードに変換します。
- Key Mime PiサーバーがHIDコードを
/dev/hidg0のUSBガジェットインターフェースに送信します。 - 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 W | 5 V / 1.2 A |
| Pi 4 | 5 V / 3.0 A |
一方、標準的なUSBポートの供給電力は次のとおりで、要件に届きません:
| USBポートの種類 | 供給電力 |
|---|---|
| USB 2.0 | 5 V / 0.5 A |
| USB 3.0 | 5 V / 0.9 A |
現在もこの問題の解決策を模索中です。以下は、まだ試していない考えられる解決策です:
Ethernetポートから電力を取るためにPoE HATを使うAC - microUSBアダプタを接続するためにZero2Go Power Adaptorを使う
追記:この問題を解決するために、私はエンジニアリング会社と協力して、PiのUSB-Cポートを2つに分岐させるカスタム基板を作成しました。1つ目のポートはUSB電源入力用で、Piに3Aのフル電力を供給できます。2つ目はUSBデータ出力用で、Piは引き続きUSBキーボードとして振る舞うことができます。


TinyPilot Power Connectorを使えば、USB OTG機能を失うことなく、USB-Cポート経由でPiに3Aの電力を供給できます。
重要なことに、この電源コネクタのデータポートにはUSBの電源ラインが含まれていません。これにより、コンピューター側の電源とPi側の電源の電圧差によって望ましくない電力の逆流が起こるのを防ぎます。
トラブルシューティング
ドライバが正常に動作しているか確認する
Key Mime Piがキーストロークをターゲットマシンに転送できない場合、まずはUSBガジェットが正常に動作しているかどうかを確認します。
次のコマンドを試してみてください:
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すべてが正常に動作していれば、PiがUSBで接続されているマシンに次の出力が表示されるはずです:
hiHIDインターフェースへの書き込みがハングする
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エクステンダーを使った動作するプロトタイプはすでにありますが、すべてを低遅延で単一のブラウザウィンドウに収めるソリューションをまだ開発中です。

HDMIエクステンダーを使えばリモートマシンのモニター出力を表示できますが、すべてをブラウザに統合する作業は進行中です。
設定済みキットが欲しいですか?
現在、ターゲットデバイスの映像出力をキャプチャしつつ、ブラウザウィンドウ内でキーストロークも送信できるソリューションが完成しました。

詳細な続報は近日公開予定ですが、それまでの間、独自のKVM Piを構築するために必要なものがすべて含まれた設定済みKVM Piキットを予約注文できます:
ソースコード
Key Mime Piのコードは、寛容なMITライセンスの下で完全にオープンソースとして公開されています:
- key-mime-pi:キーストロークをPiの仮想キーボードに転送するウェブサーバー。
- ansible-role-key-mime-pi:PiのUSBガジェット機能を設定し、ウェブサーバーをsystemdサービスとしてインストールするためのAnsibleロール。
謝辞
- raspberrypisig/pizero-usb-hid-keyboardは、私のPiで仮想USB HIDデバイスのインストールに初めて成功したサンプルコードでした。
- Fmstrat/diy-ipmiはこのプロジェクトの着想元であり、PiをKVM over IPとして機能させることが可能であることを証明してくれました。
- Rafael Medina氏は、私が見つけた中で最も分かりやすいHIDプロトコルの解説を提供してくれました。
- LinuxおよびRaspberry Pi OSの開発者の皆さんに感謝します。USBガジェット機能を実現してくれました。
記事をランダムに読む
コメント
ログインしてコメントする