一、简介
一个蓝牙设备同时作为主设备和从设备进行使用,这种能力的节点设备称为主从一体设备。
主从一体提供了扩展 BLE 蓝牙模块的能力,自从一个被称为“链路层拓扑”的功能被添加到蓝牙规范中后,就已经允许蓝牙设备同时作为主设备和从设备,在任何角色组合中操作。
二、ble_app_hrs_rscs_relay
打开工程 不同SDK\examples\ble_central_and_peripheral\experimental\ble_app_hrs_rscs_relay
2.1 main
主从一体设备中,如何实现该设备即作为主机又作为从机,关键的问题就是主机和从机事件的切换。首先必须把主机服务和从机服务搭建起来,初始化两种服务类型,然后解决主机的扫描然后发起连接部分、从机的广播然后被连接部分,当两个类型的服务被搭建后,就开始处理蓝牙事件,决定数据流向。
2.1.1 scan_init
设置一些扫描参数,如名字过滤、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
设置广播中蓝牙名称,连接参数。
/**@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
连接参数初始化主要作为从机更新连接参数使用。
/**@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
从机服务建立后,就需要对广播进行设置,广播是从机被主机发现的前提条件。
/**@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
广播和扫描同时运行
/**@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);
}
}
三、蓝牙协议栈主机事件
当主机发起连接成功,或断开连接时,产生主机事件
/**@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;
}
}
四、蓝牙协议栈从机事件
当从机被连接,或断开连接时,产生从机事件
/**@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 日
• 参考:青风电子社区
网友评论