🎮

Mac版 FF14 で使える Bluetooth コントローラーを作る過程でCircuitPythonのBLEライブラリにコミットした話

2024/12/02に公開

今年私は約1週間ほどで基盤を設計し注文してパーツを組み付けて動くキーボードを作ったのですが、この時私は思ったのです。

キーボードがこんなに簡単に作れるならコントローラーも作れるのでは?

そうして私はCircuitPythonという組み込みプログラミングのためのPythonディストリビューションを手に取り戦いに繰り出したのでした。

どういうコントローラーを作りたいのか?

「アナログスティックとジャイロセンサーが一体になった片手でキャラクターとカメラを操作できるデバイス」を作りたいと考えていました。そのためまずは加速度センサと回転角センサが内蔵されているSeeed XIAO nRF52840 Senseという基盤を購入しCircuitPythonで傾き検知のプログラムを書きました。CircuitPythonは書いたPythonのコードをデバイスにコピーするだけで走らせてくれるのでメインのコードをwatchしてrsyncするだけでリアルタイムに開発できてとてもいい体験でした。おかげでスイスイとコードを書くことができ、単純に重力加速度を使った傾き検知のプログラムを書くことができました。

コントローラーを作るぞ!あれ、そもそもどこから始めればいいの?

コントローラーを自作する上で一番むずかしかったポイントは「どうやってデバイスを認識させるか」でした。それもそのはず、キーボードはQMKZMKといった超メジャーなファームウェアが存在するのですがコントローラーにはそういった類のものは(少なくとも私の知る限り)ありません。デバイスとPCを疎通させるところから始まるわけです。

HIDディスクリプタ

Human Interface Device Descriptorとは人間がコンピュータを操作するデバイスの仕様を記述するためのフォーマットです。細かい説明は割愛しますが、「このデバイスはこのカテゴリである」や「特定の大きさの、特定の型を持った数値が、特定のバイト数書き込まれる」など、極端に単純化して言えばJSONスキーマのようなものをバイナリで記述します。HIDディスクリプタの仕様自体は公開されているので、これを使って作ることができます。

コントローラーのHIDディスクリプタ

まずは今自分がMacのFF14で使えているコントローラーがどういうディスクリプタなのかを調べて、それをそのまま流用してみようと考えました。Gamepad Testerというサイトを使ってとりあえずハードウェアの詳細を調べて同じようなことをしている人がいないか検索しました。いくつか参考になりそうなものが見つかったのでそれらを拝借しCircuitPythonのBLEライブラリを使ってMacで接続したところ、Gamepad Testerで無事コントローラーが認識されました。しめしめ、これは思ったよりも早くできそうだぞと思っていたのですがここからが沼の入口でした。

なぜか動かないFF14

Gamepad Testerで右スティックとして認識されたことを確認できたので早速FF14を起動して動かしてみよう!そう意気込んで私はログインしてキャラクター選択画面を開きました。おそるおそると基盤を傾けてみますが...... 何も起きない......

なにかが壊れたかと思いGamepad Testerを再度確認するも普通に認識されている。おかしい、今使っているコントローラーとなにかが違うのか?と動くコントローラーを繋いでみたところ、なんと全然表示が違うのです。スクショを残していなかったのですがどう見ても私が自作したコントローラーは非純正っぽい表示になっているのです。

手持ちのコントローラーを模倣してみる

紆余曲折あってどうもBLEのデバイスにはPNP IDというものがあり、この値でデバイスを判定しているということがわかりました。CircuitPythonの既存実装ではPNP IDを指定する方法がなかったので、まずはそれを可能にする実装を手元で行いました。これを行った結果Gamepad Testerで手持ちのコントローラーと同じ表示になったのでこの変更内容をPRにしました。
https://github.com/adafruit/Adafruit_CircuitPython_BLE/pull/202

その後しばらく試してみてもどうもうまく動作してくれなかったので、拾ってきたHIDディスクリプタではなくちゃんと自分が模倣しようとしているコントローラーのHIDディスクリプタを調べました。それまでに見つけたBluetoothコントローラーのHIDディスクリプタはWiresharkを使って解析されていたため同じことを試してみたのですが私のやり方が悪くうまく解析できませんでした。そこでもっと直接的な解決策はないかと検索してみたところなんとHID Explorerというブラウザ上でWebHIDを使ってHIDディスクリプタを確認できるサイトが。はじめからこれでよかったじゃんと思いつつ作者のnondebugさんに感謝しつつHIDディスクリプタを無事入手することができました。
(今更ですがこの方Dualsenseのディスクリプタとか公開してました)

それでもMacで認識されない

確認したところどうもWindowsとMacで異なることがわかっており、Mac用のディスクリプタを使って実装していたのですがそれでも認識されませんでした。この時CircuitPythonのBLEライブラリは複数のHID Reportを同時に扱う実装が存在しておらずMacのディスクリプタはコントローラーのボタン類のデータを乗せるためのHID Reportのみを飛ばしていたのですが、どうやらそれではダメなようでした。そこで複数のHID Reportに対応するように実装を手元で行い、その変更内容をPRにしました。
https://github.com/adafruit/Adafruit_CircuitPython_BLE/pull/205

ようやく認識された自作コントローラー

こうして二週間分の土日を溶かしてようやく傾き角度でカメラ操作ができるデバイスを自作することができました。その後はFF14のパッチ7.1が来たことで忙しくしていてデバイスづくりはできていないのですが、FF14専用のコントローラー自作はいずれやりたいと思っています。今回の記事はハードウェアチートなどの悪用が可能になってしまうので若干公開を迷ったのですが、自作キーボードでも同様の議論ができるので公開することにしました。皆さんもぜひ自分だけの自作コントローラーを作ってみてはいかがでしょうか。
https://gist.github.com/ktnyt/33ad0462a0e7cc49176c78530e320291?permalink_comment_id=5311698#gistcomment-5311698

Discussion