Generic Attribute Profile (GATT)

cc2640r2f,ble,mcu 2017-08-26 1795 次浏览 次点赞


通用属性配置文件(GATT)

GAP 层负责连接相关的功能,GATT 主要是负责在两个已经连接的设备之间进行交互数据。GAP 层把 BLE 设备区分为主机 Master(Central)和从机 Slave(Perpherial),在 GATT 层则区分为 Server 和 Client 。客户端读取和写入存储在服务端的特征值( Characteristics )。

Server
该设备包含由 GATT 客户端读取或写入的 characteristic。

Client
从 GATT 服务器读取或写入数据的蓝牙设备。

图1. Server 和 Client

注意:GATT 分层的 Server/Client 角色和 GAP 分层的 Master(Central)/Slave(Perpherial)并没有直接关系。如图 1 ,手机作为 Central/Client ,CC2640R2 开发板作为 Peripheral/Server。

I. GATT 的 Profile 、Service 、Characteristics 、Attributes

GATT 层的 Profile、Service、Characteristics、Attributes 概念一定要深入理解,因为一旦建立连接后,不管是嵌入式端还是应用端进行数据交互的都是这些概念。为了帮助理解,抽象以下包含关系:

图2. GATT 层四种概念之间的抽象包含关系

如图 2 ,一个或者多个 Characteristic 组成一个 Service。一个多个 Service 组成 Profile。Characteristic 又由多个 Attributes 组成。每个 Attribute 由包含 Handle、Type、Permissions 三个属性。

下面着重理解 Characteristic,它是数据交互的最终实体,每个特征包含以下 4 个 Attributes。

  • Characteristic Value(特征值)

用于 characteristic 的值

  • Characteristic Declaration(特征声明)

存储特征值的属性,位置和类型的描述符

  • Client Characteristic Configuration(客户端特征配置)

允许 GATT 服务器配置要通知的特性(异步发送消息)或指示的配置(与确认异步发送消息)

  • Characteristic User Description(特征用户描述)

描述特征的 ASCII 字符串

这些属性存储在属性表中的 GATT 服务器中,每个 Attributes 又由以下元素组成:

  • Handle(句柄)

表中属性的索引(每个属性都有一个唯一的句柄)

  • Type(类型)

指示属性数据表示什么(称为 UUID [通用唯一标识符],其中一些是蓝牙 SIG 定义的,一些是自定义的)

  • Permissions(权限)

强制 GATT 客户端设备如何访问属性的值

II. GATT 客户端抽象层

如图 3 所示,在 GATT 客户端应用程序大部分是直接使用 GATT 的 API(少部分直接使用 ATT 层 API ),没有 profile 文件。因为 GATT 客户是得到数据,不需要建立属性表和配置文件。

图3. GATT 客户端抽象层

III. GATT服务端抽象层

如图 4 所示,在 GATT 服务端大部分功能由独立的 profiles 处理,profiles 又使用 GAttservApp 模块处理( GAttservApp 是一个可配置的模块,用于储存和管理属性表,详见 BLE Stack API Reference )。

图4. GATT 服务端抽象层

在建立 GATT 服务端的时候首先需要配置 profiles 文件,profiles 文件调用 GattServAppApp 模块并使用其 API 与 GATT 层接口。这种情况下应用程序不需要直接调用 GATT 层 API,而是和 Profiles 文件接口。

IV. GATT Services 和 Profile

在概述部分讲过,GATT service 是 Characteristic 的集合,多个 service 可以组合在一起形成一个 profile ,许多 profile 仅实现一个 service ,所以 profile 和 service 可以互换。

simple_peripheral 示例应用程序项目中定义了 4 个 GATT 服务。

  • GAP GATT 服务(GGS)

此服务包含设备和访问信息,例如设备名称,供应商标识和产品标识。
为此服务定义了以下特征:

设备名称
表现( Appearance )
外围首选连接参数

  • 通用属性服务
    该服务包含有关 GATT 服务器的信息,是蓝牙低功耗协议栈的一部分。每个 GATT 服务器设备都需要根据蓝牙 5.0 版本核心规范。
  • 设备信息服务
    此服务公开了有关设备的信息,如硬件、软件版本、固件版本、规范信息、合规性信息和制造商名称。设备信息服务是蓝牙低功耗协议栈的一部分,由应用程序配置。
  • simple_gatt_profile 服务
    此服务是用于测试和演示的示例配置文件。完整的源代码在 simple_gatt_profile.c 和 simple_gatt_profile.h 文件中提供。
      // Initialize GATT attributes
      GGS_AddService(GATT_ALL_SERVICES);           // GAP GATT Service
      GATTServApp_AddService(GATT_ALL_SERVICES);   // GATT Service
      DevInfo_AddService();                        // Device Information Service
      SimpleProfile_AddService(GATT_ALL_SERVICES); // Simple GATT Profile

图 5 显示了 simple_peripheral 项目中的属性表,这些属性都是通过上面 4 个服务函数设置的。可以逐一打开查看修改。

