LoginSignup
14
15

More than 5 years have passed since last update.

PS4コントローラのスティックでarduinoを制御する

Posted at

目的

PS4コントローラのスティックでarduinoを制御する。

背景

イベント向けに、PS4コントローラのスティックで操作するラジコンを作りました。当初は一つのarduinoで制御しようとしていたのですが、センサーやモーターを追加するとすぐにPINが重複して、構成の再検討が必要になりました。
少し複雑なことをしようと思うと、途端に一つのarduinoで実現するのが難しくなります。
結局、PS4コントローラからの入力を一つ目のarduinoでi2cに送信して、二つ目のarduinoでi2cから受信してモーターを制御する様な構成にしました。
特にPS4コントローラからの入力をarduinoでi2cに送信する部分は、また使えると思ったので忘れる前にまとめます。

使用機材

Windows10パソコン
arduinoUno 2個
USB Host sheild 2.0
BLEドングル
PS4コントローラ
ブレッドボードジャンパーワイヤー 3本
(PCとarduinoを接続するための)USBケーブル 2本

環境

chocolateyを使っているならarduinoIDEのインストールは、管理者権限でプロンプト(cmd)を起動して以下のコマンドを実行します。
choco install arduino
バージョンは以下のコマンドで確認できます。
choco list arduino

Chocolatey v0.10.8
arduino 1.8.5 [Approved]
arduinoide 1.6.1 [Approved]

chocolateyのインストールは、chocolatey 基本情報まとめを参考にセットアップしました。

arduinoIDEをダウンロードしてセットアップする場合は、https://www.arduino.cc/ にアクセスしてメニューから[SOFTWARE]を選び、好きなファイル形式を選びます。
arduinoide.png

[Just Download]をクリック※気に入ったら寄付も歓迎とのこと
arduinoidedownload.png

USB Host sheild 2.0は、Gitページにアクセスして、[Clone or Download]-[Download]をクリックしてダウンロードします。usbhostsheild2zip.png

次に[スケッチ]-[ライブラリをインクルード]-[.ZIP形式のライブラリをインストール]を選んで、先ほどDownloadしたUSB Host sheild 2.0のzipファイルを選ぶとライブラリに組み込まれます。
arduinozip.png

関連リンク

購入サイト

※サインスマートのUSBホストシールドは高いですがハンダする必要が無いのでおすすめです。

ドライバ(arduinoUno互換ボードのUSBシリアルドライバ)

ドキュメント

USB Host sheild参考

PS4コントローラのスケッチ

PS4コントローラとarduinoをBLEでつなぐ場合は、
[ファイル]-[スケッチ例]-[USB Host Shield Library 2.0]-[Bluetooth]-[PS4BT]を使用します。
psblesample2.png
修正後のスケッチはGitから取得してください。

コードの説明

USBの初期化

setup()のUsb.Init()でUSBの初期化を行います。
成功すればシリアルに「PS4 Bluetooth Library Started」と表示されます。
「OSC did not start」が表示されるようならUSB Host Sheildが上手く動いていないと思われるので、arduinoUnoとの接続など再確認してください。

void setup() {
  Serial.begin(115200);
  if (Usb.Init() == -1) {
    Serial.print(F("OSC did not start\r\n"));
    while (1); // Halt
  }
  Serial.print(F("PS4 Bluetooth Library Started\r\n"));
}

PS4コントローラのペアリングと接続

loop()のUsb.Task()でUSBのシーケンスを進めてBLE接続を行います。
PS4コントローラのshareボタンとPSボタンの同時長押しを行うと、コントローラのLEDが青色点滅となってペアリングが開始されます。ペアリングが開始されるとシーケンスが進み、その後接続完了まで進むとLEDが青色点灯となります。
接続完了するとPS4.connected()が真を返すようになります。

void loop() {
  Usb.Task();
  if (PS4.connected()) {
    // 接続すると、この節に入る
  }
}

