CH579 CH573 CH582 CH592 蓝牙主机(Central)实例应用讲解

蓝牙主机(Central),顾名思义,就是一个蓝牙主设备,与从机(Peripheral)建立连接进行通信,可以接收从机通知,也可以给从机发送信息,通常Central和Peripheral结合使用。

一、官方例程Central的工作流程

从官方例程中,我们可以看到,Central的工作流程大致如下:

一、初始化完成开启扫描,

二、获取扫描信息

三、将扫描到的mac地址与目标连接mac地址作比较,扫描到目标mac就发起连接否则继续开启扫描

四、枚举服务进行通信测试

上图标注1:开始扫描
标注2将扫描获取的从机MAC地址加入扫描列表
标注3与目标连接MAC地址比较
标注4没有找到目标,重新开始扫描
标注5找到目标mac,发起连接

从上述描述中我们知道,要想与Peripheral建立连接,必须知道Peripheral的MAC地址,但实际应用中,我们很难知道Peripheral的MAC的地址,就算知道了,也很难输入Central中,毕竟大多数情况下,每个Peripheral的MAC地址是不同的,尤其是我们针对的是现有的产品时。

那么我们如何应对这种问题呢?

二、BLE广播数据中的AD Type详解

 一般来说,同一种产品,广播数据是相同的,甚至同一个厂家的同一种类型的产品,广播数据也会有一些共同的特征,我们可以通过研究产品的广播数据来解决上面提到的问题。所以我们先来了解一下广播数据中的AD Type。

AD Type是广播数据单元(AD Structure)的核心字段,用于定义后续数据(AD Data)的类型和格式。以下是常见AD Type的分类及说明:

一)、基础设备信息类

  1. Flags(类型=0x01)

    • 功能‌:标识设备的发现模式和兼容性,如是否支持BLE/BR/EDR双模。
    • 数据格式‌:1字节,各bit位含义:
      • Bit 0:LE有限发现模式(仅临时可连接)
      • Bit 1:LE普通发现模式(持续可连接)
      • Bit 2:不支持BR/EDR(纯BLE设备)
      • Bit 3-4:控制器/主机支持双模
    • 示例‌:0x06表示支持普通发现模式且不支持BR/EDR。
  2. 完整设备名称(类型=0x09)

    • 功能‌:声明设备完整名称(如Nordic_HRM)。
    • 数据格式‌:UTF-8字符串,长度由Len字段定义。
  3. 缩短设备名称(类型=0x08)

    • 功能‌:设备名称的缩写形式,用于节省广播数据空间。

二)、服务声明类

  1. 完整16位服务UUID列表(类型=0x03)

    • 功能‌:广播设备支持的所有16位标准服务UUID(如心率服务0x180D)。
    • 数据格式‌:多个2字节UUID连续排列。
  2. 非完整服务UUID列表(类型=0x02)

    • 功能‌:仅声明部分服务,需通过扫描响应或连接后获取完整列表。
  3. 32位/128位服务UUID(类型=0x04-0x07)

    • 功能‌:声明长格式服务UUID(如自定义服务)。

三)、设备能力与参数类

  1. 发射功率等级(类型=0x0A)

    • 功能‌:广播设备的发射功率值(单位dBm),用于距离估算。
    • 数据格式‌:1字节有符号整数(如0xF6表示-10 dBm)。
  2. 设备类别(类型=0x0D)

    • 功能‌:标识设备类型(如手机、传感器)。
    • 数据格式‌:3字节,按蓝牙标准分类编码。

四)、厂商自定义数据类

  • 厂商特定数据(类型=0xFF)
    • 功能‌:携带厂商自定义数据(如iBeacon、Eddystone协议)。
    • 数据格式‌:前2字节为厂商ID(如苹果为0x004C),后续为自定义内容。

五)、其他类型

  • 可连接间隔(类型=0x12)‌:声明设备建议的连接参数。
  • 服务请求(类型=0x14)‌:主动请求特定服务(如定位服务)。
  • 还有更多AD Type,这里就不细说,毕竟与我们的主题关系不大,有兴趣的朋友可以很容易从网上搜索到相关的解释。

六)、关键限制与注意事项

  1. 数据长度限制‌:单个广播包载荷(Payload)总长度不超过31字节。
  2. 组合使用‌:一个广播包可包含多个AD Structure,需合理分配类型优先级(如优先Flags和服务声明)。
  3. 动态更新‌:部分AD Type(如设备名称)支持动态修改以适应场景需求。
  4. 以上AD Type并非全部必需,可以根据产品的特性及实际需要来提供。

 三、实例讲解

根据”BLE广播数据中的AD Type详解“一节所述,以及上面三张广播包图,我们很容易知道,广播包包含的内容比较随意,没有强制要求,但AD Type 0x09,也就是设备名称通常会包含,所以蓝牙主机(Central)在扫描时,可以根据AD Type 0x09来判断是否是目标连接。当然我们根据广播包的信息,很容易知道,可以用AD Type 0x07(自定议服务UUID)或用AD TypxFF(厂家自定义的数据类型)来判断是否是目标连接。

