什么是蓝牙service和characteristic?如何理解蓝牙profile? ATT和GATT两者如何区分?什么是attribute? attribute和characteristic的区别是什么?蓝牙的互联互通为什么能够做的这么成功?

本文除了阐述上述问题 ,并重点阐述蓝牙协议栈中的ATT层和GATT层。

低功耗蓝牙协议栈构架结构:

如图所示,ATT和GATT是蓝牙协议栈最重要的两层,也是蓝牙应用开发者打交道最多的两层,用户开发应用程序或者说service/profile的时候,调用的都是GATT  API,而GATT又调用ATT  API。在讲解ATT 和  GATT之前 。我们先看一下蓝牙核心规范中一个重要概念:Client/Server (客户端/服务端)架构。

BLE  client/server(c/s)架构

  BLE采用了client/server (c/s)架构来进行数据交互,C/S架构是一种非常常见的架构,在我们身边随处可见,比如我们经常使用的浏览器和服务器也是一种C/S架构 ,这其中浏览器都是客户端client ,服务器是服务端server,server比如淘宝服务器,提供商品信息,广告,社交等服务,而浏览器就是客户端,比如微软的IE ,就可以用来请求这些服务,并使用server提供的服务。BLE与此相似,一般而言,设备提供服务,因此设备时server,手机使用设备提供的服务,因此手机就是client。比如蓝牙体温计,它可以提供 "体温" 数据服务,因此是一个server,而手机则可以请求 "体温" 数据以显示在手机上,因此手机是一个 client.

服务是以数据为载体的,所以说server提供服务其实就是提供各种有价值的数据

             图一: C/S架构

客户端要访问一个数据,就发送一个request/请求(其实就是一条命令或者PDU),服务端再把数据返回 给 客户端(一条response/响应命令或者PDU),这就是 C/S架构。

ATT


  讲解之前我们先讲解attribute,那么什么是attribute?  其实就是一条条的数据。前面说过,每个蓝牙设备就是来提供服务的,而服务就是众多数据的集合,这个集合可以称为数据库,数据库里面每一个条目都是一个attribute。所以我们把attribute翻译为数据条目。大家可以把蓝牙设备 想象称为一个表格,表格里面每一行就是一个attribute。attribute可以用下图来表示:

  • Attribute handle  ,Attribute句柄,16-bit长度。Client要访问Server的Attribute都是通过这个 句柄来访问,也就是说ATT  PUD一般都包含handle的值。用户在软件代码添加characteristic的时候,系统会自动按照顺序的 为相关attribute生成句柄。
  • Attribute  type,Attribute类型 ,2字节或者16字节长。在BLE中我们使用UUID来定义数据的类型,UUID是128bit的,所以我们有足够的UUID来表达万事万物 。其中有一个UUID非常特殊,他被蓝牙联盟官方UUID,这个UUID如下所示:

由于这个UUID众所周知,蓝牙 联盟将自己定义的attribute或者数据只用16bit  UUID来表示,比如0x1234,其实他也是128bit,完整表示为:

Attribute  type一般是由service和characteristic规格来定义,站在蓝牙协议栈角度来看,ATT层定义了一个通信的基本框架,数据的基本结构,以及通信的指令而GATT层就是定义serveice和characteristic,GATT层用来赋予每个数据一个具体的含义,让数据变得有及结构和意义。换句话说,没有GATT层 ,低功耗蓝牙也可以通信起来,但是会产生兼容性问题以及通信效率低。

  • Attribute  value,就是数据真实的值,0到512字节长。
  • Attribute  permissions,Attribute的权限属性,权限属性不会 直接 在 空口包中体现,在隐含 在ATT命令的操作结果中。假设一个attribute read属性设为open(即读操作不需要任何权限),那么client去读这个attribute时server将直接返回attribute的值;如果这个attribute read属性设为 authentication(即 需要配对才能访问),如果client没有与server配对而直接去访问这个attribute,那么server会返回一个错误码:告诉client你的权限不够,此时client会对server发起配对请求,以满足这个attribute的读属性要求,从而在第二次读操作时 server将把相应的数据返回给client。目前主要有如下四种权限属性:
    • Open ,直接可以读或者写
    • No  Access  ,禁止读或者写
    • Authentication ,需要 配对才能读或者写,由于配对有很多中类型,因此authentication又衍生多种子类型,比如带不带MITM,有没有LESC
    • Authorization,跟open一样,不过server返回attribute的 值之前需要应用先授权,也就是说应用可以在回调函数里面去修改读或者写的原始值。
    • Signed,签名后才可以读或者写,这个用的比较少。

