一、背景
链路层(LL)控制设备的射频状态,有五个设备状态:待机、广播、扫描、初始化和连接。
广播 为广播数据包,而 扫描 则是监听广播。
GAP通信中角色,中心设备(Central - 主机)用来扫描和连接 外围设备(Peripheral - 从机)。
二、配置连接参数
定义连接的参数,这个参数将在sd_ble_gap_connect()函数中被调用。
// 定义连接参数
static ble_gap_conn_params_t m_conn_params =
{
.min_conn_interval = MSEC_TO_UNITS(NRF_BLE_SCAN_MIN_CONNECTION_INTERVAL, UNIT_1_25_MS), // 最小连接间隔7.5ms
.max_conn_interval = MSEC_TO_UNITS(NRF_BLE_SCAN_MAX_CONNECTION_INTERVAL, UNIT_1_25_MS), // 最大连接间隔30ms
.slave_latency = NRF_BLE_SCAN_SLAVE_LATENCY, // 隐藏周期0
.conn_sup_timeout = MSEC_TO_UNITS(NRF_BLE_SCAN_SUPERVISION_TIMEOUT, UNIT_10_MS), // 超时时间4000ms
};
三、连接相关函数
3.1 nrf_ble_scan_stop
配置好准备连接的设备的MAC信息,在发起连接之前,我们一定要先调用nrf_ble_scan_stop()函数去停止扫描,不然会出错。
停止扫描之后,我们调用sd_ble_gap_connect()函数,发起对从机设备的连接。
3.2 sd_ble_gap_connect
3.3 sd_ble_gap_disconnect
调用该方法就可以主动断开蓝牙的连接
sd_ble_gap_disconnect(p_ble_evt->evt.gattc_evt.conn_handle, BLE_HCI_REMOTE_USER_TERMINATED_CONNECTION);
四、发起连接
在扫描回调事件处理的函数中,前面一部分就是获取扫描到的从机设备的信息。
我们对扫描到的从机设备的RSSI进行了判断,我们人为定义,当设备的RSSI大于-30dBm的时候,也就是信号强度非常好,我们就默认连接该设备。
首先,我们先定义准备连接的从机设备的地址和信息,配置m_addr.addr_type为BLE_GAP_ADDR_TYPE_RANDOM_STATIC,配置m_addr.addr为扫描到的设备地址p_scan_evt->params.p_not_found->peer_addr.addr。
配置好准备连接的设备的MAC信息,在发起连接之前,我们一定要先调用nrf_ble_scan_stop()函数去停止扫描,不然会出错。
停止扫描之后,我们调用sd_ble_gap_connect()函数,发起对从机设备的连接。
char *Util_convertBdAddr2Str(uint8_t *pAddr)
{
uint8_t charCnt;
char hex[] = "0123456789ABCDEF";
static char str[(2*B_ADDR_LEN)+3];
char *pStr = str;
*pStr++ = '0';
*pStr++ = 'x';
// Start from end of addr
pAddr += B_ADDR_LEN;
for (charCnt = B_ADDR_LEN; charCnt > 0; charCnt--)
{
*pStr++ = hex[*--pAddr >> 4];
*pStr++ = hex[*pAddr & 0x0F];
}
*pStr = NULL;
return str;
}
//******************************************************************
// fn : scan_evt_handler
//
// brief : 处理扫描回调事件
//
// param : scan_evt_t 扫描事件结构体
//
// return : none
static void scan_evt_handler(scan_evt_t const * p_scan_evt)
{
switch(p_scan_evt->scan_evt_id)
{
// 未过滤的扫描数据
case NRF_BLE_SCAN_EVT_NOT_FOUND:
{
// 判断是否为扫描回调数据
if(p_scan_evt->params.p_not_found->type.scan_response)
{
if(p_scan_evt->params.p_not_found->data.len) // 存在扫描回调数据
{
NRF_LOG_INFO("scan 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("scan data: %s","NONE");
}
NRF_LOG_INFO("rssi: %ddBm",p_scan_evt->params.p_not_found->rssi);
}
else // 否则为广播数据
{
// 打印扫描的设备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;
default:
break;
}
}
五、连接事件处理
当我们的主机设备进行连接,或者断开连接的时候,BLE的事件回调中,都会给我们状态事件提示。
连接成功的状态 BLE_GAP_EVT_CONNECTED,我们可以在这边获取到连接的设备的MAC、连接参数等等。
断开连接的状态 BLE_GAP_EVT_DISCONNECTED,我们可以获取到断开的连接的原因等等。
BLE_GAP_EVT_TIMEOUT GAP连接超时事件。当主从机连接一直不成功,超过了连接事件最大的时间,则协议栈触发GAP连接超时事件。这个连接最大的事件就是GAP中的监督超时时间(连接超时时间)。
BLE_GATT_EVT_TIMEOUT 主机GATT连接超时事件。本质上不属于连接时发生的事情,而是连接中发生的事情。当正在连接的主从机由于某种原因发生断开,主机设备发起多次连接请求没有回应就会发生GATT连接超时事件。协议栈将禁用所有ATT流量并将ATT连接标记为down,但是由应用程序必须主动的使用sd_ble_gap_disconnect()完成最后的断开连接。
char *Util_convertBdAddr2Str(uint8_t *pAddr)
{
uint8_t charCnt;
char hex[] = "0123456789ABCDEF";
static char str[(2*B_ADDR_LEN)+3];
char *pStr = str;
*pStr++ = '0';
*pStr++ = 'x';
// Start from end of addr
pAddr += B_ADDR_LEN;
for (charCnt = B_ADDR_LEN; charCnt > 0; charCnt--)
{
*pStr++ = hex[*--pAddr >> 4];
*pStr++ = hex[*pAddr & 0x0F];
}
*pStr = NULL;
return str;
}
//******************************************************************
// 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;
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)
{
NRF_LOG_DEBUG("Connection request timed out.");
}
err_code = sd_ble_gap_disconnect(p_ble_evt->evt.gattc_evt.conn_handle,
BLE_HCI_REMOTE_USER_TERMINATED_CONNECTION);
// Start scanning
scan_start();
} break;
case BLE_GATTC_EVT_TIMEOUT:
{
NRF_LOG_INFO("{0x%x BLE_GATTC_EVT_TIMEOUT}", p_gap_evt->conn_handle);
// Disconnect on GATT Client timeout event.
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);
scan_start();
} break;
default:
break;
}
}
• 由 Leung 写于 2020 年 8 月 10 日