ろろのブログ

Tundra TrackerとRP2040でオリジナルのコントローラーをつくる

更新日:2023-03-31

Tundra TrackerとRP2040で通信する方法を備忘録を兼ねて書いてみようと思います
GitHub – tundra-labs/rp2040_examples

Tundra Tracker

TundraLabsが開発しているVRトラッカーでSteamVRのLighthouseに対応しています
このトラッカー小型だったり稼働時間が長かったりと色々な特徴がありますが個人的に一番だと思っているのが拡張性です
vive trackerにも外部との接続端子がありましたがそれぞれの端子は1bitの情報しか扱えず、アナログスティックなどを搭載することはできませんでした
しかしTundra Trackerは底面のフラットケーブルを介してマイコンなどとSPI通信を行うことができるのでアナログスティックを搭載したりすることが出来ます

ドライバー

まず新しいドライバを作成する必要があります
driver_name/
 ├ resources/
 │ └ input/
 │   └ input_profile.json
 └ driver.vrdrivermanifest/

driver.vrdrivermanifest
{
	"alwaysActivate": true,
	"name" : "driver_name",
	"directory" : "",
	"resourceOnly" : false,
	"hmd_presence" : []
}

DriverManifest · ValveSoftware/openvr Wiki · GitHub

input_profile.json(一部省略)
    "input_source": {
        "/input/stick": {
            "type": "joystick",
            "binding_image_point": [
                0,
                0
            ],
            "order": 1
        },
        "/output/haptic": {
            "type": "vibration",
            "binding_image_point": [
                5,
                35
            ]
        }
    },
    "input_components": {
        "/input/stick/x": {
            "bit_offset": 0,
            "bit_width": 10,
            "type": "scalar",
            "min": 0,
            "max": 1024,
            "unit": "twosided",
            "special_value_zero":0
        },
        "/input/stick/y": {
            "bit_offset": 10,
            "bit_width": 10,
            "type": "scalar",
            "min": 0,
            "max": 1024,
            "unit": "twosided",
            "special_value_zero":0
        }
    }

input_componentsでオフセット、幅(大きさ)、種類などを指定することでSPIで送られてくるビット列を各入力に割り当てることが出来ます(Tundra Trackerオリジナル要素っぽい?)
この例だとビット列の0番目から9番目までの10ビットをスティックのx軸に、10番目から19番目までの10ビットをy軸に割り当てています

ドライバの登録

作成したドライバーを使うにはSteamVRのドライバーフォルダの中に直接ぶち込むかSteamvrに登録する必要があります
今回はSteamvrにドライバを登録する方法で行きます
これならコマンドひとつで解除できますし

ドライバの登録/解除は

<Steamのフォルダ>/steamapps/common/SteamVR/bin/win64/

にあるvrpathreg.exeから行います
ドライバを登録するには引数にadddriver <ドライバのパス>を指定します

> vrpathreg.exe adddriver <driver_path>

これでvrdrivermanifestのnameで設定した名前でドライバが登録されます

JSON File

SteamVRデバイスには種類(コントローラーなのかトラッカーなのか)や製造元、読み込むInputプロファイルのパスなどが記述されているjsonファイルが書き込まれています
これを書き換える必要があるのでデフォルトのものをデバイスからダウンロードします
パソコンにデバイスをUSB接続した状態で

<Steamのフォルダ>/steamapps/common/SteamVR/tools/lighthouse/bin/win64/lighthouse_console.exe

を実行しコンソールが開いたらdownloadconfigを実行

lh> downloadconfig
LHR-<Serialnumber>: Read config of 1261 bytes from [vid:28de, pid:2300] (LHR-<Serialnumber>) and inflated to 3567 bytes
Wrote 3567 bytes to LHR-<Serialnumber>.json
lh>

これでLHR-{シリアルナンバー}.jsonという名前でファイルが保存されます
このファイルは書き換えるので元に戻すためにバックアップを取っていてください
書き換えるのは2か所だけでまずfirmware_configに”mapped_input” : trueを追記

LHR-{Serialnumber}.json(一部省略)
   "firmware_config" : {
      "mapped_input" : true,
   }

そしてinput_profileの値を新しく作成したinput_profileのパスに変更

LHR-{Serialnumber}.json(一部省略)
"input_profile" : "{tundra_labs}/input/tundra_tracker_profile.json" -> "input_profile" : "{driver_name}/input/input_profile.json"

出来たらuploadconfigで書き換え後のファイルをデバイス内に書き込みます

lh> uploadconfig "LHR-{Serialnumber}.json"
Compressed config size is 1262
LHR-1076335E: Triggered keepalive (succeeded)
LHR-1076335E: Packet received after 0.032s, keepalive (0/1)
Wrote the contents of LHR-{Serialnumber}.json as the config on the device
lh>

SteamVRのデバイス画面 SteamVRの設定からコントローラのテストを開きデフォルトから変わっていれば成功です!!

RP2040

Tundra Trackerとマイコン(RP2040)はSPIで通信を行います
トラッカー側がマスターでマイコンがスレーブです
公式サンプルがあるのでそれを使います

rp2040_examples/tmi_ hand_controller/arduino/tmi_hand_controller/tmi_hand_controller.ino
typedef struct __attribute__( ( packed, aligned( 1 ) ) )
{
  uint8_t       system          : 1;  //0
  uint8_t       a               : 1;  //1
  uint8_t       b               : 1;  //2
  uint8_t       trigger_btn     : 1;  //3
  uint8_t       grip            : 1;  //4
  uint8_t       thumbstick      : 1;  //5
  uint8_t       menu            : 1;  //6
  uint8_t       thumbstick_en   : 1;  //7
  uint16_t      thumbstick_x    : 10; //8
  uint16_t      thumbstick_y    : 10; //18
  uint16_t      trigger         : 10; //28
  uint16_t      index           : 10; //38
  uint16_t      middle          : 10; //48
  uint16_t      ring            : 10; //58
  uint16_t      pinky           : 10; //68
} 
controller_data_t;

このコードでは専用の構造体を宣言することで分かりやすくビット配列を表現できるようにしています コロン以降はそれぞれの変数に割り当てられるビット数です

typedef struct __attribute__( ( packed, aligned( 1 ) ) )
{
  unsigned int x:10;
  unsigned int y:10;
} 
controller_data_t;

アナログスティックを搭載する場合の例です
作ってみました(色々雑ですが)