Help us understand the problem. What is going on with this article?

ESP32とBluetooth GamepadをHIDで接続してラジコンを操作してみた

この記事は古いので
https://qiita.com/coppercele/items/57354f3e5a50ec7e0573
をご覧ください

-

-

-

-

-

-

-

-

-

ESP32(BLE Client)とBluetooth Gamepad(BLE Server)を接続してみました。

今回接続したのはこのゲームパッドです。

https://www.amazon.co.jp/gp/product/B07DDCB77J/

VR CASE2という奴らしいですが現在は品切れしてます。

まずはESP32ライブラリのBLE_clientサンプルスケッチを試してみます。

まずUUIDをHID/Reportとして指定します。

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

そしてBLE_clientを実行してみるとGamepadに接続するところまでは行くのですが、
データのやり取りができません。
println()等でデバッグしても正常に処理が行われてるように見えます。
readValue()すると何かの値が返ってくることがありますがこれは関係ありません。

いったんここでGamepadの仕様を確認してみましょう。
BLE Tool - Google Play のアプリ
https://play.google.com/store/apps/details?id=com.cozyoz.bletool
このアプリを使って(同名の別アプリがあるので注意)HIDのReport Characteristicの
部分を抜き出すとこうなっています。

discriptors.gif

なぜかReportが複数存在しています?!
ここでGamepadを操作してみるとわかるのですが、
データをやり取りできるのは一番上のCharacteristicだけです。
(DiscriptorのValue fieldが01 00,04 00の物)

というわけでここでソースに戻ります。
BLE_clientの中には原因がないのでESP32_BLEのライブラリの中を見てみます。

ちなみにAruduino C++のソースはこれを使って編集してます。
http://eclipse.baeyens.it/stable.php?OS=Windows

pRemoteService->getCharacteristic(charUUID);
からソースを追っていくと BLERemoteService::retrieveCharacteristics()に
行きつくのですが、

m_characteristicMap.insert(std::pair<std::string, BLERemoteCharacteristic*>(pNewRemoteCharacteristic->getUUID().toString(), pNewRemoteCharacteristic));

ここでMapにCharacteristicを格納しているのですが、
「UUIDをキーにしているためCharacteristicが複数存在しても1つしか格納されない」
のです。

Characteristicが複数存在するとは想定されてないわけですね。
なので、たまたま正解のCharacteristicが当たればよいのですが、
はずれのCharacteristicが返ってきたら通信できないわけです。

ここで開発者さんに相談して解決策を模索しました。

まずBLERemoteService::retrieveCharacteristics()のMapのkeyをhandleに変更して
複数あるCharacteristicが登録されるようにします。

m_characteristicMapByHandle.insert(std::pair<uint16_t, BLERemoteCharacteristic*>(result.char_handle, pNewRemoteCharacteristic));

ただこのままだとどれが正解のCharacteristicだかわからないので、
BLE_clientにCharcteristicのMapをメソッドを渡してその中で処理します。

// BLERemoteServiceからマップを取得
pCharacteristicMap = pRemoteService->getCharacteristicsByHandle();
    // iteratorで処理
    for (auto itr = pCharacteristicMap->begin();
            itr != pCharacteristicMap->end(); ++itr) {

        if (charUUID.toString() == itr->second->getUUID().toString()) {
            // UUIDがReport(2a4d)なら
            Serial.print("CharUUID matched: ");
            Serial.println(itr->second->getUUID().toString().c_str());

            if (itr->second->canRead() && itr->second->canNotify()
                    && !itr->second->canWrite()) {
         // CharacteristicのPropertyがRead=1,Notify=1,Write=0
                // 添付した画像を参照
                // DiscriptorのValueFieldを取得する手段は無い
                Serial.print("Correct : ");
                // Notifyに登録
                pRemoteCharacteristic = itr->second;
                pRemoteCharacteristic->registerForNotify(notifyCallback);

            }
            else {
                Serial.println("Incorrect");
            }
        }
    }

本当はDiscriptorのValueFieldがnullなら排除したかったのですが、
Notifyに複数のCharacteristicを登録しても問題ありませんでした。

というわけで正しいCharacteristicをNotifyに登録すれば
Notify callbackが正常に動作するようになります。

Notify callback for characteristic 00002a4d-0000-1000-8000-00805f9b34fb of data length 2
Data : 0000

あらかじめ作ってあったESP32ラジコンに組み合わせるとこういう感じで動くようになります。

とりあえずノウハウを共有するために記事にしてみました。
ザックリと書いただけなので構成に難があったりよくわからない表現があったりすると思います。
疑問、ご意見があったらコメントやリプをください。
随時反映していこうと思います。

Why do not you register as a user and use Qiita more conveniently?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away