前回までの活動によって、RaspberryPi2(Jessie Lite)とdualshock3をbluetooth経由でペアリングできるようになった。今回はPython3で、コントローラーからどんな入力があったのかを解析する方法を検討する。
ポイントとしては、bluetooth経由で接続完了後、dualshoc3の入力情報は、**デバイスファイル(/dev/input/js0)**にどんどん出力されているので、これを読み取れば良い。
以下のURLにあるJoystick APIにあるような出力フォーマットらしい。
struct js_event {
__u32 time; /* event timestamp in milliseconds */
__s16 value; /* value */
__u8 type; /* event type */
__u8 number; /* axis/button number */
};
python3のインストール
Raspbianにはデフォルトで、python2系がインストールされているが、python3系がないのでインストールする。
> sudo apt install python3.4 python3-dev python3-pip
python3用のシンボリックリンクを作成しておく
> cd /usr/bin
> sudo ln -s python3.4 python3
pythonにはstruct
というモジュールがあり、これはバイト列を成形して展開?してくれるらしい。バイト数とそのデータの型を並び順に定義すると、指定したバイト数だけ取り出して展開する便利なAPIになっている。
このAPIを頼りに、うまくいけば上記構造体(8バイト)のようなバイト列を、デバイスファイルから取得してみる。
#!/usr/bin/python3
import struct
device_path = "/dev/input/js0"
# unsigned long, short, unsigned char, unsigned char
EVENT_FORMAT = "LhBB";
EVENT_SIZE = struct.calcsize(EVENT_FORMAT)
with open(device_path, "rb") as device:
event = device.read(EVENT_SIZE)
while event:
(ds3_time, ds3_val, ds3_type, ds3_num) = struct.unpack(EVENT_FORMAT, event)
print( "{0}, {1}, {2}, {3}".format( ds3_time, ds3_val, ds3_type, ds3_num ) )
event = device.read(EVENT_SIZE)
プログラムを実行して、デバイスファイルの内容が出力されることを確認する。
> sudo python3 js_watch.py
87016180, -26350, 2, 16
87016180, -15878, 2, 17
87016190, 1, 1, 14
87016190, -30742, 2, 16
87016190, -21283, 2, 17
87016190, -30404, 2, 18
87016200, 0, 1, 12
87016200, 0, 1, 13
87016200, -32767, 2, 16
87016200, -32767, 2, 17
87016200, -30742, 2, 18
87016210, -32767, 2, 18
87016220, 1, 1, 4
87016220, 0, 1, 14
1列目の値はタイムスタンプで、ボタンの同時押しなどをすると、同じ時間になる。
2列目の値は、ボタンなどの入力値になる。
3列目は入力タイプで、dualshock3の場合だとボタン操作ds3_type = 1
とレバー入力やコントローラの傾きのようなアナログ操作ds3_type=2
となる。
4列目はボタンやスティックに割り振られたユニーク番号になる。この番号を見ることにより、どんなボタンが押されたかなどが判定できる。
各入力の番号
各ボタンやスティックには、ds3_type=1
の番号が必ず存在し、ds3_type=2
の番号は一部のボタンやスティックに存在する。解析結果は以下の図になる。
例えば、L2ボタンの場合、ds3_type=1
の番号は8であり、ds3_value
の値は1(pressed)と0(released)になってデジタル的な情報がわかる。同時にds3_type=2
の番号は12であり、どこまで深く押したのかアナログ的な情報が取得できる。
レバーだけでなく、右側の4つのボタン(〇△□×)でも、アナログレベル(どの程度強く押しているか)の情報が取得できた。
図中の**[23][24][26]**は、コントローラの傾き(ロール、ピッチ、ヨー)の動きを表す。
また[25]
は図中にないが、実際にプログラムを実行すると大量に出力される。これが何を表しているのは、よくわからなかった。あとなぜか←
キーだけアナログ出力がなかった。ひょっとすると使用したコントローラのキーが壊れているのかもしれない。(読み取る機会はなさそうだが)
ボタンのアナログレベル
前述のとおり、ds3_type=2でボタンの入力レベルが取得できる。以下の表にまとめた。
- コントローラのボタン
Dualshock3入力 `ds3_type=1` `ds3_type=2` `ds3_num` `ds3_value` `ds3_num` `ds3_value` リリース プレス リリース プレス 強プレス ↑ 4 0 1 8 -32767 0 32767 → 5 0 1 5 -32767 0 32767 ↓ 6 0 1 6 -32767 0 32767 ← 7 0 1 ? ? ? ? △ 12 0 1 16 -32767 0 32767 〇 13 0 1 17 -32767 0 32767 × 14 0 1 18 -32767 0 32767 □ 15 0 1 19 -32767 0 32767 L1 10 0 1 14 -32767 0 32767 L2 8 0 1 12 -32767 0 32767 L3 1 0 1 R1 11 0 1 15 -32767 0 32767 R2 9 0 1 13 -32767 0 32767 R3 2 0 1 SELECT 0 0 1 START 3 0 1 PS 16 0 1 - スティックの入力
Dualshock3入力 `ds3_type=2` `ds3_num` `ds3_value` 上 ニュートラル 下 Left Stick (vertical) 1 -32767 0 32767 Right Stick (vertical) 3 -32767 0 32767 Dualshock3入力 `ds3_type=2` `ds3_num` `ds3_value` 左 ニュートラル 右 Left Stick (horizontal) 0 -32767 0 32767 Right Stick (horizontal) 2 -32767 0 32767 - コントローラーのロール・ピッチ
ジャイロセンサの生の値だからなのか、値の本当のMin, Maxがよくわらず。おおよその実測値を掲載。(実際にはMinとMaxの絶対値は同じになりそう。必要になったらそろえて調整する必要がありそう)Dualshock3入力 `ds3_type=2` `ds3_num` `ds3_value` 左 ニュートラル 右 Controller (Roll) 23 4255 0 -5107 Dualshock3入力 `ds3_type=2` `ds3_num` `ds3_value` 前 ニュートラル 後 Controller (Pitch) 24 4255 0 -5107 Dualshock3入力 `ds3_type=2` `ds3_num` `ds3_value` 前 ニュートラル 後 Controller (Yow) 26 4255 0 -5107 バッテリーの残量情報
/sys/class/power_supply
以下にsony_controller_battery_[MACアドレス]
というフォルダがペアリングしたときに作成される。このディレクトリにあるueventを参照すると、バッテリーの状態が分かる。> cd /sys/class/power_supply/sony_controller_battery_00:21:xx:13:xx:xx > cat uevent POWER_SUPPLY_NAME=sony_controller_battery_00:21:xx:13:xx:xx POWER_SUPPLY_PRESENT=1 POWER_SUPPLY_CAPACITY=100 POWER_SUPPLY_SCOPE=Device POWER_SUPPLY_STATUS=Full
一晩繋ぎっぱなしの状態で残量チェックすると、ちゃんと減っているのがわかる。
POWER_SUPPLY_STATUS
がFullなのはおそらくUSBケーブルでラズパイに接続し、充電状態で満タンだったからで、ケーブルを外して残量が減ると、Dischargingになっている模様。> cat uevent POWER_SUPPLY_NAME=sony_controller_battery_00:21:xx:13:xx:xx POWER_SUPPLY_PRESENT=1 POWER_SUPPLY_CAPACITY=75 POWER_SUPPLY_SCOPE=Device POWER_SUPPLY_STATUS==Discharging
これを逐一チェックすればとりあえず分かりそうだが、もっとスマートな方法があるかは不明。
次回は、上記の結果を踏まえたpythonモジュールの作成か、ラジコン側の電源・モーター制御関連をやるかもしれない。
- コントローラーのロール・ピッチ
- スティックの入力