图5. simple_peripheral 项目的属性表

使用 BTool 获取的简单 GATT 配置文件特性表。红色表示 Profile 声明、黄色表示字符声明、白色表示与特定声明相关的属性。

simple_gatt_profile 包含以下特性:

  • SIMPLEPROFILE_CHAR1

可以从 GATT 客户端设备读取或写入的 1 字节值

  • SIMPLEPROFILE_CHAR2

可以从 GATT 客户端设备读取但不能写入的 1 字节值

  • SIMPLEPROFILE_CHAR3

可以从 GATT 客户端设备写入但不能读取的 1 字节值

  • SIMPLEPROFILE_CHAR4

不能从 GATT 客户端设备直接读取或写入的 1 字节值(该值是通知属性)

  • SIMPLEPROFILE_CHAR5

可从 GATT 客户端设备读取(但不写入)的 5 字节值

0x001C 是 simple_gatt_profile 服务声明。
此声明的 UUID 为 0x2800(蓝牙定义 GATT_PRIMARY_SERVICE_UUID ),声明的值是 simple_gatt_profile(自定义)的 UUID。

0x001D 是 SimpleProfileChar1 特征声明。
该声明可以被认为是指向 SimpleProfileChar1 值的指针。UUID 为 0x2803(蓝牙定义 GATT_CHARACTER_UUID )。

下面解释 characteristic 声明值的含义( MSB 到 LSB ):

  • 字节 0 是蓝牙核心规范版本 5.0 中定义的 SimpleProfileChar1 的属性 (以下是某些相关属性)。

0x02:允许读取特征值
0x04:允许写入特征值(无响应)
0x08:允许写入特征值(带响应)
0x10:允许通知特征值(无确认)
0x20:允许通知特征值(带确认)
characteristic1 0x0A 的意义是该特性可读可写(0x02|0x08)

  • 字节 1-2:SimpleProfileChar1 的值的句柄(句柄 0x001E)
  • 字节 3-4:SimpleProfileChar1 值的 UUID(自定义 0xFFF1)
  • 0x001E 是 SimpleProfileChar1 特征值

UUID 为 0xFFF1(自定义),该值是 characteristic 的实际有效载荷数据。如 SimpleProfileChar1 声明(句柄 0x01D)所示,该值是可读写的。

  • 0x001F 是 SimpleProfileChar1 特征用户描述

UUID 为 0x2901(蓝牙定义),该描述的值是描述特征的用户可读字符串。

  • 0x0020 - 0x002C
    剩下的 4 个特征描述具有和 simpleProfileChar1 相同结构的属性。唯一不同的属性是处理 0x0028 时,描述如下:

    0x0028 是 SimpleProfileChar4 客户端特征配置。此配置的 UUID 为 0x2902(蓝牙定义)。GATT 服务器通过写入此属性可以将 SimpleProfileChar4 配置为通知(写入 0x0001 )或指示(写入 0x0002 ),将 0x0000 写入此属性将禁用通知和指示。

V. GATT 安全

如 GATT 服务端抽象所述,GATT 服务器可以为每个特性独立定义权限。服务器可能允许任何客户端访问某些 characteristic ,同时将访问其他 characteristic 仅限于认证或授权的客户端。这些权限通常被定义为更高级别的配置文件规范的一部分。对于自定义配置文件,用户可以选择他们认为合适的权限。
有关 GATT 安全性的更多信息,请参阅蓝牙核心规范版本 5.0 的安全注意事项部分([第3卷,第G部分,第8部分])。

认证( Authentication )

在客户端通过认证配对方法之前,无法访问需要身份验证的特性。此验证在堆栈内执行,无需应用程序处理。唯一的要求是使 GATT 服务端正确注册该特性。

例如,simple_gatt_profile 的 characteristic5 设置需要认证的读取。

//characteristic 5 
{ 
   {  ATT_BT_UUID_SIZE , simpleProfilechar5UUID  },
   GATT_PERMIT_AUTHEN_READ ,
   0 ,
   simpleProfileChar5 
},

当未经身份验证的客户端尝试读取此值时,GATT 服务器将自动拒绝它,不调用 simpleProfile_ReadAttrCB()。只有当认证成功之后,读写的请求才会转发到 profiles 的读写回调。

参阅代码在 simple_gatt_profile.c 中 simpleProfile_ReadAttrCB( )函数里面。

  case SIMPLEPROFILE_CHAR1_UUID:
  case SIMPLEPROFILE_CHAR2_UUID:
  case SIMPLEPROFILE_CHAR4_UUID:
    *pLen = 1;
    pValue[0] = *pAttr->pValue;
    break;

  case SIMPLEPROFILE_CHAR5_UUID:
    *pLen = SIMPLEPROFILE_CHAR5_LEN;
    VOID memcpy( pValue, pAttr->pValue, SIMPLEPROFILE_CHAR5_LEN );
    break;
    
  default:
    // Should never get here! (characteristics 3 和 4没有读权限)
    *pLen = 0;
    status = ATT_ERR_ATTR_NOT_FOUND;
    break;

