2
4

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

Arduino-ESP32のBLEをNimBLEに置き換えてみたらちょっぴり違った(クライアント側)

Posted at

#概要
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 とかが入ってるので、スキャンして見つけた NimBLEAdvertisedDevicegetAddress() で得られる 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割減くらい)のでありがたいです。

2
4
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
4

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?