Adafruit Bluefruit nRF52 向けの Arduino 開発環境で、デバイス側を Peripheral にしたとき複数の Bluetooth LE 接続を受け入れる方法を説明します。
Adafruit Bluefruit nRF52 とは
Adafruit が販売している、Nordic nRF52 系チップを搭載した開発ボードです。
Arduino 開発環境がしっかりしていて、気軽に BLE が使えて大変便利です。
https://github.com/adafruit/Adafruit_nRF52_Arduino
具体的な製品は、以下の2つになります。
Peripheral で複数の BLE コネクションを張る
Adafruit_nRF52_Arduino では、バージョン 0.10.0 から複数の BLE コネクションを同時に処理することができるようになっています。
ここでは、ボード側が Peripheral で複数の Central からの接続を受け入れるようなものを想定しています。
Central になったときの複数接続も、ライブラリは多分対応していると思う。(使ったことがない。
サンプルコード
UART サービスを用いたサンプルコードは、ライブラリの examples に含まれています。
https://github.com/adafruit/Adafruit_nRF52_Arduino/blob/master/libraries/Bluefruit52Lib/examples/Peripheral/bleuart_multi/bleuart_multi.ino
具体的な使い方
最大接続数の指定
Bluefruit ライブラリを begin()
するときに、最大接続数を指定します。
通常では、1 になっていて単一のコネクションしか受け付けません。
1 以上に値にすると、複数のコネクションを受け付けることができるようになります。
// Initialize Bluefruit with max concurrent connections as Peripheral = 2
Bluefruit.begin(2);
コネクションの特定: conn_hdl
複数コネクションが確立されたとき、どのコネクションからの要求なのかは conn_hdl
というパラメーターにより特定することができます。
例えば、ある Characteristic への write callback handler は、conn_hdl
をパラメーターに含みます。
https://github.com/adafruit/Adafruit_nRF52_Arduino/blob/master/libraries/Bluefruit52Lib/src/BLECharacteristic.h#L79
typedef void (*write_cb_t) (uint16_t conn_hdl, BLECharacteristic* chr, uint8_t* data, uint16_t len);
殆どの callback handler のパラメーターに conn_hdl
が含まれていて、どのデバイスからのオペレーションなのかが特定できます。(connect, disconnect, write など)
詳しいコネクションの情報を取得するには、conn_hdl
を利用して以下のように取得します。
BLEConnection* connection = Bluefruit.Connection(conn_hdl);
Notify や Indicate をするときに、特定のデバイスのみに Notify を行うために conn_hdl
をパラメーターに与えてあげます。
単一コネクション時は、このパラメーター無しで notify できますが、複数コネクションを扱う場合には conn_hdl
の指定が必要です。
bool notify (uint16_t conn_hdl, const void* data, uint16_t len);
Advertising の制御
また、connect されたときのハンドラで、少し Advertising の制御を行う必要があることも忘れないでください。
単一コネクションのときは、接続されたら Advertising が止まり、接続を解除されると再度 Advertising が始まります。
そして、その間に他の Central は接続することができません。
しかし、複数コネクションのときは、接続されても他の Central を接続可能にするために、最大接続数に到達していなければ、Advertising を出し続ける必要があります。
Bluefruit ライブラリはこのような制御を自動で行ってくれないので、自分で connection handler で行う必要があります。
uint8_t connection_count = 0;
void connect_callback(uint16_t conn_handle) {
connection_count++;
// Keep advertising if not reaching max
if (connection_count < MAX_PRPH_CONNECTION) {
Bluefruit.Advertising.start(0);
}
}
最大接続数を上げすぎると...
サンプルコードでは最大接続数が2になっていますが、3までは問題なく動作するようです。
Bluefruit ライブラリを読み込んでいくと、Nordic の SoftDevice は最大 20 接続まで対応しているようなことが書いてあります。
実際、ライブラリの実装もこの数字で制限をかけています。
https://github.com/adafruit/Adafruit_nRF52_Arduino/blob/8f35e83b4f39916ed996bd4ab9466107b0768804/libraries/Bluefruit52Lib/src/bluefruit.h#L49
しかし、最大接続数を 3 以上に指定すると、以下のようなエラーが出て起動しません。
(確か、このログは Debug レベルを Level 2: Full debug にして取得した)
つまり、SoftDevice に与えるメモリが足りないようです。
[CFG ] SoftDevice config require more SRAM than provided by linker.
App Ram Start must be at least 0x20006528 (provided 0x20003400)
Please update linker file or re-config SoftDevice
[CFG ] SoftDevice's RAM requires: 0x20006528
bool AdafruitBluefruit::begin(uint8_t, uint8_t): 447: verify failed, error = NRF_ERROR_NO_MEM
NRF_ERROR_NO_MEM で起動しない場合の解決方法
ライブラリを読み込んでいくと興味深いコメントを発見しました。
* Attr Table Size, HVN queue size, Write Command queue size is
* determined later in begin() depending on number of peripherals
* and central connections for optimum SRAM usage.
このあたりから推測するに、最大接続数を上げるには、attr table とか queue とかで SoftDevice に沢山メモリを与える必要があるが、それが足りていないようです。
エラーメッセージにはリンカスクリプトを変えろと出ていますので、それを見てみましょう。
/* SRAM required by S132 depend on
* - Attribute Table Size
* - Vendor UUID count
* - Max ATT MTU
* - Concurrent connection peripheral + central + secure links
* - Event Len, HVN queue, Write CMD queue
*/
RAM (rwx) : ORIGIN = 0x20003400, LENGTH = 0x20010000 - 0x20003400
確かに、エラーメッセージに含まれている provided 0x20003400
と同じアドレスがここで指定されているので、この値は固定値のようです。
流石に、SoftDevice の最大値までコネクションを張るやつはいないと思われているんでしょうか... (まあ SRAM は貴重ですからね)
ここでは、[CFG ] SoftDevice's RAM requires: 0x20006528
と言われているので、0x20003400
をこの値を超える適当な数字に書き換えればよいでしょう。
あとは、ライブラリをリンクしなおせば、無事解決するはず...
それにしても、結構 SoftDevice はメモリ持ってくようですね。
最大接続 20 は実運用では厳しいかも。