post-cover

本記事で紹介するプログラムは以下の GitHub リポジトリで公開しています

GitHub - isec-promotion/blog-mouse

はじめに

前回の記事では、Raspberry Pi Zero 2 W を USB マウスとして認識させ、LAN 経由から PC のマウス操作を行う方法について紹介しました。

今回は、さらに一歩進めて、2 台の Raspberry Pi Zero 2 W を使用し、1 台を送信機としてキーボード信号を送信し、もう 1 台を受信機としてその信号を受け取り、PC のマウスカーソルを動かす方法を解説します。

raspberry-pi-usb-hid-2-001

このシステムを利用することで、キーボード操作を通じて遠隔で PC の操作が可能になります。

関連記事

余談

当初は、マウス操作の信号をそのまま送信機から受信機へ送り、遠隔でマウスカーソルを操作することを目指していました。


しかし、マウスから発信される信号量が多く、少し動かしただけでプログラムが強制終了することが頻発しました。何度も試行錯誤を重ねたのですが、動作を安定させることが難しく、最終的にキーボードの信号を使用する方法に切り替えることにしました。


もし、マウス操作の信号を安定して送受信する方法をご存知の方がいらっしゃいましたら、ぜひご教示いただけると幸いです。


さて、今回の記事では、送信機側の Raspberry Pi Zero 2 W に接続したキーボードの信号を受信機に送り、その信号に基づいてマウスカーソルを操作する方法についてご紹介します。

受信機の設定

受信機は USB マウスとして認識される必要があります。

設定方法については、前回の記事をご確認ください。

受信側のプログラム

以下のプログラムを作成して、受信機として動作する Raspberry Pi 上で実行します。

このプログラムは送信機から送られてくるキーボード信号を受け取り、それに応じてマウスカーソルを移動させたり、クリック操作を行ったりします。

nano keyboard-rx.py
# keyboard-rx.pyimport socketimport jsonimport sysimport termiosimport ttyHOST = '0.0.0.0'PORT = 65432# HIDファイルのパスMOUSE_DEV = "/dev/hidg0"# 初期設定movement_amount = 10min_movement = 1max_movement = 127# キーボード入力を非同期で取得するための設定def get_key():    fd = sys.stdin.fileno()    old_settings = termios.tcgetattr(fd)    try:        tty.setraw(sys.stdin.fileno())        ch = sys.stdin.read(1)    finally:        termios.tcsetattr(fd, termios.TCSADRAIN, old_settings)    return ch# マウスの移動やクリックを送信する関数def move_mouse(dx, dy, buttons=0):    report = bytearray([buttons, dx & 0xFF, dy & 0xFF])    with open(MOUSE_DEV, "wb") as f:        f.write(report)# ソケットサーバーでの接続待機with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:    s.bind((HOST, PORT))    s.listen()    print('Waiting for a connection...')    conn, addr = s.accept()    with conn:        print(f'Connected by {addr}')        buffer = ""        while True:            data = conn.recv(1024)            if not data:                break            buffer += data.decode('utf-8')            while True:                try:                    # JSONオブジェクトの境界を探す                    obj, index = json.JSONDecoder().raw_decode(buffer)                    buffer = buffer[index:].lstrip()  # バッファから処理済み部分を削除                    if obj['type'] == 1:  # EV_KEY                        if obj['value'] in (1, 2):  # キーが押された、または押し続けられている                            if obj['code'] == 103:  # 上方向キー                                move_mouse(0, -movement_amount)                            elif obj['code'] == 108:  # 下方向キー                                move_mouse(0, movement_amount)                            elif obj['code'] == 105:  # 左方向キー                                move_mouse(-movement_amount, 0)                            elif obj['code'] == 106:  # 右方向キー                                move_mouse(movement_amount, 0)                            elif obj['code'] == 44:  # Zキー                                movement_amount = max(min_movement, movement_amount - 4)                            elif obj['code'] == 45:  # Xキー                                movement_amount = min(max_movement, movement_amount + 4)                            elif obj['code'] == 30:  # Aキーで左クリック                                move_mouse(0, 0, buttons=1)                            elif obj['code'] == 31:  # Sキーで右クリック                                move_mouse(0, 0, buttons=2)                            elif obj['code'] == 16:  # Qキー                                print("Q key pressed. Exiting...")                                conn.close()                                s.close()                                exit()                        elif obj['value'] == 0:  # キーが離されたとき、クリックを解除                            if obj['code'] in (30, 31):  # AキーまたはSキー                                move_mouse(0, 0, buttons=0)                    print(f"Received: {obj}")                except json.JSONDecodeError:                    breakprint("終了しました。")

プログラムの実行

以下のコマンドでプログラムを実行します。

sudo python3 keyboard-rx.py

実行すると、以下のように送信機からの接続待ち状態になります。

Waiting for a connection...

この状態で送信機が接続されると、受信機は送信機から送られたキーボード信号を受け取り、対応するマウス操作を PC 上で実行します。

送信機の設定

送信機として使用する Raspberry Pi には、USB キーボードを接続します。このキーボードからの信号を受信機に送信することで、遠隔での操作が可能になります。

送信側のプログラム

以下のプログラムを作成して、送信機として動作する Raspberry Pi 上で実行します。

このプログラムは、接続されたキーボードの信号を読み取り、それを受信機に送信します。

送信側のプログラム

nano keyboard-tx.py
# keyboard-tx.pyimport evdevimport socketimport json# 送信先のホスト名とポート番号HOST = 'zero-2.local'  # 受信側のRaspberry Piのホスト名またはIPアドレスPORT = 65432# デバイスのevent番号を指定(キーボードのイベントデバイス)device = evdev.InputDevice('/dev/input/event0')# ソケットを作成し接続with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:    try:        s.connect((HOST, PORT))        print(f"Connected to {HOST}:{PORT}")        # キーボードイベントの読み取りと送信        for event in device.read_loop():            if event.type == evdev.ecodes.EV_KEY:                # データの送信 (JSON + 改行)                data = json.dumps({'type': event.type, 'code': event.code, 'value': event.value})                s.sendall((data + '\n').encode('utf-8'))                print(f"Sent: {data}")  # 送信したデータを表示    except BrokenPipeError:        print("Connection lost. Exiting.")    except Exception as e:        print(f"An error occurred: {e}")

プログラムの実行

以下のコマンドでプログラムを実行します。

nano keyboard-tx.py

プログラムが正常に動作すると、送信機は接続されたキーボードの入力を検知し、その信号を受信機に送信します。

受信機側でこの信号を受け取り、対応するマウス操作が実行されます。

SNSでシェアしよう

この記事が役に立ったら、ぜひシェアしてください