【BLEを使う】nRF51822 のAdvertisingパケットとScanパケットの作成(Peripheral側)
神は細部に宿る
いままでの記事はこちらをご覧ください。
BLE接続が確立していない段階では、BLEのスレーブがアドバタイジングパケットを発信し、接続できるセントラルに自分の存在を報せます。
NordicのサンプルプログラムではソフトウェアS110で用意された諸々の関数や定義を用いてアドバタイジングパケットやスキャンパケットを作成しています。
今回はその作成方法について説明します。
設定しないといけない主な項目は以下になります。
①Adv PDU
アドバタイジングパケットのヘッダーの上位4bitです。 以下の種類があります。
値[3:0] | Type | 接続性 | スキャン | 指向性 |
---|---|---|---|---|
0000 | ADV_IND | 有り | 有り | 無し |
0001 | ADV_DIRECT_IND | 有り | 無し | 有り |
0010 | ADV_NONCONN_IND | 無し | 無し | 無し |
0110 | ADV_SCAN_IND | 無し | 有り | 無し |
②AD Type
AD Dataの先頭に配置される1byteのデータです。
多くの種類がありますが、今回説明するのは以下になります。
Data | Type |
---|---|
0x0 | Flags |
0x03 | Complete List of 16-bit Service Class UUIDs |
0x07 | Complete List of 128-bit Service Class UUIDs |
0x08 | Shortened Local Name |
0x09 | Complete Local Name |
0x0A | Tx Power Level |
0x19 | Appearance |
では、実際にこれらの設定をプログラムに反映させてみます。
Adv PDU
アドバタイジングPDUは、構造体ble_gap_adv_params_tのtypeで設定します。
ble_gap_adv_params_tの構造は以下を参照してください。
nRF5 SDK guides - Nordic DevZone
サンプルプログラムの多くでは、advertising_start関数やadvertising_init関数などでプログラム冒頭に設定されています。
例えば、以下のサンプルプログラムにおけるmain.cの関数advertising_startのadv_params.typeがそれになります。
nrf51-ble-app-lbs/main.c at master · NordicPlayground/nrf51-ble-app-lbs · GitHub
adv_params.type = BLE_GAP_ADV_TYPE_ADV_IND;
設定する項目は以下の定数のどれかを選択してください。
Type | 定義 | 値 |
---|---|---|
ADV_IND | BLE_GAP_ADV_TYPE_ADV_IND | 0x00 |
ADV_DIRECT_IND | BLE_GAP_ADV_TYPE_ADV_DIRECT_IND | 0x01 |
ADV_NONCONN_IND | BLE_GAP_ADV_TYPE_ADV_NONCONN_IND | 0x03 |
ADV_SCAN_IND | BLE_GAP_ADV_TYPE_ADV_SCAN_IND | 0x02 |
アドバタイジングPDUは4種類ありますがADV_DIRECT_IND は使うべきではありません。
Flags
Flagsは必須設定項目です。
構造体ble_advdata_tのflags.sizeとflags.p_dataを設定してください。
ble_advdata_tの構造は以下を参照してください。
https://devzone.nordicsemi.com/documentation/nrf51/4.3.0/html/structble__advdata__t.html
例えば、以下のサンプルプログラムにおけるmain.cの関数advertising_initをご覧ください。
nrf51-ble-app-lbs/main.c at master · NordicPlayground/nrf51-ble-app-lbs · GitHub
advertising_initの変数flagsで設定しているのが分かります。
uint8_t flags = BLE_GAP_ADV_FLAGS_LE_ONLY_LIMITED_DISC_MODE;
すると以下のようにflagsのサイズと設定値が自動的に更新されます。
advdata.flags.size = sizeof(flags);
advdata.flags.p_data = &flags;
flagsには、以下のいずれかの定数を設定してください。
定義 | 値 |
---|---|
BLE_GAP_ADV_FLAG_LE_LIMITED_DISC_MODE | 0x01 |
BLE_GAP_ADV_FLAG_LE_GENERAL_DISC_MODE | 0x02 |
BLE_GAP_ADV_FLAG_BR_EDR_NOT_SUPPORTED | 0x04 |
BLE_GAP_ADV_FLAG_LE_BR_EDR_CONTROLLER | 0x08 |
BLE_GAP_ADV_FLAG_LE_BR_EDR_HOST | 0x10 |
BLE_GAP_ADV_FLAGS_LE_ONLY_LIMITED_DISC_MODE |
(BLE_GAP_ADV_FLAG_LE_LIMITED_DISC_MODE |BLE_GAP_ADV_FLAG_BR_EDR_NOT_SUPPORTED) |
BLE_GAP_ADV_FLAGS_LE_ONLY_GENERAL_DISC_MODE |
(BLE_GAP_ADV_FLAG_LE_GENERAL_DISC_MODE | BLE_GAP_ADV_FLAG_BR_EDR_NOT_SUPPORTED) |
DEVICE_NAME
ユーザーが任意につけられる装置の名前です。
例えば、iphone(central)にadvertisingパケットが検知されたときに、iphoneのアプリ画面上にこの装置名が表示されます。
DEVICE_NAMEはプログラム中の以下の定数に入力してください。 例えば、以下のサンプルプログラムにおけるmain.cの43行目がそれです。
nrf51-ble-app-lbs/main.c at master · NordicPlayground/nrf51-ble-app-lbs · GitHub
#define DEVICE_NAME "LedButtonDemo"
定義したDEVICE_NAMEはmain.cの関数gap_params_initにおいてアドバタイジングパケットに組み込まれているのが分かります。
err_code = sd_ble_gap_device_name_set(&sec_mode, (const uint8_t *)DEVICE_NAME, strlen(DEVICE_NAME));
また、DEVICE_NAMEについては、DEVICE_NAMEの全文字を使うComplete local name(AD typeは0x09)か先頭の数byteのみを短縮名として使うShortened Local Name(AD typeは0x08)を指示します。
短縮名を使用する場合、完全なDEVICE_NAMEはGATTのdevice name characteristicを使って読み出せます。
どちらを使用するかは、構造体ble_advdata_tのname_typeで設定してください。
例えば、サンプルプログラムのmain.cの関数advertising_initのadvdata. name_typeがそれです。
advdata.name_type = BLE_ADVDATA_FULL_NAME;
advdata.name_typeには、以下のいずれかの定数を設定します。
定義 | 意味 |
---|---|
BLE_ADVDATA_NO_NAME | 装置名なし |
BLE_ADVDATA_SHORT_NAME | 装置名短縮 |
BLE_ADVDATA_FULL_NAME | 装置名Full |
外部装置
APPEARANCE(外部装置)は、main.cの関数gap_params_initの sd_ble_gap_appearance_setで設定することでアドバタイジングパケットに組み込まれます。
nrf51-ble-app-lbs/main.c at master · NordicPlayground/nrf51-ble-app-lbs · GitHub
err_code = sd_ble_gap_appearance_set(BLE_APPEARANCE_HEART_RATE_SENSOR_HEART_RATE_BELT); APP_ERROR_CHECK(err_code);
APPEARANCE(外部装置)は以下の定義から適切なものを選択します。
nRF5 SDK guides - Nordic DevZone
また、APPEARANCEを使用する場合、構造体ble_advdata_tのinclude_appearanceを設定してください。
例えば、以下のサンプルプログラムにおけるmain.cの関数advertising_initをご覧ください。
nrf51-ble-app-lbs/main.c at master · NordicPlayground/nrf51-ble-app-lbs · GitHub
advdata.include_appearance = true;
TX power
送信出力はプログラム中でBLE_GAP_AD_TYPE_TX_POWER_LEVELで設定します。
#define BLE_GAP_AD_TYPE_TX_POWER_LEVEL 0x0A /**< Transmit power level. */
スキャン応答によるデータ送信
アドバタイジングパケットは31bytesしか使用できないので、必要な情報を伝えきれないことがあります。
その場合は、centralからのスキャン要求に対するスキャン応答においてデータを追加で送信できます。
ちなみにcentralからのスキャン要求が送信されるタイミングは、centralがperipheralからのアドバタイジングパケットを検知した直後になります。
またスキャンパケットに乗せるデータは構造体ble_advdata_tを持つ変数を追加でもう1個作成してください。
例えば、以下のサンプルプログラムにおけるmain.cの関数advertising_initをご覧ください。
nrf51-ble-app-lbs/main.c at master · NordicPlayground/nrf51-ble-app-lbs · GitHub
サンプルプログラムでは以下の2つを作成しています。
ble_advdata_t advdata; ble_advdata_t scanrsp;
スキャン応答でデータを送信する場合は、関数ble_advdata_setの2番目の引数に上で作成したスキャンパケットデータの先頭アドレス&scanrspを設定します。
スキャンパケットに何もデータを乗せない場合は2番目の引数をNULLにしておきます。
//スキャンパケットに何もデータを乗せない場合 //err_code = ble_advdata_set(&advdata, NULL); //スキャンパケットでデータを送信する場合 err_code = ble_advdata_set(&advdata, &scanrsp);
サービスUUID(BLE SIGで定義済みのUUIDを使用する場合)
ここではBLE SIGであらかじめ定義されているサービスUUIDを、アドバタイジングパケットで送信する場合を考えます。
サービスUUIDの設定は、構造体ble_advdata_tのuuids_complete.uuid_cntとuuids_complete.p_uuidsを設定してください。
例えば、以下のサンプルプログラムにおけるmain.cの関数advertising_initをご覧ください。
nrf51-ble-app-lbs/main.c at master · NordicPlayground/nrf51-ble-app-lbs · GitHub
main.cにおいて ble_uuid_t adv_uuidsで設定します。サンプルプログラムと異なり、以下は既存のサービスを使う場合の設定です。
ble_uuid_t adv_uuids[] = {{BLE_UUID_BATTERY_SERVICE, BLE_UUID_TYPE_BLE}};
ble_uuid_t adv_uuidsの1番目の引数がUUIDのデータを示し、2番目の引数がUUIDのtypeを示しています。
BLE SIGで定義されたサービスUUIDを使用する場合は、BLE_UUID_TYPE_BLEを指定します。
1番目の引数には、あらかじめ定義されたサービスUUIDが使用できます。(プログラムではble_srv_common.hで定義されています。)
すると以下のように設定値が自動的に更新されます。
advdata.uuids_complete.uuid_cnt = sizeof(adv_uuids) / sizeof(adv_uuids[0]); advdata.uuids_complete.p_uuids = adv_uuids;
また、サービスUUIDが複数の場合は、以下のようにして指定します。
ble_uuid_t adv_uuids[] = { {BLE_UUID_HEART_RATE_SERVICE, BLE_UUID_TYPE_BLE}, {BLE_UUID_BATTERY_SERVICE, BLE_UUID_TYPE_BLE}, {BLE_UUID_DEVICE_INFORMATION_SERVICE, BLE_UUID_TYPE_BLE} };
サービスUUID(カスタムUUIDを使用する場合)
ここではサービスUUIDを自分で作成し、スキャンパケットで送信する場合を考えます。
既存のサービスのときと同様にble_uuid_t adv_uuidsを設定します。
ble_uuid_t adv_uuids[] = {{LBS_UUID_SERVICE, m_lbs.uuid_type}};
ble_uuid_t adv_uuidsの1番目の引数は自分で作成したUUIDを指定します。
UUIDにはnRFgo StudioのUUID生成ツールで作成したBase UUIDを使用しました。
nRF8001 Configurationを選択します。
nRF80001 SetupからEdit 128-bit UUIDsを選択します。
Add newで新しいUUIDを作成します。
プログラムにおいては、xxxxを0000で埋めて、下位byteから2byteずつ配置してください。
例)UUID = 1234xxxx-5678-9ABC-DEFA-132547698BACの場合、プログラムのどこかに以下のUUIDを定義します。
以下の例では、作成したUUIDのxxxxの部分に任意の4桁の数字を代入して、Service UUIDと複数のCharacteristic UUIDを作成しています。
#define LBS_UUID_BASE {0xAC, 0x8B, 0x69, 0x47, 0x25, 0x13, 0xFA, 0xDE, 0xBC, 0x9A, 0x78, 0x56, 0x00, 0x00, 0x34, 0x12} #define LBS_UUID_SERVICE 0x1523 #define LBS_UUID_LED_CHAR 0x1525 #define LBS_UUID_BUTTON_CHAR 0x1524
uuid_type は関数sd_ble_uuid_vs_addを使うことで各SeviceやCharacteristicに組み込まれます。
詳細はサービス初期化関数で説明します。
サンプルプログラムでは、サービスUUIDはスキャンパケットで送信するので、advdataパラメータではなく、scanrspパラメータに格納されています。
また、ble_advdata_setの2番目の引数には&scanrspを指定しています。