[BLE]低功耗蓝牙之GAP、GATT

2023-05-16

一、开篇

    本篇主要介绍一下关于BLE开发过程中必须了解的两个协议:GAP(通用访问协议)、GATT(通用属性协议)。两个协议都隶属于Host层,直接关系到应用层开发,与BLE开发人员的关系比较密切,其分别负责连接前数据广播和连接后的数据传输。


三、试验平台

Software Version:BLE_STACK_CC26XX_2.1.0

Hardware Version:CC2640/CC2650

IDE:IAR 7.40

四、GAP

    1、蓝牙低能耗技术“完成”一次连接(即扫描其它设备、建立链路、发送数据、认证和适当地结束)只需3ms。而标准蓝牙技术完成相同的连接周期需要数百毫秒。

    GAP层有4种不同类型的广播:通用的、定向的、不可连接的以及可发现的。

    设备每次广播时,会在3个广播信道上发送相同的报文。这些报文被称为一个广播事件。除了定向报文以外,其他广播事件均可以选择20ms - 10.28s不等的间隔。通常,一个广播中的设备会每一秒广播一次。广播事件之间的时间称为广播间隔。主机可以控制该间隔。但是,设备周期性的发送广播会有一个问题:由于设备间的时钟会不同程度的漂移,两个设备可能在很长一段时间同时广播而造成千扰。为防止选一情况的发生,在上一次广播事件发生后加入随机延时。它们发送下一个广播事件时也很可能不再冲突。

    通用广播:通用广播是用途最广的广播方式。进行通用广播的设备能够被扫描设备扫描到,或者在接收到连接请求时作为从设备进入一个连接。通用广播可以在没有连接的情况下发出,换句话说,没有主从设备之分。

    定向广播:有时候,设备间需要快速建立连接。如果从设备想这么做,就需要进行广播。定向广播事件就是为了尽可能快的建立连接。这种报文包含两个地址:广播者的地址和发起者的地址。发起设备收到发绐自己的定向广播报文后,可以立即发送连接请求作为回应。

    不可连接广播:不想被连接的设备使用不可连接广播事件。这种广播的典型应用包括设备只想广播数据,而不想被扫描或者连接。速也是唯一可用于只有发射机而没有接收机设备的广播类型。不可连接广播设备不会进入连接态,因此,它只能根据主机的要求在广播态和就绪态之间切换。

    可发现广播:最后一种广播事件是可发现广播。这种广播不能用于发起连接,但允许其他设备扫描该广播设备。这意味着该设备可以被发现,既可以广播数据,又可以响应扫描,但不能建立连接。这是一种适用于广播数据的广播形式,动态数据可以包含于广播数据之中,而静态数据可以包含于扫描响应数据之中。可发现广播不会进入连接态,而只能在停止后回到就绪态。

    如上面所述,BLE设备可以进行广播。但是,一个广播设备必须在广播中包含一些有用的数据。这意味着可以通过4种广播事件中的3种进行广播:通用广播、不可连接广播以及可发现广播。进行广播时,需要在广播报文中给数据打上标签。之所以要这么做,是因为并非所有设备都能理解所有可能的广播数据。因此,需要给广播数据打上标签并指出其长度。每个数据片段均起始于一个长度域,用以指示后面的类型及数据域的长度;接下来是类型域,接收机可根据其内容判断自己是否能够理解后面的数据。事例代码:

// GAP - Advertisement data (max size = 31 bytes, though this is
// best kept short to conserve power while advertisting)
static uint8_t 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
#ifdef FEATURE_OAD
  LO_UINT16(OAD_SERVICE_UUID),
  HI_UINT16(OAD_SERVICE_UUID)
#else
  LO_UINT16(SIMPLEPROFILE_SERV_UUID),
  HI_UINT16(SIMPLEPROFILE_SERV_UUID)
#endif //!FEATURE_OAD
};


五、GATT

    通用属性配置文件(GATT)在属性协议(ATT)的基础上构建,为属性协议传输和存储数据建立了一些通用操作和框架。

