JoyConの加速度センサーを取るための設定変更についての話


はじめに


JoyConの加速度センサーやジャイロセンサーの値を取得したい!


ということで方法を調べたところ、

「JoyConにデータ列を送信して、設定を変更しないといけない」

ということがわかりました。


色々と調べながら、なんとか実装をしたのですが、

「知らなかったせいで詰まっていた」ところがいくつかあったので、

今回は、設定変更のためにJoyConに送信する内容について解説し、知見を共有しようと思います。


注意


この記事は、Bluetooth初心者かつハードウェア初心者によるものです。

そのため、不正確な情報が含まれている可能性があります。

(現状、私の環境では正常に動作はしています。)


本ページの情報の使用/参照によって発生したいかなる不利益/損害について責任を負いません。


実行環境


- MacBook Pro (Retina, 13-inch, Early 2015)


前知識


その1 HIDプロファイルとは


JoyConはPCとBluetooth接続すると、自動的にHIDプロファイルのデバイスとして認識されます。


HIDプロファイルとは、Bluetoothプロファイル(通信手順を標準化したもの)の一つであり、

以下のような3タイプのレポート(データのまとまり)を利用して通信します。

  • Inputレポート ... デバイス(JoyCon)からホスト(PC)へ入力するレポート。

  • Outputレポート ... ホストからデバイスへ出力するレポート。

  • Featureレポート ... よくわからない、今回は利用しない。


詳しく知りたい方は、こちらの記事を参考にすると良いと思います。

HID/レポート - おなかすいたWiki!


その2 JoyConへのOutputレポートの形式


センサー値取得のための設定の変更には、HIDプロファイルのOutputレポートを使用します。

細かいことは省略するとして、基本的にはJoyConに対して、

// PureJavaHid-Apiの例

byte[] buf = {
  command, 
  (++(packetCount)&0xF),  //packetCountは送信ごとに1増加 0x0~0xFをループ
  0x00, 0x01, 0x40, 0x40, 0x00, 0x01, 0x40, 0x40, 
  subcommand, 
  argument[0], ... , argument[argument.length-1]
  };

といったようなデータ列bufをOutputレポートにセットして送信します。

Pure Java HID-APIでは、setOutputReport(0, buf, buf.length)のようにします。

言語やライブラリによって多少書き方などに違いはあれど、ほぼ同様な送信方法だと思います。


buf内の、command, subcommand, argumentについてはひとつずつ説明していきます。


command

簡単に言うなら、Outputレポートの種類です。

Nintendo_Switch_Reverse_Engineering/bluetooth_hid_notes.md

こちらの情報を見ると、

Output 0x01, Output 0x03, ...

というようにたくさん並んでいます。このOutputの後の16進数がcommandの値にあたります。

この中から必要なものを選んで使っていきましょう。

ちなみに今回は使うのはすべて、0x01というsubcommandを送るためのcommandです。


subcommand

簡単に言うなら、具体的な要求内容です。

Nintendo_Switch_Reverse_Engineering/bluetooth_hid_subcommands_notes.md

今度はこちらの情報を見ると、

Subcommand 0x00, Subcommand 0x01, ...

などというようにたくさん並んでいます。お察しの通り、Subcommandの後の16進数がsubcommandの値にあたります。

commandと同様に、この中から必要なものを選んで使っていきましょう。


argument

簡単に言うなら、要求のためのパラメータです。

subcommandの方のページで、Subcommand 0x03や0x38のところに表が書いてありますよね。

これが、argumentに入れる値の情報になります。argumentはbyteの配列です。

subcommandによっては複数の値を設定することもあります。


本題


設定変更のための送信については、

output(command, subcommand, argument);

という関数を作ったとして説明します。

この関数を実行すると、引数からデータ列を作成して送信してくれるものとします。


加速度・ジャイロセンサーの値の取得


// Subcommand 0x40: Enable IMU (6-Axis sensor)
// One argument of x00 Disable or x01 Enable.
output(0x01, 0x40, new byte[] {0x01});

// output()の間に適当に一定時間停止を入れる

// Subcommand 0x03: Set input report mode
// Argument 30: Standard full mode. Pushes current state @60Hz
output(0x01, 0x03, new byte[] {0x30});

output()の間には、JavaのスレッドのThread.sleep(100);などのように一定時間停止を入れないとうまく動作しない可能性が高いです。


実行すると、(私のプログラムでは)センサー値を含んだInputレポートを連続的に送ってくるようになります。

これだけでもう設定変更完了です。プログラミングってやっぱり調べる時間の方が圧倒的に長いですね。


ただ、送られてくるデータはそのままでは使えません。

Nintendo_Switch_Reverse_Engineering/imu_sensor_notes.md

こちらの情報をもとにデータを加工する必要があります。(まだ実装中)


↓ある程度実装できて、

ジャイロセンサーの値のみを利用してポインターっぽく表示したもの

youtu.be


おわりに

(おわりに の後におまけもあります)


JoyConを触り始めたときは、JoyConに対してデータを送信する必要があるとは思いませんでした。

というか、データを送信できるとも思っておらず、そのせいで延々と詰まっていました。

この記事が、私と同じように知らないせいで詰まっている人にとって役に立つと良いです。


Joy-Conには加速度・ジャイロセンサーだけでなく、もっと多くの機能があります。

IRセンサーもあれば、HD振動もある。ボタンは左右合わせて20個以上もある。

なぜか、HOMEボタンの光り方もめっちゃ制御できたりもします!笑


JoyConは、本当に多機能でおもしろいコントローラーです。

いっそ、Switchを持っていない人も、JoyConを買ってもいいんじゃないでしょうか。


本記事に関する開発はこちらで公開しています。

github.com


おまけ


今回解説した、設定変更と同様にして、

JoyConのライトなども制御することができます。

youtu.be


その1 HOMEボタンを光らせる


// Light up Home button
byte[] b = {
  byte(0xaf), byte(0x00), 
  byte(0xf0), byte(0x40), byte(0x40), 
  byte(0xf0), byte(0x00), byte(0x00), 
  byte(0xf0), byte(0x00), byte(0x00), 
  byte(0xf0), byte(0x0f), byte(0x00), 
  byte(0xf0), byte(0x00), byte(0x00)
  };
output(0x01, 0x38, b);

HOMEボタンが光ります。


JoyConのホームボタンのライトは、

  • 最大15パターンの明るさ

  • 各パターンの持続時間

  • 次パターンへのフェード時間

を設定してループさせることができるみたいです。


詳しい情報は、Subcommand 0x38に書いてあります。


その2 Playerのライトを制御する


// Light up Player button
output(0x01, 0x30, new byte[] {byte(0xa5)});

プレイヤー番号を表示するあれも制御できます。

こちらは、点滅と点灯を制御できます。

詳しい情報は、Subcommand 0x31に書いてあります。