概要
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割減くらい)のでありがたいです。