一个应用所有的 attribute组成一个database,也称为attribute table,一个attribute  table示例如下 所示:

上图:原始attribute数据库(这个表格不能算是 原始attribute,因为它已经把bin数据 转成字符了,大家可以 把相关字符都看成bin数据,就看成原始attribute表格)。

设备支持的服务不同,attribute  table就不同。这里说明一下,当你添加,修改或者删除服务时,那么attribute table就会变,attribute table变了,它占用的RAM空间就会变。

ATT,全称attribute  protocol(数据交互协议)。说到底,ATT是由 一群ATT命令组成,就是上文 所述的request(请求)和response(响应)命令。ATT也是蓝牙空口包中的最上层,也就是说,ATT就是大家对蓝牙数据包进行分析的最多的地方。

ATT命令,正式称谓ATT  PDU  (Protocol Data Unit,协议数据交互单元)包括4类:读,写,notify(通知)和 indicate(指示)。这些命令又可以分成两种:如果它需要response,那么会在相应命令后面加上request。相反,如果它只是需要ACK而不需要response,那么它 的后面就不会带request。这里要特别强调 一点,ATT所有命令都是 "必达" 的。也就是说每个命令发出去以后,就会立马等ACK信息,如果收到ACK包,发送方认为命令完成;否则发送方一直重发该命令到导致BLE连接断开。换句话说,只要你的BLE连接没有断开,那么 你之前发送的数据包,不管他是用什么ATT  PDU来发送的,它肯定被对方收到了。

  有时候经常会出现一种情况,大家有时候会碰见  "  丢包  ",其实不是在空中丢包或者被干扰了 ,而是我们 的 发送代码写的有问题,导致包没有被安全的送达到协议栈射频 FIFO中,从而 出现所谓的 " 丢包 "。

  既然每个ATT命令都必达对方,那么还需要request类型的命令做什么?

  如果一个命令带有request后缀,那么发起方就可以收到命令的response包,这个response包在应用层是有回调事件的,而前述的ACK包在应用层是没有回调事件的。换句话说,不带request的命令,虽然协议栈底层确保了该命令必达对方,但应用层其实并不知道(私有实现方法除外),当你需要实现一个通信序列的时候,这种命令就显得不足了。而采用request/response方式的命令时,request命令发出去之后,必须等到相应的response命令回复才能进行下一步操作,比如发送下一个request命令,这样应用层可以严格按照规定逻辑执行一系列的操作,这个在很多应用场合是非常有用的。Request/response命令对还有一个副作用:大大降低通信的有效速率(吞吐率)因为request/response命令必须在不同的连接间隔中出现,也就是说,你在间隔1中发送了一个request命令,那么response包必须在间隔2或者稍后间隔中回复,而不能在间隔1中回复,这就导致一个数据包的发送需要跨两个连接间隔甚至更多。而不带request后缀的ATT命令就没有这个限制,ACK可以在同一个连接间隔中回复,这样一个连接间隔中可以同时发出多个数据包,这样将大大提高通信速率。大家可以参考下图来理解request和非request命令的区别:

注:第1个连接间隔中的蓝色包为request命令,旁边的灰色包是该request的ACK;第2个连接间隔的绿色包是response包,而它的ACK是第3个连接间隔中的蓝色包

