LoginSignup
3
3

More than 5 years have passed since last update.

NUCLEO BLEのアドバタイジングをM5STACKでスキャンしてみた話

Last updated at Posted at 2018-12-16

DSC02696.JPG

はじめに

NucleoにBLE拡張ボードであるIDB05A1を接続して、BLEが利用できるM5STACKと共に遊んでみました。
ネット上にある色々な参考情報を元にNucleo他BLEデバイスから送信されている情報をM5STACK側で取得し表示までは簡単に行えたのですが、表示される内容がデバイスによってまちまちでした。
その理由を探っていった内容を投稿してみます。

BLE参考にした情報

このあたりを読んでおいていただけるとこれから先の内容は読みやすいかと思います。
https://qiita.com/moaible/items/111e2b637f3404a2de49/
https://ambidata.io/samples/m5stack/m5stack_ble_sensor/
https://sites.google.com/a/gclue.jp/ble-docs/advertising-1/advertising
https://blog.reinforce-lab.com/2013/08/13/blebook-ch2-ble-spec/

登場するデバイスの役割

Nucleoペリフェラルとしてアドバタイジングパケットを送信します。
M5STACKセントラルとして、アドバタイジングパケットLocalNameUUIDを取得し表示します。
LocalName : UUID というように:を挟んで表示させます。

アドバタイジング表示内容がペリフェラルよってまちまちなのだが…

下写真が表示内容結果です。表示パターンが様々あり???となりました。
LocalName : UUID どちらも表示される
LocalName : UUID どちらも表示されない
LocalNameのみ表示される
UUIDのみ表示される
DSC02688.JPG
セントラルであるM5STACKプログラムのバグなのか…
なおM5STACK側プログラムはESP32 BLE Arduino BLE_clientスケッチを参考に作成しています。

セントラル(M5STACK)側プログラムを見直してみる

M5STACKプログラムを見直してみました。単純な表示プログラムなので問題はなさそうです。しかしあるコードに気づきました。下記がそのコードです。
3行目にhaveServiceUUID()という関数をコールしています。どうやらアドバタイジングパケット中にUUIDを所有している場合と所有していない場合がありそうです。

M5Stack
    M5.Lcd.print(advertisedDevice.getName().c_str());
    M5.Lcd.print(":");
    if (advertisedDevice.haveServiceUUID() ) {
        M5.Lcd.println(advertisedDevice.getServiceUUID().toString().c_str());
    }

BLEアドバダイジングに関する仕様を見てみる

この仕様を簡単にいうと、アドバタイジングパケット31バイトを上限としてペリフェラル名とかUUIDとか送信電力等々をGeneric Access Profile仕様にそって格納できます。必要に応じて格納すればよい情報なのでアドバタイジングパケットの中身はペリフェラルによって異なります。
どうやらGeneric Access Profile仕様による種別搭載有無が ペリフェラルによって表示内容をまちまちにする原因のようです。

ペリフェラル(NUCLEO)側プログラムを見直してみる

前述でアドバタイジング表示内容がまちまちになると思わしき原因はつかめたので、Nucleoプログラムを見直してみます。参考にしたプログラムは stm32 STM32CubeExpansion_BLE1_4.1.0 SensorDemoです。
aci_gap_set_discoverableという関数呼び出しがありますが、第7引数にlocal_nameという変数名で今回表示する LocalNameを定義してあり、既出の写真ではセントラル側にNucleoBLEという名称で表示されていることが確認できます。

Nucleo
void setConnectable(void)
{  
  tBleStatus ret;
  const char local_name[] = {AD_TYPE_COMPLETE_LOCAL_NAME,'N','u','c','l','e','o','B','L','E'};

  /* disable scan response */
  hci_le_set_scan_resp_data(0,NULL);
  /* disable scan response */
  ret = aci_gap_set_discoverable(ADV_IND, 0, 0, PUBLIC_ADDR, NO_WHITE_LIST_USE, sizeof(local_name), local_name, 0, NULL, 0, 0);
} 

ペリフェラル(NUCLEO)側プログラムを修正してみる

さてaci_gap_set_discoverable()という関数仕様を見てみると第9引数はServiceUUIDListとなっています。どうやら第8引数にUUID長、第9引数にUUID名を設定すればセントラル側に無事表示されそうです。

