この記事は古いので
https://qiita.com/coppercele/items/57354f3e5a50ec7e0573
をご覧ください
ESP32(BLE Client)とBluetooth Gamepad(BLE Server)を接続してみました。
今回接続したのはこのゲームパッドです。
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の
部分を抜き出すとこうなっています。
なぜか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ラジコンに組み合わせるとこういう感じで動くようになります。
とりあえずノウハウを共有するために記事にしてみました。 ザックリと書いただけなので構成に難があったりよくわからない表現があったりすると思います。 疑問、ご意見があったらコメントやリプをください。 随時反映していこうと思います。#ESP32 とBTゲームパッドをBLE HIDで接続してラジコンを直接操作することに成功しました\(^o^)/
— もけ@ムギ㌠ (@coppercele) 2018年10月4日
HIDで直接通信してるのでシンプルに通信できるのがいい感じ!#arduino pic.twitter.com/TS8BR4nHn6