注:图中的绿色包就是非request命令,而紧随其后的灰色包就是它的ACK

不带request的命令只有2个:write command和notification,其余的命令都是带request:所有 read命令,所有write 命令,find命令以及indicate命令,完整的ATT命令(ATT PDU)列表如下所示:

GATT,Service(服务)和Characteristic(特征数据)

在讲解GATT之前,我们先看一下什么是profile?

   Profile是一个大家经常见到的英文单词,但是总感觉领会不到这个词的内涵。Profile,英文本意就是 脸的侧面轮廓,这里大家注意一定要注意,脸的轮廓 不等于脸本身,但是profile本身是对脸的一种抽象,描述和定义,蓝牙规范其实也是使用profile这个引申意义而已,换句话说 ,蓝牙的profile跟英文字典中的profile是同一个意思。要定义蓝牙,必须要有一个规范,这就是蓝牙核心规范v4.2/v5.0/v5.1……蓝牙规范非常复杂和庞大 ,大部分蓝牙设备只实现了蓝牙规范中很少一部分,那么没有实现的这些规范对这个蓝牙设备来说 能不能称为 规范?当然不能!所谓规范或者规格 ,就是强制的,就必须实现。针对这种情况,profile可以很好的应对。我们把蓝牙某部分规范称为profile,这个profile如果设备要实现它,那么它就是强制的,如果设备不用它,也没有关系,这就是profile。基于此,我们可以把profile翻译成子规范或者条件规范或者剖面规范。“蓝牙规范包含很多子规范”,这句话用中文说问题不是很大,但是你把它翻译成英文,那就很难了!这就是 英文需要用profile的原因(而不是spec),  以及为什么profile在规范中出现的如此频繁。

    

