はじめに
この記事はカヤックUnity アドベントカレンダー2018の5日目の記事になります。
こんにちは!デバイスエンジニアの宮野です。
今日はBluetooth内蔵のマイコンボードESP32とUnityを接続してシンプルなコントローラを作成します。 Bluetooth Serial通信でUnityにボタン情報を送り、Unity側でその情報をもとに画面上のオブジェクトを操作します。

環境と用意するもの
- Windows 10 PC
- Unity 2018.2.7f1
- Arduino IDE 1.8.7
- ESP32開発ボード
- 両面スルーホールユニバーサル基板
- タクトスイッチ
- 工具、材料類(はんだごて、はんだ、すずめっき線等)
Arduino IDEとESP32のセットアップ
1.Arduino IDEでESP32が使えるようにする
Arduino IDEを立ち上げ、ファイル > 環境設定を開きます。
追加のボードマネージャのURL:にhttps://dl.espressif.com/dl/package_esp32_index.jsonを入力してOKを押します。

ツール > ボード > ボードマネージャを開きます。
検索窓にesp32と入力し、esp32 by Espressif Systemsをインストールします。

2. テストプログラムの書き込み
arduino-esp32/SerialToSerialBT.ino at master · espressif/arduino-esp32やESP32でserial bluetooth接続 - Qiitaを参考に以下のプログラムを打ち込みます。
ファイル > スケッチ例 > BluetoothSerial > SerialToSerialBTからもサンプルプログラムを参照できます。

#include "BluetoothSerial.h" #if !defined(CONFIG_BT_ENABLED) || !defined(CONFIG_BLUEDROID_ENABLED) #error Bluetooth is not enabled! Please run `make menuconfig` to and enable it #endif BluetoothSerial SerialBT; void setup() { SerialBT.begin("ESP32"); //Bluetooth device name } void loop() { SerialBT.println("Hello"); delay(1000); }
ESP32をUSBポートに接続して、ツール > ボード:でESP32 Dev Moduleを選択し、シリアルポートでESP32が接続されているポートを選びます。
その他の設定は変更していません。

スケッチ > マイコンボードに書き込むを押し、プログラムを書き込みます。
書き込みが完了すると、IDEの下のほうにボードへの書き込みが完了しました。というメッセージが表示されます。
3. テストプログラムの確認
Windowsメニューの設定からデバイスを選択しBluetoothまたはその他のデバイスを追加するを押します。

デバイスを追加するでBluetoothを選択します。

先ほど書き込んだプログラムで指定したデバイス名(今回は)ESP32を選択してペアリングします。


デバイスマネージャーを開くとCOM4とCOM5が増えています。

Arduino IDEに戻りツール > シリアルポートでCOM4を選択し、シリアルモニタを開きます。
左下のbaudrateを115200bpsにすると、1秒おきにHelloの文字が表示されます。

うまく表示されない場合は別のポートを選択して再度試してください。
Unityで受信する
新しいプロジェクトを作成し、SerialPort または Uniduino を使った Unity と Arduino を連携させる方法調べてみた - 凹みTipsを元にシリアル通信を行うプログラムを作成します。
SerialHanderはSerialHandler.csをそのまま利用します。オブジェクトにアタッチしてInspectorで上で指定したポート名を入力してください。
シリアル通信で受け取った情報をConsoleに表示するプログラムは上記サイトのRotateByAccelerometer.csを元に作成しました。こちらもオブジェクトにアタッチして、上のSerialHandlerがアタッチされたオブジェクトをInspectorで紐づけてください。 実行するとConsoleにHelloの文字が表示されます。
using UnityEngine; using System.Collections; using System.Collections.Generic; public class ConsoleOutput : MonoBehaviour { public SerialHandler serialHandler; void Start() { serialHandler.OnDataReceived += OnDataReceived; } void OnDataReceived(string message) { var data = message.Split( new string[]{"\n"}, System.StringSplitOptions.None); if (data.Length != 1) return; Debug.Log(data[0]); } }
ESP32にボタンを接続する
ユニバーサル基板にタクトスイッチを配置して、タクトスイッチを押すとGNDに接続されるように配置します。 今回は以下のようにピンを割り当てました。
| ボタン名 | ピン番号 |
|---|---|
| 左ボタン | 33 |
| 上ボタン | 25 |
| 下ボタン | 26 |
| 右ボタン | 27 |
| Aボタン | 14 |
| Bボタン | 12 |
回路図は割愛しますが、こんな感じになりました(配線を間違えたのでピンソケットがはみ出てます)。