1)GATT定义了两个角色:服务器和客户端

    GATT的角色并不一定与特定的GAP角色有关联,但可能由更高层级的配置文件指定。GATT和ATT不是传输专用,也可以用于BR/EDR和低耗能。但是,由于GATT和ATT用作发现服务,故必须在低耗能技术中实施。GATT服务器存储通过属性协议传输的数据,并接受GATT客户端发出的属性协议请求、指令及确认。GATT服务器发送请求回复,而如果在配置时GATT服务器发生特定事件,则会向GATT客户端异步发送指示和通知。GATT还指定GATT服务器中所载的数据格式。

    属性在当经由属性协议传输时,会被格式化为相关的服务和特性。服务可能包括许多特征。特征包括单一值和许多描述特征值的描述符。

凭借经定义的服务、特征和特征描述符架构,并非配置文件特定的GATT客户端仍然可以遍历GATT服务器,并向用户显示特征值。特征描述符可用于显示特征值的描述符,从而可让用户了解该值。

2)GATT配置文件层级

    GATT配置文件规格规定了交换配置文件数据的架构。此架构定义了配置文件所用的基本元素,例如服务和特征。

该层级的最高层是配置文件(profile)。配置文件由实现用例所需的一个或多个服务组成。服务由特征或有关其它服务的引用组成。每一个特征包括一个值,还可能包括有关该值的可选信息。服务、特征以及特征的组件(即特征值和特征描述符)构成了配置文件数据,并全部存储在服务器的属性中。

    英文原版(摘自Core_V4.1 vol 1:6.5,p226):The top level of the hierarchy is a profile. A profile is composed of oneor more services necessary to fulfill a use case. A service is composed of characteristicsor references to other services. Each characteristic contains a value and maycontain optional information about the value. Theservice and characteristic and the components of the characteristic (i.e.,value and descriptors) contain the profile data and are all stored in Attributes on theserver.

3)服务

    服务是数据和完成设备或设备的某些部分的特定功能或特征的相关行为的集合。服务可能涉及其它主要或次要服务和/或构成该服务的特征集合。

服务分为两种类型:主要服务和次要服务。主要服务提供设备的主要功能。次要服务提供设备的辅助功能,引用自该设备至少一项主要服务。

为了令早前的客户端保持向后兼容性,服务定义的其后修订仅可增加新引用的服务或可选特征。服务定义的其后修订也不得改变该服务定义先前修订的特征。

服务可能用于一个或多个配置文件,以实现特定用例。

4)特征

    特征,连同属性和有关如何访问该值的配置信息以及有关如何显示或表述该值的信息,是用于服务的值。特征定义包含特征声明、特征属性和值。它还可能包含描述该值或允许服务器配置有关特征值的描述符。

协议栈代码实现如下:

/*********************************************************************
 * Profile Attributes - variables
 */
// Simple Profile Service attribute
static CONST gattAttrType_t simpleProfileService = { ATT_BT_UUID_SIZE, simpleProfileServUUID };
// Simple Profile Characteristic 1 Properties
static uint8 simpleProfileChar1Props = GATT_PROP_READ | GATT_PROP_WRITE;
// Characteristic 1 Value
static uint8 simpleProfileChar1 = 0;
// Simple Profile Characteristic 1 User Description
static uint8 simpleProfileChar1UserDesp[17] = "Characteristic 1";


/*********************************************************************
 * Profile Attributes - Table
 */