授权( Authorization )

授权是发生在认证之后对一些文档的访问权限进行更改或删除。授权是 BLE 已经实现的一个安全层,由于应用程序需要定义自己的授权要求,所以协议栈将这些特性的读/写请求转发到配置文件的应用程序层。

从 profile 文件注册 GATT 服务器的授权信息必须使用堆栈定义一个授权回调。如下面伪代码(例程中没有使用授权):

  1. 注册授权回调
    CONST  gattServiceCBs_t  simpleProfileCBs  = { 
       simpleProfile_ReadAttrCB ,      //读回调函数指针
       simpleProfile_WriteAttrCB ,     //写回调函数指针
       simpleProfile_authorizationCB   //授权回调函数指针
    };
  1. 实现授权回调代码
    static  bStatus_t  simpleProfile_authorizationCB ( uint16  connHandle , gattAttribute_t  * pAttr , uint8  opcode  )
    { 
       //这只是一个示例实现,正常的用例将需要
       更复杂的逻辑来确定设备是否被授权
    
       if (clientIsAuthorized )
          return SUCCESS ; 
       else
          return ATT_ERR_INSUFFICIENT_AUTHOR ; 
    }

授权回调在协议栈上下文中执行,因此在这个函数中不应该执行太复杂的处理,具体实现由开发人员决定。上述回调应该被视为一个 shell。如果客户端有权访问该特征返回 SUCCESS ,如果无权访问返回 ATT_ERR_INSUFFICIENT_AUTHOR 。授权需要事先通过连接进行身份验证,否则 ATT_ERR_INSUFFICIENT_AUTHEN 将作为错误响应发送。

如果需要授权的特性注册了授权回调,但没有定义应用级授权回调,那么协议栈将返回 ATT_ERR_UNLIKELY 。因为这个错误并不明确,所以 TI 建议使用授权回调。

VI. 直接使用 GATT 层

上面客户端抽象介绍过应用程序也可以直接使用 GATT 层 API,本节就介绍如何在应用程序中使用 GATT 层。

GATT 层的功能在库中实现,但头文件功能可在 gatt.h 中找到。BLE Stack API Reference 中有完整的 API 参考。GATT 客户端(在 simple_central 项目中)使用 GATT 层的一般过程如下:

图6. GATT 客户端使用 GATT 层的过程

图 6 中可以看出,首先 GATT 客户端发送命令,ICALL 会给协议栈发送 ATT 命令,协议栈处理之后返回响应。然后,再由 ICALL 以 ATT 事件通知应用程序,应用程序得到 ATT 事件之后异步处理。

注意:除了收到对自己命令的响应外,GATT 客户端还可以从 GATT 服务器接收异步数据作为指示或通知。使用 GATT_RegisterForInd (selfEntity) 注册接收这些 ATT 通知和指示。这些通知和指示也作为 ATT 事件( ATT_HANDLE_VALUE_NOTI& ATT_HANDLE_VALUE_INDG )发送到应用程序。这些事件必须按照 GATT Services 和 Profile 中的说明进行处理。

VII. GAP GATT 服务(GGS)

GGS 服务包含设备和访问信息。例如设备名称、Appearance、外围首选连接参数。GGS 的目的是在设备发现和连接启动过程中进行辅助。有关 GGS 的更多信息,请参阅蓝牙核心规范版本 5.0 的“ GAT service ”和“ Characteristics for GATT Server ”部分([Vol 3],C 部分,第 12 节)。

  1. 包含标题
#include  “gapgattserver.h”
  1. 初始化 GGS 参数
// GAP GATT Attributes 
static  uint8_t  attDeviceName [ GAP_DEVICE_NAME_LEN ]  =  “This is a text” ;    
GGS_SetParameter (GGS_DEVICE_NAME_ATT , GAP_DEVICE_NAME_LEN , attDeviceName );
  1. 使用 GGS 初始化应用程序回调(可选)。当 GGS 中的任何特征发生变化时,都会通知应用程序。
GGS_RegisterAppCB (&appGGSCBs );
  1. 将 GGS 添加到 GATT 服务器。
bStatus_t  GGS_AddService (GATT_ALL_SERVICES );

经过上面 4 个步骤就成功设置了 GGS 的参数,在 central 设备连接外围设备时就能获取这些参数,使用我们公司提供的 XXX.apk 可以在手机界面直接查看设置的参数值。

VIII. 加入我们

文章所有代码、工具、文档开源。加入我们QQ群 591679055获取更多支持,共同研究CC2640R2F&BLE5.0。

CC2640R2F&BLE5.0-乐控畅联 © Copyright 2017, 成都乐控畅联科技有限公司.


本文由 Jay 创作,采用 知识共享署名 3.0,可自由转载、引用,但需署名作者且注明文章出处,点赞3

还不快抢沙发

添加新评论