ESP32にボタン用のプログラムを書き込む
先ほどのHelloの代わりに、押されたボタンに応じてUp、Down、Right、Left、A、Bの文字を送るプログラムを書きます。
#include "BluetoothSerial.h" #if !defined(CONFIG_BT_ENABLED) || !defined(CONFIG_BLUEDROID_ENABLED) #error Bluetooth is not enabled! Please run `make menuconfig` to and enable it #endif // Pin Assingment const int buttonLeft = 33; const int buttonUp = 25; const int buttonDown = 26; const int buttonRight = 27; const int buttonA = 14; const int buttonB = 12; BluetoothSerial SerialBT; void setup() { SerialBT.begin("ESP32"); //Bluetooth device name pinMode(buttonLeft, INPUT_PULLUP); pinMode(buttonUp, INPUT_PULLUP); pinMode(buttonDown, INPUT_PULLUP); pinMode(buttonRight, INPUT_PULLUP); pinMode(buttonA, INPUT_PULLUP); pinMode(buttonB, INPUT_PULLUP); } void loop() { if(digitalRead(buttonLeft) == LOW){ SerialBT.println("Left"); } else if(digitalRead(buttonUp) == LOW){ SerialBT.println("Up"); } else if(digitalRead(buttonDown) == LOW){ SerialBT.println("Down"); } else if(digitalRead(buttonRight) == LOW){ SerialBT.println("Right"); } else if(digitalRead(buttonA) == LOW){ SerialBT.println("A"); } else if(digitalRead(buttonB) == LOW){ SerialBT.println("B"); } else { SerialBT.println(); } delay(100); }
ボタン情報に応じたUnityプログラムを作成する
ESP32から送られてくる文字をもとに、画面上のオブジェクトを動かすプログラムを書いてCubeにアタッチします。
using UnityEngine; using System.Collections; using System.Collections.Generic; public class ObjectController : MonoBehaviour { public SerialHandler serialHandler; public float delta = 0.01f; private float deltaPos; private Vector3 pos; void Start() { serialHandler.OnDataReceived += OnDataReceived; } void OnDataReceived(string message) { Vector3 pos = transform.localPosition; var data = message.Split( new string[]{"\n"}, System.StringSplitOptions.None); Debug.Log(message); switch (data[0]){ case "Up": deltaPos = delta * 1; pos.y += deltaPos; break; case "Down": deltaPos = delta * -1; pos.y += deltaPos; break; case "Left": deltaPos = delta * -1; pos.x += deltaPos; break; case "Right": deltaPos = delta * 1; pos.x += deltaPos; break; default: break; } transform.localPosition = pos; } }
動かしてみるとこんな感じです。

ESP32につながっているUSBをPCから抜いてモバイルバッテリーにすると、より無線感が味わえます。 今回A、Bボタンは使いませんでしたが、色を変えるなりジャンプさせるなり、好きに使ってください。
ユニバーサル基板ではなく基板を設計して切削や外注で作ったり、外装を3Dプリンタで作ったりすれば、より実用的になると思います。ボタンも用途に応じて増やしたり減らしたり、ボタンではなく別のセンサを使ったりと必要に応じてカスタマイズしてください!
最後に
カヤックではゲームだけでなくインタラクティブサイネージやAR/VR案件でもUnityをよく使っています。私のようなデバイスエンジニアもUnityを使うことは多いです。 興味のある人は採用ページをのぞいてみてください。
明日は、中山大輔による「動的に枠を描く話」です。