static gattAttribute_t simpleProfileAttrTbl[SERVAPP_NUM_ATTR_SUPPORTED] = 
{
  // Simple Profile Service
  { 
    { ATT_BT_UUID_SIZE, primaryServiceUUID }, /* type */
    GATT_PERMIT_READ,                         /* permissions */
    0,                                        /* handle */
    (uint8 *)&simpleProfileService            /* pValue */
  },
    // Characteristic 1 Declaration
    { 
      { ATT_BT_UUID_SIZE, characterUUID },
      GATT_PERMIT_READ, 
      0,
      &simpleProfileChar1Props 
    },
      // Characteristic Value 1
      { 
        { ATT_BT_UUID_SIZE, simpleProfilechar1UUID },
        GATT_PERMIT_READ | GATT_PERMIT_WRITE, 
        0, 
        &simpleProfileChar1 
      },
      // Characteristic 1 User Description
      { 
        { ATT_BT_UUID_SIZE, charUserDescUUID },
        GATT_PERMIT_READ, 
        0, 
        simpleProfileChar1UserDesp 
      },      

5)关于句柄handle 和UUID

    Handle即是地址(记住它,类比于C语言的指针操作)

    属性句柄:一台设备可以有许多的属性,例如温度传感器可能包含温度属性、设备名称属性和电池电量属性。表面看来,通过属性类型似乎足以判别某种属性。比如使用温度属性来获取温度,通过设备名称属性来获取设备名等。但是,如果设备包含了两种温度属性,比如一个室内温度传感器加上室外温度传感器,情况会变得怎样。这时你便无法直接读取温度传感器,而必须读取第一个或第二个温度属性。考虑到可能有任意多个温度传感器,问题将变得更加复杂。为了解决这个同题,我们使用了一个16位的地址,也就是属性句柄。有效的句柄范围从0x0001--xFFFF。0x0000为无效句柄,不能用于寻址属性。可以根据在软硬件或嵌入式方面的背景,把句柄(Handle)相应地想象为内存地址、端口号、属性值对应的硬件寄存器地址

    属性类型:可以被公开的数据有许许多多的类型:温度、压强、体积、距离、功率、时间、充电状态、开关状态、状态机的状态等。所公开的数据的种类称作属性类型。为了区分如此多的数据类型,一串128位的数字被用来标识属性的类型。这个唯一标识码就叫做通用唯一识别码(UUID),128位的UUID相当长,设备间为了识别数据的类型需要发送长达16个字节的数据。为了提高传输效率,蓝牙技术联盟( SIG)定义了一种称为“蓝牙UUID基数”的128位通用唯一识别码,结合一个较短的16位数使用。二者仍然遵循通用唯一识别码的分配规则,只不过在设备间传输常用的UUID时,只发送较短的16位版本,接收方收到后补上蓝牙的UUID基数即可。

蓝牙UUID基数如下:

000000000000—1000—8000—00805F9B34FB

例如要发送的16位识别码位0X2A01,完整的128位UUID便是:

00002A010000—1000—8000—00805F9B34FB

    由上所述,所以在协议栈代码中经常见到的都是16位的UUID而不常见128位的UUID的原因所在,下面是官方demo的UUID,供参考。

// Simple Profile Service UUID
#define SIMPLEPROFILE_SERV_UUID               0xFFF0
    
// Key Pressed UUID
#define SIMPLEPROFILE_CHAR1_UUID            0xFFF1
#define SIMPLEPROFILE_CHAR2_UUID            0xFFF2
#define SIMPLEPROFILE_CHAR3_UUID            0xFFF3
#define SIMPLEPROFILE_CHAR4_UUID            0xFFF4
#define SIMPLEPROFILE_CHAR5_UUID            0xFFF5