Nucleo
void setConnectable(void)
{  
  tBleStatus ret;
  const char local_name[] = {AD_TYPE_COMPLETE_LOCAL_NAME,'N','u','c','l','e'};
  static uint8_t discoverable_uuid[17];

  /* disable scan response */
  hci_le_set_scan_resp_data(0,NULL);
  /* UUIDをここで設定 */
  discoverable_uuid[0] = AD_TYPE_128_BIT_SERV_UUID_CMPLT_LIST;
  uint8_t* uuid_start = &discoverable_uuid[1];
  COPY_ENV_SENS_SERVICE_UUID(uuid_start);
  /* disable scan response */
  ret = aci_gap_set_discoverable(ADV_IND, 0, 0, PUBLIC_ADDR, NO_WHITE_LIST_USE, sizeof(local_name), local_name, sizeof(discoverable_uuid), (uint8_t*)discoverable_uuid, 0, 0);
} 

思惑通りNucle:UUIDで表示されることが確認できました。
DSC02694.JPG

補足

第9引数がServiceUUIDListというリストを意味させている理由は、短縮UUIDを複数乗せられると解釈しています。

ペリフェラル(NUCLEO)側プログラム修正時の注意事項

前項にて思惑通りNucle:UUIDで表示されていることが確認できましたと書きましたが…
LocalNameについて以下の違いがあります。
修正前:NucleoBLE
修正後:Nucle

理由 その1

aci_gap_set_discoverable()関数内でif ((LocalNameLen+ServiceUUIDLen+14) > sizeof(buffer))と制限されています。またこの判定文はアドバタイジングパケット31バイト制限とは異なることも注意してください。

0バイト目にはデータ種別が入るため、各名称+1バイトのサイズとなることを理解しておくこと
local_name[10] = {AD_TYPE_COMPLETE_LOCAL_NAME,'N','u','c','l','e','o','B','L','E'};
discoverable_uuid[17] = {'AD_TYPE_128_BIT_SERV_UUID_CMPLT_LIST','U','U'/*略*/,'I','D'};

10 + 17 + 14 = 41となりaci_gap_set_discoverable()関数は失敗します。

Nucleo
tBleStatus aci_gap_set_discoverable(uint8_t AdvType, uint16_t AdvIntervMin, uint16_t AdvIntervMax,uint8_t OwnAddrType, uint8_t AdvFilterPolicy, uint8_t LocalNameLen,const char *LocalName, uint8_t ServiceUUIDLen, uint8_t* ServiceUUIDList,uint16_t SlaveConnIntervMin, uint16_t SlaveConnIntervMax)
{
  struct hci_request rq;
  uint8_t status;    
  uint8_t buffer[40];
  uint8_t indx = 0;

  if ((LocalNameLen+ServiceUUIDLen+14) > sizeof(buffer))
    return BLE_STATUS_INVALID_PARAMS;
  // 以下略

理由 その2

アドバタイジングパケットの最大データ長は31バイトです。
UUIDは128ビットUUIDを用いているため、データ長とデータ種別を含めた合計18バイトを必要とします。1(データ長) + 1(データ種別) + 16(UUID) = 18
そのため残りアドバタイジングパケットは13バイト使えることになりますが、LocalNameUUID同様にデータ長とデータ種別が必要になります。そのためLocalNameとして使用できるのは11バイトとなります。31 - 18(UUID) - 1(データ長) - 1データ種別 = 11(LocalName)
しかしながら、Nucleまで名称を短くしないとセントラル側で名称を表示できませんでした。Nucleは5バイトなので6バイト分11 - 5 = 6 が何かに利用されていると推測しています。

理由 その3

理由その2の続きになりますが、名称に5バイトしか利用できず残りの6バイトはなぜ使えないのかと疑問がわきます。理由についてはおそらく送信電力に関する情報がアドバタイジングパケットの一部として乗っかっていると推測しています。セントラル側でRSSIを取得可能なことがその根拠です。そのためアドバタイジングパケットとしてバッファが足りなくなった場合には、LocalNameなどの後付けデータが無効になってしまうのではないかと。
現状ではどこの処理(プログラム)でその情報が付加されているか追い切れていません。また、これらは推測です。

その他のペリフェラルに着目してみる

最後の写真にあるCASIOペリフェラルデータに着目してみると、LocalName14バイトもあるため、UUIDやデータ長、データ種別とあわせるとアドバタイジングパケット最大値である31バイトをオーバします。それが可能なからくりは16ビット短縮UUIDを用いて送出しているためと思っています。そしてわかりやすいペリフェラル名アドバタイジングパケットに乗せられているのではないでしょうか。

最後に

NUCLEOM5STACKという人気で遊びやすいデバイスを用いて、BLEの一部に触れることができIoTな領域に一歩踏み出せました。同様に遊んでみてなんだかおかしいなとか思っていた方の参考になれば幸いです。また、本文中で解釈が間違っていることが多少あるかとは思いますがご容赦ください。
これからも様々なBLEデバイスのパケットをのぞいてみたり、デバイス同士でお話ができるスキルを得ていきたいです。

3
3
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
3
3