GATT,全称generic  attribute  profile,对数据进行一般化/抽象化的子规范,说白了就是对数据进行逻辑化表达的规定。前面说过了,attribute就是一条一条的数据,那么这条数据表示什么?如何对其进行分类?这就是GATT要做的事情,GATT将数据赋予含义,并呈现一定的逻辑结构

  Service和characteristic就是GATT层定义的,前面说过,server端提供 服务,服务就是数据,而数据就是一条一条的attribute,而service和characteristic就是数据的逻辑呈现,后者说用户能看到的数据最终都转化为 service和characteristic。比如,一个数据 “37”。有可能是说体温 “37度”,也可能说心率 “37次”或者湿度 “37%”,因此必须对数据进行分类和定义。

  在蓝牙规格中,每一个具体的蓝牙应用是由多个service组成,而每一个service又是多个characteristic组成,这样我们可以把上面的图一转化为图3.

             图三:service和characteristic

 那么service/characteristic和attribute之间到底是一个怎样的关系?如前所述,service/charateristic是attribute的逻辑表现形式,而attribute是service/characteristic具体实现方式。尤其要注意的是,一条characteristic不是对应一条attribute具体实现方式。尤其注意的是,一条chararcteristic不是对应一条attribute,而是由多条attribute组成。虽然一个数据最有价值的部分是它的值(value )。=,但是 仅有value是不够的,比如27,到底是表示27°温度还是27%湿度;如果表示的是温度,那么它的单位是摄氏度还是华氏度。同时每个数据还有相应的读写属性以及权限属性,因此一个characteristic包含三种类型的数据条目(attribute):characteristic声明体条目(declaration attribute),characteristic 值条目(value characteristic)以及characteristic描述符条目(descriptor attribute)  (一个characteristic可以有多个描述符条目),如下所示:

  由于一个service可以包含多个characteristic,characteristic declaration就是每个characteristic的分界符,解析时一旦遇到characteristic  declaration,就可以认为接下来又是一个新的characteristic了,同时characteristic declaration还将包含value attribute的读写属性等。Charateristic value就是数据的值了,他也是一个单独的attribute,这个比较好理解就不再说了。Charactristic descriptor就是数据的额外信息,比如 温度的单位是什么,数据是用小数表示还是百分比表示等之类的数据描述信息。Descriptor属于可选条目,也就是说,一个characteristic可以不包含任何一条descriptor。这里着重提一种特殊的descriptor:CCCD。一般而言,都是client来访问server的characteristic,即通过ATT读或者写PDU访问相关数据。如果server想直接把自己的characteristic的值告诉client,就需要通过notiy或者indicate PDU,跟其他PDU相比,这两个PUD是由server自己决定什么时候开始传送,而不是被动接收client的命令请求。但client毕竟是客户,他得有自主权,所以引入了一个CCCD来帮助client控制server的行为。client可以通过禁止CCCD以允许notify或者 indicate命令,client可以通过禁止CCCD以允许notify或者indicate命令。重新总结一下。当CCCD使能的情况下,server可以随时notify或者indicate数据 给client;当禁止的时候,哪怕server有数据,它也不能notify或者indicate给client。这里强调一下。当characteristic具有notify或者indicate操作功能时,蓝牙规范要求必须为其添加CCCD attribute.

  重复强调,不管是characteristic declaration,characteristic value还是characteristic descriptor,实现的时候,都是一条数据条目,即attribute.

  引入了GATT,我们就可以把图2的 attribute table进行GATT化,得到下面有内涵,有层次,有定义的数据表格:

  所谓开发蓝牙应用程序,其实就是开发service和characteristic。通过API,添加自己需要的characteristic和service,你自己的蓝牙设备就诞生了。只要characteristic和service是符合GATT规范的,你可以随意添加任何characteristic和service,并将他们组合成一个专门的蓝牙设备。由于这个蓝牙设备是按照规范来定义的,所以它可以与任何其他蓝牙设备,比如手机,互联互通,并完成所要求的的交互动作。这里的蓝牙设备,我们还可以进一步细分为蓝牙profile设备和非profile蓝牙设备。前面也提过,profile就是一个子规范,蓝牙profile设备包含的所有实际service和characteristic都是按照profile规格来添加和定义的,比如说心率计profile,就是一个蓝牙联盟定义的蓝牙设备,蓝牙联盟有一份专门的spec来定义心率计profile,在这份spec中规定了心率计profile除了包含心率service,还包含电池service,设备信息service等。从这可以看出,心率profile和心率service是包含关系,前者包含后者。

