0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

Raspberry Piでキーボードエミュレーションをしてみる

Posted at

最近Windows不信が募ってきて、RTX 3050が載っているマウスDAIVをゲーム用に持っているのに、ついついPS5を買ってしまいました。

やるのは主に原神なわけなんですが、「マルチプレイ募集掲示板からUIDを転記して参加する」のがつらすぎる!

ほなキーボードエミュレーションしますか

用意するもの

  • どこのご家庭にも余っているRaspberry Pi Zero W (2があるならなお良い)
  • 比較的まともそうなmicroSDカード
  • まともなUSB A-microUSBケーブル (一昔まえのスマートフォンをパソコンにつなぐのに使うのに十分なグレードのもの)
  • SDカードを読み書きできるパソコン
  • Wi-Fi環境のある家庭
  • やる気
  • HDMI液晶ディスプレイと有線キーボードはあったほうがいいかもしれないけど別にいらない

SDカードにRasberry Pi OSを焼く

普通にRaspberry Pi Imagerを使いましょう。

スクリーンショット 2025-02-12 23.24.29.png
今回はPi Zeroなので、その通りに選択。

スクリーンショット 2025-02-12 23.24.36.png

Pi ZeroにデスクトップつきのDebianなんか焼いたら◯んでしまうので、Otherを開く。

スクリーンショット 2025-02-12 23.24.41.png

Lite を選ぶ。
あとは焼くSDカードを選ぶ。

スクリーンショット 2025-02-12 23.25.20.png

ここで「設定を編集する」

hoge.png

こんな感じで適当に設定する。
Wi-Fiのところは注意が必要で、今どきの5GHzアクセスポイントにつなぐことができません。ルーターから5Gみたいなのとそうじゃないのがあるときは、そうじゃない方にしましょう。

スクリーンショット 2025-02-12 23.25.29.png

SSH鍵も設定しておくと楽。何を言っているかわからない人は「パスワード認証」にチェックを入れておけばOK。
保存して、先程のダイアログを「はい」で書き込みに進む。

Piを起動する

書き込みの終わったSDカードはPiに挿して電源を入れましょう。電源供給をする場合は右の端子に挿してくださいね。
初回起動の設定は割と時間かかるので、お茶でも飲んで待ちましょう。

SSHログインする

最近はWindowsでもsshコマンドが使えるはずなので、Win + Rして wt と入れてEnter押してください。Macの人はターミナル。Linuxの人はこの記事読む必要ある?

ホスト名を nanapi.local 、ユーザーを nekono で設定したので、コマンドラインに

ssh nekono@nanapi.local

と入れてEnterを押しましょう。初回だと「この接続先信頼していい?」みたいなことを訊かれるのでyesと答える。SSH鍵を設定した人はすぐログイン完了。パスワードの人はパスワードを入れる (打ったパスワードは一切画面に表示されないので、打てたと思ったらEnterを押せば良い)。

入れたのを確認したら

sudo shutdown now

HIDガジェットの設定をする

参考

vimで色々できるならそうすればいいですが、難しいと思うので、一旦パソコンにSDカードを挿し直してください。

config.txt というファイルが見えると思いますので、 先頭に1行書き足しちゃいましょう。

dtoverlay=dwc2,dr_mode=peripheral

それから cmdline.txtというのがありますので、 rootwait の後ろに書き足す。

... rootwait modules-load=dwc2,libcomposite ...

SDカードを挿して起動、SSH接続。以下は参考記事からほぼそのまま拝借。

sudo tee /usr/local/bin/setup_hid_keyboard.sh

下記をコピペで貼り付けたら Ctrl + D

#!/bin/bash

# モジュールロード
# 起動時に、/boot/firmware/cmdline.txt で libcomposite がロードされるはずなので、必ずしも必要ではないが念のため
modprobe libcomposite

# configfs が自動マウントされていない場合に備え、ディレクトリの存在チェック
# if [ ! -e /sys/kernel/config/usb_gadget ]; then
#     mount -t configfs none /sys/kernel/config
# fi

# 実際にマウントされているかどうかをチェック
if ! mountpoint -q /sys/kernel/config; 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 に用意していますが、ご自身の環境に合わせて変更してください。
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" > 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
# 特定の UDC 名がわかっているのであれば、明示的に指定するほうがより安全


# ここだけオリジナルに追記
chown nekono:nekono /dev/hidg0

これは、「私はUSBキーボードですよ」とケーブルを通じて主張する部分です。最後の部分は、 /dev/hidg0 というこのあと使うファイルの所有権を変える部分なので、ユーザー名は設定したものを使ってください。
権限設定は本当はudevっていうのを使うべきなんだけど、たかがRas Piにそこまでこだわるのもだるいのでこれでいいかと。

sudo chmod +x /usr/local/bin/setup_hid_keyboard.sh

実行権限を付与しておきます。

