因为笔者周围蓝牙设备过多,所以就不做直接连接实验了,但是仍就会先从直接连接讲。
1.过滤连接
1.1主机端
想要连接设备,我们的主机要发起连接,但是发起之后,怎么进行连接,这个是由连接参数决定,这就又回到了设置扫描初始化那个结构体:
连接参数结构体定义:
typedef struct
{
uint16_t min_conn_interval; /**< Minimum Connection Interval in 1.25 ms units, see @ref BLE_GAP_CP_LIMITS.*/
uint16_t max_conn_interval; /**< Maximum Connection Interval in 1.25 ms units, see @ref BLE_GAP_CP_LIMITS.*/
uint16_t slave_latency; /**< Slave Latency in number of connection events, see @ref BLE_GAP_CP_LIMITS.*/
uint16_t conn_sup_timeout; /**< Connection Supervision Timeout in 10 ms units, see @ref BLE_GAP_CP_LIMITS.*/
} ble_gap_conn_params_t;
参数意义可以参考以下文档的3.4节:
蓝牙技术详解
这里我们定义:
然后它被放在和扫描参数一起初始化:
?
然后只需要在扫描回调事件中连接:
ret_code_t err_code;
// 配置准备连接的设备MAC
ble_gap_addr_t m_addr;
m_addr.addr_id_peer = 1;
m_addr.addr_type = BLE_GAP_ADDR_TYPE_RANDOM_STATIC;
memcpy(m_addr.addr,p_scan_evt->params.p_not_found->peer_addr.addr,BLE_GAP_ADDR_LEN);
// 停止扫描
nrf_ble_scan_stop();
// 发起连接
err_code = sd_ble_gap_connect(&m_addr,&m_scan_params,&m_conn_params,APP_BLE_CONN_CFG_TAG);
APP_ERROR_CHECK(err_code);
在直接连接的例子中,我们使用的是判断信号强度,然后直接把扫描到的设备的MAC地址复制到我们想要连接设备的地址里:
?
但是前文说过,笔者周围蓝牙设备太多,所以不做直接连接了,直接做过滤连接:
?
我把连接处理放在了,过滤匹配后的事件回调内,但没有删掉对信号强度的判断,也就是除了过滤要符合,信号还要够强。
注意,我们把扫描关闭后,就不再会有扫描事件被触发回调了,而到了BLE事件中:
//******************************************************************
// fn : ble_evt_handler
//
// brief : BLE事件回调
// details : 包含以下几种事件类型:COMMON、GAP、GATT Client、GATT Server、L2CAP
//
// param : ble_evt_t 事件类型
// p_context 未使用
//
// return : none
static void ble_evt_handler(ble_evt_t const * p_ble_evt, void * p_context)
{
ble_gap_evt_t const * p_gap_evt = &p_ble_evt->evt.gap_evt;
ble_gap_evt_connected_t const * p_connected_evt = &p_gap_evt->params.connected;
switch (p_ble_evt->header.evt_id)
{
// 连接
case BLE_GAP_EVT_CONNECTED:
NRF_LOG_INFO("Connected. conn_DevAddr: %s\nConnected. conn_handle: 0x%04x\nConnected. conn_Param: %d,%d,%d,%d",
Util_convertBdAddr2Str((uint8_t*)p_connected_evt->peer_addr.addr),
p_gap_evt->conn_handle,
p_connected_evt->conn_params.min_conn_interval,
p_connected_evt->conn_params.max_conn_interval,
p_connected_evt->conn_params.slave_latency,
p_connected_evt->conn_params.conn_sup_timeout
);
break;
// 断开连接
case BLE_GAP_EVT_DISCONNECTED:
NRF_LOG_INFO("Disconnected. conn_handle: 0x%x, reason: 0x%04x",
p_gap_evt->conn_handle,
p_gap_evt->params.disconnected.reason);
// 如果需要异常断开重连,可以打开下面的注释
// scan_start(); // 重新开始扫描
break;
default:
break;
}
}
当设备连接和断开后,分别会触发:
BLE_GAP_EVT_CONNECTED?和?BLE_GAP_EVT_DISCONNECTED这两个事件,其他事件还有:
?
总的扫描回调函数:
//******************************************************************
// fn : scan_start
//
// brief : 开始扫描
//
// param : none
//
// return : none
static void scan_start(void)
{
ret_code_t ret;
ret = nrf_ble_scan_start(&m_scan);
APP_ERROR_CHECK(ret);
}
//******************************************************************
// fn : scan_evt_handler
//
// brief : 处理扫描回调事件
//
// param : scan_evt_t 扫描事件结构体
//
// return : none
static void scan_evt_handler(scan_evt_t const * p_scan_evt)
{
uint32_t err_code;
ble_gap_addr_t peer_addr;
ble_gap_addr_t const * p_peer_addr;
switch(p_scan_evt->scan_evt_id)
{
// 白名单设置请求
case NRF_BLE_SCAN_EVT_WHITELIST_REQUEST:
{
memset(&peer_addr, 0x00, sizeof(peer_addr));
peer_addr.addr_id_peer = 1;
peer_addr.addr_type = BLE_GAP_ADDR_TYPE_RANDOM_STATIC;
peer_addr.addr[5] = 0xD0;
peer_addr.addr[4] = 0xD3;
peer_addr.addr[3] = 0x07;
peer_addr.addr[2] = 0xCE;
peer_addr.addr[1] = 0xDF;
peer_addr.addr[0] = 0xF5;
p_peer_addr = &peer_addr;
// 设置白名单
err_code = sd_ble_gap_whitelist_set(&p_peer_addr, 0x01);
if (err_code == NRF_SUCCESS)
{
NRF_LOG_INFO("Successfully set whitelist!");
}
APP_ERROR_CHECK(err_code);
}break;
// 扫描到的白名单设备数据
case NRF_BLE_SCAN_EVT_WHITELIST_ADV_REPORT:
{
// 判断是否为扫描回调数据
if(p_scan_evt->params.p_whitelist_adv_report->type.scan_response)
{
if(p_scan_evt->params.p_whitelist_adv_report->data.len) // 存在扫描回调数据
{
NRF_LOG_INFO("scan data: %s",
Util_convertHex2Str(
p_scan_evt->params.p_whitelist_adv_report->data.p_data,
p_scan_evt->params.p_whitelist_adv_report->data.len));
}
else
{
NRF_LOG_INFO("scan data: %s","NONE");
}
NRF_LOG_INFO("rssi: %ddBm",p_scan_evt->params.p_whitelist_adv_report->rssi);
}
else // 否则为广播数据
{
// 打印扫描的设备MAC
NRF_LOG_INFO("Device MAC: %s",
Util_convertBdAddr2Str((uint8_t*)p_scan_evt->params.p_whitelist_adv_report->peer_addr.addr));
if(p_scan_evt->params.p_whitelist_adv_report->data.len) // 存在广播数据
{
NRF_LOG_INFO("adv data: %s",
Util_convertHex2Str(
p_scan_evt->params.p_whitelist_adv_report->data.p_data,
p_scan_evt->params.p_whitelist_adv_report->data.len));
}
else
{
NRF_LOG_INFO("adv data: %s","NONE");
}
}
} break;
case NRF_BLE_SCAN_EVT_NOT_FOUND:
{
NRF_LOG_INFO("@@@@@@@@@@@@-------------------------- EVENT ------------------------------");
// 判断是否为扫描回调数据
if(p_scan_evt->params.p_not_found->type.scan_response)
{
if(p_scan_evt->params.p_not_found->data.len) // 存在扫描回调数据
{
NRF_LOG_INFO("@@@@@@@@@@@@ScanRsp Data:%s",
Util_convertHex2Str(
p_scan_evt->params.p_not_found->data.p_data,
p_scan_evt->params.p_not_found->data.len));
}
else
{
NRF_LOG_INFO("@@@@@@@@@@@@ScanRsp Data:%s","NONE");
}
}
else // 否则为广播数据
{
//打印设备RSSI信号
NRF_LOG_INFO("@@@@@@@@@@@@Device RSSI: %ddBm",p_scan_evt->params.p_not_found->rssi);
// 打印扫描的设备MAC
NRF_LOG_INFO("@@@@@@@@@@@@Device MAC: %s",
Util_convertBdAddr2Str((uint8_t*)p_scan_evt->params.p_not_found->peer_addr.addr));
if(p_scan_evt->params.p_not_found->data.len) // 存在广播数据
{
NRF_LOG_INFO("@@@@@@@@@@@@Adv Data: %s",
Util_convertHex2Str(
p_scan_evt->params.p_not_found->data.p_data,
p_scan_evt->params.p_not_found->data.len));
}
else
{
NRF_LOG_INFO("@@@@@@@@@@@@Adv Data: %s","NONE");
}
}
如果扫描到的设备信号强度大于-30dBm
//if(p_scan_evt->params.p_not_found->rssi > (-30))
//{
// ret_code_t err_code;
// // 配置准备连接的设备MAC
// ble_gap_addr_t m_addr;
// m_addr.addr_id_peer = 1;
// m_addr.addr_type = BLE_GAP_ADDR_TYPE_RANDOM_STATIC;
// memcpy(m_addr.addr,p_scan_evt->params.p_not_found->peer_addr.addr,BLE_GAP_ADDR_LEN);
// // 停止扫描
// nrf_ble_scan_stop();
// // 发起连接
// err_code = sd_ble_gap_connect(&m_addr,&m_scan_params,&m_conn_params,APP_BLE_CONN_CFG_TAG);
// APP_ERROR_CHECK(err_code);
//}
} break;
case NRF_BLE_SCAN_EVT_FILTER_MATCH:
{
// 下面这一段我们只保留了扫描回调数据获取的部分,因为从机筛选广播的UUID在扫描回调数据
// 判断是否为扫描回调数据
if(p_scan_evt->params.filter_match.p_adv_report->type.scan_response)
{
NRF_LOG_INFO("Device MAC: %s",
Util_convertBdAddr2Str((uint8_t*)p_scan_evt->params.filter_match.p_adv_report->peer_addr.addr));
if(p_scan_evt->params.filter_match.p_adv_report->data.len) // 存在扫描回调数据
{
NRF_LOG_INFO("ScanRsp Data:%s",
Util_convertHex2Str(
p_scan_evt->params.filter_match.p_adv_report->data.p_data,
p_scan_evt->params.filter_match.p_adv_report->data.len));
}
else
{
NRF_LOG_INFO("ScanRsp Data:%s","NONE");
}
NRF_LOG_INFO("Device RSSI: %ddBm",p_scan_evt->params.filter_match.p_adv_report->rssi);
}
// 如果扫描到的设备信号强度大于-30dBm
if(p_scan_evt->params.filter_match.p_adv_report->rssi > (-30))
{
ret_code_t err_code;
// 配置准备连接的设备MAC
ble_gap_addr_t m_addr;
m_addr.addr_id_peer = 1;
m_addr.addr_type = BLE_GAP_ADDR_TYPE_RANDOM_STATIC;
memcpy(m_addr.addr,p_scan_evt->params.filter_match.p_adv_report->peer_addr.addr,BLE_GAP_ADDR_LEN);
// 停止扫描
nrf_ble_scan_stop();
// 发起连接
err_code = sd_ble_gap_connect(&m_addr,&m_scan_params,&m_conn_params,APP_BLE_CONN_CFG_TAG);
APP_ERROR_CHECK(err_code);
}
// else // 否则为广播数据
// {
// // 打印扫描的设备MAC
// NRF_LOG_INFO("Device MAC: %s",
// Util_convertBdAddr2Str((uint8_t*)p_scan_evt->params.filter_match.p_adv_report->peer_addr.addr));
//
// if(p_scan_evt->params.filter_match.p_adv_report->data.len) // 存在广播数据
// {
// NRF_LOG_INFO("adv data: %s",
// Util_convertHex2Str(
// p_scan_evt->params.filter_match.p_adv_report->data.p_data,
// p_scan_evt->params.filter_match.p_adv_report->data.len));
// }
// else
// {
// NRF_LOG_INFO("adv data: %s","NONE");
// }
// }
} break;
default:
break;
}
}
?
1.2从机端
从机主要就在蓝牙事件回调中添加了对连接断开事件的处理,和主机端一样:
//******************************************************************
// fn : ble_evt_handler
//
// brief : BLE事件回调
// details : 包含以下几种事件类型:COMMON、GAP、GATT Client、GATT Server、L2CAP
//
// param : ble_evt_t 事件类型
// p_context 未使用
//
// return : none
static void ble_evt_handler(ble_evt_t const * p_ble_evt, void * p_context)
{
ble_gap_evt_t const * p_gap_evt = &p_ble_evt->evt.gap_evt;
ble_gap_evt_connected_t const * p_connected_evt = &p_gap_evt->params.connected;
switch (p_ble_evt->header.evt_id)
{
// 连接
case BLE_GAP_EVT_CONNECTED:
NRF_LOG_INFO("Connected. conn_DevAddr: %s\nConnected. conn_handle: 0x%04x\nConnected. conn_Param: %d,%d,%d,%d",
Util_convertBdAddr2Str((uint8_t*)p_connected_evt->peer_addr.addr),
p_gap_evt->conn_handle,
p_connected_evt->conn_params.min_conn_interval,
p_connected_evt->conn_params.max_conn_interval,
p_connected_evt->conn_params.slave_latency,
p_connected_evt->conn_params.conn_sup_timeout
);
break;
// 断开连接
case BLE_GAP_EVT_DISCONNECTED:
NRF_LOG_INFO("Disconnected. conn_handle: 0x%x, reason: 0x%04x",
p_gap_evt->conn_handle,
p_gap_evt->params.disconnected.reason);
break;
default:
// No implementation needed.
break;
}
}
?注意这个回调的参数:
/**@brief Common BLE Event type, wrapping the module specific event reports. */
typedef struct
{
ble_evt_hdr_t header; /**< Event header. */
union
{
ble_common_evt_t common_evt; /**< Common Event, evt_id in BLE_EVT_* series. */
ble_gap_evt_t gap_evt; /**< GAP originated event, evt_id in BLE_GAP_EVT_* series. */
ble_gattc_evt_t gattc_evt; /**< GATT client originated event, evt_id in BLE_GATTC_EVT* series. */
ble_gatts_evt_t gatts_evt; /**< GATT server originated event, evt_id in BLE_GATTS_EVT* series. */
ble_l2cap_evt_t l2cap_evt; /**< L2CAP originated event, evt_id in BLE_L2CAP_EVT* series. */
} evt; /**< Event union. */
} ble_evt_t;
可以看到,ble事件中,有:
普通事件、gap事件、gatt客户端事件、gatt服务端事件、l2cap事件
而gap事件中包含连接和断开:
?
而每个结构体里包含该事件所能获取到的信息,就比如连接事件中:
?
?我们可以获取,对方的地址、角色(中心或外围设备)、连接参数等。
1.3实际现象
主机端打印:
?从机端打印:
2.过滤自动连接
2.1主机端
这里主要更改的是主机端,我们不再需要提供MAC地址去连接对应的设备,而是使用自动连接:
这个参数我们之前就看到过,只不过没有做讲解,也没有设置它:
?
然后我们把之前在?NRF_BLE_SCAN_EVT_FILTER_MATCH?中手动连接的代码注释掉,添加两个事件回调:
?
2.2从机端
和上文相同,略。
2.3实际现象
?
?可以看到,在扫描到符合过滤设置后,设备直接被连接上了。
3.?白名单自动连接
3.1主机端
同样的道理,我们只需要在以前白名单扫描模式的基础上,开启自动连接就可以了:
?
?
这部分代码和之前基本上一模一样,完全可以举一反三。
3.2从机
略。
3.3实验现象
?
完全与预期相符。
4.白名单手动连接
不再赘述,和前面一模一样,只是回调函数里面放个手动连接而已:
?
?
?
|