#概要
Arduino-ESP32 1.0.1 の頃に書いたBLEライブラリを使ったクライアント(BLEセントラル)のコードを、 1.0.5 + NimBLE に書き換えてみた(2021/3時点)
基本的には #include
するヘッダを <BLE*.h>
をやめて <NimBLEDevice.h>
1つだけにしてから、BLE* ってクラスを NimBLE* ってクラスにすれば9割5分完成、なんだけど、ちょいちょい仕様が違ったので、自分のコードで違ったところ4箇所まとめ。
(全ての機能を使っているわけでも、全ての違いを網羅したわけでもありません)
#見つかった違い
###1. NimBLEAdvertisedDeviceCallbacks::onResult の引数の型
BLEAdvertisedDeviceCallbacks::onResult
のパラメーターの BLEAdvertisedDevice
はオブジェクト渡しで、
NimBLEAdvertisedDeviceCallbacks::onResult
のパラメーターの NimBLEAdvertisedDevice*
はポインタ渡し。Nim付けてコンパイルするとエラーで教えてくれる。
NimBLE_Client.ino サンプルのように MyNimBLEAdvertisedDeviceCallbacks
派生させてるなら、その中の onResult
のパラメータ型変えて、 onResult
内の .
を ->
にしまくり、&
でアドレス渡してたら&
とらずポインタそのままにする感じで。
###2. NimBLEAddress は PUBLIC, RANDOM のような type 情報も持つ
自分は古いコードで
static BLEAddress bleaddrServer[NUMBER_OF_TARGET_DEVICE] = {
std::string("XX:XX:XX:XX:XX:XX"),
std::string("YY:YY:YY:YY:YY:YY"),
std::string("ZZ:ZZ:ZZ:ZZ:ZZ:ZZ")
};
的にやってて BLEClient::connect
時にこれ使って、BLE_ADDR_TYPE_RANDOM
指定していたので、配列を単純に NimBLEAddress
に置き換えたら、コンストラクタが第二引数を type をデフォルト値で補うことになる。デフォルト値は
NimBLEAddress(uint8_t address[6], uint8_t type = BLE_ADDR_PUBLIC);
なので、BLE_ADDR_PUBLIC
にされちゃいます。RANDOMにしたかったので普通にコケた。(細かいが定数名に _TYPEが 入るか入らないかの違いもある)
適切に初期化するには、複数引数をコンストラクタに渡して配列を初期化する必要がある。
static NimBLEAddress bleaddrServer[NUMBER_OF_TARGET_DEVICE] = {
NimBLEAddress(std::string("XX:XX:XX:XX:XX:XX"), BLE_ADDR_RANDOM),
NimBLEAddress(std::string("YY:YY:YY:YY:YY:YY"), BLE_ADDR_RANDOM),
NimBLEAddress(std::string("ZZ:ZZ:ZZ:ZZ:ZZ:ZZ"), BLE_ADDR_RANDOM)
};
(まぁ違う方法で解決しちゃったのだけど。3参照)
###3.BLEClient::connect
BLEClient::connect
のとき、引数に std::string
なアドレスと BLE_ADDR_TYPE_RANDOM
等を指定できるオーバーロード関数があったが、NimBLEにはない。
2で書いたとおり、NimBLEAddress型で NimBLEAddress( std::string なアドレス, BLE_ADDR_RANDOM )
と準備してから connect
に渡せば良い。(定数名の _TYPE 有無に注意)
なお、スキャンして見つかったら接続、というコードであれば、スキャンで見つかった情報に RANDOM/PUBLIC とかが入ってるので、スキャンして見つけた NimBLEAdvertisedDevice
の getAddress()
で得られる NimBLEAddress
型のアドレスを、グローバル変数にでもコピーしておいて(実体7バイトだからええやろ) connect
に渡せば自分で PUBLICだなんだ考えなくても良い。
サンプルコード NimBLE_Client.ino では、「NimBLEAdvertisedDevice
のポインタ」をグローバル変数に入れてたが、ポインタの先の本体がいつどうなるか知らない自分には怖いし、NimBLEClient::connect
のパラメーターに NimBLEAdvertisedDevice*
渡す場合の関数見たけど中で device->getAddress()
してるだけだったので、だったら NimBLEAddress
オブジェクトのコピーを7バイト、グローバル変数においとけばいいかな、って感じ。
###4. 16bit UUID
CCCD の UUID は NimBLEUUID(uint16_t(0x2902))
だとうまくいったが、NimBLEUUID uuidCCCD( "00002902-0000-1000-8000-00805f9b34fb" )
って128bit表記から生成したらうまくいかなかった。
NimBLEUUID は内部で ble_uuid_any_t m_uuid
ってメンバ変数を持っていて、コンストラクタも
NimBLEUUID::NimBLEUUID(uint16_t uuid) {
m_uuid.u.type = BLE_UUID_TYPE_16;
m_uuid.u16.value = uuid;
m_valueSet = true;
} // NimBLEUUID
なーんてやってるから、128bit と 16bit で同じにならないみたい。(同じだと思っていたのに)
なにやら 32bitってのもある模様。
#おわりに
こんだけで、NimBLERemoteService
とか NimBLERemoteCharacteristic
, NimBLERemoteDescriptor
はそのまんまで動いた。client や notify の callback も。
(全ての機能を使っているわけでも、全ての違いを網羅したわけでもありません)
サーバー(ペリフェラル)側も UUIDとAddress は共通として、その他の差はそんなにないんじゃないかな。ペリフェラルのコード自分で書いたことないから知らんけど。
bin は NoOTA にしなくても普通に収まるくらい小さい(3割減くらい)のでありがたいです。