一、背景
链路层(LL)控制设备的射频状态,有五个设备状态:待机、广播、扫描、初始化和连接。
广播 为广播数据包,而 扫描 则是监听广播。
GAP通信中角色,中心设备(Central - 主机)用来扫描和连接 外围设备(Peripheral - 从机)。
大部分情况下外围设备通过广播自己来让中心设备发现自己,并建立 GATT 连接,从而进行更多的数据交换。
也有些情况是不需要连接的,只要外设广播自己的数据即可,用这种方式主要目的是让外围设备,把自己的信息发送给多个中心设备。
本篇是关于解析扫描到的广播数据包,配置以及启动或关闭扫描的流程查看 CC2640R2F学习笔记(9)——GAP主机端扫描
1.1 不连接方式获取从机信息的意义
不连接的方式下,从机的广播信息可发给无数台主机。
比如从机是 ibeacon 且被放置于商场的厕所,周边的人只需要使用 app 等蓝牙主机进行扫描
ibeacon,即可导向至厕所。
1.2 主机可以获取的从机信息
- addr - 6字节MAC地址
- rssi - 信号强度,可用于测距、定位
- *pEvtData - 该数据为广播包或者扫描应答包取决于当前主机收到数据包的类型
1.3 广播包
- 本篇中“广播包”包含“广播包数据”和“扫描应答数据”
- 广播数据包和扫描应答数据包的数据格式,都是由多个“数据长度+数据类型+数据”组成。
因此,可以通过判断数据类型,来获取这个类型的数据。
1.3.1 广播包数据格式
SimpleBLEPeripheral 工程中的广播数据包格式如下:
// GAP - Advertisement data (max size = 31 bytes, though this is
// best kept short to conserve power while advertisting)
static uint8 advertData[] =
{
// Flags; this sets the device to use limited discoverable
// mode (advertises for 30 seconds at a time) instead of general
// discoverable mode (advertises indefinitely)
0x02, // length of this data
GAP_ADTYPE_FLAGS,
DEFAULT_DISCOVERABLE_MODE | GAP_ADTYPE_FLAGS_BREDR_NOT_SUPPORTED,
// service UUID, to notify central devices what services are included
// in this peripheral
0x03, // length of this data
GAP_ADTYPE_16BIT_MORE, // some of the UUID's, but not all
LO_UINT16( SIMPLEPROFILE_SERV_UUID ),
HI_UINT16( SIMPLEPROFILE_SERV_UUID ),
};
1.3.2 扫描应答包数据格式
SimpleBLEPeripheral 工程中的扫描应答数据包格式如下:
// GAP - SCAN RSP data (max size = 31 bytes)
static uint8 scanRspData[] =
{
// complete name
0x14, // length of this data
GAP_ADTYPE_LOCAL_NAME_COMPLETE,
0x53, // 'S'
0x69, // 'i'
0x6d, // 'm'
0x70, // 'p'
0x6c, // 'l'
0x65, // 'e'
0x42, // 'B'
0x4c, // 'L'
0x45, // 'E'
0x50, // 'P'
0x65, // 'e'
0x72, // 'r'
0x69, // 'i'
0x70, // 'p'
0x68, // 'h'
0x65, // 'e'
0x72, // 'r'
0x61, // 'a'
0x6c, // 'l'
// connection interval range
0x05, // length of this data
GAP_ADTYPE_SLAVE_CONN_INTERVAL_RANGE,
LO_UINT16( DEFAULT_DESIRED_MIN_CONN_INTERVAL ), // 100ms
HI_UINT16( DEFAULT_DESIRED_MIN_CONN_INTERVAL ),
LO_UINT16( DEFAULT_DESIRED_MAX_CONN_INTERVAL ), // 1s
HI_UINT16( DEFAULT_DESIRED_MAX_CONN_INTERVAL ),
// Tx power level
0x02, // length of this data
GAP_ADTYPE_POWER_LEVEL,
0 // 0dBm
};
二、添加文件和程序
2.1 添加头文件
#include
#include
2.2 添加获取广播数据或扫描应答的函数
/**
@brief 获取广播数据或扫描应答数据中adType对应的数据函数
@param adType 数据类型
@param pData 广播包或扫描应答包
@param pAdTypeData_index 对应的adType类型数据的偏移值
@param pAdTypeData_len 对应的adType类型数据的长度
@return TRUE - 成功;FALSE - 失败
*/
static bool getAdtypeData(uint8 adType, uint8 *pData, uint8 dataLen, uint8 *pAdTypeData_index, uint8 *pAdTypeData_len)
{
(void)pAdTypeData_index; // 防止编译报错
(void)pAdTypeData_len; // 防止编译报错
uint8 adLen; // 对应数据段的长度
uint8 *pCurrent; // 当前位置的指针
uint8 *pEnd; // 尾指针
pEnd = pData + dataLen - 1; // 指向包尾
pCurrent = pData; // 当前指针指向包头
while(pCurrent < pEnd) // 判断当前指针是否还未到包尾
{
adLen = *pCurrent++; // 获取本段数据段的长度
if(adLen > 0)
{
if(adType == *pCurrent) //如果找到了adType
{
*pAdTypeData_index = (pCurrent + 1) - pData; // 数据段在数据包中的偏移值
*pAdTypeData_len = adLen - 1; // 数据段长度
return TRUE;
}
else // 没找到adType则指向下一个数据段
{
pCurrent += adLen;
}
}
}
return FALSE; // 本数据串中没有找到adType
}
2.3 添加十六进制转字符串的函数
/**
@brief 十六进制数组转字符串
@param pHexArr 十六进制数组
@param hexArrLen 数组长度
@return 字符串数组
*/
char *HexArr2Str(uint8 *pHexArr, uint8 hexArrLen)
{
char hexStr[] = "0123456789ABCDEF";
char destStr[hexArrLen * 2];
char *pDestStr = destStr;
for(uint8 i = hexArrLen; i > 0; i--)
{
*pDestStr++ = hexStr[*pHexArr >> 4];
*pDestStr++ = hexStr[*pHexArr++ & 0x0F];
}
return destStr;
}
三、使用方法
3.1 获取MAC地址和RSSI
以SDK2.4 multi_role工程为例,在 multi_role_processRoleEvent()函数的GAP_DEVICE_INFO_EVENT扫描过滤部分
这里RSSI使用的是正数,加了128。
case GAP_DEVICE_INFO_EVENT:
{
if(ENABLE_UNLIMITED_SCAN_RES == FALSE)
{
multi_role_addDeviceInfo(pEvent->deviceInfo.addr,
pEvent->deviceInfo.addrType);
uint8 deviceAddr[6] = {0};
uint8 currDevRssi = 0;
// MAC地址
deviceAddr[0] = pEvent->deviceInfo.addr[5];
deviceAddr[1] = pEvent->deviceInfo.addr[4];
deviceAddr[2] = pEvent->deviceInfo.addr[3];
deviceAddr[3] = pEvent->deviceInfo.addr[2];
deviceAddr[4] = pEvent->deviceInfo.addr[1];
deviceAddr[5] = pEvent->deviceInfo.addr[0];
// RSSI
pEvent->deviceInfo.rssi = pEvent->deviceInfo.rssi + 128;
currDevRssi = pEvent->deviceInfo.rssi;
// 显示MAC地址
UART_Send("found device:", 13);
UART_Send(HexArr2Str(deviceAddr, 6));
UART_Send("\r\n", 2);
// 显示RSSI
char rssi[10] = {0};
sprintf(rssi, "rssi:%d\r\n", (int)currDevRssi);
UART_Send((uint8 *)rssi, strlen(rssi));
}
UART发送查看CC2640R2F学习笔记(6)——UART串口使用
3.2 获取名称字段数据
以SDK2.4 multi_role工程为例,在 multi_role_processRoleEvent()函数的GAP_DEVICE_INFO_EVENT扫描过滤部分
case GAP_DEVICE_INFO_EVENT:
{
if(ENABLE_UNLIMITED_SCAN_RES == FALSE)
{
multi_role_addDeviceInfo(pEvent->deviceInfo.addr,
pEvent->deviceInfo.addrType);
uint8 nameAdType = GAP_ADTYPE_LOCAL_NAME_COMPLETE; // 名称字段
uint8 nameAdTypeData_index = 0; // 名称字段在数据包中的偏移值
uint8 nameAdTypeData_len = 0; // 名称字段的长度
char *pFiltering_Name1 = "Y11";
// 获取名称字段数据
if(getAdtypeData(nameAdType, pEvent->deviceInfo.pEvtData,
pEvent->deviceInfo.dataLen, &nameAdTypeData_index,
&nameAdTypeData_len))
{
// 自定义处理,如根据名称1进行过滤
if(memcmp((pEvent->deviceInfo.pEvtData+nameAdTypeData_index), pFiltering_Name1, 3) == 0)
{
···
}
}
}
}
3.3 获取自定义FF段数据
以SDK2.4 multi_role工程为例,在 multi_role_processRoleEvent()函数的GAP_DEVICE_INFO_EVENT扫描过滤部分
case GAP_DEVICE_INFO_EVENT:
{
if(ENABLE_UNLIMITED_SCAN_RES == FALSE)
{
multi_role_addDeviceInfo(pEvent->deviceInfo.addr,
pEvent->deviceInfo.addrType);
uint8 customAdType = GAP_ADTYPE_MANUFACTURER_SPECIFIC; // 自定义FF字段
uint8 customAdTypeData_index = 0; // 自定义FF字段在数据包中的偏移值
uint8 customAdTypeData_len = 0; // 自定义FF字段的长度
// 获取自定义FF段数据
if(getAdtypeData(customAdType, pEvent->deviceInfo.pEvtData,
pEvent->deviceInfo.dataLen, &customAdTypeData_index,
&customAdTypeData_len))
{
// 自定义处理,如拷贝FF段数据前4字节
uint8 cacheValue[4] = {0};
memcpy(cacheValue, (pEvent->deviceInfo.pEvtData + customAdTypeData_index), 4);
}
}
}
• 由 Leung 写于 2019 年 4 月 25 日
• 参考:【BLE-CC2640】CC2640之主机端获取广播包数据