繋がった後の処理は「if (PS4.connected()) {」の節に記述します。

スティックの傾きを取得する

以下の通りPS4コントローラのインスタンスPS4を作ります。

#include <PS4BT.h>
USB Usb;
BTD Btd(&Usb);
PS4BT PS4(&Btd, PAIR);

そしてメソッドgetAnalogHat()によって、スティックの傾き(0~255)が取得できます。
多少のブレはありますが、スティックのセンターは127です。
ps4stick.jpg
矢印の方向に値が大きくなるので、左=0、右=255、上=0、下=255となります。

uint8_t L_X = PS4.getAnalogHat(LeftHatX);
uint8_t L_Y = PS4.getAnalogHat(LeftHatY);
uint8_t R_X = PS4.getAnalogHat(RightHatX);
uint8_t R_Y = PS4.getAnalogHat(RightHatY);

左スティックの左右はLeftHatX、左スティックの上下はLeftHatY、右スティックの左右はRightHatX、右スティックの上下はRightHatY となります。

データの変換

ところで、スティックの傾きは256分割ですが、その精度を使う場面はあまり思いつかなかったので、15分割程度に丸めて使用することにしました。
ついでに、表示する際にわかりやすいので、0~255をA~Oの文字に変換します。
ただし、そのままmapで変換すると偏ってしまうので、センターより大きい値の時は補正します。

    char newer_L_X = 'A' + map(128 < L_X ? --L_X : L_X,0,255,0,15);
    char newer_L_Y = 'A' + map(128 < L_Y ? --L_Y : L_Y,0,255,0,15);
    char newer_R_X = 'A' + map(128 < R_X ? --R_X : R_X,0,255,0,15);
    char newer_R_Y = 'A' + map(128 < R_Y ? --R_Y : R_Y,0,255,0,15);
getAnalogHatの範囲 取りうる値の数 補正してmapで変換後の値 変換後の文字
0-16 17 0 A
17-33 17 1 B
34-50 17 2 C
51-67 17 3 D
68-84 17 4 E
85-101 17 5 F
102-118 17 6 G
119-136 センター 18 7 H
137-153 17 8 I
154-170 17 9 J
171-187 17 10 K
188-204 17 11 L
205-221 17 12 M
222-238 17 13 N
239-255 17 14 O

変換後の文字が変わらない間は処理しないようにします。

    static char older_L_X = 'A';
    static char older_L_Y = 'A';
    static char older_R_X = 'A';
    static char older_R_Y = 'A';
    if(older_L_X != newer_L_X || older_L_Y != newer_L_Y || older_R_X != newer_R_X || older_R_Y != newer_R_Y){
      older_L_X = newer_L_X;
      older_L_Y = newer_L_Y;
      older_R_X = newer_R_X;
      older_R_Y = newer_R_Y;

i2c送信

二つ目のarduinoに送信します。

      Wire.beginTransmission(8); //transmit to device #8
      Wire.write(newer_L_X);
      Wire.write(newer_L_Y);
      Wire.write(newer_R_X);
      Wire.write(newer_R_Y);
      Wire.endTransmission(); //stop transmitting

送信するデータをシリアルにも出して、比較できるようにしておきます。

      Serial.print(newer_L_X);
      Serial.print(newer_L_Y);
      Serial.print(newer_R_X);
      Serial.print(newer_R_Y);
      Serial.print(F("\r\n"));

i2c受信のスケッチ

二つ目のarduinoで受信します。
i2cスレーブのベースコードは[ファイル]-[スケッチ例]-[Wire]-[slave_receiver]を使用します。
slave_receiversample.png
修正後のスケッチはGitから取得してください。
i2cから受信する毎に4byteのデータをシリアルに出力する処理に変更しました。

#include <Wire.h>

char newer_L_X;
char newer_L_Y;
char newer_R_X;
char newer_R_Y;

void setup() {
  Wire.begin(8);                // join i2c bus with address #8
  Wire.onReceive(receiveEvent); // register event
  Serial.begin(115200);
}

void loop(){
  //...
  delay(100);
}

void receiveEvent(int howMany) {
  String str = "";
  while (Wire.available()) {
    char c = Wire.read();
    str += String(c);
    Serial.print(c);
  }
  Serial.print(F("\r\n"));
  newer_L_X = str.charAt(0);
  newer_L_Y = str.charAt(1);
  newer_R_X = str.charAt(2);
  newer_R_Y = str.charAt(3);
}

i2cのつなぎ方

i2cは3本のPIN(GND,A4,A5)をそれぞれ繋ぎます。
IMG_E1722.JPG
左がi2c送信側、右がi2c受信側です。上手く受け取れたようです。
psbletera.png

おわりに

今回はPS4コントローラを使用しましたが、PS3コントローラやXboxコントローラも、ヘッダファイルとクラスを変えれば、同様に使用できると思います。

14
15
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
14
15