谈到16位的UUID,通常不直接使用数值,而是冠以一个名称并加上书名号(这些UUID可以在gatt_profile_uuid.h中查看到

0x1800 - 0x26FF用作服务类通用唯一识别码

0x2700 - 0x27FF用于标识计量单位

0x2800 - 0x28FF用于区分属性类型

0x2900 - 0x29FF用作特性描述

0x2A00- 0x7FFF用于区分特性类型

UUID,就是用来唯一识别一个特征值的ID。

handle,就是对应的attribute的一个句柄。

具体细节详见:Generic Attribute Profile (GATT)

摘抄一部分源码供参考:

/**
 * GATT Service UUIDs
 */
#define IMMEDIATE_ALERT_SERV_UUID       0x1802  // Immediate Alert
#define LINK_LOSS_SERV_UUID             0x1803  // Link Loss
#define TX_PWR_LEVEL_SERV_UUID          0x1804  // Tx Power
#define CURRENT_TIME_SERV_UUID          0x1805  // Current Time Service
#define REF_TIME_UPDATE_SERV_UUID       0x1806  // Reference Time Update Service

/**
 * GATT Characteristic UUIDs
 */
#define ALERT_LEVEL_UUID                0x2A06  // Alert Level
#define TX_PWR_LEVEL_UUID               0x2A07  // Tx Power Level
#define DATE_TIME_UUID                  0x2A08  // Date Time
#define DAY_OF_WEEK_UUID                0x2A09  // Day of Week
#define DAY_DATE_TIME_UUID              0x2A0A  // Day Date Time
#define EXACT_TIME_256_UUID             0x2A0C  // Exact Time 256

/**
 * GATT Unit UUIDs
 */
#define GATT_UNITLESS_UUID                    0x2700  // <Symbol>, <Expressed in terms of SI base units>
#define GATT_UNIT_LENGTH_METER_UUID           0x2701  // m, m
#define GATT_UNIT_MASS_KGRAM_UUID             0x2702  // kg, kg

    所有对特征值的操作,都是通过对UUID 的搜索得到对应的handle之后,通过handle来操作特征值的。对于蓝牙通信来说,其都是通过一个个不同的UUID来标识区分不同的服务,区分不同的特性,甚至服务和特性之间的类别。



六、总结

    不知道写什么了,就这样吧,后续想到了写吧,学习BLE也不久,路还很长。路漫漫其修远兮,吾将上下而求索~~~

    PS:该死的房价。。。。啊啊啊啊啊啊。。。。

    参考:1)蓝牙技术联盟官方网站

               2)Bluetooth开发者门户

               3)Bluetooth Spec Core_V4.1






本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

