/**@brief Function for handling the advertising report BLE event. * * @param[in] p_ble_evt Bluetooth stack event. */ static void on_adv_report(const ble_evt_t * const p_ble_evt) { uint32_t err_code; uint8_array_t adv_data; uint8_array_t dev_name; bool do_connect = false; // For readibility. const ble_gap_evt_t * const p_gap_evt = &p_ble_evt->evt.gap_evt; const ble_gap_addr_t * const peer_addr = &p_gap_evt->params.adv_report.peer_addr; // Initialize advertisement report for parsing adv_data.p_data = (uint8_t *)p_gap_evt->params.adv_report.data; adv_data.size = p_gap_evt->params.adv_report.dlen; //search for advertising names bool found_name = false; err_code = adv_report_parse(BLE_GAP_AD_TYPE_COMPLETE_LOCAL_NAME, &adv_data, &dev_name); if (err_code != NRF_SUCCESS) { // Look for the short local name if it was not found as complete err_code = adv_report_parse(BLE_GAP_AD_TYPE_SHORT_LOCAL_NAME, &adv_data, &dev_name); if (err_code != NRF_SUCCESS) { // If we can't parse the data, then exit return; } else { found_name = true; } } else { found_name = true; } if (found_name) { if (strlen(m_target_periph_name) != 0) { if(memcmp(m_target_periph_name, dev_name.p_data, dev_name.size) == 0) { do_connect = true; } } } if (do_connect) { // Initiate connection. err_code = sd_ble_gap_connect(peer_addr, &m_scan_param, &m_connection_param); if (err_code != NRF_SUCCESS) { APPL_LOG("[APPL]: Connection Request Failed, reason %d\r\n", err_code); } } }
/**@brief Function for searching a UUID in the advertisement packets. * * @details Use this function to parse received advertising data and to find a given * UUID in them. * * @param[in] p_adv_report advertising data to parse. * @param[in] uuid_to_find UUIID to search. * @return true if the given UUID was found, false otherwise. */ static bool find_adv_uuid(const ble_gap_evt_adv_report_t *p_adv_report, const uint16_t uuid_to_find) { uint32_t err_code; data_t adv_data; data_t type_data; // Initialize advertisement report for parsing. adv_data.p_data = (uint8_t *)p_adv_report->data; adv_data.data_len = p_adv_report->dlen; err_code = adv_report_parse(BLE_GAP_AD_TYPE_16BIT_SERVICE_UUID_MORE_AVAILABLE, &adv_data, &type_data); if (err_code != NRF_SUCCESS) { // Look for the services in 'complete' if it was not found in 'more available'. err_code = adv_report_parse(BLE_GAP_AD_TYPE_16BIT_SERVICE_UUID_COMPLETE, &adv_data, &type_data); if (err_code != NRF_SUCCESS) { // If we can't parse the data, then exit. return false; } } // Verify if any UUID match the given UUID. for (uint32_t u_index = 0; u_index < (type_data.data_len / sizeof(uint16_t)); u_index++) { uint16_t extracted_uuid; extracted_uuid = uint16_decode(&type_data.p_data[u_index * sizeof(uint16_t)]); if (extracted_uuid == uuid_to_find) { return true; } } return false; }
/**@brief Function for searching a given name in the advertisement packets. * * @details Use this function to parse received advertising data and to find a given * name in them either as 'complete_local_name' or as 'short_local_name'. * * @param[in] p_adv_report advertising data to parse. * @param[in] name_to_find name to search. * @return true if the given name was found, false otherwise. */ static bool find_adv_name(ble_gap_evt_adv_report_t const * p_adv_report, char const * name_to_find) { uint32_t err_code; data_t adv_data; data_t dev_name; bool found = false; // Initialize advertisement report for parsing. adv_data.p_data = (uint8_t *)p_adv_report->data; adv_data.data_len = p_adv_report->dlen; // Search for matching advertising names. err_code = adv_report_parse(BLE_GAP_AD_TYPE_COMPLETE_LOCAL_NAME, &adv_data, &dev_name); if ( (err_code == NRF_SUCCESS) && (strlen(name_to_find) == dev_name.data_len) && (memcmp(name_to_find, dev_name.p_data, dev_name.data_len) == 0)) { found = true; } else { // Look for the short local name if the complete name was not found. err_code = adv_report_parse(BLE_GAP_AD_TYPE_SHORT_LOCAL_NAME, &adv_data, &dev_name); if ( (err_code == NRF_SUCCESS) && (strlen(name_to_find) == dev_name.data_len) && (memcmp(m_target_periph_name, dev_name.p_data, dev_name.data_len) == 0)) { found = true; } } return found; }
/**@brief Function for handling the Application's BLE Stack events. * * @param[in] p_ble_evt Bluetooth stack event. */ static void on_ble_evt(ble_evt_t * p_ble_evt) { uint32_t err_code; const ble_gap_evt_t * p_gap_evt = &p_ble_evt->evt.gap_evt; 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 = (uint8_t *)p_gap_evt->params.adv_report.data; adv_data.data_len = p_gap_evt->params.adv_report.dlen; err_code = adv_report_parse(BLE_GAP_AD_TYPE_16BIT_SERVICE_UUID_MORE_AVAILABLE, &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_16BIT_SERVICE_UUID_COMPLETE, &adv_data, &type_data); } // Verify if short or complete name matches target. if (err_code == NRF_SUCCESS) { uint16_t extracted_uuid; // UUIDs found, look for matching UUID for (uint32_t u_index = 0; u_index < (type_data.data_len/UUID16_SIZE); u_index++) { UUID16_EXTRACT(&extracted_uuid,&type_data.p_data[u_index * UUID16_SIZE]); APPL_LOG("\t[APPL]: %x\r\n",extracted_uuid); if(extracted_uuid == TARGET_UUID) { // Stop scanning. err_code = sd_ble_gap_scan_stop(); if (err_code != NRF_SUCCESS) { APPL_LOG("[APPL]: Scan stop failed, reason %d\r\n", err_code); } err_code = bsp_indication_set(BSP_INDICATE_IDLE); APP_ERROR_CHECK(err_code); m_scan_param.selective = 0; // Initiate connection. err_code = sd_ble_gap_connect(&p_gap_evt->params.adv_report.peer_addr, &m_scan_param, &m_connection_param); m_whitelist_temporarily_disabled = false; if (err_code != NRF_SUCCESS) { APPL_LOG("[APPL]: Connection Request Failed, reason %d\r\n", err_code); } break; } } } break; } case BLE_GAP_EVT_TIMEOUT: if (p_gap_evt->params.timeout.src == BLE_GAP_TIMEOUT_SRC_SCAN) { APPL_LOG("[APPL]: Scan timed out.\r\n"); scan_start(); } else if (p_gap_evt->params.timeout.src == BLE_GAP_TIMEOUT_SRC_CONN) { APPL_LOG("[APPL]: Connection Request timed out.\r\n"); } break; case BLE_GAP_EVT_CONN_PARAM_UPDATE_REQUEST: // Accepting parameters requested by peer. err_code = sd_ble_gap_conn_param_update(p_gap_evt->conn_handle, &p_gap_evt->params.conn_param_update_request.conn_params); APP_ERROR_CHECK(err_code); break; default: break; } }
/**@brief Function for handling the Application's BLE Stack events. * * @param[in] p_ble_evt Bluetooth stack event. */ static void on_ble_evt(ble_evt_t * p_ble_evt) { uint32_t err_code; 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. if ((err_code == NRF_SUCCESS) && (0 == memcmp(TARGET_DEV_NAME,type_data.p_data,type_data.data_len))) { err_code = sd_ble_gap_scan_stop(); if (err_code != NRF_SUCCESS) { APPL_LOG("[APPL]: Scan stop failed, reason %d\r\n", err_code); } err_code = sd_ble_gap_connect(&p_ble_evt->evt.gap_evt.params.adv_report.\ peer_addr, &m_scan_param, &m_connection_param); if (err_code != NRF_SUCCESS) { APPL_LOG("[APPL]: Connection Request Failed, reason %d\r\n", err_code); } } break; } case BLE_GAP_EVT_TIMEOUT: if(p_ble_evt->evt.gap_evt.params.timeout.src == BLE_GAP_TIMEOUT_SRC_SCAN) { APPL_LOG("[APPL]: Scan Timedout.\r\n"); } else if (p_ble_evt->evt.gap_evt.params.timeout.src == BLE_GAP_TIMEOUT_SRC_CONN) { APPL_LOG("[APPL]: Connection Request Timedout.\r\n"); } break; default: break; } }
/**@brief Function for handling Peer Manager events. * * @param[in] p_evt Peer Manager event. */ static void pm_evt_handler(pm_evt_t const * p_evt) { ret_code_t err_code; switch(p_evt->evt_id) { case PM_EVT_BONDED_PEER_CONNECTED: NRF_LOG_PRINTF("PM_EVT_BONDED_PEER_CONNECTED: peer_id=%d\n", p_evt->peer_id); err_code = pm_peer_rank_highest(p_evt->peer_id); NRF_LOG_PRINTF("peer_rank err_code: %d\r\n", err_code); break; case PM_EVT_CONN_SEC_START: NRF_LOG_PRINTF("PM_EVT_CONN_SEC_START: peer_id=%d\n", p_evt->peer_id); break; case PM_EVT_CONN_SEC_SUCCEEDED: NRF_LOG_PRINTF("PM_EVT_CONN_SEC_SUCCEEDED Role: %d. conn_handle: %d, Procedure: %d\r\n", ble_conn_state_role(p_evt->conn_handle), p_evt->conn_handle, p_evt->params.conn_sec_succeeded.procedure); err_code = pm_peer_rank_highest(p_evt->peer_id); NRF_LOG_PRINTF("peer_rank err_code: %d\r\n", err_code); break; case PM_EVT_CONN_SEC_FAILED: NRF_LOG_PRINTF("PM_EVT_CONN_SEC_FAILED: peer_id=%d, error=%d\n", p_evt->peer_id, p_evt->params.conn_sec_failed.error); switch (p_evt->params.conn_sec_failed.error) { case PM_CONN_SEC_ERROR_PIN_OR_KEY_MISSING: // Rebond if one party has lost its keys. err_code = pm_conn_secure(p_evt->conn_handle, true); if (err_code != NRF_ERROR_INVALID_STATE) { APP_ERROR_CHECK(err_code); } break; default: break; } break; case PM_EVT_CONN_SEC_CONFIG_REQ: { // Reject pairing request from an already bonded peer. pm_conn_sec_config_t conn_sec_config = {.allow_repairing = false}; pm_conn_sec_config_reply(p_evt->conn_handle, &conn_sec_config); } case PM_EVT_STORAGE_FULL: // Run garbage collection on the flash. err_code = fds_gc(); if (err_code == FDS_ERR_BUSY || err_code == FDS_ERR_NO_SPACE_IN_QUEUES) { // Retry. } break; case PM_EVT_ERROR_UNEXPECTED: // Assert. APP_ERROR_CHECK(p_evt->params.error_unexpected.error); break; case PM_EVT_PEER_DATA_UPDATE_SUCCEEDED: NRF_LOG_PRINTF("PM_EVT_PEER_DATA_UPDATE_SUCCEEDED: peer_id=%d data_id=0x%x action=0x%x\n", p_evt->peer_id, p_evt->params.peer_data_update_succeeded.data_id, p_evt->params.peer_data_update_succeeded.action); break; case PM_EVT_PEER_DATA_UPDATE_FAILED: // Assert. APP_ERROR_CHECK_BOOL(false); break; case PM_EVT_PEER_DELETE_SUCCEEDED: break; case PM_EVT_PEER_DELETE_FAILED: // Assert. APP_ERROR_CHECK(p_evt->params.peer_delete_failed.error); break; case PM_EVT_PEERS_DELETE_SUCCEEDED: adv_scan_start(); break; case PM_EVT_PEERS_DELETE_FAILED: // Assert. APP_ERROR_CHECK(p_evt->params.peers_delete_failed_evt.error); break; case PM_EVT_LOCAL_DB_CACHE_APPLIED: break; case PM_EVT_LOCAL_DB_CACHE_APPLY_FAILED: // The local database has likely changed, send service changed indications. pm_local_database_has_changed(); break; case PM_EVT_SERVICE_CHANGED_IND_SENT: break; case PM_EVT_SERVICE_CHANGED_IND_CONFIRMED: break; } } /**@brief Handles events coming from the Heart Rate central module. */ static void hrs_c_evt_handler(ble_hrs_c_t * p_hrs_c, ble_hrs_c_evt_t * p_hrs_c_evt) { switch (p_hrs_c_evt->evt_type) { case BLE_HRS_C_EVT_DISCOVERY_COMPLETE: { if (m_conn_handle_hrs_c == BLE_CONN_HANDLE_INVALID) { ret_code_t err_code; m_conn_handle_hrs_c = p_hrs_c_evt->conn_handle; NRF_LOG_PRINTF("HRS discovered on conn_handle 0x%x\r\n", m_conn_handle_hrs_c); err_code = ble_hrs_c_handles_assign(p_hrs_c, m_conn_handle_hrs_c, &p_hrs_c_evt->params.peer_db); APP_ERROR_CHECK(err_code); // Initiate bonding. err_code = pm_conn_secure(m_conn_handle_hrs_c, false); if (err_code != NRF_ERROR_INVALID_STATE) { APP_ERROR_CHECK(err_code); } // Heart rate service discovered. Enable notification of Heart Rate Measurement. err_code = ble_hrs_c_hrm_notif_enable(p_hrs_c); APP_ERROR_CHECK(err_code); } } break; // BLE_HRS_C_EVT_DISCOVERY_COMPLETE case BLE_HRS_C_EVT_HRM_NOTIFICATION: { NRF_LOG_PRINTF("Heart Rate = %d\r\n", p_hrs_c_evt->params.hrm.hr_value); } break; // BLE_HRS_C_EVT_HRM_NOTIFICATION default: // No implementation needed. break; } } /**@brief Function for searching a given name in the advertisement packets. * * @details Use this function to parse received advertising data and to find a given * name in them either as 'complete_local_name' or as 'short_local_name'. * * @param[in] p_adv_report advertising data to parse. * @param[in] name_to_find name to search. * @return true if the given name was found, false otherwise. */ static bool find_adv_name(const ble_gap_evt_adv_report_t *p_adv_report, const char * name_to_find) { uint32_t err_code; data_t adv_data; data_t dev_name; // Initialize advertisement report for parsing adv_data.p_data = (uint8_t *)p_adv_report->data; adv_data.data_len = p_adv_report->dlen; //search for advertising names err_code = adv_report_parse(BLE_GAP_AD_TYPE_COMPLETE_LOCAL_NAME, &adv_data, &dev_name); if (err_code == NRF_SUCCESS) { if(memcmp(name_to_find, dev_name.p_data, dev_name.data_len )== 0) { return true; } } else { // Look for the short local name if it was not found as complete err_code = adv_report_parse(BLE_GAP_AD_TYPE_SHORT_LOCAL_NAME, &adv_data, &dev_name); if (err_code != NRF_SUCCESS) { return false; } if(memcmp(m_target_periph_name, dev_name.p_data, dev_name.data_len )== 0) { return true; } } return false; }
/**@brief Function for handling the Application's BLE Stack events. * * @param[in] p_ble_evt Bluetooth stack event. */ static void on_ble_central_evt(ble_evt_t * p_ble_evt) { uint32_t err_code; const ble_gap_evt_t * p_gap_evt = &p_ble_evt->evt.gap_evt; 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 = (uint8_t *)p_gap_evt->params.adv_report.data; adv_data.data_len = p_gap_evt->params.adv_report.dlen; err_code = adv_report_parse(BLE_GAP_AD_TYPE_16BIT_SERVICE_UUID_MORE_AVAILABLE, &adv_data, &type_data); if (err_code != NRF_SUCCESS) { // Look for the services in 'complete' if it was not found in 'more available'. err_code = adv_report_parse(BLE_GAP_AD_TYPE_16BIT_SERVICE_UUID_COMPLETE, &adv_data, &type_data); } // Verify if short or complete name matches target. if (err_code == NRF_SUCCESS) { uint16_t extracted_uuid; // UUIDs found, look for matching UUID for (uint32_t u_index = 0; u_index < (type_data.data_len/UUID16_SIZE); u_index++) { UUID16_EXTRACT(&extracted_uuid,&type_data.p_data[u_index * UUID16_SIZE]); APPL_LOG("\t[APPL]: %x\r\n",extracted_uuid); if(extracted_uuid == BLE_UUID_HEART_RATE_SERVICE || extracted_uuid == BLE_UUID_RUNNING_SPEED_AND_CADENCE) { if(extracted_uuid == BLE_UUID_HEART_RATE_SERVICE) { printf("HRS found\n\r"); memcpy(&m_hrs_peripheral_address, &p_gap_evt->params.adv_report.peer_addr,sizeof(ble_gap_addr_t)); } if(extracted_uuid == BLE_UUID_RUNNING_SPEED_AND_CADENCE) { printf("RSC found\n\r"); memcpy(&m_rscs_peripheral_address, &p_gap_evt->params.adv_report.peer_addr,sizeof(ble_gap_addr_t)); } m_scan_param.selective = 0; // Initiate connection. err_code = sd_ble_gap_connect(&p_gap_evt->params.adv_report.peer_addr, &m_scan_param, &m_connection_param); m_whitelist_temporarily_disabled = false; if (err_code != NRF_SUCCESS) { APPL_LOG("[APPL]: Connection Request Failed, reason %d\r\n", err_code); } break; } } } break; } case BLE_GAP_EVT_TIMEOUT: if (p_gap_evt->params.timeout.src == BLE_GAP_TIMEOUT_SRC_SCAN) { APPL_LOG("[APPL]: Scan timed out.\r\n"); scan_start(); } else if (p_gap_evt->params.timeout.src == BLE_GAP_TIMEOUT_SRC_CONN) { APPL_LOG("[APPL]: Connection Request timed out.\r\n"); } break; case BLE_GAP_EVT_CONN_PARAM_UPDATE_REQUEST: // Accepting parameters requested by peer. err_code = sd_ble_gap_conn_param_update(p_gap_evt->conn_handle, &p_gap_evt->params.conn_param_update_request.conn_params); APP_ERROR_CHECK(err_code); break; case BLE_GAP_EVT_DISCONNECTED: /* if(p_gap_evt->conn_handle == m_conn_handle_central_hrs) { m_conn_handle_central_hrs = BLE_CONN_HANDLE_INVALID; } else if(p_gap_evt->conn_handle == m_conn_handle_central_rsc) { m_conn_handle_central_rsc = BLE_CONN_HANDLE_INVALID; } if((m_conn_handle_central_rsc == BLE_CONN_HANDLE_INVALID) && (m_conn_handle_central_hrs == BLE_CONN_HANDLE_INVALID)) { LEDS_OFF(CENTRAL_CONNECTED_LED); } */ break; default: break; } }
/**@brief Function for handling BLE Stack events concerning central applications. * * @details This function keeps the connection handles of central applications up-to-date. It * parses scanning reports, initiating a connection attempt to peripherals when a target UUID * is found, and manages connection parameter update requests. Additionally, it updates the status * of LEDs used to report central applications activity. * * @note Since this function updates connection handles, @ref BLE_GAP_EVT_DISCONNECTED events * should be dispatched to the target application before invoking this function. * * @param[in] p_ble_evt Bluetooth stack event. */ static void on_ble_central_evt(const ble_evt_t * const p_ble_evt) { // The addresses of peers we attempted to connect to. static ble_gap_addr_t periph_addr_hrs; static ble_gap_addr_t periph_addr_rsc; // For readability. const ble_gap_evt_t * const p_gap_evt = &p_ble_evt->evt.gap_evt; switch (p_ble_evt->header.evt_id) { /** Upon connection, check which peripheral has connected (HR or RSC), initiate DB * discovery, update LEDs status and resume scanning if necessary. */ case BLE_GAP_EVT_CONNECTED: { uint32_t err_code; // For readability. const ble_gap_addr_t * const peer_addr = &p_gap_evt->params.connected.peer_addr; /** Check which peer has connected, save the connection handle and initiate DB discovery. * DB discovery will invoke a callback (hrs_c_evt_handler and rscs_c_evt_handler) * upon completion, which is used to enable notifications from the peer. */ if(memcmp(&periph_addr_hrs, peer_addr, sizeof(ble_gap_addr_t)) == 0) { NRF_LOG_PRINTF("HRS central connected\r\n"); // Reset the peer address we had saved. memset(&periph_addr_hrs, 0, sizeof(ble_gap_addr_t)); m_conn_handle_hrs_c = p_gap_evt->conn_handle; NRF_LOG_PRINTF("Starting DB discovery for HRS\r\n"); err_code = ble_db_discovery_start(&m_ble_db_discovery_hrs, p_gap_evt->conn_handle); APP_ERROR_CHECK(err_code); } else if(memcmp(&periph_addr_rsc, peer_addr, sizeof(ble_gap_addr_t)) == 0) { NRF_LOG_PRINTF("RSC central connected\r\n"); // Reset the peer address we had saved. memset(&periph_addr_rsc, 0, sizeof(ble_gap_addr_t)); m_conn_handle_rscs_c = p_gap_evt->conn_handle; NRF_LOG_PRINTF("Starting DB discovery for RSCS\r\n"); err_code = ble_db_discovery_start(&m_ble_db_discovery_rsc, p_gap_evt->conn_handle); APP_ERROR_CHECK(err_code); } /** Update LEDs status, and check if we should be looking for more * peripherals to connect to. */ LEDS_ON(CENTRAL_CONNECTED_LED); if (ble_conn_state_n_centrals() == MAX_CONNECTED_CENTRALS) { LEDS_OFF(CENTRAL_SCANNING_LED); } else { // Resume scanning. LEDS_ON(CENTRAL_SCANNING_LED); scan_start(); } } break; // BLE_GAP_EVT_CONNECTED /** Upon disconnection, reset the connection handle of the peer which disconnected, update * the LEDs status and start scanning again. */ case BLE_GAP_EVT_DISCONNECTED: { uint8_t n_centrals; if (p_gap_evt->conn_handle == m_conn_handle_hrs_c) { NRF_LOG_PRINTF("HRS central disconnected (reason: %d)\r\n", p_gap_evt->params.disconnected.reason); m_conn_handle_hrs_c = BLE_CONN_HANDLE_INVALID; } else if(p_gap_evt->conn_handle == m_conn_handle_rscs_c) { NRF_LOG_PRINTF("RSC central disconnected (reason: %d)\r\n", p_gap_evt->params.disconnected.reason); m_conn_handle_rscs_c = BLE_CONN_HANDLE_INVALID; } // Start scanning // scan_start(); // Update LEDs status. LEDS_ON(CENTRAL_SCANNING_LED); n_centrals = ble_conn_state_n_centrals(); if (n_centrals == 0) { LEDS_OFF(CENTRAL_CONNECTED_LED); } } break; // BLE_GAP_EVT_DISCONNECTED case BLE_GAP_EVT_ADV_REPORT: { uint32_t err_code; data_t adv_data; data_t type_data; // For readibility. const ble_gap_addr_t * const peer_addr = &p_gap_evt->params.adv_report.peer_addr; // Initialize advertisement report for parsing. adv_data.p_data = (uint8_t *)p_gap_evt->params.adv_report.data; adv_data.data_len = p_gap_evt->params.adv_report.dlen; err_code = adv_report_parse(BLE_GAP_AD_TYPE_16BIT_SERVICE_UUID_MORE_AVAILABLE, &adv_data, &type_data); if (err_code != NRF_SUCCESS) { // Look for the services in 'complete' if it was not found in 'more available'. err_code = adv_report_parse(BLE_GAP_AD_TYPE_16BIT_SERVICE_UUID_COMPLETE, &adv_data, &type_data); if (err_code != NRF_SUCCESS) { // If we can't parse the data, then exit. break; } } // Verify if any UUID match the Heart rate or Running speed and cadence services. for (uint32_t u_index = 0; u_index < (type_data.data_len / UUID16_SIZE); u_index++) { bool do_connect = false; uint16_t extracted_uuid; UUID16_EXTRACT(&extracted_uuid, &type_data.p_data[u_index * UUID16_SIZE]); /** We do not want to connect to two peripherals offering the same service, so when * a UUID is matched, we check that we are not already connected to a peer which * offers the same service. We then save the peer address, so that upon connection * we can tell which peer has connected and update its respective connection * handle. */ if ((extracted_uuid == BLE_UUID_HEART_RATE_SERVICE) && (m_conn_handle_hrs_c == BLE_CONN_HANDLE_INVALID)) { do_connect = true; memcpy(&periph_addr_hrs, peer_addr, sizeof(ble_gap_addr_t)); } else if ((extracted_uuid == BLE_UUID_RUNNING_SPEED_AND_CADENCE) && (m_conn_handle_rscs_c == BLE_CONN_HANDLE_INVALID)) { do_connect = true; memcpy(&periph_addr_rsc, peer_addr, sizeof(ble_gap_addr_t)); } if (do_connect) { // Initiate connection. err_code = sd_ble_gap_connect(peer_addr, &m_scan_param, &m_connection_param); if (err_code != NRF_SUCCESS) { APPL_LOG("[APPL]: Connection Request Failed, reason %d\r\n", err_code); } } } } break; // BLE_GAP_ADV_REPORT case BLE_GAP_EVT_TIMEOUT: { // We have not specified a timeout for scanning, so only connection attemps can timeout. if (p_gap_evt->params.timeout.src == BLE_GAP_TIMEOUT_SRC_CONN) { APPL_LOG("[APPL]: Connection Request timed out.\r\n"); } } break; // BLE_GAP_EVT_TIMEOUT case BLE_GAP_EVT_CONN_PARAM_UPDATE_REQUEST: { // Accept parameters requested by peer. ret_code_t err_code; err_code = sd_ble_gap_conn_param_update(p_gap_evt->conn_handle, &p_gap_evt->params.conn_param_update_request.conn_params); APP_ERROR_CHECK(err_code); } break; // BLE_GAP_EVT_CONN_PARAM_UPDATE_REQUEST default: // No implementation needed. break; } }
/**@brief Function for handling Peer Manager events. * * @param[in] p_evt Peer Manager event. */ static void pm_evt_handler(pm_evt_t const * p_evt) { ret_code_t err_code; switch(p_evt->evt_id) { case PM_EVT_BONDED_PEER_CONNECTED: { NRF_LOG_PRINTF_DEBUG("Connected to previously bonded device\r\n"); err_code = pm_peer_rank_highest(p_evt->peer_id); if (err_code != NRF_ERROR_BUSY) { APP_ERROR_CHECK(err_code); } }break;//PM_EVT_BONDED_PEER_CONNECTED case PM_EVT_CONN_SEC_START: break;//PM_EVT_CONN_SEC_START case PM_EVT_CONN_SEC_SUCCEEDED: { NRF_LOG_PRINTF("Link secured. Role: %d. conn_handle: %d, Procedure: %d\r\n", ble_conn_state_role(p_evt->conn_handle), p_evt->conn_handle, p_evt->params.conn_sec_succeeded.procedure); err_code = pm_peer_rank_highest(p_evt->peer_id); if (err_code != NRF_ERROR_BUSY) { APP_ERROR_CHECK(err_code); } }break;//PM_EVT_CONN_SEC_SUCCEEDED case PM_EVT_CONN_SEC_FAILED: { /** In some cases, when securing fails, it can be restarted directly. Sometimes it can * be restarted, but only after changing some Security Parameters. Sometimes, it cannot * be restarted until the link is disconnected and reconnected. Sometimes it is * impossible, to secure the link, or the peer device does not support it. How to * handle this error is highly application dependent. */ switch (p_evt->params.conn_sec_failed.error) { case PM_CONN_SEC_ERROR_PIN_OR_KEY_MISSING: // Rebond if one party has lost its keys. err_code = pm_conn_secure(p_evt->conn_handle, true); if (err_code != NRF_ERROR_INVALID_STATE) { APP_ERROR_CHECK(err_code); } break;//PM_CONN_SEC_ERROR_PIN_OR_KEY_MISSING default: break; } }break;//PM_EVT_CONN_SEC_FAILED case PM_EVT_CONN_SEC_CONFIG_REQ: { // Reject pairing request from an already bonded peer. pm_conn_sec_config_t conn_sec_config = {.allow_repairing = false}; pm_conn_sec_config_reply(p_evt->conn_handle, &conn_sec_config); }break;//PM_EVT_CONN_SEC_CONFIG_REQ case PM_EVT_STORAGE_FULL: { // Run garbage collection on the flash. err_code = fds_gc(); if (err_code == FDS_ERR_BUSY || err_code == FDS_ERR_NO_SPACE_IN_QUEUES) { // Retry. } else { APP_ERROR_CHECK(err_code); } }break;//PM_EVT_STORAGE_FULL case PM_EVT_ERROR_UNEXPECTED: // Assert. APP_ERROR_CHECK(p_evt->params.error_unexpected.error); break;//PM_EVT_ERROR_UNEXPECTED case PM_EVT_PEER_DATA_UPDATE_SUCCEEDED: break;//PM_EVT_PEER_DATA_UPDATE_SUCCEEDED case PM_EVT_PEER_DATA_UPDATE_FAILED: // Assert. APP_ERROR_CHECK_BOOL(false); break;//PM_EVT_PEER_DATA_UPDATE_FAILED case PM_EVT_PEER_DELETE_SUCCEEDED: break;//PM_EVT_PEER_DELETE_SUCCEEDED case PM_EVT_PEER_DELETE_FAILED: // Assert. APP_ERROR_CHECK(p_evt->params.peer_delete_failed.error); break;//PM_EVT_PEER_DELETE_FAILED case PM_EVT_PEERS_DELETE_SUCCEEDED: adv_scan_start(); break;//PM_EVT_PEERS_DELETE_SUCCEEDED case PM_EVT_PEERS_DELETE_FAILED: // Assert. APP_ERROR_CHECK(p_evt->params.peers_delete_failed_evt.error); break;//PM_EVT_PEERS_DELETE_FAILED case PM_EVT_LOCAL_DB_CACHE_APPLIED: break;//PM_EVT_LOCAL_DB_CACHE_APPLIED case PM_EVT_LOCAL_DB_CACHE_APPLY_FAILED: // The local database has likely changed, send service changed indications. pm_local_database_has_changed(); break;//PM_EVT_LOCAL_DB_CACHE_APPLY_FAILED case PM_EVT_SERVICE_CHANGED_IND_SENT: break;//PM_EVT_SERVICE_CHANGED_IND_SENT case PM_EVT_SERVICE_CHANGED_IND_CONFIRMED: break;//PM_EVT_SERVICE_CHANGED_IND_CONFIRMED default: // No implementation needed. break; } } /**@brief Handles events coming from the Heart Rate central module. */ static void hrs_c_evt_handler(ble_hrs_c_t * p_hrs_c, ble_hrs_c_evt_t * p_hrs_c_evt) { switch (p_hrs_c_evt->evt_type) { case BLE_HRS_C_EVT_DISCOVERY_COMPLETE: { if (m_conn_handle_hrs_c == BLE_CONN_HANDLE_INVALID) { ret_code_t err_code; m_conn_handle_hrs_c = p_hrs_c_evt->conn_handle; NRF_LOG_PRINTF("HRS discovered on conn_handle 0x%x\r\n", m_conn_handle_hrs_c); err_code = ble_hrs_c_handles_assign(p_hrs_c, m_conn_handle_hrs_c, &p_hrs_c_evt->params.peer_db); APP_ERROR_CHECK(err_code); // Initiate bonding. err_code = pm_conn_secure(m_conn_handle_hrs_c, false); if (err_code != NRF_ERROR_INVALID_STATE) { APP_ERROR_CHECK(err_code); } // Heart rate service discovered. Enable notification of Heart Rate Measurement. err_code = ble_hrs_c_hrm_notif_enable(p_hrs_c); APP_ERROR_CHECK(err_code); } } break; // BLE_HRS_C_EVT_DISCOVERY_COMPLETE case BLE_HRS_C_EVT_HRM_NOTIFICATION: { ret_code_t err_code; NRF_LOG_PRINTF("Heart Rate = %d\r\n", p_hrs_c_evt->params.hrm.hr_value); err_code = ble_hrs_heart_rate_measurement_send(&m_hrs, p_hrs_c_evt->params.hrm.hr_value); if ((err_code != NRF_SUCCESS) && (err_code != NRF_ERROR_INVALID_STATE) && (err_code != BLE_ERROR_NO_TX_PACKETS) && (err_code != BLE_ERROR_GATTS_SYS_ATTR_MISSING) ) { APP_ERROR_HANDLER(err_code); } } break; // BLE_HRS_C_EVT_HRM_NOTIFICATION default: // No implementation needed. break; } } /**@brief Handles events coming from Running Speed and Cadence central module. */ static void rscs_c_evt_handler(ble_rscs_c_t * p_rscs_c, ble_rscs_c_evt_t * p_rscs_c_evt) { switch (p_rscs_c_evt->evt_type) { case BLE_RSCS_C_EVT_DISCOVERY_COMPLETE: { if (m_conn_handle_rscs_c == BLE_CONN_HANDLE_INVALID) { ret_code_t err_code; m_conn_handle_rscs_c = p_rscs_c_evt->conn_handle; NRF_LOG_PRINTF("Running Speed and Cadence service discovered on conn_handle 0x%x\r\n", m_conn_handle_rscs_c); err_code = ble_rscs_c_handles_assign(p_rscs_c, m_conn_handle_rscs_c, &p_rscs_c_evt->params.rscs_db); APP_ERROR_CHECK(err_code); // Initiate bonding. err_code = pm_conn_secure(m_conn_handle_rscs_c, false); if (err_code != NRF_ERROR_INVALID_STATE) { APP_ERROR_CHECK(err_code); } // Running speed cadence service discovered. Enable notifications. err_code = ble_rscs_c_rsc_notif_enable(p_rscs_c); APP_ERROR_CHECK(err_code); } } break; // BLE_RSCS_C_EVT_DISCOVERY_COMPLETE: case BLE_RSCS_C_EVT_RSC_NOTIFICATION: { uint32_t err_code; ble_rscs_meas_t rscs_measurment; NRF_LOG_PRINTF("Speed = %d\r\n", p_rscs_c_evt->params.rsc.inst_speed); rscs_measurment.is_running = p_rscs_c_evt->params.rsc.is_running; rscs_measurment.is_inst_stride_len_present = p_rscs_c_evt->params.rsc.is_inst_stride_len_present; rscs_measurment.is_total_distance_present = p_rscs_c_evt->params.rsc.is_total_distance_present; rscs_measurment.inst_stride_length = p_rscs_c_evt->params.rsc.inst_stride_length; rscs_measurment.inst_cadence = p_rscs_c_evt->params.rsc.inst_cadence; rscs_measurment.inst_speed = p_rscs_c_evt->params.rsc.inst_speed; rscs_measurment.total_distance = p_rscs_c_evt->params.rsc.total_distance; err_code = ble_rscs_measurement_send(&m_rscs, &rscs_measurment); if ((err_code != NRF_SUCCESS) && (err_code != NRF_ERROR_INVALID_STATE) && (err_code != BLE_ERROR_NO_TX_PACKETS) && (err_code != BLE_ERROR_GATTS_SYS_ATTR_MISSING) ) { APP_ERROR_HANDLER(err_code); } } break; // BLE_RSCS_C_EVT_RSC_NOTIFICATION default: // No implementation needed. break; } } /**@brief Function for searching a given name in the advertisement packets. * * @details Use this function to parse received advertising data and to find a given * name in them either as 'complete_local_name' or as 'short_local_name'. * * @param[in] p_adv_report advertising data to parse. * @param[in] name_to_find name to search. * @return true if the given name was found, false otherwise. */ static bool find_adv_name(const ble_gap_evt_adv_report_t *p_adv_report, const char * name_to_find) { uint32_t err_code; data_t adv_data; data_t dev_name; // Initialize advertisement report for parsing adv_data.p_data = (uint8_t *)p_adv_report->data; adv_data.data_len = p_adv_report->dlen; //search for advertising names err_code = adv_report_parse(BLE_GAP_AD_TYPE_COMPLETE_LOCAL_NAME, &adv_data, &dev_name); if (err_code == NRF_SUCCESS) { if(memcmp(name_to_find, dev_name.p_data, dev_name.data_len )== 0) { return true; } } else { // Look for the short local name if it was not found as complete err_code = adv_report_parse(BLE_GAP_AD_TYPE_SHORT_LOCAL_NAME, &adv_data, &dev_name); if (err_code != NRF_SUCCESS) { return false; } if(memcmp(m_target_periph_name, dev_name.p_data, dev_name.data_len )== 0) { return true; } } return false; }
/**@brief Function for handling the Application's BLE Stack events. * * @param[in] p_ble_evt Bluetooth stack event. */ static void on_ble_evt(ble_evt_t * p_ble_evt) { uint32_t err_code; const ble_gap_evt_t * p_gap_evt = &p_ble_evt->evt.gap_evt; 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 = (uint8_t *)p_gap_evt->params.adv_report.data; adv_data.data_len = p_gap_evt->params.adv_report.dlen; err_code = adv_report_parse(BLE_GAP_AD_TYPE_128BIT_SERVICE_UUID_MORE_AVAILABLE, &adv_data, &type_data); if (err_code != NRF_SUCCESS) { // Compare 128 UUID. err_code = adv_report_parse(BLE_GAP_AD_TYPE_128BIT_SERVICE_UUID_COMPLETE, &adv_data, &type_data); } // Verify if short or complete name matches target. if (err_code == NRF_SUCCESS) { if(!memcmp( nus_service_uuid,type_data.p_data,16)) { // Stop scanning. err_code = sd_ble_gap_scan_stop(); if (err_code != NRF_SUCCESS) { printf("[APPL]: Scan stop failed, reason %d\r\n", (int)err_code); } nrf_gpio_pin_clear(SCAN_LED_PIN_NO); m_scan_param.selective = 0; // Initiate connection. err_code = sd_ble_gap_connect(&p_gap_evt->params.adv_report.\ peer_addr, &m_scan_param, &m_connection_param); if (err_code != NRF_SUCCESS) { printf("[APPL]: Connection Request Failed, reason %d\r\n", (int)err_code); } break; } // } } break; } case BLE_GAP_EVT_TIMEOUT: if(p_gap_evt->params.timeout.src == BLE_GAP_TIMEOUT_SRC_SCAN) { if (m_scan_mode == BLE_WHITELIST_SCAN) { m_scan_mode = BLE_FAST_SCAN; // Start non selective scanning. scan_start(); } } else if (p_gap_evt->params.timeout.src == BLE_GAP_TIMEOUT_SRC_CONN) { // } break; case BLE_GAP_EVT_CONN_PARAM_UPDATE_REQUEST: // Accepting parameters requested by peer. err_code = sd_ble_gap_conn_param_update(p_gap_evt->conn_handle, &p_gap_evt->params.conn_param_update_request.conn_params); APP_ERROR_CHECK(err_code); break; default: break; } }