vgamepad でゲームパッドをエミュレートする

# 概要
python vgamepad でゲームパッドをエミュレートする。

自作したゲームパッドからUART等でパソコンにデータを送信しても、他のアプリはゲームパッドの入力として認識しない。
そのためpythonでCOMポートからデータを受信し、vgamepad でゲームパッドの入力と認識させる。

# vgamepad のインストール
途中でドライバのインストールを求められるのでインストールする。

```
pip install vgamepad
```

## vgamepad の使い方
vgamepad の使用方法は下記に記載されている。
https://pypi.org/project/vgamepad/

<details><summary>コードとメモ</summary><div>

```python
import vgamepad as vg

# 初期化 (内部でresetとupdateが行われる)
gamepad = vg.VDS4Gamepad() # DualShock4 gamepad.

# ボタンを押す (button1=0x0010, button2=0x0020, button3=0x0040 ... button12=0x8000)
gamepad.press_button(button=vg.DS4_BUTTONS.DS4_BUTTON_TRIANGLE)
gamepad.press_special_button(special_button=vg.DS4_SPECIAL_BUTTONS.DS4_SPECIAL_BUTTON_TOUCHPAD)
gamepad.update()
# ボタンを離す
gamepad.release_button(button=vg.DS4_BUTTONS.DS4_BUTTON_TRIANGLE)
gamepad.release_special_button(special_button=vg.DS4_SPECIAL_BUTTONS.DS4_SPECIAL_BUTTON_TOUCHPAD)
gamepad.update()

# 十字キー (0=上,1=右上,2=右,3=右下,4=下,5=左下,6=左,7=右上,8=入力無し)
gamepad.directional_pad(direction=vg.DS4_DPAD_DIRECTIONS.DS4_BUTTON_DPAD_NORTHWEST)
gamepad.update()

# トリガー (0.0=入力無し, 1.0=最大)
gamepad.left_trigger_float(value_float=0.5)
gamepad.right_trigger_float(value_float=0.5)
gamepad.update()
# 引数を整数で受け取る場合 (0x00=入力無し, 0xFF=最大)
gamepad.left_trigger( 0x80 )
gamepad.right_trigger( 0x00 )
gamepad.update()

# ジョイスティック (-1.0=左/下, 0.0=中央(入力無し), 1.0=右/上)
gamepad.left_joystick_float(x_value_float=0.0, y_value_float=0.2)
gamepad.right_joystick_float(x_value_float=-1.0, y_value_float=1.0)
gamepad.update()
# 引数を整数で受け取る場合 (0x00=左/下, 0x80=中央(入力無し), 0xFF=右/上)
gamepad.left_joystick( 0x20, 0x80 )
gamepad.right_joystick( 0x20, 0x80 )
gamepad.update()

# プログラム終了前に状態をデフォルトに戻す
gamepad.reset()  # 全ての入力をデフォルト（無し）に戻す
gamepad.update() # 変更を反映する
```

</div></details>

# COMポートから読み出してゲームパッドに反映するサンプル
COMポートから16進文字列を読み出してゲームボタンの押下に反映する。
データの区切りは改行コードで、例えば右上を押しながら×を押した場合は `C2\r\n`。
8bitのうち上位4bitが十字キー、下位4bitがボタンで詳細は下記の通り。

|7|6|5|4|3|2|1|0|
|---|---|---|---|---|---|---|---|
|↑|→|↓|←|△|○|×|□|

<details><summary>コード</summary><div>

```python
import vgamepad as vg
import time
import serial

# pip install vgamepad
# pip install pyserial

def get_pad_dir(pad):
  PAD_N = 0x8 # ↑
  PAD_E = 0x4 # →
  PAD_S = 0x2 # ↓
  PAD_W = 0x1 # ←
  if pad == PAD_N | PAD_W: return vg.DS4_DPAD_DIRECTIONS.DS4_BUTTON_DPAD_NORTHWEST
  if pad == PAD_N:         return vg.DS4_DPAD_DIRECTIONS.DS4_BUTTON_DPAD_NORTH
  if pad == PAD_N | PAD_E: return vg.DS4_DPAD_DIRECTIONS.DS4_BUTTON_DPAD_NORTHEAST
  if pad == PAD_E:         return vg.DS4_DPAD_DIRECTIONS.DS4_BUTTON_DPAD_EAST
  if pad == PAD_S | PAD_E: return vg.DS4_DPAD_DIRECTIONS.DS4_BUTTON_DPAD_SOUTHEAST
  if pad == PAD_S:         return vg.DS4_DPAD_DIRECTIONS.DS4_BUTTON_DPAD_SOUTH
  if pad == PAD_S | PAD_W: return vg.DS4_DPAD_DIRECTIONS.DS4_BUTTON_DPAD_SOUTHWEST
  if pad == PAD_W:         return vg.DS4_DPAD_DIRECTIONS.DS4_BUTTON_DPAD_WEST
  return vg.DS4_DPAD_DIRECTIONS.DS4_BUTTON_DPAD_NONE

if __name__ == "__main__":
  buttons = [
    vg.DS4_BUTTONS.DS4_BUTTON_TRIANGLE, # △
    vg.DS4_BUTTONS.DS4_BUTTON_CIRCLE,   # ○
    vg.DS4_BUTTONS.DS4_BUTTON_CROSS,    # ×
    vg.DS4_BUTTONS.DS4_BUTTON_SQUARE    # □
  ]
  gamepad = vg.VDS4Gamepad()
  port_num = '10'
  with serial.Serial('COM' + port_num, baudrate=38400, stopbits=serial.STOPBITS_ONE, parity=serial.PARITY_NONE) as comport:
    try:
      prev_str = ''
      while True:
        time.sleep(10.0 / 1000)

        recv_str_list = (prev_str + comport.read_all().decode()).replace('\r', '\n').split('\n')
        prev_str = ''
        for recv_str in recv_str_list:
          if len(recv_str) >= 2:
            # print(recv_str)
            recv_data = int(recv_str, 16) & 0xFF

            gamepad.directional_pad( get_pad_dir(recv_data >> 4) )

            button_stat = recv_data & 0x0F
            for i in range(4):
              if (button_stat & (0x8 >> i)) != 0:
                gamepad.press_button(button=buttons[i])
              else:
                gamepad.release_button(button=buttons[i])

            gamepad.update()
          elif len(recv_str) >= 1:
            prev_str = recv_str

    except KeyboardInterrupt: # Ctrl + C で終了
      pass

  gamepad.reset()
  gamepad.update()
```

</div></details>

# 入力テスト
下記サイトでゲームパッドとして認識されているか確認できる。
https://gamepad-tester.com/