【BLE】CC2541之主机端获取广播包数据

本篇博文最后修改时间: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

香瓜BLE之Android群:541462902

香瓜单片机之STM8/STM32群:164311667
甜甜的大香瓜的小店(淘宝店):https://shop217632629.taobao.com/?spm=2013.1.1000126.d21.hd2o8i

四、 实验前提
1、在进行本文步骤前,请先 阅读 以下博文:
暂无

2、在进行本文步骤前,请先 实现以下博文:
暂无


五、基础知识

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'


因此,实验成功。


  • 2
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 9
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 9
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值