ESP32とBluetoothコントローラーをBLE HIDで接続する方法

#※Arduino-ESP32 1.0.2当時の情報です※

ESP32とBluetoothコントローラーをBLE HIDで接続する方法(ESP32 1.0.3編) - Qiita
https://qiita.com/coppercele/items/57354f3e5a50ec7e0573
が最新ですのでご覧ください

-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-


Bluetooth 3.0(Bluetooth classic)で接続する場合はこちらへ

ESP32とBluetoothコントローラーをHID(Bluetooth Classic)で接続する簡単な方法 - Qiita
https://qiita.com/coppercele/items/b1df50cea8fa883fc692



##コントローラーのHIDを確認
まずコントローラーのHIDがどうなっているのかを確認しましょう。

Androidならこのアプリを使ってHuman Interface DeviceのReport部分を確認します。

BLE Tool - Google Play のアプリ
https://play.google.com/store/apps/details?id=com.cozyoz.bletool

![discriptors.gif](https://qiita-image-store.s3.amazonaws.com/0/298776/0ffd12db-3e2e-e257-55b1-21b54cae65dc.gif)

ここでReportが一個だけならBLE UUIDの変更のみでつながります(多分)
Reportが複数ある場合は以下の手順が必要です。

##BLE_clientの編集
ESP32ライブラリのBLE_clientを使用します。

BLE UUIDをHIDとReportの物に変更します。

```cpp
// UUID HID
static BLEUUID serviceUUID("00001812-0000-1000-8000-00805f9b34fb");
// UUID Report Charcteristic
static BLEUUID charUUID("00002a4d-0000-1000-8000-00805f9b34fb");
```

##BLERemoteServiceの編集

ESP32ライブラリ内のBLERemoteService.cppを編集します

このアプリを使うとArduino C++スケッチを編集してスケッチの送信まで出来るので便利です。
http://eclipse.baeyens.it/stable.php?OS=Windows

void BLERemoteService::retrieveCharacteristics()メソッド内のMapにCharacteristicを格納する部分を変更します。
std::map<uint16_t, BLERemoteCharacteristic *> m_characteristicMapByHandleというキーがhandleになっているMapがある(現状使われていない)ので
こちらにもCharacteristicを追加するようにします。

```cpp
m_characteristicMap.insert(std::pair<std::string, BLERemoteCharacteristic*>(pNewRemoteCharacteristic->getUUID().toString(), pNewRemoteCharacteristic));
↓
m_characteristicMap.insert(std::pair<std::string, BLERemoteCharacteristic*>(pNewRemoteCharacteristic->getUUID().toString(), pNewRemoteCharacteristic));
m_characteristicMapByHandle.insert(std::pair<uint16_t, BLERemoteCharacteristic*>(result.char_handle, pNewRemoteCharacteristic));
```

BLERemoteService::getCharacteristic(UUID)と
BLERemoteService::getCharacteristics()はm_characteristicMapByHandleに
対応していないのでBLERemoteService::getCharacteristics()をコピーして
BLERemoteService::getCharacteristicsByHandle()を追加します。

```cpp
std::map<uint16_t, BLERemoteCharacteristic *> * BLERemoteService::getCharacteristicsByHandle() {

	if (!m_haveCharacteristics) {
		retrieveCharacteristics();
	}
	ESP_LOGD(LOG_TAG, "<< getCharacteristicsByHandle() for service: %s", getUUID().toString().c_str());
	return &m_characteristicMapByHandle;
} // getCharacteristicsByHandle
```

##BLE_clientの編集
BLERemoteCharacteristic* BLERemoteService::getCharacteristic(BLEUUID uuid)
はCharacteristicが複数ある事を想定していないので
std::map<std::string, BLERemoteCharacteristic *> * BLERemoteService::getCharacteristicsByHandle()
でCharacteristicのMapを丸ごと取得してCharacteristic UUIDとPropertyなどを確認してNotifyに登録します。

BLE_client.connectToServer()内

```Cpp

    pRemoteCharacteristic = pRemoteService->getCharacteristic(charUUID);
    if (pRemoteCharacteristic == nullptr) {
      Serial.println(charUUID.toString().c_str());
      return false;
    }
    Serial.println(" - Found our characteristic");

    // Read the value of the characteristic.
    std::string value = pRemoteCharacteristic->readValue();
    Serial.print("The characteristic value was: ");
    Serial.println(value.c_str());
   pRemoteCharacteristic->registerForNotify(notifyCallback);

↓

    std::map<uint16_t, BLERemoteCharacteristic *> * pCharacteristicMap;
    pCharacteristicMap = pRemoteService->getCharacteristicsByHandle();
    // CharacteristicのMapを取得
    Serial.print("Number of Charactirstic: ");
    Serial.println(pCharacteristicMap->size());

    for (auto itr = pCharacteristicMap->begin();
            itr != pCharacteristicMap->end(); ++itr) {
        Serial.print("first(Handle): ");
        Serial.print(itr->first);
        Serial.print(" second(UUID):");
        Serial.println(itr->second->getUUID().toString().c_str());

        if (charUUID.toString() == itr->second->getUUID().toString()) {
        // Characteristic UUIDをチェック
            Serial.print("CharUUID matched: ");
            Serial.println(itr->second->getUUID().toString().c_str());

            Serial.print("Correct : ");
            pRemoteCharacteristic = itr->second;
                // Notifyに登録
                pRemoteCharacteristic->registerForNotify(notifyCallback);

        }

    }
```

まずは複数あるPropertyを全部Notifyに登録します。
不都合があればHandleを調べて除外しましょう。

接続がうまくいくとボタンを押すたびにnotifyCallback()が実行されてデータが送られてきます。
Notify callback for characteristic 00002a4d-0000-1000-8000-00805f9b34fb of data length 2
Data : 0000

自分が使ってるコントローラーは16bitでデータを送ってくるのでこのように処理しています。

```C++
        // *pDataはuint8_t*
        int data = *pData;
        data = data << 8;
        data += *(pData + 1);
        // 16bit intに結合

        sprintf(str, "%04x", data);
        Serial.print(str);
        Serial.print(" ");
        // 以下キーコード別の処理
        decodeData(data);
```
```c++
const uint16_t LEVER_UP = 0x0040;    // 00000000 01000000
const uint16_t LEVER_DOWN = 0x0080;  // 00000000 10000000

static void decodeData(int data) {
    if (data & LEVER_UP) {
        if (data & LEVER_DOWN) {
            Serial.println("DOWN");
        }
        else {
            Serial.println("UP");
        }
    }
    else if (leftMoving) {
        leftStop();
    }
    if (data & LEVER_LEFT) {
        if (data & LEVER_RIGHT) {
            Serial.println("RIGHT");
        }
        else {
            Serial.println("LEFT");
        }
    }
    if (data & BUTTON_A) {
        Serial.println("A");
    }
    if (data & BUTTON_B) {
        Serial.println("B");
    }
以下略
```

<blockquote class="twitter-tweet" data-lang="ja"><p lang="ja" dir="ltr"><a href="https://twitter.com/hashtag/ESP32?src=hash&amp;ref_src=twsrc%5Etfw">#ESP32</a> とBTゲームパッドをBLE HIDで接続してラジコンを直接操作することに成功しました＼(＾o＾)／<br>HIDで直接通信してるのでシンプルに通信できるのがいい感じ！<a href="https://twitter.com/hashtag/arduino?src=hash&amp;ref_src=twsrc%5Etfw">#arduino</a> <a href="https://t.co/TS8BR4nHn6">pic.twitter.com/TS8BR4nHn6</a></p>&mdash; もけ@ムギ㌠ (@coppercele) <a href="https://twitter.com/coppercele/status/1047896666639020032?ref_src=twsrc%5Etfw">2018年10月4日</a></blockquote>

以上です。