低功耗蓝牙 ATT/GATT/Service/Characteristic 规格解读的更多相关文章

  1. 低功耗蓝牙ATT/GATT/Profile/Service/Characteristic规格解读

    什么是蓝牙service和characteristic?到底怎么理解蓝牙profile?ATT和GATT两者如何区分?什么又是attribute?attribute和characteristic的区别 ...

  2. Android BLE与终端通信(五)——Google API BLE4.0低功耗蓝牙文档解读之案例初探

    Android BLE与终端通信(五)--Google API BLE4.0低功耗蓝牙文档解读之案例初探 算下来很久没有写BLE的博文了,上家的技术都快忘记了,所以赶紧读了一遍Google的API顺便 ...

  3. BLE Hacking:使用Ubertooth one扫描嗅探低功耗蓝牙

    0×00 前言 低功耗蓝牙(Low Energy; LE),又视为Bluetooth Smart或蓝牙核心规格4.0版本.其特点具备节能.便于采用,是蓝牙技术专为物联网(Internet of Thi ...

  4. Android使用BLE(低功耗蓝牙,Bluetooth Low Energy)

    背景 在学习BLE的过程中,积累了一些心得的DEMO,放到Github,形成本文.感兴趣的同学可以下载到源代码. github: https://github.com/vir56k/bluetooth ...

  5. 永不消逝的电波(三):低功耗蓝牙(BLE)入门之如何调戏别人的小米手环

    0×00 前言 蓝牙(Bluetooth),一种无线技术标准,用来让固定与移动设备,在短距离间交换数据,以形成个人局域网(PAN).其使用短波特高频(UHF)无线电波,经由2.4至2.485 GHz的 ...

  6. 低功耗蓝牙BLE外围模式(peripheral)-使用BLE作为服务端

    低功耗蓝牙BLE外围模式(peripheral)-使用BLE作为服务端 Android对外模模式(peripheral)的支持 从Android5.0开始才支持 关键术语和概念 以下是关键BLE术语和 ...

  7. BLE低功耗蓝牙关键技术解析与应用

    BLE基础知识 1.传统蓝牙的传输距离几十米到几百米不等,BLE 则规定为 100 米(实际上没有那么远,50米以内比较稳定,和设备发射功率有关) 2.为了实现极低的功耗,BLE 协议设计为:在不必要 ...

  8. TI低功耗蓝牙(BLE)介绍

    TI低功耗蓝牙(BLE)介绍 本文档翻译和修改自参考资料:CC2540Bluetooth Low Energy Software Developer’s Guide (Rev. B),部分图片直接引用 ...

  9. Android 低功耗蓝牙BLE 开发注意事项

    基本概念和问题 1.蓝牙设计范式? 当手机通过扫描低功耗蓝牙设备并连接上后,手机与蓝牙设备构成了客户端-服务端架构.手机通过连接蓝牙设备,可以读取蓝牙设备上的信息.手机就是客户端,蓝牙设备是服务端. ...

随机推荐

  1. Hbase Java API包括协处理器统计行数

    package com.zy; import java.io.IOException; import org.apache.commons.lang.time.StopWatch; import or ...

  2. std::thread线程库详解(4)

    目录 目录 前言 条件变量 一些需要注意的地方 总结 前言 本文主要介绍了多线程中的条件变量,条件变量在多线程同步中用的也比较多.我第一次接触到条件变量的时候是在完成一个多线程队列的时候.条件变量用在 ...

  3. SCZ 20170812 T2 MFS

    题面照例十分暴力,我再次重写一下吧-- 题目描述 有\(n\)个数构成的数列\(A\)元素为\(a_i\),你要构造一个数列\(B\),元素为\(b_i\),使得满足\(b_{i}>0,a_{i ...

  4. HDU6430 Problem E. TeaTree【dsu on tree】

    Problem E. TeaTree Problem Description Recently, TeaTree acquire new knoledge gcd (Greatest Common D ...

  5. Codeforces Round #582 (Div. 3) E. Two Small Strings (构造,思维,全排列)

    题意:给你两个长度为\(2\)的字符串\(s\)和\(t\),你需要构造一个长度为\(3n\)的字符串,满足:含有\(n\)个\(a\),\(n\)个\(b\),\(n\)个\(c\),并且\(s\) ...

  6. hdu5459 Jesus Is Here

    Problem Description I've sent Fang Fang around 201314 text messages in almost 5 years. Why can't she ...

  7. HTTP的传输编码(Transfer-Encoding:chunked) / net::ERR_INVALID_CHUNKED_ENCODING

    https://blog.csdn.net/m0_37668842/article/details/89138733 https://www.cnblogs.com/jamesvoid/p/11297 ...

  8. k8s-0-集群

    Docker回顾 docker容器封装应用程序好处 内核在3.8以上,才能完整使用docker隔离功能(所有centos6不推荐用) Docker容器化封装应用程序缺点 容器编排工具有哪些 一: K8 ...

  9. DDL 数据定义语言

    目录 创建数据库(CREATE) 删除数据库(DROP) 修改数据库(ALTER) 创建数据表(CREATE) 数据表的数据属性 数据类型属性(Type) 其他属性(Null,Key,Default, ...

  10. MySQL 多实例及其主从复制

    目录 Mysql 实例 Mysql 多实例 创建多实例目录 编辑配置文件 初始化多实例数据目录 授权目录 启动多实例 连接多实例并验证 Mysql 多实例设置密码 设置密码后连接 Mysql 多实例主 ...