CC2640R2F学习笔记(18)——GAP主机端获取从机广播数据

一、背景

链路层(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 <stdlib.h>
#include <string.h>

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之主机端获取广播包数据

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 213,558评论 6 492
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,002评论 3 387
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 159,036评论 0 349
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,024评论 1 285
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,144评论 6 385
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,255评论 1 292
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,295评论 3 412
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,068评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,478评论 1 305
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,789评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,965评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,649评论 4 336
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,267评论 3 318
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,982评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,223评论 1 267
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,800评论 2 365
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,847评论 2 351

推荐阅读更多精彩内容