一只酥、簡介
一個(gè)藍(lán)牙設(shè)備同時(shí)作為主設(shè)備和從設(shè)備進(jìn)行使用褥实,這種能力的節(jié)點(diǎn)設(shè)備稱為主從一體設(shè)備。
主從一體提供了擴(kuò)展 BLE 藍(lán)牙模塊的能力层皱,自從一個(gè)被稱為“鏈路層拓?fù)洹钡墓δ鼙惶砑拥剿{(lán)牙規(guī)范中后性锭,就已經(jīng)允許藍(lán)牙設(shè)備同時(shí)作為主設(shè)備和從設(shè)備,在任何角色組合中操作叫胖。
二草冈、ble_app_hrs_rscs_relay
打開工程 不同SDK\examples\ble_central_and_peripheral\experimental\ble_app_hrs_rscs_relay
2.1 main
主從一體設(shè)備中,如何實(shí)現(xiàn)該設(shè)備即作為主機(jī)又作為從機(jī)瓮增,關(guān)鍵的問題就是主機(jī)和從機(jī)事件的切換怎棱。首先必須把主機(jī)服務(wù)和從機(jī)服務(wù)搭建起來,初始化兩種服務(wù)類型绷跑,然后解決主機(jī)的掃描然后發(fā)起連接部分拳恋、從機(jī)的廣播然后被連接部分,當(dāng)兩個(gè)類型的服務(wù)被搭建后砸捏,就開始處理藍(lán)牙事件谬运,決定數(shù)據(jù)流向。
2.1.1 scan_init
設(shè)置一些掃描參數(shù)垦藏,如名字過濾梆暖、UUID過濾。
/**@brief Function for initialization the scanning and setting the filters.
*/
static void scan_init(void)
{
ret_code_t err_code;
nrf_ble_scan_init_t init_scan;
memset(&init_scan, 0, sizeof(init_scan));
init_scan.p_scan_param = &m_scan_param;
err_code = nrf_ble_scan_init(&m_scan, &init_scan, scan_evt_handler);
APP_ERROR_CHECK(err_code);
if (strlen(m_target_periph_name) != 0)
{
err_code = nrf_ble_scan_filter_set(&m_scan,
SCAN_NAME_FILTER,
m_target_periph_name);
APP_ERROR_CHECK(err_code);
}
err_code = nrf_ble_scan_filter_set(&m_scan,
SCAN_UUID_FILTER,
&m_adv_uuids[HART_RATE_SERVICE_UUID_IDX]);
APP_ERROR_CHECK(err_code);
err_code = nrf_ble_scan_filter_set(&m_scan,
SCAN_UUID_FILTER,
&m_adv_uuids[RSCS_SERVICE_UUID_IDX]);
APP_ERROR_CHECK(err_code);
err_code = nrf_ble_scan_filters_enable(&m_scan,
NRF_BLE_SCAN_ALL_FILTER,
false);
APP_ERROR_CHECK(err_code);
}
2.1.2 gap_params_init
設(shè)置廣播中藍(lán)牙名稱掂骏,連接參數(shù)轰驳。
/**@brief Function for initializing the GAP.
*
* @details This function sets up all the necessary GAP (Generic Access Profile) parameters of the
* device, including the device name, appearance, and the preferred connection parameters.
*/
static void gap_params_init(void)
{
ret_code_t err_code;
ble_gap_conn_params_t gap_conn_params;
ble_gap_conn_sec_mode_t sec_mode;
BLE_GAP_CONN_SEC_MODE_SET_OPEN(&sec_mode);
err_code = sd_ble_gap_device_name_set(&sec_mode,
(const uint8_t *)DEVICE_NAME,
strlen(DEVICE_NAME));
APP_ERROR_CHECK(err_code);
memset(&gap_conn_params, 0, sizeof(gap_conn_params));
gap_conn_params.min_conn_interval = m_scan.conn_params.min_conn_interval;
gap_conn_params.max_conn_interval = m_scan.conn_params.max_conn_interval;
gap_conn_params.slave_latency = m_scan.conn_params.slave_latency;
gap_conn_params.conn_sup_timeout = m_scan.conn_params.conn_sup_timeout;
err_code = sd_ble_gap_ppcp_set(&gap_conn_params);
APP_ERROR_CHECK(err_code);
}
2.1.3 conn_params_init
連接參數(shù)初始化主要作為從機(jī)更新連接參數(shù)使用。
/**@brief Function for initializing the Connection Parameters module.
*/
static void conn_params_init(void)
{
ret_code_t err_code;
ble_conn_params_init_t cp_init;
memset(&cp_init, 0, sizeof(cp_init));
cp_init.p_conn_params = NULL;
cp_init.first_conn_params_update_delay = FIRST_CONN_PARAMS_UPDATE_DELAY;
cp_init.next_conn_params_update_delay = NEXT_CONN_PARAMS_UPDATE_DELAY;
cp_init.max_conn_params_update_count = MAX_CONN_PARAMS_UPDATE_COUNT;
cp_init.start_on_notify_cccd_handle = BLE_CONN_HANDLE_INVALID; // Start upon connection.
cp_init.disconnect_on_fail = true;
cp_init.evt_handler = NULL; // Ignore events.
cp_init.error_handler = conn_params_error_handler;
err_code = ble_conn_params_init(&cp_init);
APP_ERROR_CHECK(err_code);
}
2.1.4 advertising_init
從機(jī)服務(wù)建立后弟灼,就需要對廣播進(jìn)行設(shè)置级解,廣播是從機(jī)被主機(jī)發(fā)現(xiàn)的前提條件。
/**@brief Function for initializing the advertising functionality.
*/
static void advertising_init(void)
{
ret_code_t err_code;
ble_advertising_init_t init;
memset(&init, 0, sizeof(init));
init.advdata.name_type = BLE_ADVDATA_FULL_NAME;
init.advdata.include_appearance = true;
init.advdata.flags = BLE_GAP_ADV_FLAGS_LE_ONLY_GENERAL_DISC_MODE;
init.advdata.uuids_complete.uuid_cnt = sizeof(m_adv_uuids) / sizeof(m_adv_uuids[0]);
init.advdata.uuids_complete.p_uuids = m_adv_uuids;
init.config.ble_adv_fast_enabled = true;
init.config.ble_adv_fast_interval = APP_ADV_INTERVAL;
init.config.ble_adv_fast_timeout = APP_ADV_DURATION;
init.evt_handler = on_adv_evt;
err_code = ble_advertising_init(&m_advertising, &init);
APP_ERROR_CHECK(err_code);
ble_advertising_conn_cfg_tag_set(&m_advertising, APP_BLE_CONN_CFG_TAG);
}
2.1.5 adv_scan_start
廣播和掃描同時(shí)運(yùn)行
/**@brief Function for initializing the advertising and the scanning.
*/
static void adv_scan_start(void)
{
ret_code_t err_code;
//check if there are no flash operations in progress
if (!nrf_fstorage_is_busy(NULL)) // 檢測如果沒有flash操作
{
// Start scanning for peripherals and initiate connection to devices which
// advertise Heart Rate or Running speed and cadence UUIDs.
scan_start(); // 開始掃描
// Turn on the LED to signal scanning.
bsp_board_led_on(CENTRAL_SCANNING_LED);
// Start advertising. // 開始廣播
err_code = ble_advertising_start(&m_advertising, BLE_ADV_MODE_FAST);
APP_ERROR_CHECK(err_code);
}
}
三田绑、藍(lán)牙協(xié)議棧主機(jī)事件
當(dāng)主機(jī)發(fā)起連接成功勤哗,或斷開連接時(shí),產(chǎn)生主機(jī)事件
/**@brief Function for handling BLE events from the central application.
*
* @details This function parses scanning reports and initiates a connection to peripherals when a
* target UUID is found. It updates the status of LEDs used to report the central application
* activity.
*
* @param[in] p_ble_evt Bluetooth stack event.
*/
static void on_ble_central_evt(ble_evt_t const * p_ble_evt)
{
ret_code_t err_code;
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 is connected (HR or RSC), initiate DB
// discovery, update LEDs status, and resume scanning, if necessary.
case BLE_GAP_EVT_CONNECTED:
{
NRF_LOG_INFO("Central connected");
// If no Heart Rate sensor or RSC sensor is currently connected, try to find them on this peripheral.
if ( (m_conn_handle_hrs_c == BLE_CONN_HANDLE_INVALID)
|| (m_conn_handle_rscs_c == BLE_CONN_HANDLE_INVALID))
{
NRF_LOG_INFO("Attempt to find HRS or RSC on conn_handle 0x%x", p_gap_evt->conn_handle);
err_code = ble_db_discovery_start(&m_db_discovery[0], p_gap_evt->conn_handle);
if (err_code == NRF_ERROR_BUSY)
{
err_code = ble_db_discovery_start(&m_db_discovery[1], p_gap_evt->conn_handle);
APP_ERROR_CHECK(err_code);
}
else
{
APP_ERROR_CHECK(err_code);
}
}
// Assign connection handle to the QWR module.
multi_qwr_conn_handle_assign(p_gap_evt->conn_handle);
// Update LEDs status, and check whether to look for more peripherals to connect to.
bsp_board_led_on(CENTRAL_CONNECTED_LED);
if (ble_conn_state_central_conn_count() == NRF_SDH_BLE_CENTRAL_LINK_COUNT)
{
bsp_board_led_off(CENTRAL_SCANNING_LED);
}
else
{
// Resume scanning.
bsp_board_led_on(CENTRAL_SCANNING_LED);
scan_start();
}
} break; // BLE_GAP_EVT_CONNECTED
// Upon disconnection, reset the connection handle of the peer that disconnected,
// update the LEDs status and start scanning again.
case BLE_GAP_EVT_DISCONNECTED:
{
if (p_gap_evt->conn_handle == m_conn_handle_hrs_c)
{
NRF_LOG_INFO("HRS central disconnected (reason: %d)",
p_gap_evt->params.disconnected.reason);
m_conn_handle_hrs_c = BLE_CONN_HANDLE_INVALID;
err_code = nrf_ble_scan_filter_set(&m_scan,
SCAN_UUID_FILTER,
&m_adv_uuids[HART_RATE_SERVICE_UUID_IDX]);
APP_ERROR_CHECK(err_code);
}
if (p_gap_evt->conn_handle == m_conn_handle_rscs_c)
{
NRF_LOG_INFO("RSC central disconnected (reason: %d)",
p_gap_evt->params.disconnected.reason);
m_conn_handle_rscs_c = BLE_CONN_HANDLE_INVALID;
err_code = nrf_ble_scan_filter_set(&m_scan,
SCAN_UUID_FILTER,
&m_adv_uuids[RSCS_SERVICE_UUID_IDX]);
APP_ERROR_CHECK(err_code);
}
if ( (m_conn_handle_rscs_c == BLE_CONN_HANDLE_INVALID)
|| (m_conn_handle_hrs_c == BLE_CONN_HANDLE_INVALID))
{
// Start scanning.
scan_start();
// Update LEDs status.
bsp_board_led_on(CENTRAL_SCANNING_LED);
}
if (ble_conn_state_central_conn_count() == 0)
{
bsp_board_led_off(CENTRAL_CONNECTED_LED);
}
} break; // BLE_GAP_EVT_DISCONNECTED
case BLE_GAP_EVT_TIMEOUT:
{
// No timeout for scanning is specified, so only connection attemps can timeout.
if (p_gap_evt->params.timeout.src == BLE_GAP_TIMEOUT_SRC_CONN)
{
NRF_LOG_INFO("Connection Request timed out.");
}
} break;
case BLE_GAP_EVT_CONN_PARAM_UPDATE_REQUEST:
{
// Accept 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_PHY_UPDATE_REQUEST:
{
NRF_LOG_DEBUG("PHY update request.");
ble_gap_phys_t const phys =
{
.rx_phys = BLE_GAP_PHY_AUTO,
.tx_phys = BLE_GAP_PHY_AUTO,
};
err_code = sd_ble_gap_phy_update(p_ble_evt->evt.gap_evt.conn_handle, &phys);
APP_ERROR_CHECK(err_code);
} break;
case BLE_GATTC_EVT_TIMEOUT:
// Disconnect on GATT Client timeout event.
NRF_LOG_DEBUG("GATT Client Timeout.");
err_code = sd_ble_gap_disconnect(p_ble_evt->evt.gattc_evt.conn_handle,
BLE_HCI_REMOTE_USER_TERMINATED_CONNECTION);
APP_ERROR_CHECK(err_code);
break;
case BLE_GATTS_EVT_TIMEOUT:
// Disconnect on GATT Server timeout event.
NRF_LOG_DEBUG("GATT Server Timeout.");
err_code = sd_ble_gap_disconnect(p_ble_evt->evt.gatts_evt.conn_handle,
BLE_HCI_REMOTE_USER_TERMINATED_CONNECTION);
APP_ERROR_CHECK(err_code);
break;
default:
// No implementation needed.
break;
}
}
四掩驱、藍(lán)牙協(xié)議棧從機(jī)事件
當(dāng)從機(jī)被連接俺陋,或斷開連接時(shí),產(chǎn)生從機(jī)事件
/**@brief Function for handling BLE events from peripheral applications.
* @details Updates the status LEDs used to report the activity of the peripheral applications.
*
* @param[in] p_ble_evt Bluetooth stack event.
*/
static void on_ble_peripheral_evt(ble_evt_t const * p_ble_evt)
{
ret_code_t err_code;
ble_gap_evt_t const * p_gap_evt = &p_ble_evt->evt.gap_evt;
switch (p_ble_evt->header.evt_id)
{
case BLE_GAP_EVT_CONNECTED:
NRF_LOG_INFO("Peripheral connected");
bsp_board_led_off(PERIPHERAL_ADVERTISING_LED);
bsp_board_led_on(PERIPHERAL_CONNECTED_LED);
// Assign connection handle to the QWR module.
multi_qwr_conn_handle_assign(p_ble_evt->evt.gap_evt.conn_handle);
break;
case BLE_GAP_EVT_DISCONNECTED:
NRF_LOG_INFO("Peripheral disconnected. conn_handle: 0x%x, reason: 0x%x",
p_gap_evt->conn_handle,
p_gap_evt->params.disconnected.reason);
bsp_board_led_off(PERIPHERAL_CONNECTED_LED);
break;
case BLE_GAP_EVT_PHY_UPDATE_REQUEST:
{
NRF_LOG_DEBUG("PHY update request.");
ble_gap_phys_t const phys =
{
.rx_phys = BLE_GAP_PHY_AUTO,
.tx_phys = BLE_GAP_PHY_AUTO,
};
err_code = sd_ble_gap_phy_update(p_ble_evt->evt.gap_evt.conn_handle, &phys);
APP_ERROR_CHECK(err_code);
} break;
case BLE_GATTC_EVT_TIMEOUT:
// Disconnect on GATT Client timeout event.
NRF_LOG_DEBUG("GATT Client Timeout.");
err_code = sd_ble_gap_disconnect(p_ble_evt->evt.gattc_evt.conn_handle,
BLE_HCI_REMOTE_USER_TERMINATED_CONNECTION);
APP_ERROR_CHECK(err_code);
break;
case BLE_GATTS_EVT_TIMEOUT:
// Disconnect on GATT Server timeout event.
NRF_LOG_DEBUG("GATT Server Timeout.");
err_code = sd_ble_gap_disconnect(p_ble_evt->evt.gatts_evt.conn_handle,
BLE_HCI_REMOTE_USER_TERMINATED_CONNECTION);
APP_ERROR_CHECK(err_code);
break;
default:
// No implementation needed.
break;
}
}
? 由 Leung 寫于 2020 年 9 月 30 日
? 參考:青風(fēng)電子社區(qū)