概要
- 前編 では、Raspberry Pi Zero 2 W を USB OTG ガジェットモード(HIDキーボード) として認識させ、gRPC + protobuf によってリモート(ネット経由)から文字列を受け取り、ターゲットPC へキーボード入力を注入するシステムを構築しました。
- 本記事 (前編+続編の統合版) では、さらに「マウス操作」も同じUSBガジェットに追加し、キーボード+マウス の複合HIDとして Target PC に見せる仕組みを紹介します。
- これにより、gRPC呼び出しで「キー入力」だけでなく「マウス移動・クリック」もターゲットPCに送れる強力なリモート制御が可能になります。
1. 全体構成イメージ
-
Raspberry Pi Zero 2 W
- USB OTGガジェット:キーボード+マウス(HID) を合成
- gRPCサーバ:
SendText()
/SendMouse()
RPC でキー操作&マウス操作リクエストを受け取り -
/dev/hidg0
(キーボード用) と/dev/hidg1
(マウス用) に書き込む
-
ターゲットPC
- Pi Zero 2 W が「キーボード&マウスを備えたUSBデバイス」として認識される
- 受け取ったキー入力やマウス移動がOS上で反映される
-
クライアント (Windows / Linux / Mac など)
- ネットワーク経由で Pi に gRPC リクエストを送る
- たとえば Python 版
stub.SendText(...)
/stub.SendMouse(...)
ファイル構成例(フォルダmy_hid_project
):
my_hid_project/
├─ keyboard.proto // gRPC定義 (SendText & SendMouse)
├─ server.py // Pi側サーバ (キーボード+マウス両対応)
├─ client.py // クライアント (キーボード+マウス両対応)
├─ keyboard_report.desc // HIDキーボードのレポートデスクリプタ (ブートプロトコル等)
├─ mouse_report.desc // HIDマウスのレポートデスクリプタ
└─ ... (その他設定ファイル等)
2. USBガジェット設定(キーボード+マウス)
2-1. config.txt / cmdline.txt
-
/boot/config.txt
に:dtoverlay=dwc2
-
/boot/cmdline.txt
のrootwait
のすぐ後に:modules-load=dwc2,libcomposite
- 再起動後、
dwc2
&libcomposite
が有効になり、PiをUSBデバイスモードで設定できる。
2-2. 複合HIDスクリプト例
sudo modprobe libcomposite
cd /sys/kernel/config/usb_gadget
sudo mkdir myhid
cd myhid
# ベンダーID/プロダクトIDなど共通情報
echo 0x1d6b > idVendor
echo 0x0104 > idProduct
echo 0x0200 > bcdUSB
echo 0x0100 > bcdDevice
# 文字列記述子
mkdir strings/0x409
echo "1234567890" > strings/0x409/serialnumber
echo "MyPiVendor" > strings/0x409/manufacturer
echo "PiKeyboardMouse" > strings/0x409/product
# config
mkdir configs/c.1
mkdir configs/c.1/strings/0x409
echo "Config 1: Keyboard+Mouse" > configs/c.1/strings/0x409/configuration
echo 120 > configs/c.1/MaxPower
# 1) キーボード (hid.usb0)
mkdir functions/hid.usb0
echo 1 > functions/hid.usb0/protocol # 1=キーボード
echo 1 > functions/hid.usb0/subclass
echo 8 > functions/hid.usb0/report_length
cat /home/pi/keyboard_report.desc > functions/hid.usb0/report_desc
ln -s functions/hid.usb0 configs/c.1/
# 2) マウス (hid.usb1)
mkdir functions/hid.usb1
echo 2 > functions/hid.usb1/protocol # 2=マウス
echo 1 > functions/hid.usb1/subclass
echo 4 > functions/hid.usb1/report_length
cat /home/pi/mouse_report.desc > functions/hid.usb1/report_desc
ln -s functions/hid.usb1 configs/c.1/
# バインド
ls /sys/class/udc
echo "20200000.usb" > UDC
-
keyboard_report.desc
:キーボード用レポートデスクリプタ(ブートプロトコル8バイト等) -
mouse_report.desc
:マウス用(4バイト程度、[buttons, x, y, wheel]) - 実行後、USBケーブルでターゲットPCに繋ぐと「キーボード & マウス」の複合デバイスとして認識される。
3. .proto ファイル (keyboard.proto)
3-1. 前編の「SendText」+続編の「SendMouse」両方
syntax = "proto3";
package hid;
service KeyboardService {
// 1) キーボード入力
rpc SendText(TextMessage) returns (Empty);
// 2) マウス操作
rpc SendMouse(MouseMessage) returns (Empty);
}
message TextMessage {
string text = 1;
}
message MouseMessage {
// 移動量 (相対), 左ボタン押下/離し
int32 dx = 1;
int32 dy = 2;
bool leftDown = 3;
bool leftUp = 4;
// 必要に応じて右クリック, 中クリック, ホイールなど拡張
}
message Empty {}
ポイント:
- 前編で使った
SendText
/TextMessage
はそのまま。 -
MouseMessage
とSendMouse()
を追加して、一つのサービスKeyboardService
で両方対応。
4. サーバプログラム (server.py)
4-1. 前編でのキーボード部
-
/dev/hidg0
への書き込み関数(ASCIIをキーコードに変換 → ファイルにバイナリ出力) -
SendText(TextMessage)
RPC
4-2. 続編でマウスを追加
-
/dev/hidg1
を使う -
SendMouse(MouseMessage)
RPC
4-3. 統合した最終例
import grpc
from concurrent import futures
import keyboard_pb2
import keyboard_pb2_grpc
# HID デバイスファイル
HID_KEYBOARD = "/dev/hidg0"
HID_MOUSE = "/dev/hidg1"
def send_text_as_keys(text):
# 前編: テキストをキーボード入力に変換
with open(HID_KEYBOARD, "wb") as f:
# 例: 8バイトレポート [modifier, ..., keycodes...]
# ASCII→キーコード変換をここに書く
pass
def send_mouse(dx, dy, leftDown, leftUp):
# 4バイト [buttons, x, y, wheel=0]
buttons = 0
if leftDown:
buttons |= 0x01 # 左ボタン(ビット0)
# dx, dy は ±127 程度にクリップ
def clamp(v):
return max(-127, min(127, v))
bx = clamp(dx) & 0xFF
by = clamp(dy) & 0xFF
report = bytes([buttons, bx, by, 0x00])
with open(HID_MOUSE, "wb") as f:
f.write(report)
class KeyboardServiceServicer(keyboard_pb2_grpc.KeyboardServiceServicer):
def SendText(self, request, context):
send_text_as_keys(request.text)
return keyboard_pb2.Empty()
def SendMouse(self, request, context):
send_mouse(request.dx, request.dy, request.leftDown, request.leftUp)
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("Server started on port 50051")
server.wait_for_termination()
if __name__ == "__main__":
serve()
挿入ポイント:
-
send_text_as_keys()
(前編) → そのままコピーし、このファイルのトップレベルに追加。 -
send_mouse()
(新規) をその下に配置。 -
KeyboardServiceServicer
の中にSendText()
(前編) とSendMouse()
(今回追加) を並べて実装する。
5. クライアントプログラム (client.py)
5-1. 前編のキーボード送信
import grpc
import keyboard_pb2
import keyboard_pb2_grpc
def main():
channel = grpc.insecure_channel('192.168.x.y:50051')
stub = keyboard_pb2_grpc.KeyboardServiceStub(channel)
# キーボード入力
stub.SendText(keyboard_pb2.TextMessage(text="Hello from client!"))
if __name__ == "__main__":
main()
5-2. 続編のマウス送信を追加
def main():
channel = grpc.insecure_channel('192.168.x.y:50051')
stub = keyboard_pb2_grpc.KeyboardServiceStub(channel)
# 1) キーボード
stub.SendText(keyboard_pb2.TextMessage(text="Hello from client!"))
# 2) マウス例: 右方向へ50px移動 + 左ボタン押し下げ
stub.SendMouse(keyboard_pb2.MouseMessage(dx=50, dy=0, leftDown=True, leftUp=False))
# ボタン離し
stub.SendMouse(keyboard_pb2.MouseMessage(dx=0, dy=0, leftDown=False, leftUp=True))
if __name__ == "__main__":
main()
挿入ポイント:
- 前編のまま
SendText(...)
呼び出しを残しておき、 -
続けて
SendMouse(...)
呼び出しを追加すればOK。
6. 注意点
-
マウスのレポートデスクリプタ
-
mouse_report.desc
の中身とsend_mouse()
のレポート構造を一致させる。 - 例えば 4バイト
[buttons, X, Y, wheel]
なら、デスクリプタも同じ順序で定義。
-
-
クリックのタイミング
-
leftDown=True, leftUp=False
→ ボタン押しっぱなし - すぐ後に
leftDown=False, leftUp=True
で離す - ドラッグ操作をする場合は、押しっぱなし状態で複数の移動 RPC を発行など工夫が必要。
-
-
連続移動
- 大量の RPC 呼び出しはネットワーク負荷が高いかもしれないので、適度にインターバルを入れるなど調整が必要。
-
ターゲットPC 側の認識
- Pi Zero 2 W は 1つのUSBポートから「Keyboard + Mouse」HIDデバイスを出すため、正常に合成デバイスとして認識されるかチェック。
- Windows / Linux / MacOS など大抵は問題なく認識するが、特殊OSだと複合HIDが怪しいケースも。
7. まとめ
- 前編の「SendText」(キーボード) と、続編の「SendMouse」(マウス) を同じprotoファイル&同じサーバプログラムにまとめることで、キーボード+マウス複合HIDをターゲットPCに見せられる。
-
複合HIDガジェットをlibcompositeで設定し、
hid.usb0
(keyboard) とhid.usb1
(mouse) を同時にconfigs/c.1
に紐付けるだけ。 -
gRPC で受信した「キー入力」「マウス移動」要求を Pi が
/dev/hidg0
//dev/hidg1
に書き込む → Target PC にリアルタイムで操作が注入される。
これで、**ネット経由でPiに指示し、Piを介してターゲットPCをリモート操作(キー&マウス)**する環境が完成します。ぜひ応用してみてください!
参考リンク
- 前編: Raspberry Pi Zero 2 W + gRPC + Protobuf でキーボード操作を送る
- Linux USB Gadget - 複合HID設定 (libcomposite)
- Protocol Buffers + gRPC (Python)
- USB HIDレポートの例 (マウス/キーボード)
以上