次に自動起動設定

sudo tee /etc/systemd/system/hidgadget.service

以下を貼り付けてCtrl + D


[Unit]
Description=Setup HID Gadget for USB OTG
# After=systemd-modules-load.service network.target sysinit.target local-fs.target
# 環境によっては configfs の自動マウント設定を確認する必要がある
After=systemd-modules-load.service network.target sys-kernel-config.mount
ConditionPathExists=/sys/kernel/config

[Service]
# 今回は一度設定を行うだけなので Type=oneshot とし、RemainAfterExit=yes にしている
Type=oneshot
ExecStart=/usr/local/bin/setup_hid_keyboard.sh
RemainAfterExit=yes

[Install]
WantedBy=multi-user.target

そして有効化と再起動。

sudo systemctl enable hidgadget.service
sudo reboot

ここまでやると、再起動後にPS5側で「キーボードが接続されました」とか出るようになるはず。

タイピングスクリプトをこしらえる

/dev/hidg0っていうファイルにキーボードの状態が変わった旨のメッセージを書き込むだけなんですが、まあそれを自動化しないと意味がないわけですね。

こういう面倒なのはChatGPTに丸投げしましょうかね。

cat > type.py

貼り付け内容は以下の通り。

#!/usr/bin/env python3

import sys
import time

NULL_CHAR = chr(0)
ENTER_KEY = 40  # HID usage ID for Enter key
SHIFT_KEY = 0x02  # HID usage ID for Shift key
DELAY_TIME = 0.05  # Constant delay time (seconds)

def get_key_code(char):
    """Return the HID usage ID for a given character and whether Shift is required."""
    # Handle digits 0-9
    if '0' <= char <= '9':
        return (ord(char) - ord('1') + 30 if char != '0' else 39, False)
    # Handle lowercase letters a-z
    elif 'a' <= char <= 'z':
        return (ord(char) - ord('a') + 4, False)
    # Handle uppercase letters A-Z (Shift is required)
    elif 'A' <= char <= 'Z':
        return (ord(char) - ord('A') + 4, True)
    return None  # Ignore unsupported characters

def write_report(report):
    with open('/dev/hidg0', 'rb+') as fd:
        fd.write(report.encode())

def send_key(char):
    key_code_data = get_key_code(char)
    if key_code_data:
        key_code, shift_required = key_code_data
        # If Shift is required, press it along with the key
        if shift_required:
            write_report(chr(SHIFT_KEY) + NULL_CHAR + chr(key_code) + NULL_CHAR*4)
            time.sleep(DELAY_TIME)
            write_report(NULL_CHAR*8)  # Release all keys
        else:
            write_report(NULL_CHAR*2 + chr(key_code) + NULL_CHAR*5)
            time.sleep(DELAY_TIME)
            write_report(NULL_CHAR*8)  # Release all keys
        time.sleep(DELAY_TIME)

def press_alt_backquote():
    # Press and hold Alt (modifier 0x04)
    write_report(chr(0x04) + NULL_CHAR + NULL_CHAR*6)
    time.sleep(DELAY_TIME)
    # Press backquote (usage ID 0x35)
    write_report(chr(0x04) + NULL_CHAR + chr(0x35) + NULL_CHAR*5)
    time.sleep(DELAY_TIME)
    # Release backquote, keep holding Alt
    write_report(chr(0x04) + NULL_CHAR + NULL_CHAR*6)
    time.sleep(DELAY_TIME)
    # Release Alt
    write_report(NULL_CHAR*8)
    time.sleep(DELAY_TIME)

def press_enter():
    # Press Enter
    write_report(NULL_CHAR*2 + chr(ENTER_KEY) + NULL_CHAR*5)
    time.sleep(DELAY_TIME)
    # Release Enter
    write_report(NULL_CHAR*8)
    time.sleep(DELAY_TIME)

def main():
    if len(sys.argv) != 2:
        print("Usage: python3 send_keys.py <string_of_text>")
        sys.exit(1)

    input_string = sys.argv[1]

    # Perform the Alt + backquote sequence
    press_alt_backquote()

    # Send the input string as keystrokes
    for char in input_string:
        send_key(char)

    # Press and release Enter
    press_enter()

if __name__ == "__main__":
    main()
chmod +x type.py

使い方ですが、まず原神のユーザー検索モードを UID にします。
スクリーンショット 2025-02-13 1.04.28.png

□ボタンで入力画面が出ます。
スクリーンショット 2025-02-13 1.05.20.png

ここでSSH接続したPi上で下のように入力

./type.py 869999999

「日本語入力を解除して」「UIDを入れて」「Enterキーを押す」ところまでやってくれます。

私のキーボードはUS配列なので日本語解除は Alt + Backquote ですが、日本語配列設定の場合はAltキーを押す必要はないかと思います (全角半角キーはBackquoteキーと同じキーです) 。

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?