改訂内容
前回の記事の内容に、以下を追記しました。
- OTG構成時のUDC名の違いへの対応
-
/dev/hidg0
のパーミッション問題とudev
ルール設定例 - 起動時の自動設定 (systemdサービス)
- Windows での RNDIS / macOS・Linux での ECM などの言及
- キーマッピング(Shift・記号など)についての簡単な補足
- gRPCサーバを複数クライアントから同時リクエストされる場合の注意点を追加
- 既存のキーボードのUSB属性値を調べる方法を追加
- keyboard_report.desc の作り方を追加
概要
Raspberry Pi Zero 2 W は、USB OTG (On-The-Go) 機能を通じて、他のPC(以下「ターゲットPC」)から見たときに「USBキーボード」として振る舞うことができます。
本記事では、gRPC + Protobuf を使ってネットワーク経由でテキストを送信し、そのテキストを Pi 側の仮想キーボード経由でターゲットPCに入力させる仕組みを紹介します。
「リモートから文字列を送る → ターゲットPCにキーボード入力させる」ための実装例として、Python + gRPCサーバを Pi Zero 2 W に配置し、Windowsクライアントからテキストを送信する流れを解説します。
ゴールイメージ
-
Raspberry Pi Zero 2 W(以下、Piと呼ぶ)
- USB OTG 機能を利用して「キーボード (HID)」として設定。
- 同時に gRPC サーバを起動して、ネットワーク経由で文字列を受け付ける。
-
Windows クライアント
- gRPC のクライアントプログラム(Python / C# / Go / C++ などでOK)を実行し、Pi に対してテキストを送信する。
-
ターゲットPC
- USBケーブルで Pi を接続しているPC。
- Pi は「USBキーボード」として見えており、送信されたテキストがキーボード入力として注入される。
前提
- Raspberry Pi Zero 2 W が手元にある(Zero W でも原理は同じですが、Zero 2 W のほうがCPU性能が上なので便利)。
- Pi の microSD に Raspberry Pi OS (32bit or 64bit) を導入済み。SSHやWi-Fi接続ができる状態。
- Windows クライアント側にも Python を導入し、
pip
などが使える状態(本記事のサンプルでは Python クライアントを使います)。 - Linux系コマンド(
sudo
,modprobe
,lsusb
等)にある程度なじみがあることを想定しています。
前準備: システムのアップデートとVimのインストール
まずはシステムを最新の状態にします。
sudo apt update
sudo apt upgrade -y
Vimをインストールしていない場合はインストールします。
vim-tiny がインストールされている場合は削除してから再度 Vim をインストールする。
必要に応じて .vimrc を /root にコピーする。
sudo apt remove vim-tiny
sudo apt install -y vim
sudo cp ~/.vimrc /root/.vimrc
ステップ1: Raspberry Pi Zero 2 W を「USBキーボード」にする
1-1. OTG対応の設定
config.txt の修正
sudo vi /boot/firmware/config.txt
末尾あたりにある以下の行を
dtoverlay=dwc2,dr_mode=host
以下に書き換えます。
dtoverlay=dwc2,dr_mode=peripheral
cmdline.txt の修正
sudo vi /boot/firmware/cmdline.txt
一行で長い設定が書かれていますが、rootwait
の後ろにスペースを挟んで以下を追加してください。
modules-load=dwc2,libcomposite
- 例:
... rootwait modules-load=dwc2,libcomposite quiet splash ...
- 改行は入れず、同じ行に続けて書きます。
- 保存後、再起動します。
これにより、ブート直後に dwc2
と libcomposite
がロードされ、configfs を使ったUSBガジェット設定が可能になります。
補足1:
厳密にはcmdline.txt
の同じ行であれば、どこに追記してもmodules-load=dwc2,libcomposite
は有効です。ただしrootwait
の直後に追加することで、ファイルシステムが読み込まれる前にモジュールがロードされやすくなり、より安定動作が期待できるため、本記事では「rootwait の直後」に挿入する方法を例示しています。
補足2:
起動時に/boot/firmware/cmdline.txt
でlibcomposite
がロードされるため、後述のスクリプト内でmodprobe libcomposite
を呼び出すと重複ロードになる場合があります。ただし、重複ロードしてもエラーになることはほぼなく、無害にスキップされるため、サンプルコードのようにmodprobe
しても動作には問題ありません。
補足3:
環境によっては、Raspberry Pi OS のブートパーティションが /boot/firmware ではなく /boot にマウントされる場合があります。その場合、以下のファイルのパスが変わります。
config.txt → /boot/config.txt
cmdline.txt → /boot/cmdline.txt
1-2. HIDガジェットの設定スクリプトを用意
次に、Pi がブートしたら自動で USB HID キーボード
設定を行えるようにします。
本記事では systemd サービスを使った方法をおすすめし、以下の手順を示します。
基本的に、configfs を使って USB HID ガジェットの設定を行うと、その設定はメモリ上に作成されるため、再起動すると消えてしまいます。つまり、起動の度に設定を再実行する必要があるため、systemd を使って自動実行するようにしておきます。
まずは libcomposite
と configfs
を使った設定の中身をスクリプトとしてまとめます。
例えば /usr/local/bin/setup_hid_keyboard.sh
のようなファイルを作成してください。
sudo vi /usr/local/bin/setup_hid_keyboard.sh
以下は内容の例です。
#!/bin/bash
# モジュールロード(※実際には cmdline.txt でロード済みの場合もある)
modprobe libcomposite
# configfs が自動マウントされていない場合に備え、念のためマウント(必要に応じて)
if [ ! -e /sys/kernel/config/usb_gadget ]; then
mount -t configfs none /sys/kernel/config
fi
cd /sys/kernel/config/usb_gadget/
mkdir -p mykeyboard
cd mykeyboard
# ベンダーID / プロダクトID 設定 (例)
echo 0x1d6b > idVendor
echo 0x0104 > idProduct
# USBバージョン設定 (例: USB2.0 = 0x0200)
echo 0x0200 > bcdUSB
echo 0x0100 > bcdDevice # デバイスバージョン
# 文字列記述子の設定 (英語:0x409)
mkdir -p strings/0x409
# 文字列 (シリアル/メーカー/製品名)
echo "1234567890" > strings/0x409/serialnumber
echo "MyPiVendor" > strings/0x409/manufacturer
echo "MyPiKeyboard" > strings/0x409/product
# Config ディレクトリの作成
mkdir -p configs/c.1
mkdir -p configs/c.1/strings/0x409
echo "Config 1: Keyboard" > configs/c.1/strings/0x409/configuration
echo 120 > configs/c.1/MaxPower
# HID 機能の追加 (ブートプロトコル対応キーボード)
mkdir -p functions/hid.usb0
echo 1 > functions/hid.usb0/protocol # 1=キーボード
echo 1 > functions/hid.usb0/subclass # 1=ブートインターフェース
echo 8 > functions/hid.usb0/report_length
# レポートディスクリプタ
# keyboard_report.desc は /home/pi に用意しておく
# なお、本記事では例として /home/pi に用意していますが、ご自身の環境に合わせて変更してください。
cat /home/pi/keyboard_report.desc > functions/hid.usb0/report_desc
# 上で作ったHID機能をConfigに紐づけ
ln -s functions/hid.usb0 configs/c.1/
# UDC(USB Device Controller)のバインド
UDCNAME=$(ls /sys/class/udc | head -n 1)
echo $UDCNAME > UDC
保存したら実行権限を付与します。
sudo chmod +x /usr/local/bin/setup_hid_keyboard.sh
※
UDCNAME=$(ls /sys/class/udc | head -n 1)
の部分は、実際に存在するUDCの最初のエントリを取得する方法の一例です。
Zero / Zero 2W / OSバージョンに応じて20980000.usb
や20200000.usb
など異なる場合があるため、こうして自動取得しています。
1-3 /home/pi/keyboard_report.desc の作り方
USB HID キーボードがホストに提示するレポートディスクリプタは、送信するキー入力データの形式(フォーマット)を定義します。このレポートディスクリプタは、ホスト側に対して「自分がどのようなデータを送るのか」を説明する重要な情報です。
たとえば、標準的な8バイトのキーボードレポートでは、最初の1バイトに修飾キー(Shift, Ctrl など)、2バイト目に予約領域、残りの6バイトに同時押しされているキーのスキャンコードを入れる、という構成になっています。
ここでは、標準的な8バイトキーボードレポートディスクリプタの作成方法を紹介します。
1-3.1. レポートディスクリプタの基本例
以下は、標準的な8バイトHIDキーボード用レポートディスクリプタの例です。
16進数のデータとして定義されています。
05 01 # Usage Page (Generic Desktop)
09 06 # Usage (Keyboard)
A1 01 # Collection (Application)
05 07 # Usage Page (Key Codes)
19 E0 # Usage Minimum (224) - Modifier Keys
29 E7 # Usage Maximum (231)
15 00 # Logical Minimum (0)
25 01 # Logical Maximum (1)
75 01 # Report Size (1)
95 08 # Report Count (8)
81 02 # Input (Data, Var, Abs) - Modifier keys
95 01 # Report Count (1)
75 08 # Report Size (8)
81 03 # Input (Constant) - Reserved byte
95 06 # Report Count (6)
75 08 # Report Size (8)
15 00 # Logical Minimum (0)
25 65 # Logical Maximum (101) - Key Codes
05 07 # Usage Page (Key Codes)
19 00 # Usage Minimum (0)
29 65 # Usage Maximum (101)
81 00 # Input (Data, Array) - Keys
95 05 # Report Count (5)
75 01 # Report Size (1)
05 08 # Usage Page (LEDs)
19 01 # Usage Minimum (1)
29 05 # Usage Maximum (5)
91 02 # Output (Data, Var, Abs) - LED report
95 01 # Report Count (1)
75 03 # Report Size (3)
91 03 # Output (Constant) - LED padding
C0 # End Collection
このディスクリプタは、以下の情報をホストに伝えます。
- 1バイト目: 修飾キー (Ctrl, Shift, Alt, GUI) の状態 (各1ビット)
- 2バイト目: 予約領域 (常に0)
- 3〜8バイト目: 同時に押される最大6キーのスキャンコード
- 出力部: LED 状態(NumLock, CapsLock など)の報告
1-3.2. 作成方法
方法①: コマンドを使ってバイナリファイルを直接作成
以下のコマンドを Pi 上で実行すると、バイナリ形式の keyboard_report.desc
が作成されます。
echo -ne "\x05\x01\x09\x06\xa1\x01\x05\x07\x19\xe0\x29\xe7\x15\x00\x25\x01\x75\x01\x95\x08\x81\x02\x95\x01\x75\x08\x81\x03\x95\x06\x75\x08\x15\x00\x25\x65\x05\x07\x19\x00\x29\x65\x81\x00\x95\x05\x75\x01\x05\x08\x19\x01\x29\x05\x91\x02\x95\x01\x75\x03\x91\x03\xc0" > /home/pi/keyboard_report.desc
このコマンドは、\xNN
形式の16進数データをバイナリ出力し、/home/pi/keyboard_report.desc
に保存します。
方法②: テキストエディタを利用して作成(※変換が必要)
テキストエディタで16進数の値を記述し、後で xxd
や類似ツールでバイナリに変換する方法もありますが、上記コマンドで直接作成する方法が簡単です。
1-3.3. カスタマイズ
- 標準の8バイトレポートでは、最大6キーまでの同時押しに制限されます。
- Nキーロールオーバー (NKRO) 対応 や、マウス・ゲームパッド用に変更する場合は、HID Usage Tables などを参考にレポートディスクリプタの内容を変更してください。
保存した keyboard_report.desc
は、設定スクリプト内で読み込まれ、HIDガジェットのレポートディスクリプタとして適用されます。
1-4. systemd サービスを作成して自動実行
起動時に上記スクリプトを呼び出す systemd サービスファイルを作り、
再起動しても毎回自動で HID キーボードガジェットが有効になるようにします。
- サービスファイルを作成
sudo vi /etc/systemd/system/hidgadget.service
- 内容の例:
[Unit] Description=Setup HID Gadget for USB OTG After=network.target sysinit.target local-fs.target [Service] Type=oneshot ExecStart=/usr/local/bin/setup_hid_keyboard.sh RemainAfterExit=yes [Install] WantedBy=multi-user.target
- 有効化・起動
sudo systemctl daemon-reload sudo systemctl enable hidgadget.service sudo systemctl start hidgadget.service
- 再起動テスト
再起動後、USBケーブルをターゲットPCに挿せば Pi がキーボードとして認識されるか確認してください。なお、環境によっては configfs が自動でマウントされない場合があります。もし動作に問題が出た場合は、
sudo reboot
などで、手動でマウントする必要があるかもしれません。sudo mount -t configfs none /sys/kernel/config
補足:
After=network.target
などを指定していますが、本質的には**configfsが使える状態(sysinit.targetの後)**であれば十分な場合もあります。- モジュールロードを
systemd-modules-load.service
に任せるなら、modprobe
が不要になるケースもあります。- いずれにせよ、記事中の例のままでも動作に問題なければOKです。
なお、
/etc/modules
にlibcomposite
を記載したり、rc.local
を使う方法もありますが、この様に systemd で管理するほうがモジュール依存関係や起動タイミングを整理しやすく、推奨される手法です。
/dev/hidg0
へのアクセス権限について
上記設定で作成された HID デバイスは /dev/hidg0
というファイルとして提供されます。
root
ユーザであれば直接書き込みできますが、一般ユーザで操作する場合はパーミッションの問題でエラーになることがあります。
例えば以下のような udev
ルールを作り、作成されるデバイスの権限を変更させることが可能です。
-
udev
ルールファイルの作成例:sudo vi /etc/udev/rules.d/99-hidg.rules
- 内容の例:
KERNEL=="hidg[0-9]*", MODE="0660", GROUP="plugdev"
-
hidg0, hidg1, hidg2, ...
の全デバイスに対して MODE="0660" (所有者とグループのみ読み書き可) とし、グループをplugdev
に設定する例です。 - 一般ユーザがこれを操作する場合は、そのユーザが
plugdev
グループに所属していれば書き込みが可能になります。
-
- ルール更新:
sudo udevadm control --reload-rules sudo udevadm trigger
- ユーザをグループに追加:
ログアウト/再ログインまたは再起動後に反映されます。
sudo usermod -aG plugdev <ユーザ名>
ステップ2: gRPC サーバを構築する
ここではPython + gRPC を例に手順を示します。
「ターゲットPC から見れば、Pi はキーボード」ですが、同時に Pi に Wi-Fi などネットワーク接続されている想定です。(SSHやLANが使えないと開発が難しいため)
2-1. gRPC のインストール (Python)
sudo apt-get update
sudo apt-get install python3-pip
pip3 install grpcio grpcio-tools
2-2. Protobuf 定義ファイル作成
keyboard.proto
という名前で以下を用意。
syntax = "proto3";
service KeyboardService {
rpc SendText(TextMessage) returns (Empty);
}
message TextMessage {
string text = 1;
}
message Empty {}
-
SendText
というRPCで文字列を受け取り、何も返さないイメージです。
2-3. .proto のコンパイル
同じディレクトリで以下を実行します。
python3 -m grpc_tools.protoc -I. --python_out=. --grpc_python_out=. keyboard.proto
すると keyboard_pb2.py
と keyboard_pb2_grpc.py
が生成されます。
2-4. Python サーバスクリプト
server.py
(一例)
import grpc
from concurrent import futures
import keyboard_pb2
import keyboard_pb2_grpc
import time
import threading
# 例: 複数クライアントから同時アクセスされた場合の競合を防ぐためのロック
lock = threading.Lock()
def convert_char_to_scan_code(ch):
"""
1文字を対応するHIDスキャンコード(8バイト)に変換する関数(簡易例)。
実際はa-z, A-Z, 記号, シフトキーなどの処理が必要。
"""
# 例示: 'a' -> 0x04
# 'A' の場合は Shift が必要になるが、ここでは簡易実装
if ch == 'a':
return b"\x00\x00\x04\x00\x00\x00\x00\x00" # "a"押下(修飾キーなし)
return b"\x00\x00\x00\x00\x00\x00\x00\x00"
def send_text_as_keys(text):
"""
テキストを1文字ずつHIDのキー押下+リリースとして送る。
/dev/hidg0 に書き込むとターゲットPCが「キーボード入力」として認識する。
なお複数クライアントから同時に呼ばれると混在しやすいため、
ロックを使って排他制御を行う例を示す。
"""
with lock:
with open("/dev/hidg0", "wb") as f:
for ch in text:
# キー押下
code = convert_char_to_scan_code(ch)
f.write(code)
# キーリリース (全キーオフ)
f.write(b"\x00\x00\x00\x00\x00\x00\x00\x00")
# キー入力の間隔を少しあける (10ms程度)
# 環境によっては 0.01 だと取りこぼしが起きる場合があるため
# 0.02~0.03程度にするとより安定することがあります。
time.sleep(0.01)
class KeyboardServiceServicer(keyboard_pb2_grpc.KeyboardServiceServicer):
def SendText(self, request, context):
text = request.text
print(f"Received text: {text}")
send_text_as_keys(text)
return keyboard_pb2.Empty()
def serve():
server = grpc.server(futures.ThreadPoolExecutor(max_workers=1))
keyboard_pb2_grpc.add_KeyboardServiceServicer_to_server(
KeyboardServiceServicer(), server
)
server.add_insecure_port('[::]:50051')
server.start()
print("gRPC server started at port 50051")
server.wait_for_termination()
if __name__ == "__main__":
serve()
上記を起動すると、ポート 50051
で gRPC サーバが立ち上がり、SendText
RPC の呼び出しを待機します。
受け取った文字列は send_text_as_keys
関数によって /dev/hidg0
へ書き込み、ターゲットPC 上でキー入力として再生されます。
補足: Shift・記号などの実装
例のconvert_char_to_scan_code(ch)
は非常に単純化しています。大文字("A")や記号("!"など)を入力するには、Shiftキー押下・リリースを組み合わせた複数回の書き込みが必要です。日本語キーボード配列ではさらに細かい修正が要る場合もあり、実装には注意してください。
ステップ3: Windows クライアントでテキスト送信
3-1. Windows側のセットアップ
- Python をインストールして
pip install grpcio grpcio-tools
などを行い、同様にkeyboard.proto
を用意しコンパイルしてください。 - あるいは他言語(C#/Go/Node.js など)でも同じように gRPC クライアントを実装できます。
3-2. Python クライアント例
# client.py
import grpc
import keyboard_pb2
import keyboard_pb2_grpc
def run():
# Pi Zero 2 W のIPアドレス or ホスト名を設定
# 例: ピアドレスを 192.168.1.50 と仮定
channel = grpc.insecure_channel('192.168.1.50:50051')
stub = keyboard_pb2_grpc.KeyboardServiceStub(channel)
text_msg = keyboard_pb2.TextMessage(text="hello from windows!")
response = stub.SendText(text_msg)
print("SendText RPC completed.")
if __name__ == "__main__":
run()
python client.py
を実行すると、Pi が受信し、ターゲットPC 上で「hello from windows!」の文字が入力される…という仕組みになります(実際には上のサンプルでは 'a' 以外は送れないので改良が必要です)。
ネットワーク構成のヒント
1. LAN (Wi-Fi) 経由
- Pi Zero 2 W が Wi-Fi でルーターに接続され、Windows PC も同じルーター経由で IP を取得している場合、
192.168.x.x:50051
などで通信できます。
2. USB Gadget の Ethernet 機能を併用 (RNDIS / ECM)
- libcomposite で HID と同時に ECM(Ethernet)や RNDIS を構成すると、1本のUSBケーブルだけで (1) キーボード入力 (2) ネットワーク通信 の両方が可能。
- Windows では ECM ドライバが標準でない場合があり、RNDIS ドライバを利用する構成が一般的です。
- macOS や Linux なら ECM が標準で動きやすい傾向があります。
- いずれの場合も、Pi と Windows (または他OS) が USB イーサネットとして互いに IP アドレスを振り合い、そのIP経由で gRPC 接続が可能になります。
よくある疑問と注意点
Q1. キーマッピングはどうやって実装する?
convert_char_to_scan_code(ch)
関数で、ASCII文字や記号をHIDスキャンコードに変換する必要があります。
大文字には Shift を押す、数字や記号はシフトやAltGrを押す、など複雑なので、実際は下記のような方法がおすすめです。
-
既存ライブラリやスクリプトを参考にする (例:
hid-gadget-test
にあるテーブル) - 「英字キーボードの簡易実装」にとどめ、頻出する文字だけマッピング。
- Shiftキーの押下・リリースを含めて送ることで大文字やシンボルに対応する。
- 日本語/マルチレイアウト対応をする場合はさらに大変。
簡単な例として、「A
(大文字)」を入力するときは
$$
\text{Shift(左)を押す} \to \text{0x04(a)を送る} \to \text{Shiftを離す} \to \text{aを離す}
$$
のように複数ステップになるため、コード中で都度 $ \x02 $
(左Shift) を立てるバイト列と、キーリリースのバイト列を挟む必要があります。
Q2. 文字が全部正しく入力されない・欠ける
キーを押してすぐ離す処理を連続で行うと、稀にターゲットPC側で文字の取りこぼしが起きる場合があります。
time.sleep(0.01)
や time.sleep(0.02)
のように短い遅延を適宜入れることで解消するかもしれません。
必要に応じて間隔を広げる(0.05秒など)と、より確実に入力できる場合があります。
Q3. セキュリティは大丈夫?
- Pi がネットワークから無制限にアクセス可能だと、第三者が gRPC を叩いて勝手にキーボード入力できてしまうリスクがあります。
- VPN や ファイアウォール、ローカルネットワークのみで使うなどの対策を行いましょう。
- gRPC の SSL/TLS 設定で安全に通信する方法もあります。
Q4. gRPC ではなく他のAPIでもOK?
もちろん HTTP / MQTT / WebSocket 等、他の手段でテキストを送信しても構いません。
gRPC は高速&型安全なRPCが書きやすいメリットがあるため、本記事では例示として利用しました。
Q5. gRPCサーバを同時に複数クライアントから呼ばれたら?
上記サンプルでは、1リクエストにつき send_text_as_keys()
を呼び出して /dev/hidg0
を開閉して書き込んでいます。
もし同時アクセスが起きると、
- 両方のリクエストが同時に
/dev/hidg0
に書き込む競合 - 文字入力が混じってしまう
などのリスクがあります。
対策例:
- 記事中でも示したように、スレッドロック (
threading.Lock
) を用いた排他制御でsend_text_as_keys
を直列化する - リクエストをキューに入れて順番に処理するワーカーを作る
- そもそも複数同時リクエストが来ない運用を想定する
Q6. Vendor ID (VID)/ Product ID (PID) の扱い
記事内では
echo 0x1d6b > idVendor # Linux Foundation の例
echo 0x0104 > idProduct
という設定例を挙げていますが、Linux FoundationのVIDを無断で商用利用することは本来推奨されません。学習・趣味程度であれば問題になりにくいですが、もし将来的に量産や公共向け製品にする際は独自のVID/PIDを取得またはライセンス契約などを検討してください。
Q7. 既存のキーボードのUSB属性値を使いたい
動作や互換性の検証目的で、実際のキーボードと同じVID/PID/文字列を真似したい場合は、以下の "既存のキーボードのUSB属性値を調べる方法" の様に、lsusb -v
などで取得した情報を echo 0x1234 > idVendor
のように設定すれば、ホスト側でほぼ同一のデバイス名として認識されます。
ただし、他社製品のVID/PIDを流用することは商標・ライセンス上の問題をはらむ場合があるので注意しましょう。
既存のキーボードのUSB属性値を調べる方法(参考)
動作や互換性の検証目的で、あるキーボード挿したときと同じ情報で認識させたいという場合は、以下のような手順で該当キーボードの Vendor ID / Product ID / シリアル番号 / Manufacturer / Product などを取得します。
ここでは Linux環境を想定し、lsusb
コマンドを用いた例を紹介します。(Windows では「USBDeview」「USB Device Viewer」等のツールを利用しても可)
-
対象のキーボードをPCに接続
例として、LinuxマシンにUSBで挿します。 -
lsusb コマンドで一覧を確認
lsusb
実行すると、例えば以下のような表示があるかもしれません:
Bus 001 Device 005: ID 1234:5678 ExampleCorp ExampleKeyboard
-
ID 1234:5678
がVendorID:ProductID
(16進数) -
ExampleCorp ExampleKeyboard
はlsusb
が持っている簡易情報
-
-
詳細情報を確認
sudo lsusb -d 1234:5678 -v
すると、iManufacturer, iProduct, iSerial などが表示され、さらに Report Descriptor: の項目も確認できます。
例:
iManufacturer 1 ExampleCorp iProduct 2 ExampleKeyboard iSerial 3 ABCD12345 ...
ここに表示されている文字列 (Manufacturer, Product, SerialNumber) や、
idVendor (0x1234)
,idProduct (0x5678)
などがわかります。 -
上記情報を Pi の USB Gadget設定でそっくり設定
この記事の「ステップ1」でecho 0x1d6b > idVendor
としている箇所を、取得した Vendor ID (0x1234
等) に、
文字列設定部分(echo "MyPiVendor" > strings/0x409/manufacturer
など ) を「ExampleCorp」「ExampleKeyboard」などに書き換えればOKです。 -
シリアル番号も同様に変更
echo "1234567890" > strings/0x409/serialnumber
としている箇所を、実際のキーボードのiSerial
と同じ値にすれば、ホストPCから見てほぼ同一デバイス名として認識されます。 -
レポートディスクリプタの取得と保存
表示された「Report Descriptor:」の16進数データをコピーし、テキストエディタでreport_descriptor.txt
として保存します。
次に、以下のコマンドでバイナリに変換し、keyboard_report.desc
として保存します。xxd -r -p report_descriptor.txt > /home/pi/keyboard_report.desc
これにより、既存のキーボードと全く同じレポートディスクリプタを得ることもできます。
注意:
- 他社製キーボードの完全クローンを作ることは、動作や互換性の検証以外の目的では推奨されません。
- 同じVendor IDを無断で使うと衝突や混乱が起きる可能性もあります。
- あくまで趣味・研究目的で自己責任で行ってください。
以上の手順で得られる情報を、この記事の USB Gadget 設定手順に反映すれば、まったく同じVendorID / ProductID / 文字列情報を持つHIDデバイスをPi上でエミュレートできます。
まとめ
- Raspberry Pi Zero 2 W の USB OTG 機能を利用し、ターゲットPC に対して「USBキーボード」として振る舞う設定を行いました。
- システム起動時に自動でこの設定を有効化する方法として、systemd サービスを使う手順を紹介しました。
-
gRPC + Protobuf を使ってネットワーク越し(Wi-Fi / USB Ethernet)でテキストをやり取りし、Pi 側で受信したテキストを
/dev/hidg0
へ書き込むことで物理キーボード入力相当を実現しました。 - キーマップ対応(Shiftや記号など) や 複数リクエストの排他制御, 複合ガジェット構成 (RNDIS/ECM), セキュリティなど、運用用途に応じて検討すべき要素があります。
ネット経由で文字列を受け取り、それをターゲットPC 上に“打鍵”として送る装置としては、非常に柔軟な仕組みとなるはずです。ぜひ応用してみてください。
参考リンク
- Raspberry Pi 公式ドキュメント - USB OTG / Gadgetモード
- Linux USB Gadget - ConfigFS Documentation (kernel.org)
- gRPC (Python)
- hid-gadget-test (Linux)
- USB HID Usage Tables
以上