[Unity]M5StickCとBLE通信(iOS実機/macOSエディタ

# Unity と M5StickC で BLE通信(iOS/macOSエディタ
* Unity の iOSアプリや、mac エディタで、M5StcikC や M5Stack などと Bluetooth Low Energy で通信する事ができました
* M5StickC から Unity に通知したり、Unity から M5StickC に書き込む事で、相互にデータを送受信する事が可能です
* 下の動画↓では、M5StickC のボタンを押すと Unity 内の数字が増え、Unity 内のボタンを押すと M5StickC の数値が増える事を確認できます

<blockquote class="twitter-tweet"><p lang="ja" dir="ltr">unityとm5stickでBLE通信できた！ <a href="https://t.co/wMqooDpYM5">pic.twitter.com/wMqooDpYM5</a></p>&mdash; ふじき (@fzkqi) <a href="https://twitter.com/fzkqi/status/1422012021902512130?ref_src=twsrc%5Etfw">August 2, 2021</a></blockquote> <script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>

# サンプル
* リポジトリはこちらです↓

https://github.com/fuziki/UnityCoreBluetooth

* Unity には BLE の機能がないので、Swift で Native Plugin を作って実装しています
* [Examples/UnityExample/Assets/Plugins/UnityCoreBluetooth](https://github.com/fuziki/UnityCoreBluetooth/tree/develop/Examples/UnityExample/Assets/Plugins/UnityCoreBluetooth) をプロジェクトにコピーする事で、導入する事が可能です

# M5StickC の実装
* 基本的に、BLE周りの処理は M5StcikC は M5Stack と同じです
* スケッチは[こちら](https://github.com/fuziki/UnityCoreBluetooth/blob/develop/Examples/M5Peripheral/M5Peripheral.ino)

## 設定
* M5StickC や M5Stack は Peripheral として動かします
* Server 名は "M5StickC" や "M5Stack" などわかりやすい名前であれば何でも良いです
* SERVICE_UUID と CHARACTERISTIC_UUID は適当に決めます
* 今回は、1つの Characteristic で送受信します

```cpp
#define SERVICE_UUID        "FE55"
#define CHARACTERISTIC_UUID "beb5483e-36e1-4688-b7f5-ea07361b26a8"
#define SERVER_NAME         "M5StickC"
```

## Write のコールバック

* BLECharacteristic の getValue で書き込まれた値を取得します

```cpp
class MyCallbacks: public BLECharacteristicCallbacks {
  void onWrite(BLECharacteristic *pCharacteristic) {
    std::string value = pCharacteristic->getValue();
    M5.Lcd.setCursor(0, 160);
    M5.Lcd.setTextSize(5);
    M5.Lcd.println(value.c_str());
  }
};
```

## setup ble
* MyCallbacks をコールバックとして登録した Characteristic が追加された Service を作成し、Advertising を開始して、Unity から接続できるようにします

```cpp
void setup() {
  BLEDevice::init(SERVER_NAME);
  BLEServer *pServer = BLEDevice::createServer();
  pServer->setCallbacks(new MyServerCallbacks());
  BLEService *pService = pServer->createService(SERVICE_UUID);
  pCharacteristic = pService->createCharacteristic(
                                         CHARACTERISTIC_UUID,
                                         BLECharacteristic::PROPERTY_WRITE_NR |
                                         BLECharacteristic::PROPERTY_NOTIFY |
                                         BLECharacteristic::PROPERTY_INDICATE
                                       );
  pCharacteristic->setCallbacks(new MyCallbacks());
  pCharacteristic->addDescriptor(new BLE2902());

  pService->start();
  BLEAdvertising *pAdvertising = pServer->getAdvertising();
  pAdvertising->start();  
}
```

## 値の通知
* ボタンが押されたら、Characteristic に値を設定し、通知します

```cpp
void loop() {
  if(M5.BtnB.wasPressed()) {
    pCharacteristic->setValue(counter);
    pCharacteristic->notify();
    counter += 1;
  }
  M5.update();
}
```

# Unity の実装
## スキャンの開始
* CoreBluetoothManager のシングルトンのインスタンスを取得します
* poweredOn になることを確認して、スキャンを開始します

```cs
manager = CoreBluetoothManager.Shared;
manager.OnUpdateState((string state) =>
{
    Debug.Log("state: " + state);
    if (state != "poweredOn") return;
    manager.StartScan();
});
```

## 接続したいデバイス名のデバイスを見つけたら接続する

```cs
manager.OnDiscoverPeripheral((CoreBluetoothPeripheral peripheral) =>
{
    if (peripheral.name != "")
        Debug.Log("discover peripheral name: " + peripheral.name);
    if (peripheral.name != "M5StickC") return;

    manager.StopScan();
    manager.ConnectToPeripheral(peripheral);
});
```

## デバイスに接続したらサービスを探す

```cs
manager.OnConnectPeripheral((CoreBluetoothPeripheral peripheral) =>
{
    Debug.Log("connected peripheral name: " + peripheral.name);
    peripheral.discoverServices();
});
```

## 対象の uuidの サービスが見つかったら characteristic を探す

```cs
manager.OnDiscoverService((CoreBluetoothService service) =>
{
    Debug.Log("discover service uuid: " + service.uuid);
    if (service.uuid != "FE55") return;
    service.discoverCharacteristics();
});
```

## usage が notify の characteristic が見つかったら通知を有効にする
* 通知を有効にすることで、M5StickC からデータを受け取ることが可能になります

```cs
manager.OnDiscoverCharacteristic((CoreBluetoothCharacteristic characteristic) =>
{
    string uuid = characteristic.uuid;
    string usage = characteristic.propertis[0];
    Debug.Log("discover characteristic uuid: " + uuid + ", usage: " + usage);
    if (usage != "notify") return;
    characteristic.setNotifyValue(true);
});
```

## characteristic から通知があったら、データを受け取る
* リアルタイムで受け取れるのですが、メインスレッド保証ではないです
* そこで、一度変数に格納して、Update関数などでUIに反映させます

```cs
manager.OnUpdateValue((CoreBluetoothCharacteristic characteristic, byte[] data) =>
{
    this.value = data;
    this.flag = true;
});
```

## Manager の動作開始
* コールバックの設定が終わった後に、Start関数を呼ぶことで、Bluetooth機能を起動します

```cs
manager.Start();
```

## 書き込み
* 書き込み先の characteristic に書き込みます
* System.Text.Encoding.UTF8.GetBytes を使って、文字列からバイト配列を取得します

```c#
private int counter = 0;
public void Write()
{
    characteristic.Write(System.Text.Encoding.UTF8.GetBytes($"{counter}"));
    counter++;
}
```

# おわりに
* M5StickC や M5Stack と Unity が外部機器無しで通信できるようになる BLE はとても便利です
* Swift で実装しているので、iOS実機、macエディタ向けの Native Plugin を同時に開発できました
* エディタでも動くので、動作検証が容易になります