マイコン不要　Poke-Controller+BluetoothでSwitch自動化

# はじめに

表題の通り、このたび、Poke-ControllerでBlutoothを使用した自動化が可能になるライブラリを開発しました。この記事では、Bluetooth自動化環境の導入を解説します。

<blockquote class="twitter-tweet"><p lang="ja" dir="ltr">まだ少し手順が複雑ですが、Poke-Controller MODIFIEDでBluetooth接続できるようになりました。<br><br>DLL作成：みずようかん先生（<a href="https://twitter.com/AT12806379?ref_src=twsrc%5Etfw">@AT12806379</a>） <a href="https://t.co/ai8YXOyU7p">pic.twitter.com/ai8YXOyU7p</a></p>&mdash; メイユール (@meilleur_pkmn) <a href="https://twitter.com/meilleur_pkmn/status/1598667468251463680?ref_src=twsrc%5Etfw">December 2, 2022</a></blockquote> <script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>

# BluetoothでSwitchを自動操作する方法

## 従来手法の解説

[Poke-Controller](https://github.com/KawaSwitch/Poke-Controller)および[Poke-Controller MODIFIED](https://github.com/Moi-poke/Poke-Controller-Modified)では、マイコンとUSBシリアル変換モジュールで有線接続のコントローラーに偽装して、Switchを操作する方法を採用しています。操作はシリアル通信として、指定されたポート名を通じてUSBシリアル変換モジュールに送信されています。

```mermaid
flowchart LR
    subgraph PokeCon
        Command[PythonCommand etc...] --> Serial
    end
    subgraph H/W
        Converter --> MCU
    end
    Serial --> Converter["Converter\n(COMx)"]
    MCU --as a Controller--> Switch
```

最近はこの図のH/W部分を1つの基板に納めたマイコンも販売されており、導入の簡略化にひと役買っています。

しかし、多少簡略化されるとはいえ、この方法は機材を用意する手間がかかります。ドライバのインストールやプログラムの書き込みにつまづいた方も多いのではないでしょうか。

## Bluetooth自動化の先例

ところで、Switchは有線接続のコントローラーのほか、Pro Controllerに代表されるBluetooth接続の無線コントローラも使用することができます。Bluetooth接続を使用した自動化の取り組みについては、以下の記事が詳しいので参照してください。

[Switch自動化の歴史と無線化、そしてWindows対応のお話](https://note.com/gamewagashi/n/n6251719c5867)

PokeConと人気を二分するSwitch自動化ソフトウェアである[NX Macro Controller](https://bzl.hatenablog.com/entry/2020/12/13/204230)では、前述のようなマイコン接続のほか、Bluetoothを利用した操作にも対応しています。

```mermaid
flowchart LR
    subgraph NX[NX Macro Controller]
        Macro
    end
    subgraph H/W
        Dongle
    end
    NX --> Dongle --via Bluetooth--> Switch
```

この方法の利点は、なにより導入が簡単で、費用も抑えられることです。対応するBluetoothドングルを用意してドライバを置き換えるだけで、Switchと接続することができます。

# PokeConにおけるBluetooth自動化の実現

## 準備するもの

### Poke-Controller MODIFIED

[GitHub](https://github.com/Moi-poke/Poke-Controller-Modified)

もちろん、PokeConが必要です。導入解説はGitHubのほか、すでに公開されているほかの記事をご覧ください。

:::note
ちなみに、私はフォルダの外を汚さずに使用したいため、[自作のバッチファイル](https://github.com/mukai1011/pokecon-installer)を利用しています。開発者向けですが、多分これが1番早いと思います。
:::

### pokecon-extensions

[GitHub](https://github.com/mukai1011/pokecon-extensions)

私が開発している、PokeCon機能拡張ライブラリ群です。直接インストールしていただいてもかまいませんが、トラブルシューティングしづらいので、クローンしてEditable modeでインストールするのがよいと思います。

```sh
# To remove previous installation:
#   pip uninstall pokecon-extensions

cd ./Poke-Controller-Modified/SerialController/Commands/PythonCommands/
git clone https://github.com/mukai1011/pokecon-extensions.git -b main
pip install -e ./pokecon-extensions
```

:::note
pipのバージョンが古いとインストールできないかもしれません。

```sh
python -m pip install --upgrade pip
```
:::

### btkeyLib.dll

[NX Macro ControllerのBluetooth修正パッチを作った＆ドライバの話](https://note.com/gamewagashi/n/n4a76066320e9)

上記の記事から`btkeyLib.dll`をダウンロードし、`pokecon-extensions/pokecon-extensions/bluetooth/btkeyLib/`フォルダに配置してください。

:::note
ソースコードはこちらで公開されています。[GitHub](https://github.com/mizuyoukanao/btstack)
:::

### com0com

[OSDN](https://ja.osdn.net/projects/sfnet_com0com/)

Windows用の仮想シリアルポートドライバです。導入解説は、以下の記事を参照してください。

[仮想シリアル(COM) ポートドライバ「com0com」によるシリアル通信](https://qiita.com/yaju/items/e5818c99857883a59033)

:::note
*nixの場合は、socatが代わりになるかもしれません。まずはDLLの移植が必要ですが......

[socatで仮想シリアルポートを作る](https://qiita.com/uhey22e/items/dc41d7fa1075970e66a1)
:::

### Bluetoothドングル

USB接続のBluetoothドングルを用意してください。[CSR8510 A10が推奨チップ](https://twitter.com/AT12806379/status/1597938264069263360)です。具体的には[ELECOM LBT-UAN05C2/N](https://www.elecom.co.jp/products/LBT-UAN05C2N.html)で動作を確認しましたが、このドングルであれば必ず成功することを保証するものではありません。

### Zadig

[Zadig - USB driver installation made easy](https://zadig.akeo.ie/)

ドライバをWinUSBに置き換えます。ドライバを置き換えたドングルは、もとのドライバに戻すまで通常のBluetoothドングルとしては使用できません。もろもろ自己責任でお願いします。

## 導入手順

現状は試験的機能なので、2種類の方法を提供しています。

### 共通

以下はそのままなので、特に解説はしません。

1. BluetoothドングルをPCに接続し、ZadigでドライバをWinUSBに置き換える
2. PokeConのポート名を、com0comで作った仮想ポートに設定する

### PokeConの外で変換プログラムを立てる方法

簡単なほうの方法です。PokeConのほかに、その通信内容を常時読み取りドングルに送信するソフトウェアを起動します。PokeConと変換プログラムは仮想シリアルポートドライバを経由して通信し、**PokeCon側は一切手を加えることなく**Bluetooth自動化を実現できます。

```mermaid
flowchart LR
    subgraph S/W
        subgraph PokeCon
            Command[PythonCommand etc...] --> Serial-out
        end
        subgraph Converter
            Serial-in
        end
    end
    subgraph H/W
        Dongle
    end
    Serial-out --via com0com--> Serial-in
    Converter --> Dongle --via Bluetooth--> Switch
```

`pokecon-extensions/tests/bluetooth/converter.py`を、先ほど設定した仮想ポートと対になるポートに設定してください。`baudrate`はPokeConに設定している値を指定します。

```python
config = Config(port="COM6", baudrate=4800)
```

`converter.py`を実行すると、以下のように表示されます。`running on ~`がおかしな値になっていなければ、ひとまずドライバの置き換えは成功しており、認識されているようです。

```
> python converter.py
BTstack/windows-winusb booting up
BTstack up and running on 00:1B:DC:FE:D4:27.
```

Switchを「持ちかた/順番を変える」画面で待機させると以下の表示が流れ、自動的に無線コントローラがペアリングされます。ペアリングされた状態で、PokeConからキーボード操作などをおこなうと、動作しているか確認できます。

```
pairing_state:0
pairing_state:0
pairing_state:0

...
```

なお、converterを終了する際はCtrl+Cを入力してください。

```
CTRL-C - SIGINT received, shutting down..
pairing_state:0

...

usb_sco_stop
```

### PythonCommandに変換プログラムを組み込む方法

かっこいいほうの方法です。前述のプログラムをPythonコマンドに組み込んでしまいます。PokeConはこちらも通常通りシリアル通信を出力しますが、その通信を仮想シリアルポートドライバを経由して受け取りドングルに渡すのは**PythonCommandに組み込まれた**変換プログラムです......伝わりますでしょうか。

```mermaid
flowchart LR
    subgraph PokeCon
        subgraph PythonCommand
            subgraph Converter
                Serial-in
            end
        end
        PythonCommand --> Serial-out
        Serial-out --via com0com--> Serial-in
    end

    subgraph H/W
        Dongle
    end
    Converter --> Dongle --via Bluetooth--> Switch
```

この方法では、Pythonコマンドを編集します。サンプルは、`pokecon-extensions/tests/bluetooth/command.py`を確認してください。

```python
@bluetooth(Config(port="COM6", baudrate=4800))
def do(self):
    pass
```

いつも操作を記述しているdoメソッドの上に`@bluetooth`デコレーターを追加し、前述のようにConfigを設定すると、doメソッドの実行前にBluetooth接続をおこない、終了および中断時には安全に接続を破棄します。

この方法の場合、Pythonコマンドの開始位置は「持ちかた/順番を変える」画面に固定されます。この画面でないと無線コントローラがペアリングされないためです。通常、自動化プログラムは開始時のゲームの状況を指定するので、たいした問題ではないといえばないのですが、毎回開始位置からゲーム内に復帰する操作が必要になるほか、キーボード入力で操作するなどPythonコマンドの外ではBluetooth接続できないため、かっこいいわりに実用性はびみょうかもしれません。

# 開発者向け情報

もちろん、仮想ポート経由でPokeConの通信内容を受け取る現在のプログラムは、いずれも仮のものです。

今回開発したBluetooth接続機能の本体は、SessionクラスとAdapterクラスです。AdapterクラスはSerial同様BinaryIOインターフェースを実装しており、基本的にはSerialと置き換えが可能です。将来的にPokeConに組み込む際は、送信部分の数行を書き換えるだけで済むように考えられています。[参考](https://github.com/mukai1011/pokecon-extensions/tree/main/pokecon_extensions/bluetooth)

```mermaid
flowchart LR
    subgraph PokeCon
        PythonCommand --> Serial
        PythonCommand --> Adapter
    end
    subgraph H/W
        Converter --> MCU
        Adapter --> Dongle
    end
    Serial --> Converter["Converter\n(COMx)"]
    MCU --as a Controller--> Switch
    Dongle --via Bluetooth--> Switch
```

選択部分を実装するのがめんどうくさくてやっていませんが、PokeConに内蔵する提案がありましたら喜んでお手伝いします。

そのほか、[pokecon-extensions](https://github.com/mukai1011/pokecon-extensions)では、バグ報告や開発協力を歓迎しています。

# おわりに

PokeConでBluetoothを利用した自動化を実現できるようになりました。映像を受信するキャプチャーボードもあるため完全無線化とはいきませんが、安価なセットアップが可能になり、Switch自動化の敷居はさらに下がると思います。

ぜひ1度お試しください。動作／バグ報告などお待ちしています。

# 謝辞

btkeyLib.dllの作者であり、Pythonラッパーのひな形を提示していただいたみずようかんさん（[@AT12806379](https://twitter.com/AT12806379)）、PokeCon+Bluetooth自動化を提起し、*nixへの移植に挑戦していただいたアカツキさん（[@pokemium](https://twitter.com/pokemium)）、ありがとうございます。