nRF51822のセントラル機能試験結果報告(後半)
(作成中:暫定公開:最終版は来週に公開予定です)
本記事の内容について、間違いや不明点があればご指摘願います。
疑問点があればいつでもお問い合わせください。(掲示板やメールはたまにしか確認していないので、ツイッターなら確実に連絡がつきます。)
また、本記事の内容について一切の権利を放棄しますので、自由に改変、拡散していただいても構いません。
【サンプルプログラムの修正】
今回はサンプルプログラムを修正してmbed HRM1017をセントラルとして使用します。
サンプルプログラム内にある通信関数やログ関数APPL_LOGは、開発ボード以外ではハードエラーの要因になるので削除しました。
また、mbed用にクロックの設定を外部水晶からRC発振に変更します。
①UUID作成
まずはble_app_multilink_centralのclient_handling.cやble_app_multilink_peripheralのmain.cのUUIDを変更します。
サンプルのUUID
#define MULTILINK_PERIPHERAL_BASE_UUID {0xB3, 0x58, 0x55, 0x40, 0x50, 0x60, 0x11, \ 0xe3, 0x8f, 0x96, 0x08, 0x00, 0x00, 0x00, \ 0x9a, 0x66} /**< 128bit UUID base used for example. */ #define MULTILINK_PERIPHERAL_SERVICE_UUID 0x9001 /**< Serrvice UUID over the 128-bit base used for the example. */ #define MULTILINK_PERIPHERAL_CHAR_UUID 0x900A /**< Characteristic UUID over the 128-bit base used for the example. */
変更例
#define TEST_UUID_BASE {0x12, 0x34, 0x56, 0x78, 0x9A, 0xBC, 0xDE, 0xF0, 0x12, 0x34, 0x56, 0x78, 0x00, 0x00, 0x9A, 0xBC} #define TEST_UUID_SERVICE 0xFFFF #define TEST_UUID_NOTIFY_CHAR 0xAAAA
また、TEST_UUID_NOTIFY_CHAR がサンプルプログラムのMULTILINK_PERIPHERAL_CHAR_UUIDに相当します。
UUIDはネットのサイトやNordicのnRFgo studioで作成できるので詳細はここでは説明しません。
また、サンプルプロジェクトble_app_multilink_centralのUUIDを書き換えます。
例)
関数client_handling_init内
ble_uuid128_t base_uuid = MULTILINK_PERIPHERAL_BASE_UUID; ble_uuid128_t base_uuid = TEST_UUID_BASE; uuid.uuid = MULTILINK_PERIPHERAL_SERVICE_UUID; uuid.uuid = TEST_UUID_SERVICE;
例)
関数db_discovery_evt_handler内
if ((p_characteristic->characteristic.uuid.uuid == MULTILINK_PERIPHERAL_CHAR_UUID) if ((p_characteristic->characteristic.uuid.uuid == TEST_UUID_NOTIFY_CHAR)
他のUUIDも同様に置換します。
また、ble_app_multilink_peripheralも同様にUUIDを検索して置換ください。
②デバイス名作成
ble_app_multilink_centralのmain.cやble_app_multilink_peripheralのmain.cのDEVICE_NAMEを変更します。
サンプルのDEVICE_NAME
#define TARGET_DEV_NAME "Multilink" /**< Target device name that application is looking for. */
ble_app_multilink_peripheralについては、ペリフェラルは異なるDEVICE_NAMEを作成して別々に書き込みます。この異なるペリフェラルのデバイス名をセントラルに記憶させることで、両者を識別できるようにします。
#define DEVICE_NAME "TEST_A"
と
#define DEVICE_NAME "TEST_B"
ble_app_multilink_centralについては、2つのDEVICE_NAMEを両方とも用意します。
#define TARGET_DEV_NAME_A " TEST_A " /**< Target device name that application is looking for. */ #define TARGET_DEV_NAME_B " TEST_B" /**< Target device name that application is looking for. */
③アドバタイジング検知時の処理
アドバタイジング検知時に呼び出されるmain.cの関数on_ble_evtの分岐BLE_GAP_EVT_ADV_REPORT内の処理を変更します。
type_data.p_dataにアドバタイジングパケットのデバイス名が格納されているので、それをmemcmp関数で比較して一致しているかを確認して分岐します。
デバイス名が一致していた場合、フラグ変数type_indexの一致していたペリフェラルを示すbitをアサートさせます。
switch (p_ble_evt->header.evt_id) { case BLE_GAP_EVT_ADV_REPORT: { data_t adv_data; data_t type_data; // Initialize advertisement report for parsing. adv_data.p_data = p_ble_evt->evt.gap_evt.params.adv_report.data; adv_data.data_len = p_ble_evt->evt.gap_evt.params.adv_report.dlen; err_code = adv_report_parse(BLE_GAP_AD_TYPE_COMPLETE_LOCAL_NAME, &adv_data, &type_data); if (err_code != NRF_SUCCESS) { // Compare short local name in case complete name does not match. err_code = adv_report_parse(BLE_GAP_AD_TYPE_SHORT_LOCAL_NAME, &adv_data, &type_data); } // Verify if short or complete name matches target. //TARGET DEVICEがペリフェラルAの場合 if ((err_code == NRF_SUCCESS) && (0 == memcmp(TARGET_DEV_NAME_A,type_data.p_data,type_data.data_len))) { err_code = sd_ble_gap_scan_stop(); err_code = sd_ble_gap_connect(&p_ble_evt->evt.gap_evt.params.adv_report.peer_addr, &m_scan_param, &m_connection_param); //TARGET DEVICEがペリフェラルAと接続したことを示すフラグをアサート type_index |= TARGET_A; } //TARGET DEVICEがペリフェラルBの場合 else if((err_code == NRF_SUCCESS) && (0 == memcmp(TARGET_DEV_NAME_B,type_data.p_data,type_data.data_len))) { err_code = sd_ble_gap_scan_stop(); err_code = sd_ble_gap_connect(&p_ble_evt->evt.gap_evt.params.adv_report.peer_addr, &m_scan_param, &m_connection_param); //TARGET DEVICEがペリフェラルBと接続したことを示すフラグをアサート type_index |= TARGET_B; } break; }
ここでは以下の定数と変数を追加しています。
#define TARGET_A 0x01 #define TARGET_B 0x02 static uint8_t type_index;
④BLE接続後の処理
BLE接続時に呼び出されるmain.cのdevice_manager_event_handlerの分岐DM_EVT_CONNECTION内の処理を変更します。
アドバタイジング検知時にアサートしたtype_indexの値を判定して、p_handle->connection_idの値を配列ch_typeに格納します。
この処理が完了したらtype_indexは用済みなので、ペリフェラルを示すbitをクリアします。
switch(p_event->event_id) { case DM_EVT_CONNECTION: #ifdef ENABLE_DEBUG_LOG_SUPPORT ble_gap_addr_t * p_peer_addr; p_peer_addr = &p_event->event_param.p_gap_param->params.connected.peer_addr; #endif // ENABLE_DEBUG_LOG_SUPPORT err_code = client_handling_create(p_handle, p_event->event_param.p_gap_param->conn_handle); APP_ERROR_CHECK(err_code); m_peer_count++; //TARGET DEVICE Aと接続したindex番号をバッファに格納 if(TARGET_A == (TARGET_A & type_index)) { ch_type[0] = (p_handle->connection_id); type_index ^= TARGET_A; //フラグクリア } //TARGET DEVICE Bと接続したindex番号をバッファに格納 else if(TARGET_B == (TARGET_B & type_index)) { ch_type[1] = (p_handle->connection_id); type_index ^= TARGET_B; //フラグクリア } break;
ここで配列ch_typeをグローバル変数として追加しました。
uint8_t ch_type[2];
extern uint8_t ch_type[2];
⑤Characteristicの確認
BLE接続確立時に呼び出されるclient_handling.cの関数db_discovery_evt_handlerも修正します。
修正と言ってもNotifyのUUIDを置換するだけです。
if ((p_characteristic->characteristic.uuid.uuid == LOWFREQ_UUID_NOTIFY_CHAR)
⑥Notify
ペリフェラルからNotifyがあると呼び出されるon_evt_hvxを修正します。
この関数の変数indexはペリフェラルの番号を示しており、配列ch_typeに格納された値を比較することでペリフェラルAとBのどちらからのNotifyかが区別できます。
Notifyのデータはp_ble_evt->evt.gattc_evt.params.hvx.dataに格納されているので、私のプログラムではそれを一時退避バッファnotify_char_aやnotify_char_bに格納させました。
なお私はNotifyを1パケット20byteにしています。
static void on_evt_hvx(ble_evt_t * p_ble_evt, client_t * p_client, uint32_t index) { uint8_t notify_char_a[20]; uint8_t notify_char_b[20]; uint8_t i; if (p_ble_evt->evt.gattc_evt.params.hvx.handle == p_client->srv_db.services[0].charateristics[p_client->char_index].characteristic.handle_value) { //ペリフェラルAの場合 if(index == ch_type[0]) { //Notifyデータの保存 for(i=0; i<20; i++) { notify_char_a[i] = p_ble_evt->evt.gattc_evt.params.hvx.data[i]; } } //ペリフェラルBの場合 else if(index == ch_type[1]) { //Notifyデータの保存 for(i=0; i<20; i++) { notify_char_b[i] = p_ble_evt->evt.gattc_evt.params.hvx.data[i]; } }
⑦Write
サンプルプログラムはNotifyしかできなくて不便だったので、以下を参照してWrite用関数を作成しました。
nrf51-ble-app-lbs communication with S120 Multilink - Nordic Developer Zone
void send(uint32_t type, uint8_t *data, uint8_t length) { ble_gattc_write_params_t gattc_params; gattc_params.flags = BLE_GATT_EXEC_WRITE_FLAG_PREPARED_WRITE; gattc_params.handle = m_client[type].srv_db.services[0].charateristics[0].characteristic.handle_value; //service handle gattc_params.len = length; gattc_params.offset = 0; gattc_params.write_op = BLE_GATT_OP_WRITE_CMD; gattc_params.p_value = &data[0]; sd_ble_gattc_write(m_client[type].srv_db.conn_handle, &gattc_params); }
この関数の第1引数に送信したい対象ペリフェラルのch_typeを代入してください。
例えば
send(ch_type[0], &temp_data[0], 3);
で、3byteのデータがペリフェラルAに送信されます。