本篇博文最后修改时间:2017年01月06日,11:06。
一、简介
本文以SimpleBLECentral工程为例,介绍CC2541作为主机时是如何获取从机广播包数据的。
注:本篇中的“广播包”包含“广播包数据”和“扫描应答数据”。
二、实验平台
协议栈版本:BLE-CC254x-1.4.0
编译软件:IAR 8.20.2
硬件平台:Smart RF(主芯片CC2541
三、版权声明
博主:甜甜的大香瓜
声明:喝水不忘挖井人,转载请注明出处。
原文地址:http://blog.csdn.NET/feilusia
联系方式:897503845@qq.com
香瓜BLE之CC2541群:127442605
香瓜BLE之CC2640群:557278427
五、基础知识
1、广播包是什么?
答:广播包是从机端发出的数据包。
2、广播包的格式是如何的?
答:
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 ),
};
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
};
广播数据包和扫描应答数据包的数据格式,都是由多个“数据长度+数据类型+数据”组成。
因此,我们可以通过判断数据类型,来获取这个类型的数据。
六、实验步骤
1、添加一个获取广播数据段的函数Get_Adtype_Data
1)定义一个获取广播数据段的函数Get_Adtype_Data(SimpleBLECentral.c中)
//******************************************************************************
//name: Get_Adtype_Data
//introduce: 获取广播数据或扫描应答数据中adType对应的数据
//input parameter: adType:数据类型
// pData:广播包或扫描应答包
// dataLen:广播包或扫描应答包的数据长度
//output parameter: adTypeData_index:对应的adType类型数据的偏移值
// adTypeData_len:对应的adType类型数据的长度
//return: TRUE:找到adType类型的数据
// FALSE:没找到adType类型的数据
//******************************************************************************
static bool Get_Adtype_Data( uint8 adType, uint8 *pData, uint8 dataLen, uint8 *adTypeData_index, uint8 *adTypeData_len)
{
(void)adTypeData_index; //防止编译报错
(void)adTypeData_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
{
*adTypeData_index = (pCurrent + 1) - pData; //数据段在数据包中的偏移值
*adTypeData_len = adLen - 1; //数据段长度
return TRUE; //返回TRUE
}
else //没找到adType则指向下一个数据段
{
pCurrent += adLen;
}
}
}
return FALSE; //本数据串中没有找到adType
}
该函数是香瓜参考simpleBLEFindSvcUuid写出来的,通过这个函数可以找出某个类型的数据段位置。
2)声明函数Get_Adtype_Data(SimpleBLECentral.c中)
static bool Get_Adtype_Data( uint8 adType, uint8 *pData, uint8 dataLen, uint8 *adTypeData_index, uint8 *adTypeData_len);
2、定义一个十六进制转字符串的函数Hex_To_Str(SimpleBLECentral.c中)
//**************************************************
//name: Hex_To_Str
//input: 十六进制进制转字符串
//return: 修改后的字符串
//**************************************************
char* Hex_To_Str( uint8 *pHex )
{
char hex[] = "0123456789ABCDEF";
static char str[100];
char *pStr = str;
for ( uint8 i = 0; i < sizeof(pHex); i++ )
{
*pStr++ = hex[*pHex >> 4];
*pStr++ = hex[*pHex++ & 0x0F];
}
return str;
}
这里字符串大小我定了100个字节,也就是最多只能转50个十六进制。将就用吧。
3、声明函数Hex_To_Str(SimpleBLECentral.c中)
char *Hex_To_Str( uint8 *pHex );
4、分别获取广播数据与扫描应答数据的举例
1)使用举例一——获取广播数据中的数据段
①代码修改(SimpleBLECentral.c中)
case GAP_DEVICE_INFO_EVENT:
{
// if filtering device discovery results based on service UUID
if ( DEFAULT_DEV_DISC_BY_SVC_UUID == TRUE )
{
if ( simpleBLEFindSvcUuid( SIMPLEPROFILE_SERV_UUID,
pEvent->deviceInfo.pEvtData,
pEvent->deviceInfo.dataLen ) )
{
simpleBLEAddDeviceInfo( pEvent->deviceInfo.addr, pEvent->deviceInfo.addrType );
{
//读广播包或扫描应答包的某个数据段
uint8 adType = GAP_ADTYPE_FLAGS; //需要扫描的类型数据
uint8 adTypeData_index = 0; //数据段在数据包中的偏移值
uint8 adTypeData_len = 0; //数据段的长度
bool status = FALSE;
status = Get_Adtype_Data( adType,
pEvent->deviceInfo.pEvtData,
pEvent->deviceInfo.dataLen,
&adTypeData_index,
&adTypeData_len);
if(status == TRUE)
{
NPI_PrintString("GAP_ADTYPE_FLAGS:");
NPI_WriteTransport((uint8 *)(Hex_To_Str(pEvent->deviceInfo.pEvtData + adTypeData_index)),
adTypeData_len*2);
NPI_PrintString("\r\n");
NPI_PrintValue("size:", adTypeData_len, 10);
NPI_PrintString("\r\n");
}
}
/*
{
//读广播包或扫描应答包的某个数据段
uint8 adType = GAP_ADTYPE_16BIT_MORE; //需要扫描的类型数据
uint8 adTypeData_index = 0; //数据段在数据包中的偏移值
uint8 adTypeData_len = 0; //数据段的长度
bool status = FALSE;
status = Get_Adtype_Data( adType,
pEvent->deviceInfo.pEvtData,
pEvent->deviceInfo.dataLen,
&adTypeData_index,
&adTypeData_len);
if(status == TRUE)
{
NPI_PrintString("GAP_ADTYPE_16BIT_MORE:");
NPI_WriteTransport((uint8 *)(Hex_To_Str(pEvent->deviceInfo.pEvtData + adTypeData_index)),
adTypeData_len*2);
NPI_PrintString("\r\n");
NPI_PrintValue("size:", adTypeData_len, 10);
NPI_PrintString("\r\n");
}
} */
}
}
}
break;
在GAP_DEVICE_INFO_EVENT事件中会有广播数据包和扫描应答数据包进来,因此本段代码先判断了UUID来确认此数据包是“广播数据包”。
然后再通过Get_adType_Data去获取GAP_ADTYPE_FLAGS数据段的数据。
注:注释部分是获取GAP_ADTYPE_16BIT_MORE数据段的代码,由于串口同一时间打印太多会不好使,所以我分开来编译GAP_ADTYPE_FLAGS数据段和GAP_ADTYPE_16BIT_MORE数据段的代码。
②实验结果
其中,GAP_ADTYPE_FLAGS数据段获取结果
其中,GAP_ADTYPE_16BIT_MORE数据段获取结果
2)使用举例二——获取扫描应答数据包的设备名称段
①代码修改(SimpleBLECentral.c中)
case GAP_DEVICE_INFO_EVENT:
{
{
//读广播包或扫描应答包的某个数据段
uint8 adType = GAP_ADTYPE_LOCAL_NAME_COMPLETE; //需要扫描的类型数据
uint8 adTypeData_index = 0; //数据段在数据包中的偏移值
uint8 adTypeData_len = 0; //数据段的长度
bool status = FALSE;
status = Get_Adtype_Data( adType,
pEvent->deviceInfo.pEvtData,
pEvent->deviceInfo.dataLen,
&adTypeData_index,
&adTypeData_len);
if(status == TRUE)
{
//NPI_PrintValue("GAP_ADTYPE_FLAGS:", *p_adTypedata, 10);
NPI_PrintString("GAP_ADTYPE_LOCAL_NAME_COMPLETE:");
NPI_WriteTransport((uint8 *)(Hex_To_Str(pEvent->deviceInfo.pEvtData + adTypeData_index)),
adTypeData_len*2);
NPI_PrintString("\r\n");
NPI_PrintValue("size:", adTypeData_len, 10);
NPI_PrintString("\r\n");
}
}
②实验结果
串口输出结果不太好用,只输出了设备名的前两个字节“0x53(S)”和“0x69(i)”。
(群友指出上面只显示2个字节的原因是:16进制转字符串的函数中,“sizeof(pHex)”只是取指针大小,因为指针是2字节大小,所以只输出了2个字节)。
所以在仿真中直接查看设备名是否有被获取到:
对照下面这个设备名,可见设备名已经存在于pEvent->deviceInfo.pEvtData中了,并且首地址是“pEvent->deviceInfo.pEvtData + adTypeData_index”,长度是adTypeData_len的0x13个(19个)。
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'
因此,实验成功。