ESP32を使ったBLE実装時の再接続について

ペリフェラル側の実装です。

# はじめに
この記事は、BLEを接続し切断後の検索に引っかからないことや接続できないことについて対処する方法を書きます。そのため、GATT通信などについては本記事では触れませんのでご了承ください。
筆者自身、ESP32をBLEに接続し再接続するためには電源をリセットするのが当たり前だと半年ほど思っていました。そのためBLEに長くかかわることで知識が増えてしまった（？）ので、詳しく解説できるところは解説していきます。

## 結論
Disconnectされた時にAdvertisingを再始動することで解決できました。
実装部分とその説明については[こちら](https://qiita.com/drafts/00fc746892570f8d1c38/edit#class-servercallbacks)をご覧ください

# BLEの知識
コードだけ見たい方は[こちら](https://qiita.com/drafts/00fc746892570f8d1c38/edit#%E5%AE%9F%E8%A3%85)へお飛びください。

## 通信の仕組み
こちらBLE通信の構造図です。
![BLE通信構造](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1408220/b0f37dd5-e5a8-c6fa-7a02-20c995ac6fff.png)

### Peripheral - ペリフェラル
例）ワイヤレスイヤホン、スマートウォッチなど
接続を要求する通信パケットを送信する。

### Central - セントラル
例）スマートフォン、タブレットなど
アドバタイズをスキャンしペリフェラルと接続する

### Advertising - アドバタイジング
::: note info
再接続の鍵はここになります。
:::
直訳すると**広告宣伝**となります。
これをBLEで置き換えて考えてみると、Scanをしてくれるセントラルへ自分のことを探してもらえるように、電波を発信してると考えるといいと思います。

機能的には、ペリフェラル側でAdvertisingで電波を発信することにより、セントラル側でScanでその電波を受信することによりそのデバイスを発見する/されることができます。

### Scan - スキャン
アドバタイジングのところで少し紹介したのですが、ペリフェラル側で発信されるAdvertisingを検索する役割です。

### Connect - コネクト
Scanで発見したデバイスの、名前やアドレスを使用してペリフェラルへ接続要求を送信しコネクション状態へ遷移します。
一連のBLE処理が終了したのちに、Disconnect処理でペリフェラルと通信を終了します。

## include<BLEDevice.h>
このライブラリの内部では
```cpp:BLEDevice.h
#include "BLEServer.h"
#include "BLEClient.h"
#include "BLEUtils.h"
#include "BLEScan.h"
#include "BLEAddress.h"
```
これらがincludeされているので、基本的なBLE機能を活用したい場合はBLEDevice.hをincludeするだけで良さそうです。
以下の関数などの説明はSetupの流れに従って書いていきます。

### BLEの初期化
まずはライブラリでもある`BLEDevice`を初期化します。初期化したBLEDeviceから`BLEServer`を生成し、接続のCallbackを受け取るためにCallback Classをセットします。最後に生成したBLEServerから`BLEService`を生成し、BLEとしての機能を開始し、初期化を完了します。

### アドバタイジングを開始する
BLEDeviceから`BLEAdvertising`を取得する。AdvertisingはScanされるときに、Peripheral側の情報をCentral側へ渡すため、`ServiceUUID`やレスポンスの有無を設定する。
`setMinPreferred(0x06)`や`setMinPreferred(0x12)`はiPhoneをCentralとしたときに不具合が発生するため、これらの設定を追加する。調べた情報によるものなので、再現方法や原因はわかりかねます。
Advertisingの設定を完了したら`startAdvertising()`でアドバタイジングを開始する。

### class ServerCallbacks
`BLEServerCallbacks`を継承することで、BLEServerのCallbackとして機能させる。
`void onConnect(BLEServer* pServer)`が接続時に呼び出されるため、その時点でSetupで開始したAdvertisingを停止する。もしかしたら内部処理で`stopAdvertising()`が呼び出されているかもしれないが一応停止させる。
`void onDisconnect(BLEServer* pServer)`は接続が切断された時に呼び出される。**そのためここで`startAdvertising`することで、もう一度Scanに引っかかるようにする**。これでデバイスを再起動しなくとも一度切断した後のデバイスを再利用することができるようになった。

# 実装
```cpp:example.ino
// ライブラリをプロジェクトにincludeする
#include <BLEDevice.h>

// UUIDを生成する際は以下のページをご覧ください: 
// https://www.uuidgenerator.net/
#define BLE_NAME            "任意のデバイス名に置き換えてください"
#define SERVICE_UUID        "オリジナルのUUIDに置き換えてください"

// Serverのコールバックで接続に対する処理を行う
class ServerCallbacks : public BLEServerCallbacks {
  // 接続時に呼び出される
  void onConnect(BLEServer* pServer) {
    // 接続されたらAdvertisingを停止する
    BLEDevice::stopAdvertising();
  }

  // 切断された時に呼び出される
  void onDisconnect(BLEServer* pServer) {
    // 再接続のためにもう一度Advertisingする
    BLEDevice::startAdvertising();
  }
};

void setup() {
  // BLEの初期化
  BLEDevice::init(DEVICE_NAME);
  BLEServer* pServer = BLEDevice::createServer();
  pServer->setCallbacks(new ServerCallbacks());
  BLEService* pService = pServer->createService(SERVICE_UUID);
  
  pService->start();

  // Scanに引っかかるようにアドバタイジングする
  BLEAdvertising* pAdvertinsing = BLEDevice::getAdvertising();
  pAdvertinsing->addServiceUUID(SERVICE_UUID);
  pAdvertinsing->setScanResponse(true);
  // iPhoneの接続問題を解決するため
  pAdvertinsing->setMinPreferred(0x06); 
  pAdvertinsing->setMinPreferred(0x12);
  BLEDevice::startAdvertising();
}
```

# 参考
http://marchan.e5.valueserver.jp/cabin/comp/jbox/arc212/doc21201.html
http://zattouka.net/GarageHouse/micon/ESP/ESP32-WROOM-32E/ESP32E_BLE1.htm