接下来,我们以佳能相机蓝牙遥控器为例来讲解如何根据AD Type 0x09及AD Type 0x07来判断是否是目标连接,如果是,则发起连接请求。(因为该项目是商用项目,我们没办法所完整的源码上传,所以只会贴一部分与本主题有关的代码)。

#define PAIR_MODE_TYPE 0x07

const uint8_t Serv_uuid[ATT_UUID_SIZE] = {0x21,0xa8,0xff,0x2f,0x49,0xd8,0x00,0x00,0x00,0x10,0x00,0x00,0x00,0x00,0x05,0x00};

const uint8_t cannon_EOSM50[DEVICE_NAME_MAX_LEN] = {0x45,0x4F,0x53,'M',0x35,0x30,0,0,0,0,0,0,0,0,0,0};
const uint8_t cannon_EOS800D[DEVICE_NAME_MAX_LEN] = {0x45,0x4F,0x53,0x38,0x30,0x30,0x44,0,0,0,0,0,0,0,0,0};
const uint8_t cannon_SX70[DEVICE_NAME_MAX_LEN] = {0x53,0x78,0x37,0x30,0,0,0,0,0,0,0,0,0,0,0,0};
const uint8_t * Device_List[DEVICE_COUNT] = {cannon_EOSM50,cannon_EOS800D,cannon_SX70};




        case GAP_DEVICE_INFO_EVENT:
        {
            // Add device to list
//            centralAddDeviceInfo(pEvent->deviceInfo.addr, pEvent->deviceInfo.addrType);
          if (IsPaired)
            break;
          uint8_t cmystata;
          uint8_t UserNamelen,len;
          uint8_t EvtData[31];

#ifdef DEBUG
          sprintf(EvtData,"%s",pEvent->deviceInfo.pEvtData);    //将扫描的数据格式化成字符串进行子字符串匹配
#endif
              //以下为字符串匹配
          for (uint8_t i = 0; i < pEvent->deviceInfo.dataLen;)
          {
            if(pEvent->deviceInfo.pEvtData[i]>=2)
            {
              len = pEvent->deviceInfo.pEvtData[i];
              if(pEvent->deviceInfo.pEvtData[i+1] == PAIR_MODE_TYPE)
              {
#if 0
                for (uint8_t idx = 0; idx < DEVICE_COUNT; idx ++)
                {
                  cmystata = tmos_memcmp(&pEvent->deviceInfo.pEvtData[i+2],Device_List[idx],len-1);
                  if (cmystata == TRUE)
                  {
//                    Device_Ctrl.Device_Idx = idx;
                    break;
                  }
                }
#else
                cmystata = tmos_memcmp(&pEvent->deviceInfo.pEvtData[i+2],Serv_uuid,len-1);
#endif
                if(cmystata == TRUE) //  找到了
                {
//                StrMatchingFlag = TRUE;
                  GAPRole_CentralCancelDiscovery();     //取消设备扫描发现
                  centralAddDeviceInfo(pEvent->deviceInfo.addr, pEvent->deviceInfo.addrType);
                }
                PRINT("\r\n");
                return;
              }
              else
              {
                i += len+1;
              }

            }
            else
            {
              i++;
            }
          }
        }
        break;


        case GAP_DEVICE_DISCOVERY_EVENT:
        {
                PRINT("Device found...\n");
                GAPRole_CentralEstablishLink(DEFAULT_LINK_HIGH_DUTY_CYCLE,
                                             DEFAULT_LINK_WHITE_LIST,
                                             centralDevList[0].addrType,
                                             centralDevList[0].addr);

//                BLEConnected = BLE_CONNECTING;
               // Start establish link timeout event
                tmos_start_task(centralTaskId, ESTABLISH_LINK_TIMEOUT_EVT, ESTABLISH_LINK_TIMEOUT);
                PRINT("Connecting...\n");
        }
        break;



以上代码,简单阐述一下,本来我们开始是打算根据AD Type 0x09(也就是设备名称)来判断是否是目标连接,但后来客户不断的增加相机类型,甚至还要求支持没有提供的设备,所以转为根据AD Type 0x07(自定议服务UUID)来判断。

#define PAIR_MODE_TYPE 0x07就是指AD Type 0x07,如果要根据AD Type 0x09,这个地方需要修改,程序代码可能也需要略作修改,毕竟我们后面的代码全部是根据AD Type 0x07(自定议服务UUID)来开发的。

代码本身比较简单,与官方例程不同的是, case GAP_DEVICE_INFO_EVENT:官方例程只是把扫描到的信息添加到相应的列表中, case GAP_DEVICE_DISCOVERY_EVENT: 在这里才判断是否是目标连接。而我们的实例则是在case GAP_DEVICE_INFO_EVENT:就判断是否是目标连接,而在case GAP_DEVICE_DISCOVERY_EVENT: 只是简单的发起连接请求。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

永远的元子

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值