一、简介
本篇以SimpleBLECentral工程为例,介绍CC2541作为主机时是如何获取从机广播包数据的。
二、实验平台
协议栈版本:BLE-CC254x-1.3.2
编译软件:IAR 8.20.2
硬件平台:smart RF开发板
三、版权声明
博主:甜甜的大香瓜
声明:喝水不忘挖井人,转载请注明出处。
原文地址:http://blog.csdn.net/feilusia
联系方式:[email protected]
技术交流QQ群:127442605
四、简介广播包
广播包是从机端发出的数据包。
注:本篇中的“广播包”包含“广播包数据”和“扫描应答数据”。
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 };
因此,我们可以通过判断数据类型,来获取这个类型的数据。
五、代码修改(SimpleBLECentral.c)
1、定义一个获取广播数据段的函数Get_Adtype_Data
//****************************************************************************** //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写出来的,通过这个函数可以找出某个类型的数据段位置。
static bool Get_Adtype_Data( uint8 adType, uint8 *pData, uint8 dataLen, uint8 *adTypeData_index, uint8 *adTypeData_len);
3、定义一个十六进制转字符串的函数Hex_To_Str
//************************************************** //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个十六进制。将就用吧。
4、声明函数Hex_To_Str
char *Hex_To_Str( uint8 *pHex );
5、使用举例一——获取广播数据
代码修改:
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数据段的代码。
实验结果:
1)GAP_ADTYPE_FLAGS数据段获取结果
2)GAP_ADTYPE_16BIT_MORE数据段获取结果
6、使用举例二——获取扫描应答数据包的设备名称段
代码修改:
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)”。
所以在仿真中直接查看设备名是否有被获取到:
对照下面这个设备名,可见设备名已经存在于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'