2015年11月11日水曜日

nRF51822 の Advertise で Scan レスポンスパケットを設定する方法

概要

Scan レスポンスパケットは Advertising でスキャンした後にレスポンスとして Peripheral が Central に送信できるおまけパケットです
Advertising パケットは上限が 31 バイトになっておりその上限を超えて何かデータを送りたいときなどに使います
データの構造は Advertising パケットと同様になっています

環境

  • Windows7 64bit
  • nRF51822
  • nRF51 DK
  • nRF51 SDK 9.0.0

背景 (なぜ Scanパケットを設定したかったのか)

ちょっと長くなりますが背景を詳しく説明します

サンプルのせいにするわけではないですが nRF51 SDK に含まれている各種サンプルでは基本的に Scanパケットを設定していません
で、Central 側の実装を Python ベースの gattlib というライブラリを使って実装していたのですが、Advertising パケットをスキャンした際に DeviceName が含まれずに困っていました
具体的には DiscoveryService.discover というメソッドを使うと DeviceName が取得できない状況でした

切り分けのため TI 社が提供する SensorTag や iPhone で実装した Peripheral デバイスを使ってスキャンしてみたのですが、その場合はうまく DeviceName が取得できました
なので、nRF51822 上に実装しているアプリが悪そうだ、ということでいろいろ調べたところ Scan レスポンスパケットを設定してあげることで、nRF51822 でも DeviceName を取得することができるようになりました

更に余談になってしまうのですが、hcitool の lescan というコマンドを使っていると DeviceName が (unknown) と表示されることにも気付きました
unknown がちょくちょく出ていましたが、ちゃんと DeviceName が表示されることもあったので初めは無視していました
また、nRF51822 の場合でも SensorTag の場合でも unknown が表示されていたので「こんなもんなのか」と思っていたのですが、discover すると nRF51822 だけ DeviceName が取得できないのはおかしいということに気が付き詳しく調べてみたところ Scan レスポンスパケットという仕様にたどり着き設定してみたらうまくいった感じです

ということは今思うと SensorTag は Scan レスポンスパケットを設定しておりかつ、gattlib の DiscoveryService.discover は Advertising パケットの DeviceName を見ているわけではなく、Scan レスポンスパケットの DeviceNam を見ている ( のではと今になって予想しています、コードレベルまで見ていないので正解がどうかは不明です )

長くなりましたが、まとめると

  • gattlib の DiscoveryService.discover で DeviceName が取得したかった

ために設定した感じです

設定方法

前置きかなり長くなりましたが実装方法です
nRF51 SDK のサンプルでは多くの場合、 main メソッド内で各処理の初期化を行っています
その中の advertising_init というメソッドで Advertising パケットの設定をしておりそこを修正します

具体的なコードの全貌は以下のとおり

static void advertising_init(void) {
    uint32_t      err_code;
    ble_advdata_t advdata;
    ble_advdata_t rspdata;

    // Build advertising data struct to pass into @ref ble_advertising_init.
    memset(&advdata, 0, sizeof(advdata));
    memset(&rspdata, 0, sizeof(rspdata));

    advdata.name_type               = BLE_ADVDATA_FULL_NAME;
    advdata.include_appearance      = true;
    advdata.flags                   = BLE_GAP_ADV_FLAGS_LE_ONLY_GENERAL_DISC_MODE;
    advdata.uuids_complete.uuid_cnt = sizeof(m_adv_uuids) / sizeof(m_adv_uuids[0]);
    advdata.uuids_complete.p_uuids  = m_adv_uuids;

    rspdata.name_type               = BLE_ADVDATA_FULL_NAME;

    ble_adv_modes_config_t options = {0};
    options.ble_adv_fast_enabled  = BLE_ADV_FAST_ENABLED;
    options.ble_adv_fast_interval = APP_ADV_INTERVAL;
    options.ble_adv_fast_timeout  = APP_ADV_TIMEOUT_IN_SECONDS;

    err_code = ble_advertising_init(&advdata, &rspdata, &options, on_adv_evt, NULL);
    APP_ERROR_CHECK(err_code);
}

ポイントは rspdata 変数を宣言して rspdata.name_type = BLE_ADVDATA_FULL_NAME; として DeviceName を設定するところです

冒頭でも説明していますが、Advertising パケットと Scan レスポンスパケットはデータ構造が同じです
なので、すでに宣言している advdata を使いたいところですがこれを使うとうまく動作しませんでした

また、 rspdata に DeviceName だけ表示するように設定していますが、これもポイントで他のパラメータも advdata と同じように設定したところ、これまたうまく動作しませんでした
( うまく動作しない明確な理由がわからない状態です、すいません )

なのでとりあえず上記のように、記載することで Scan レスポンスパケットを設定することができました

最後に

これで再度 hcitool lescan をすると nRF51822 で実装した BLE デバイスに unknown が表示されなくなっていることが確認できると思います
Advertising + Scan レスポンスパケット両方で DeviceName を設定するようにしたためです

ただ、他のデバイスを見るとほとんどのデバイスで unknown になることがあるので、おすすめ実装的にはやっぱりどっちかだけで DeviceName を送信するほうがいいのかもしれません
まぁ確かに同じデータを 2 回送るのであれば無駄といえば無駄ですからね
とは言え Central 側の実装も考慮しなければいけないと考えるととりあえず両方設定しておいたほうが無難といえば無難なのでしょうか

相変わらず BLE の実装は大変です

0 件のコメント:

コメントを投稿