[BLE]低功耗蓝牙之GAP、GATT 的相关文章

  • UOJ 2016 [APIO 2016] Gap

    传送门思路参考代码交互题 交互题大致形式Windows 平台下 xff08 Dev C 43 43 xff09 Ubuntu 平台下 传送门 思路 唉 xff0c 我太弱了 xff0c 什么都不会 xff0c 题也做不来 这道题简直就是利用
  • [BLE]低功耗蓝牙介绍

    一 BLE的协议栈框架 BLE协议栈包括两个部分 xff0c 主机 Host 和控制器 Controller 二者通过HCI Host Controller Interface 标准接口相互通信 常用的单芯片单模BLE芯片有TI的CC254
  • BLE Mesh(二)BLE Mesh 基础

    Bluetooth Mesh Basics 1 Mesh vs Point to Point2 Devices and Nodes3 Elements4 Messages5 Addresses6 Publish Subscribe7 Sta
  • 蓝牙BLE---DA14683的SPI主机通信讲解

    DA14683的SPI主机通信例程 Date 2018 12 19 Create Jim 导入例程 首先导入ble peripheral例程或者pxp reporter例程 再到以下位置打开硬件SPI的宏定义 xff1a 获取SPI例程源码
  • BLE连接建立过程详解

    同一款手机 为什么跟某些设备可以连接成功 而跟另外一些设备又连接不成功 同一个设备 为什么跟某些手机可以建立连接 而跟另外一些手机又无法建立连接 同一个手机 同一个设备 为什么他们两者有时候连起来很快 有时候连起来又很慢 Master是什么
  • App 和设备通过蓝牙连接收发数据

    一 Android 中进行蓝牙开发需要用到的类和执行过程 1 使用BluetoothAdapter startLeScance来扫描设备 2 在扫描到设备的回调函数中的得到BluetoothDevice 对象 并使用Bluetooth st
  • 基于富芮坤fr8016 蓝牙5.0 芯片设计的BLE HID Joystick 游戏摇杆设备

    文章目录 ble hid 学习笔记 HID报告描述符与BLE HID profile之间关系 1 HID报告描述符 富芮坤fr8016 设计Joystick例子 1 描述X轴Y轴Z轴 2 描述按钮 3 Joystick 报告描述符 4 程序
  • 2021/9/29 TX POWER 蓝牙发射功率

    1 觉得挺奇葩 如果不是扩展包的话 就是TX power 设定 没法通过hci 下发 就是 在controller 设定好 也没法中途去变化 扩展包 有这个选项 能够改变 可以通过HCI 下发指令来更改 2 AD9361 设置发射功率 要注
  • CC2642 数据长度扩展(LE Data Length Extension)

    概要 数据长度扩展 LE Data Length Extension 功能允许LE控制器在连接状态下发送具有高达251字节的PDU 在连接期间的任何时刻 主从设备可以协商该PDU大小 这个和MTU不同 MTU是应用层的数据传输长度 这个是链
  • BLE蓝牙协议 — 自适应调频算法简单实现

    写在前面 转载文章 若有不妥 通知后我会立即删除 最近看了大神刘权写的 BLE4 0低功耗蓝牙协议总结 感觉收获颇丰 其中有一节是讲解蓝牙的自适应调频算法的 但是代码实现不方便阅读 原文是这样的 小生不才 斗胆做了一下调整 还望大神海涵 下
  • 蓝牙PHY6222添加OTA升级功能

    主要步骤 1 代码添加 ota app service 2 keil软件添加ota app代码 3 烧录软件添加ota设置 4 手机ota app升级 准备 奉加微电子官网下载6222的代码SDK V3 0以上 下载官网的PhyPlusKi
  • ESP32-C3 应用 篇(实例二、通过蓝牙将传感器数据发送给手机,手机端控制 SK6812 LED)

    ESP32 C3 蓝牙部分我们学习了GATT 本文尝试使用蓝牙做一个简单的小应用 目录 前言 一 整体框架 二 数据传输部分 2 1 添加温湿度驱动组件 2 2 传感器数据传输程序 再次说明 ESP GATTS READ EVT 事件 2
  • NRF52832学习笔记(2)—— 添加DFU功能(基于SDK15.3)

    前言 SDK版本15 3 评估板 pca10040 在 uart 的例程中添加 DFU 功能 使用 s132 的协议栈 因为官方的 BootLoader 工程用的是s132的协议栈 一 准备工作 在开始实验之前必须先准备以下软件 gcc a
  • BLE蓝牙协议 — BLE连接建立过程梳理(一)

    文章出处 枫之星雨 转载文章 如有不妥 通知后我会立即删除 连接建立 应付比广播更为复杂的数据传输 或者要在设备之间实现可靠的数据交付 这些都要依赖于连接 连接使用数据信道在两个设备之间可靠地发送信息 它采取了自适应跳频增强鲁棒性 同时使用
  • Dialog DA14585——基本硬件资源配置

    GPIO DA14585共有3中类型的封装 WLCSP34pin QFN40pin QFN48pin 除去专用的RST GND XTAL ANT VBAT脚后 分别有14 25 32个IO口可用 目前最常用的封装类型是QFN40 处理单元
  • 使用Arduino开发ESP32(22):蓝牙作为客户端使用

    文章目录 目的 基础准备 搜索蓝牙设备 搜索设备 信息查询 连接与交互 总结 目的 ESP32的蓝牙除了作为服务器 从设备 使用还可以作为客户端 主机 使用 这篇文章将对相关内容做个简单说明 基础准备 这篇文章中测试需要先准备一个蓝牙服务器
  • Telink BLE MESH开发

    一 前言 官网资料介绍建议采用DMA传输 串口数据的接收是放到了fifo中 但是串口发送也是采用的DMA 问题在于串口发送并没有建立缓冲器 而是判断当前DMA是否忙 如果忙数据直接丢弃 这样做显然不合理 如果发送时DMA忙应该将数据放到缓冲
  • BluetoothGatt:协商新的 MTU 成功,但无法使用新的大小(相差 3 个字节)

    我正在开发一个使用 BLE 在设备之间交换数据的应用程序 为了获得更好的性能 在连接两个设备后 我正在协商增加 MTU 以便通过 BLE 交换更大的数据包 连接蓝牙设备并读取所有服务和特征后 我请求使用以下方法增加 MTU private
  • 原生 Android BLE 实现本质上是同步的吗?

    我记得读过 指导和提示 http img developer samsung com contents authCdn ble Guide and Hints for Samsung BLE API pdf gda 1375430976 5
  • 使用 HCITool 宣传蓝牙 LE 服务

    我正在尝试在我的 Linux 计算机上创建蓝牙低功耗外设 目标是通过蓝牙从 iPhone 发送数据 我目前正在使用工具hciconfig hcitool and hcidump 我当前的实验是宣传具有特定 UUID 的服务 iOS Core

随机推荐