一文搞定蓝牙基础及蓝牙协议栈 ble(GAP ATT GATT协议栈,profile/uuid/特征值/service,广播,扫描,连接)
参考网站
Bluetooth LE Security Study Guide 翻译
华为蓝牙开发者论坛
https://www.cnblogs.com/cps666/p/17248346.html
https://gitcode.com/Open-source-documentation-tutorial/c88c7
https://blog.csdn.net/xuewenyu_/article/details/147752462
后续补充
https://ost.51cto.com/posts/30524
BLE
从1994年由电信商爱立信提出“希望创建一个RS-232数据线的无线通信替代版本,它能够链接多个设备,克服同步的问题”以来,蓝牙经过了20余个岁月的发展,蓝牙技术也经历了五次重要的迭代,目前由SIG(蓝牙技术联盟)来负责维护其技术标准。
蓝牙,是一种支持设备短距离通信(一般10m内)的无线电技术,能在包括移动电话、PDA、无线耳机、笔记本电脑、相关外设等众多设备之间进行无线信息交换。利用“蓝牙” 技术,能够有效地简化移动通信终端设备之间的通信,也能够成功地简化设备与因特网Internet 之间的通信,从而数据传输变得更加迅速高效,为无线通信拓宽道路。简单的说,蓝牙技术是一种利用低功率无线电在各种 3C 设备间彼此传输数据的技术。蓝牙工作在全球通用的 2.4GHz ISM(即工业、科学、医学)频段,使用 IEEE802.11 协议。作为一种新兴的短距离无线通信技术,正有力地推动着低速率无线个人区域网络的发展。
当前的蓝牙技术主要分为BR/EDR(Basic Rate /Enhanced Data Rate)和低耗能(LE)两种技术类型。低功耗蓝牙不能向后兼容原有的蓝牙协议(经典蓝牙协议),所以它们之间是不能互通的。蓝牙4.0规范允许设备同时支持经典与低功耗蓝牙协议。低功耗技术是低成本、短距离、可互操作的鲁棒性无线技术,该技术采用非常快速的连接方式,因此平时可以处于“非连接”状态(节省能源),此时链路两端相互间只是知晓对方,只有在必要时才开启链路,然后在尽可能短的时间内关闭链路(每次最多传输20字节)。

总之,蓝牙 4.0 是第一个蓝牙综合协议规范,将三种规格集成在一起。其中最重要的变化就是 BLE(Bluetooth Low Energy)低功耗功能,提出了低功耗蓝牙、传统蓝牙和高速蓝牙三种模式:”高速蓝牙“主攻数据交换与传输;“传统蓝牙”则以信息沟通、设备连接为重点;”低功耗蓝牙“以不需占用太多带宽的设备连接为主,功耗较老版本降低了90%。BLE 前身是 NOKIA 开发的 Wibree 技术,本是作为一项专为移动设备开发的极低功耗的移动无线通信技术,在被 SIG 接纳并规范化之后重命名为 Bluetooth Low Energy(后简称低功耗蓝牙)。这三种协议规范还能够互相组合搭配、从而实现更广泛的应用模式。传统蓝牙可以用来连接蓝牙耳机,也可以连接支持SPP协议的蓝牙模块;蓝牙低功耗可以用来连接外部的智能外设。此外,蓝牙4.0还把蓝牙的传输距离提升到 100 米以上(低功耗模式条件下)。拥有更快的响应速度,最短可在3 毫秒内完成连接设置并开始传输数据。更安全的技术,使用AES-128 CCM 加密算法进行数据包加密和认证。代表作:苹果 iPhone 4S 是第一款支持蓝牙 4.0 标准的智能手机。
蓝牙协议栈
BLE协议栈的构成
BLE协议栈主要分为三个部分:控制器(Controller),主机(Host),应用层(Application),Host是正真意义的蓝牙协议,Controller为蓝牙底层,或者说是基带芯片。Profiles(配置文件)和应用总是基于GAP和GATT 之上。在单芯片方案中,Controller 和HOST、Profiles、应用层都在同一个芯片上。

这里再用一个表格简要概括:


在开发过程中,我们一般又把蓝牙的架构简单分为Host和Controller两个模块,Host主要是各种业务场景需求的实现,Controller部分主要负责的是蓝牙报文的收发以及蓝牙物理连接的管理这些基本功能。所以通常绝大部分的开发工作都是在Host端进行,Controller部分的工作大都是由专门的蓝牙芯片厂商来负责;Host和Controller分模块的最初设计理念是想让这两个模块单独运行在两颗不同的芯片甚至系统上,之间通过硬件通信端口(串口,USB)使用HCI协议进行连接和通信,这样可以方便替换和升级。
低功耗蓝牙协议栈层层打包,最后空中实际传输的数据将变成下图所示的模样:

低功耗蓝牙协议栈由许多层和功能模块组成,其中一些是强制性的,一些是可选的。协议栈的这些部分分布在host和controller的两个主要架构块上,标准逻辑接口定义了这两个组件可以通信的方式。
host通常是类似于操作系统的东西,而controller通常是一个片上系统。但是,情况也并不一定真的如此,蓝牙规范并没有规定具体的实现细节。重要的是,在架构中作为独立逻辑容器的host和controller可以通过某种方式在物理上独立的组件中实现,并为它们之间的通信定义一个标准接口。这允许一个蓝牙系统由来自不同制造商的host和controller组件组成。
Controller层
蓝牙控制器(BR/EDR Controller,LE Controller,AMP Controller等)屏蔽了蓝牙的基带部分,提供了统一的数据进入基带的方法。蓝牙有以下几种控制器:
• BR/EDR Controller
• LE Controller
• AMP Controller
前两种称为Primary Controller,AMP是蓝牙3.0后加上的,支持High Speed传输。主要包括PHY层、Link层、HCI层等。
Physical layer:
PHY层用来指定BLE所用的无线频段(2.4G),调制解调方式和方法、跳频等。PHY层做得好不好,直接决定整个BLE芯片的功耗,灵敏度以及selectivity等射频指标。射频层方面有RF特性,运行在2.4GHz ISM band,40频道2MHz的通道间隙,3个广播通道,37个自适应自动调频通道。
具体在蓝牙通信中的主要作用包括:
①规定基础射频参数:PHY层规定了BLE(蓝牙低功耗)通信的基础射频参数,这些参数包括信号频率、调制方式等。例如,BLE使用1Mbps(蓝牙5.0还支持2Mbps)自适应跳频的GFSK(高斯频移键控)射频,工作在2.4GHz频段(2400MHz~2480MHz)。
②信号调制与解调:PHY层定义了BLE的调制方式,即如何将数字信号转换为模拟信号进行传输,以及如何将接收到的模拟信号还原为数字信号。在BLE中,通常使用GFSK作为调制方式。
③频段与通道管理:PHY层指定了BLE使用的无线频段,并定义了频段的范围。此外,它还管理了BLE的通信通道,包括数据通道和广播通道。这些通道的设置有助于减少不同设备之间的干扰,提高通信质量。
管理的2种信道:广播信道和数据信道
1)广播信道:提供给还没有建立连接的蓝牙设备提供发射广播、扫描、建立连接的信道。BLE有3个广播信道:37、38、39,在每一个广播事件发生时,advertiser分别在这3个信道上各发送一次广播信号。传统蓝牙的广播信道有16-32个,而BLE只有3个,这就是为什么BLE的广播时间比较短的原因。
2)数据信道:提供给已经建立蓝牙连接的master和slave端提供可靠的数据通信信道。BLE规定,数据信道有37个。为加强通讯的可靠性,避开干扰,BLE设备通过自适应跳频的方式在这37个信道上传输数据。
④实现与硬件设备的接口:PHY层还负责实现与硬件设备的接口,确保数字数据能够正确转换为无线信号,并通过硬件设备发送出去。同时,它也能够接收硬件设备接收到的无线信号,并将其转换为数字数据。
⑤影响射频指标:PHY层的设计和实现直接影响到整个BLE芯片的功耗、灵敏度以及选择性等射频指标。一个优秀的PHY层设计可以带来更好的通信性能和更低的功耗。
Link Layer:
LL层是整个BLE协议栈的核心,也是BLE协议栈的难点和重点。LL层要做的事情非常多,比如具体选择哪个射频通道进行通信,怎么识别空中数据包,具体在哪个时间点把数据包发送出去,怎么保证数据的完整性,ACK如何接收,如何进行重传,以及如何对链路进行管理和控制等等。LL层只负责把数据发出去或者收回来,对数据进行怎样的解析则交给上面的GAP或者ATT。其中主要负责以下几个方面的工作:
广播、扫描、建立和维护连接:链路层负责在设备之间广播信息,使其他设备能够发现和识别自己。同时,它还负责扫描周围的设备,寻找可以与之建立连接的设备。一旦找到目标设备,链路层将负责建立和维护连接,确保数据能够在两个设备之间可靠地传输。
数据包的组织、校验和加密:链路层负责确保数据包按正确的方式组织,包括添加必要的头部和尾部信息。它还负责生成和验证数据包的校验值,以确保数据的完整性和准确性。此外,链路层还提供了数据加密功能,以保护传输的数据免受未经授权的访问和篡改。
控制射频模块状态:链路层能够控制设备的射频模块处于不同的状态,包括待机、广播、扫描、正在启动或已连接等。这些状态的切换对于蓝牙设备的正常工作和节能非常重要。
定义设备角色:链路层定义了蓝牙设备在通信过程中的四种角色:广播设备、扫描设备、主设备和从设备。这些角色决定了设备在通信中的行为和功能。
数据传输和管理:一旦连接建立,链路层将负责在设备之间传输数据。它负责将数据封装成数据包,并在适当的时候将数据包发送出去。同时,链路层还负责接收来自其他设备的数据包,并将其传递给上层协议进行处理。此外,链路层还提供了重传机制,以确保数据在传输过程中的可靠性。
其中BLE 的链路层决定蓝牙所处的状态,蓝牙链路层的蓝牙状态切换图如图所示,每个状态描述如下:
1)待机状态(Standby):设备没有传输和发送数据,并且没有连接到任何设备
2)广播状态(Advertiser):周期性广播状态
3)扫描状态(Scanner):主动地寻找正在广播的设备
4)发起连接状态(Initiator):主动向某个设备发起连接
5)已连接状态,主机状态(Master)
6)已连接状态,从机状态(Slave)
连接建立后,两个设备将在数据通道上通信,而不再使用广播和扫描的方式。

以手机蓝牙连接设备1蓝牙模块为例, 手机为主设备,设备1为从设备。
上电之后,手机和设备1就都处于待机态。
设备1开启广播功能,它就处在了广播态。
手机打开蓝牙,扫描周围的设备,它就处在了扫描态。
手机尝试连接设备1,手机此时就处在了发起连接态。
连接成功之后,二者就都处于连接态状态。也就是当蓝牙连接成功时,此时蓝牙设备将从广播态转换成连接态。此外,当主设备主动断开,或者其他因素断开连接后,此时主设备将从连接态进入就绪态,要想设备能够继续广播被别的设别发现,需要在蓝牙断开事件中再次启动广播,使设备再次进入广播态。
同时,实际上数据链路层定义了4个角色:1)scaner和advertiser;2)master和slave
在建立连接之前,scanner负责扫描、发起扫描请求和发起连接请求;而advertiser(智能外设)任务是发起广播、响应扫描请求信号、响应连接请求进而跟scanner端建立连接。数据链路层同时也负责将各种数据包按正确的数据格式组织起来,正确地发送到对方。建立连接以后scanner被称为master,advertiser被称为slave。BLE通信之所以为主从模式,是因为Slave不能“随性”给Master发信息,它只有等到Master给它发了一个packet后,然后才能把自己的数据回传给Master。
HCI:HCI主要负责透过 transport把协议栈的数据发送给蓝牙芯片,并且接受来自蓝牙芯片的数据。在Host和Controller之间提供一个标准化的接口,该层可以由软件api实现或者使用硬件接口uart,spi,usb来控制;Host蓝牙协议需要按照蓝牙联盟规范的HCI指令即可控制蓝牙controller,蓝牙协议栈(Host)跟芯片(Controller)一般是通过HCI数据来沟通,这也是HCI层的作用所在。控制信号(C-Plane)和语音信号(U-Plane)在Controller层、Transport层和Host层之间传输,完成数据之间的交互。
通俗来讲,就是定义了特定的格式由蓝牙协议栈来控制蓝牙芯片来做相应的动作(比如 inquiry,connect,disconnect)。并且由蓝牙芯片回应蓝牙协议栈状态以及事件消息。为什么我们发送的 HCI data 芯片能识别并且执行相应的动作呢?因为蓝牙芯片中也有 HCI 协议代码,烧到 Bluetooth chip 中,所以我们发送的 data 可以被正确的 parse,执行相应的动作后再准确的回送给我们 response。
总之,HCI主要负责透过 transport把协议栈的数据发送给蓝牙芯片,并且接受来自蓝牙芯片的数据。Transport层通过一些数据格式与上层L2CAP进行数据交互,进而完成Controller和Host之间的信息交流。
Host层
Host层为主机协议层,涉及到BR/EDR或LE相关的蓝牙上层协议,是在蓝牙核心协议的基础上,根据具体的应用需求,定义出的各种策略,如FTP,文件传输,局域网等。应用层协议根据经典蓝牙和低功耗蓝牙分为很多种规范(profile),每种规范实现一种功能用于蓝牙设备之间的数据传输。profile的类别及功能详见https://www.bluetooth.com/specifications/specs/。从开发者的角度,协议栈就是将各个层定义的协议都集合在一起,以函数的形式实现,并提供一些应用层API,供用户调用。虽然协议是统一的,但是协议的具体实现形式是变化的,即不同厂商提供的协议栈是有区别的,例如:函数名称和参数列表可能有区别,选择协议栈以后,需要学习具体的例子,查看厂商提供的Demo演示程序、说明文档来学习各个函数的使用方式,进而快速地使用协议栈进行应用程序的开发工作。
协议栈部分有(Host):
逻辑链路控制及自适应协议层(L2CAP):L2CAP对LL进行了一次简单封装,LL只关心传输的数据本身,L2CAP就要区分是加密通道还是普通通道,同时还要对连接间隔进行管理。相当于快递,将数据打包。
安全管理层(SMP):安全服务层,提供配对和秘钥的分发,实现安全连接和数据交换;
通用访问配置文件层(GAP):定义了在非连接状态下可以是有的操作模式和过程,比如如何使用广播进行无连接通信以及怎样执行设备发现。定义安全等级和模式,定义某些用户接口标准。它是蓝牙低功耗(BLE)设备内部功能对外的接口层,它定义了BLE设备在通信过程中的一些基本行为和规范。
GAP层定义了BLE设备在通信过程中的四种角色,分别是外围设备(Peripheral)、中央设备(Central)、播报设备(Broadcaster)和观察设备(Observer)。这些角色决定了设备在通信中的行为和功能。虽然GAP层和链路层都定义了设备的角色,但它们的侧重点和目的不同。GAP层关注于设备在通信过程中的行为和功能,而链路层则关注于通信过程中的物理层和数据传输方面的细节。因此,这两个层次的角色定义并不完全重复,而是相互补充,共同构成了BLE通信的完整框架。
属性协议层(ATT):负责数据检索,允许一个设备暴露一些数据块给其他设备,其他设备称之为“属性”。展示“属性”的设备称为服务器,与之配对的设备成为客户端;链路层状态(主机从机)与设备的ATT角色是相互独立的;根据底层属性表中的属性,定义了被称作服务、特征和描述符的高层数据类型。定义了使用ATT去处理属性表的更高级过程。
它主要用于描述和管理设备的各种服务和特征,包括设备名称、服务、特征、描述等。通过ATT协议,设备可以了解彼此的属性,以便进行相应的通信和数据交换。ATT协议是建立在GATT(Generic Attribute Profile)之上的一种协议,它定义了设备之间如何交换属性信息和实现通信的规范。这使得不同设备之间可以进行安全、高效的数据交换和通讯。ATT协议的灵活性使得它适用于多种应用场景,从简单的传感器应用到复杂的物联网设备。通过ATT协议,这些设备可以相互通信,实现数据的共享和交换。
通用属性配置文件层(GATT):它规范了数据内容、提供了数据传输和存储的架构、实现了数据的分类管理、确保了蓝牙设备的互联互通,并定义了设备角色和层次结构组织。其中定义了使用ATT的服务框架,规定了配置文件profile的结构。
以下是GATT的主要作用:
数据传输和存储架构:GATT层是传输真正数据所在的层,它提供了一个数据传输和存储的架构以及基本操作。这个架构使得两个蓝牙设备之间可以高效、可靠地传输数据。
数据内容规范:GATT用来规范attribute(属性)中的数据内容。Attribute是指一条带有标签的、可以被寻址的数据,每个属性都对应一个唯一的handle(句柄)。通过规范数据内容,GATT确保了数据的准确性和一致性。
数据分类管理:GATT运用group(分组)的概念对attribute进行分类管理。这种分类管理使得设备可以更加高效地处理和组织数据,提高了数据处理的效率。
蓝牙设备互联互通:虽然没有GATT,BLE(低功耗蓝牙)协议栈也能运行,但互联互通可能会出现问题。GATT和各种各样的应用profile使得BLE摆脱了Zigbee等无线协议的兼容性困境,成为了出货量最大的2.4G无线通信产品。
定义设备角色:GATT定义了两类角色:服务端(server)和客户端(client)。服务端是数据的提供者,客户端是数据的请求者。这种角色定义使得设备之间的通信更加清晰和有序。
蓝牙链路层定义的角色更多地关注于设备在蓝牙网络中的连接和传输行为,而GATT定义的角色则更多地关注于在已建立的连接上,设备之间如何进行数据的交互和通信。这两个层次的角色定义共同构成了蓝牙通信的完整框架。
层次结构组织:一个GATT服务器通过一个称为属性表的表格组织数据。这个属性表包含了用于真正发送的数据,以及数据的属性和描述。这种层次结构使得数据的管理和传输更加有序和高效。
BLE连接过程
BLE的连接过程可以简单说明如下,从设备slave发出广播信号,主设备master收到信号后发出扫描请求,从设备回应扫描(主动扫描),主设备发出连接请求,从设备收到并回应请求,接下来完成交换安全密钥(配对)和保存密钥方便快速认证(绑定)过程,整个过程完成后即连接建立,开始数据通信。
①广播
在设备A(通常是手机或中央设备)和设备B(例如蓝牙耳机或外围设备)建立连接之前,设备B需要先进行广播。广播是以一定的广播间隔周期性地广播数据,目的是让周围的设备能够发现并识别它。广播数据包中包含了设备B的一些基本信息,如设备名称、服务UUID等。即设备B(Advertiser)不断发送广播信号,t为广播间隔。每发送一次广播包,我们称其为一次广播事件(advertising event),因此t也称为广播事件间隔。广播事件有一个持续时间,蓝牙芯片只有在广播事件期间才打开射频模块,这个时候功耗比较高,其余时间蓝牙芯片都处于idle状态,因此平均功耗非常低。根据蓝牙协议,每一个广播事件包含三个广播包,即分别在37/38/39三个通道上同时广播相同的信息。

以一定的时间间隔广播数据,这个间隔称为称为advertising interval,为了改善设备的兼容性,在两个广播时间间隔之间,还有加上一个随机的0-10ms的delay时间。时间关系如下:
Advertising event的周期= advertising interval+delay
其中:delay是一个0-10ms的随机数。advertising interval的范围:20ms-10.24s,并且要求是0.625ms的整数倍。
设备B不断发送广播信号给设备A(Observer),如果设备A不开启扫描窗口,是收不到设备B的广播的,只有设备A的射频接收窗口跟广播发送的发射窗口匹配成功,设备A才能收到设备B的广播信号。由于这种匹配成功是一个概率事件,因此设备A扫到设备B也是一个概率事件,也就是说,设备A有时会很快扫到设备B,比如只需要一个广播事件,有时又会很慢才能扫到设备B,比如需要10个广播事件甚至更多。具体见下方扫描说明。
②扫描请求
扫描请求:由链路层处于扫描态的设备发送,链路层处于广播态的设备接收。
从目前的介绍来看,似乎广播过程中广播者只发不收,扫描过程中扫描者只收不发。事实上,扫描行为分为以下两种:
被动扫描 (Passive Scanning):这种情况,扫描者不发送任何信息,只监听广播数据,扫描者能收到广播,广播者并不知道扫描设备的存在:

主动扫描 (Active Scanning):扫描者在接收广播数据包以后,还向可扫描广播者发送扫描请求 (Scan Request),广播者在接收到扫描请求之后,会广播扫描响应 (Scan Response) 数据包,以向感兴趣的扫描者发送更多的广播信息。

扫描响应数据包的结构与广播数据包完全一致,区别在于 PDU 头中的 PDU 类型不同。处于扫描态的设备可以接收广播信道的报文,通过扫描可以侦听哪些设备正在广播。扫描分为主动扫描和被动扫描。主动扫描发送扫描请求给处于广播态的设备,并通过处于广播态的设备返回的扫描响应获取额外的数据。而被动扫描仅仅接收广播报文,不会发送扫描请求。扫描事件交替地发生在三个特定的广播信道:37,38,39,即第一次开始扫描是在 37 信道,第二次开始扫描是在 38 信道,第三次开始扫描是在 39 信道,第四次开始扫描是在 37 信道……扫描还有 2 个重要的参数:

扫描窗口(scan window):每次扫描事件的持续时间。
扫描间隔(scan interval):两个连续的扫描窗口的起始时间之间的时间差,包括扫描休息的时间和扫描进行的时间。当然,如果扫描窗口=扫描间隔,则设备会一直扫描,没有间隔。
advertiser 和 scanner之间不存在任何同步方式,只有广播和扫描所在的通道随机重合时,双方才能发现。对于广播和扫描而言,功耗主要消耗在发送或者接收数据期间,因此,广播间隔和扫描间隔对功耗有很大影响,不同的应用应根据实际设置合适的间隔。

扫描请求报文的内容与广播报文类似,不同的PDU载荷内容由ScanA(扫描设备地址)和AdvA组成(广播设备地址),ScanA是扫描设备的公共或随机地址(由TxAdd确定),AdvA是广播设备的公共或随机地址(由RxAdd确定)。当然,如此多的广播包,需要过滤扫描结果,可以通过ScanFilter类指定设备mac地址或广播包中如服务UUID等信息过滤。
③扫描响应
扫描响应:由链路层处于广播态的设备发送,链路层处于扫描态的设备接收。扫描响应报文的内容与广播报文类似,不同的PDU载荷由AdvA(广播设备地址)和ScanRspData组成(扫描响应数据),AdvA是广播设备的公共或随机地址(由TxAdd确定)。
综上,广播与扫描过程简单描述如下:
设备(Service)–>>手机(client): 定时向周围发送广播包(Advertising Data)
手机(client)->>设备(Service): 当手机接收到广播包的时候,发送扫描请求(Advertising Scan Request)
设备(Service)–>>手机(client): 返回广播的数据即扫描响应(Scan Response Data)。这样手机就知道这是一个可连接的设备。
④连接请求
当接到广播数据后,scanner可以发起建立连接请求,建立连接请求的数据(CONNECT_REQ)是在广播通道上发起的。CONNECT_REQ请求中会带上重要的连接参数,包括连接时间间隔,supervision timeout,以及和跳频相关的参数,并且在建立连接后,切换到数据通道上交互数据。在BLE连接中,采用了先进的跳频方案,使得两个设备能够在预设的特定时间和频道上精确地进行数据的发送与接收。随后,这些设备会在链路层的精心调度下,切换到新的通信频道,并在约定的时间点再次相遇,这一过程被形象地称为“连接事件”。
建立连接请求:设备A在发现设备B后,会向设备B发送连接请求。这个连接请求通常是通过发送一个连接请求包来实现的,其中包含了连接参数、连接请求地址等信息。
响应连接请求:设备B收到连接请求后,可以选择接受或拒绝连接。如果设备B接受连接请求,它会发送一个连接响应包给设备A;如果拒绝连接,则不发送连接响应包。
接下来如果有的话完成交换安全密钥(配对)和保存密钥方便快速认证(绑定)过程,整个过程完成后即连接建立。
根据蓝牙spec规定,advertiser发送完一个广播包之后150us(T_IFS),advertiser必须开启一段时间的射频Rx窗口,以接收来自observer的数据包。Observer就可以在这段时间里给advertiser发送连接请求。详细的连接请求与响应过程如下图所示。

注:图中M代表手机,S代表设备B,M->S表示手机将数据包发给设备B,即手机开启Tx窗口,设备B开启Rx窗口;S->M正好相反,表示设备B将数据包发给手机,即设备B开启Tx窗口,手机开启Rx窗口。
如图所示,手机在收到A1广播包ADV_IND后,以此为初始锚点(这个锚点不是连接的锚点),T_IFS后给Advertiser发送一个connection request命令,即A2数据包,告诉advertiser我将要过来连你,请做好准备。Advertiser根据connect_req命令信息做好接收准备,connect_req包含Transmit window offset,Transmit window size等关键信息。connect_req其实是在告诉advertiser,手机将在Transmit Window期间发送第一个同步包(P1)给你,请在这段时间里把你的射频接收窗口打开。设备B收到P1后,T_IFS时间后将给手机回复数据包P2。一旦手机收到数据包P2,连接即可认为建立成功。后续手机将以P1为锚点(原点),Connection Interval为周期,周期性地给设备B发送Packet,Packet除了充当数据传送功能,它还有如下两个非常重要的功能:
同步手机和设备的时钟,也就是说,设备每收到手机发来的一个包,都会把自己的时序原点重新设置,以跟手机同步。
告诉设备你现在可以传数据给我了。连接成功后,BLE通信将变成主从模式,因此把连接发起者(手机)称为Master或者Central,把被连接者(之前的Advertiser)称为Slave或者Peripheral。BLE通信之所以为主从模式,是因为Slave不能“随性”给Master发信息,它只有等到Master给它发了一个packet后,然后才能把自己的数据回传给Master。
连接成功后,master和slave在每一个connection interval开始的时候,都必须交互一次,即master给slave发一个包,slave再给master发一个包,整个交互过程称为一个connection event,即主设备和从设备之间的一次相互发送数据包的过程称为一个连接事件。连接间隔是指两个连续的连接事件开始处的时间距离,可以是 7.5ms~4s 内的任意值,但必须为 1.25ms 的整数倍。蓝牙芯片只有在connection event期间才把射频模块打开,此时功耗比较高,其余时间蓝牙芯片都是处于idle状态的,因此蓝牙芯片平均功耗就非常低。每个连接事件中,都需要由Master发起包,再由Slave回复。在BLE的两个设备的连接中使用跳频机制。两个设备使用特定的信道发送和接收数据,然后过一段时间后再使用新的信道(BLE协议栈的链路层处理信道的切换)。尽管没有应用数据被发送和接收,两个设备仍旧会交换链路层数据(空包 Empty PDU)来维持连接。连接后,如果双方长时间无法通信(就是连接事件没有响应,可能因为距离太远导致没信号或有一方突然断电),蓝牙就会认为连接已断开。监控超时的含义是判断连接断开的无数据通信的最大时间间隔。监控超时必须大于有效连接间隔。一般建议至少给从设备留出 6 次侦听的机会,就是建议监控超时大于 6 倍的有效连接间隔。
举例,设备 A 和设备 B 正保持着蓝牙连接,监控超时参数是 10s,突然设备 A 断电了(实际上这时候蓝牙连接就已经断开了),但设备 B 不知道设备 A 发生了什么事,直到 10s 后,因双方的多次连接事件没响应,设备 B 才认为连接断开了。
⑤数据收发
蓝牙连接后,双方的通信模型是服务器–客户端。服务器上有一个属性数据库,存有各种类型的数据。一般通信过程是,客户端发起请求,服务器做出对应的响应。客户端发送数据给服务器的方式是请求更改服务器的数据库内容。服务器发送数据给客户端的方式有 2 种,一是,服务器事先把数据写到自身数据库中,然后等待客户端请求读取数据库的内容;二是,服务器有某些权限,当把数据写到自身数据库时,可以主动地数据发送给客户端。BLE一般而言设备提供服务,因此设备是server,手机使用设备提供的服务,因此手机是client。比如蓝牙体温计,它可以提供 “体温” 数据服务,因此是一个server,而手机则可以请求“体温”数据以显示在手机上,因此手机是一个client。服务是以数据为载体的,所以说server提供服务其实就是提供各种有价值的数据。
一个设备可以同时作为客户端和服务器。就是说主机可以是客户端也可以是服务器,从机亦然。之后就是数据收发过程,在BLE协议栈中进行数据发送分为两个方面,一个是GATT的client主动向service发送数据,另一个是GATT的server主动向client发送数据。服务器上的数据库是由一条一条的attribute组成的,见1.1.4 att协议。连接完成之后的认证与加密通信阶段见1.2章节。
现在我们看看连接状态下是如何把数据0x53发送出去的,从中大家可以体会到蓝牙协议栈分层的妙处。
假设有设备A和设备B,设备A要把自己目前的电量状态83%(十六进制表示为0x53)发给设备B,该怎么做呢?作为一个开发者,他希望越简单越好,对他而言,他希望调用一个简单的API就能完成这件事,比如send(0x53),实际上我们的BLE协议栈就是这样设计的,开发者只需调用send(0x53)就可以把数据发送出去了,其余的事情BLE协议栈帮你搞定。很多人会想,BLE协议栈是不是直接在物理层就把0x53发出去,就如下图所示:

这种方式初看起来挺美的,但由于很多细节没有考虑到,实际是不可行的。首先,它没有考虑用哪一个射频信道来进行传输,在不更改API的情况下,我们只能对协议栈进行分层,为此引入LL层,开发者还是调用send(0x53),send(0x53)再调用send_LL(0x53,2402M)(注:2402M为信道频率)。这里还有一个问题,设备B怎么知道这个数据包是发给自己的还是其他人的,为此BLE引入access address概念,用来指明接收者身份,其中,0x8E89BED6这个access address比较特殊,它表示要发给周边所有设备,即广播。如果你要一对一的进行通信(BLE协议将其称为连接),即设备A的数据包只能设备B接收,同样设备B的数据包只能设备A接收,那么就必须生成一个独特的随机access address以标识设备A和设备B两者之间的连接。由于设备B可以同时接收到很多设备的广播,因此数据包还必须包含设备A的device address以确认该广播包来自设备A,LL层还要检查数据的完整性,即数据在传输过程中有没有发生窜改,为此引入CRC24对数据包进行检验。同时设备B什么时候开启射频窗口以接收空中数据包?所以LL层还必须定义通信时序。当设备B拿到数据0x53后,该如何解析这个数据呢?它到底表示湿度还是电量,还是别的意思?这个就是GAP层要做的工作,即对有效数据进行解析。
有了PHY,LL和GAP,就可以发送广播包了,但广播包携带的信息极其有限,而且还有如下几大限制:无法进行一对一双向通信 (广播是一对多通信,而且是单方向的通信);由于不支持组包和拆包,因此无法传输大数据;通信不可靠及效率低下。广播信道不能太多,否则将导致扫描端效率低下。为此,BLE只使用37(2402MHz) /38(2426MHz) /39(2480MHz)三个信道进行广播和扫描,因此广播不支持跳频。由于广播是一对多的,所以广播也无法支持ACK。这些都使广播通信变得不可靠。扫描端功耗高。由于扫描端不知道设备端何时广播,也不知道设备端选用哪个频道进行广播,扫描端只能拉长扫描窗口时间,并同时对37/38/39三个通道进行扫描,这样功耗就会比较高。
而连接则可以很好解决上述问题,连接实际是让2个设备有共同的通信媒介,并让两者时钟同步起来。蓝牙连接有何尝不是这个道理,所谓设备A和设备B建立蓝牙连接,就是指设备A和设备B两者一对一“同步”成功,其具体包含以下几方面:
设备A和设备B对接下来要使用的物理信道达成一致
设备A和设备B双方建立一个共同的时间锚点,也就是说,把双方的时间原点变成同一个点
设备A和设备B两者时钟同步成功,即双方都知道对方什么时候发送数据包什么时候接收数据包。

如上图所示,一旦设备A和设备B连接成功(此种情况下,我们把设备A称为Master或者Central,把设备B称为Slave或者Peripheral),设备A将周期性以CI(connection interval)为间隔向设备B发送数据包,而设备B也周期性地以CI为间隔打开射频接收窗口以接收设备A的数据包。同时按照蓝牙spec要求,设备B收到设备A数据包150us后,设备B切换到发送状态,把自己的数据发给设备A;设备A则切换到接收状态,接收设备B发过来的数据。由此可见,连接状态下,设备A和设备B的射频发送和接收窗口都是周期性地有计划地开和关,从而大大降低系统功耗并大大提高系统效率。
现在我们再看看连接状态下是如何把数据0x53发送出去的,从中大家可以体会到蓝牙协议栈分层的妙处。以下是数据一层层封装的过程:
对开发者来说,很简单,他只需要调用send(0x53) ,即调用一个简单的API。
GATT层定义数据的类型和分组,方便起见,我们用0x0013表示电量这种数据类型,这样GATT层把数据打包成130053(小端模式:先看到的是低位,后看到的是高位)
ATT层用来选择具体的通信命令,比如读/写/notify/indicate等,这里选择notify命令0x1B,这样数据包变成了:1B130053
L2CAP用来指定connection interval(连接间隔),比如每10ms同步一次(CI不体现在数据包中),同时指定逻辑通道编号0004(表示ATT命令),最后把ATT数据长度0x0004加在包头,这样数据就变为:040004001B130053
LL层要做的工作很多,首先LL层需要指定用哪个物理信道进行传输(物理信道不体现在数据包中),然后再给此连接分配一个Access address(0x50655DAB)以标识此连接只为设备A和设备B直连服务,然后加上LL header和payload length字段,LL header标识此packet为数据packet,而不是control packet等,payload length为整个L2CAP字段的长度,最后加上CRC24字段,以保证整个packet的数据完整性。
我们最终是以应用为目的,使用蓝牙实际上还是使用蓝牙做一些数据的传输交互应用,介绍下重点的蓝牙开发过程中的必要协议内容。即便搜索了很多资料,蓝牙协议的专有名词定义相对其他很多物联网无线协议来说较为复杂,所以在这里尽可能将重点的部分作筛选和总结,以便我们对BLE协议栈有更深入的理解。
GAP(Generic Access Profile)
了解 GATT 之前,需要了解 GAP(Generic Access Profile),它在用来控制设备连接和广播。GAP 使你的设备被其他设备可见,并决定了你的设备是否可以或者怎样与其他设备进行交互。即若BLE设备之间要进行数据传输,就需要形成一个通信通道。GAP(通用访问配置文件)来负责这个通道的形成和维护。具体地说,它定义了设备如何广播、扫描、发现和建立连接,以及配置工作角色(Role)、可发现性、广播数据内容和安全相关的参数。从编程视角来看,GAP中的内容就像是一个config文件,BLE协议栈其他层的工作都要从GAP中获取初始化参数和配置信息。GAP中定义了一系列模式(Mode)和规程(Procedure),二者相互配合,完成广播和连接的工作。
①GAP角色有以下几种:
Central:中央设备 扫描广播数据和发送连接请求,进而建立连接
Peripheral:外围设备 可以发送广播数据和接收连接请求,进而建立连接
Broadcaster:广播者 可以发送广播和接收扫描请求,通常不建立连接
Observer:观察者 可以扫描广播和发起扫描请求,通常不建立连接
每个特定设备可以同时扮演一个或多个角色,规范对此没有任何限制。许多开发者错误地将BLE GATT客户端和服务器角色与GAP角色关联起来。实际上,它们之间没有任何连接,任何设备都可以是GATT客户端、服务器或两者,这取决于应用和情况。例如,考虑一个与智能手机配对的健康追踪器。健康追踪器的GAP角色是外围,当手机请求其传感器数据时,它扮演GATT服务器的角色。当它请求准确的时间数据从智能手机来更新内部时钟以进行数据时间戳时,它有时也会扮演GATT客户端的角色。GATT客户端/服务器角色完全取决于数据请求和响应事务的流向,而GAP角色在健康追踪器的外围角色和智能手机的中央角色中保持不变。
设备的可发现性指的是外设如何向其他设备广播自己的存在,并且其他设备可以或应该如何处理这些信息。不同的可发现模式和发现程序之间的差异涉及到广播和扫描是否实际进行,但也考虑到广播数据包中包含的数据的性质。更具体地说,广播数据中由SIG定义的可选字段Flags AD来管理设备的可发现模式。
②发现模式为外围设备设计提供了一定的灵活性,根据设计优先级(电池寿命、快速连接时间等)有三种模式,其中我们需要简单了解以下规程:
Central使用普通发现规程,则能够发现“受限发现模式”和“普通发现模式”的设备,不能发现“不可发现模式”设备。
Central使用受限发现规程,则仅能够发现“受限发现模式”的设备。
对于观察规程,则能发现各种发现模式下的广播设备。
不可发现模式:
不可发现意味着其他设备无法了解外围设备的存在或查询其性质。不可发现模式的广播数据与其他两种模式相同,所以其广播数据仍然能够被扫描设备正确读取,但由于没有设置相应的标志位,扫描设备在解析广播数据时应该尊重其不愿意被发现的意图,主动忽略该广播数据。处于此模式的设备仍然可以发送广播数据包。注意,这里不被发现是指不被执行“普通发现规程”的Central设备发现。如果Central设备执行观察规程(obverving procedure),仍然能够看到广播信号。而手机BLEAPP等软件默认为观察规程,所以假如使用手机BLE扫描APP来测试,依旧可以看到设为“不可发现模式”的广播设备。
有限发现模式:
此模式允许设备在有限时间内以较低的优先级处于可发现状态。处于此模式的设备将发送带有Flags AD字段中“有限发现”标志的广播数据包。执行有限发现过程的中心设备只能检测到处于此模式的设备。这里“受限”是指受时间限制。与普通发现模式相比,这个模式好像只是强制要求广播超时参数,其他相同。
普通发现模式:
此模式是默认模式,它没有时间限制。切换到此模式的设备表示其希望被中心对等设备发现,一般是为了建立连接。普通可发现模式是用的最多的模式,在该模式下,我们可以设置广播持续时间,最常用的场景是默认一直持续广播。
外围设备通常在与中心设备进行绑定之前处于可发现模式,但在初始绑定过程完成后进入不可发现模式,仅允许将来与该中心设备进行独占连接。在这种情况下,通常将其恢复到出厂默认设置可以使其再次进入可发现模式。
③建立连接GAP指示两台将要连接和通信的设备的角色,一台作为Central(一般为智能手机这种功能强大的设备),另一台作为Peripheral(一般指需要较少功率的设备)。设备之间建立连接的流程如下:
(1)当作为Peripheral的设备准备好连接时,它必须进入广播状态并在主广播通道(37、38和39)上广播数据包
(2)作为Central的设备必须转换到Scanning状态,并在这三个通道上监听广播包。
(3)当检测到广播包时,Central通过发送连接请求包来向Peripheral发起连接请求。其中,Central设备负责管理连接,可以直接决定连接的一些参数,其中一些参数为:
Connection Interval:连接的间隔时间
Peripheral Latency:指定Peripheral可以忽略多少的连接事件
Channel Map:指定将使用37个数据传输通道中的哪一个进行传输
Central决定这些参数,以连接请求包的方式将这些参数发送到Peripheral,此时就创建了连接。接着,Central将在一个Connection Interval后向Peripheral发送数据包。如果Peripheral接收到数据包并向Central返回一个数据包,则认为连接已经建立。

设备建立连接后,设备之间就可以进行双向数据传输。那么,数据传输到底是如何完成的呢?这就是ATT和GATT要做的事了。
ATT(Attribute Protocol)
ATT(Attribute Protocol)属性层是 GATT 和 GAP 的基础,它定义了BLE协议栈上层的数据结构和组织方式。ATT把services和characteristic以及相关的数据保存在一张简单的查找表中,该表使用16-bit的id作为索引。ATT协议定义了一些角色,每个角色都有它特定的功能。这些角色都是基于Client-Server架构的。
服务器(Server):作为一个数据库,存储可被客户端读写的数据
客户端(Client):请求服务器上的数据
作为服务端时,ATT协议负责为服务端设备提供客户端能读写的指定格式化的数据,以及客户端访问、写入和读取数据的机制。
1.Attribute构成
站在蓝牙协议栈角度来看,ATT层定义了一个通信的基本框架,数据的基本结构,以及通信的指令。ATT协议定义了一个叫attribute的数据结构。Attribute 是属于 ATT属性层的东西,它是 ATT层 的核心。Attribute其实就是一条一条的数据。每个蓝牙设备就是用来提供服务的,而服务就是众多数据的合集,这个合集可以称为数据库,数据库里面每个条目都是一个attribute。Attribute 由以下 4 部分组成:

①属性句柄(Attribute Handler):用于给客户端查找一个服务端attribute的uint16_t类型的唯一标识符,也就是给attribute一个“地址”。犹如指向属性实体的指针,对端设备可通过属性句柄来访问该属性,它是一个2字节长度的十六进制码,起始于0x0001,在系统初始化时候,各个属性的句柄逐步加一,最大不超过0xFFFF。
②属性类型(AttributeType):作用是用以区分当前属性是服务项或是特征值等,它用UUID来表示。
UUID(universally uniqueidentifier,通用唯一识别码)是一个软件构建标准,全局唯一的2或16字节的UUID标识符,定义储存于Attribute Value中的数值的具体类型和含义,即说明属性值代表什么类型的数据。
UUID分为两种:Service UUID和Characteristic UUID。在Bluetooth SIG中,把UUID的范围(0x0000 - 0xFFFF)划分为若干个区域,即把attribute type类型分为几种类型,详见《16-bit UUID Numbers Document》。
1.GATT Declaration: 0x2800 - 0x2803, 声明类型;
2.GATT Descriptor:0x2900 - 0x290F,描述类型;
3.GATT Service: 0x1800 - 0x1852,服务类型;
4.GATT Unit: 0x2700 - 0x27C5,单位类型;
5.GATT Characteristic and Object Type: 0x2A00 - 0x2BD9,特性值类型;
说明:
1)1,2,3,4类型attribute type为功能型UUID,通过这些UUID完成某种功能,比如要添加一个描述,可以使用0x2900开始的UUID,他对应的值就描述的内容;
2)5类型attribute type为数据型UUID,通过这些UUID实现短数据通信,比如要添加物理量星期,可以使用0xFF00开始的UUID,他对应的值就是星期,手机端和设备端就是通过这个UUID实现数据通信;
UUID模板为:0000XXXX-0000-1000-8000-00805F9B34FB,从左数第3、4个字节“XXXX”就是变化位,其他为固定位。如:UUID=0x2A00在系统内部会转换成00002A00-0000-1000-8000-00805F9B34FB。UUID是16个字节的字符串,为什么这里只使用了2字节?因为这些是常用的UUID,为了减少传输的数据量,BLE协议做了一个转换约定,给定一个固定的16字节模板,只设置2个字节为变化量,其他为常量,2字节的UUID在系统内部会被替换,进而转换成标准的16字节UUID。
③属性值(Attribute Value):表示设备公开的信息,也是用于通信的数据,具体的含义由属性类型决定。
④属性权限(Attribute Permissions):它表示访问Attribute Value的权限,或者说设置一个属性的安全等级,作用是保护Attribute Value。
2.Attribute访问方式
ATT协议还定义了读写属性的方式。具体的方式取决于发起属性访问过程的是客户端还是服务端。
①客户端发起属性访问,有两种操作——Write和Read
客户端使用Read从服务器读取属性的值,服务器响应属性的值。
客户端使用Write将一个属性的值写入服务器,服务器响应写操作是否成功。
②服务端发起属性访问,有两种操作——Notification和Indication
Notification:当Attribute发生改变时,服务端使用该种方式向客户端发送更新后的属性值,客户端收到后不响应此操作。
Indication:与Notification类似,但是客户端必须发送是否正确收到该Attribute的响应。
GATT(Generic Attribute Profile)
GATT层就是前文所述的service和characteristic,GATT层用来赋予每个数据一个具体的内涵,让数据变得有结构和意义。GATT 是脱胎于ATT, ATT是协议,而GATT是规范,两者不同点在于 ATT规定了数据格式,而 GATT则是按照 ATT 的格式将具体数据填充进去。GATT封装了ATT,主要用来规范attribute中的数据内容,并将不同的attribute进行分组分类。负责协调蓝牙LE链路中配置文件的交换,配置文件包括诸如handle分配、UUID和权限等信息和数据。
现在低功耗蓝牙(BLE)连接都是建立在 GATT协议之上。GATT 是一个在蓝牙连接之上的发送和接收很短的数据段的通用规范,这些很短的数据段被称为属性(Attribute)。GATT定义两个 BLE 设备通过叫做 Service 和 Characteristic的东西进行通信。attribute是一条一条的数据,那么这条数据表示什么?如何对其进行分类?这就是GATT要做的事情,GATT将对数据赋予含义,并呈现一定的逻辑结构。对于我们的应用来说,蓝牙无非是用来进行数据交互,我们直接从应用说起,我们使用手机和开发板进行蓝牙通讯,对应的 手机就是 Client 设备,蓝牙开发板就是 Server设备。我们从 BLE通信的这两种设备入手 :Client 和 Server。
对于 Client 和 Server 的通讯,先来看一张图:

从上面的示意图可以看出,对于 GATT server 有很多分支,可想而知对于他的协议规定会有很多专有名词,我们的应用程序也大多是对 GATT server 这些参数进行修改配置。
其中Service 可组成一个叫 profile 的概念。蓝牙官方文档中对于 Profile 说明图:


服务和特性创建
蓝牙设备要在进入广播态之前创建服务和特性。比如我们要创建一个UUID为9011的服务,该服务里面包含两个特性;这两个特性的UUID分别是9012和9013;9012的特性拥有Read和Write的权限,9013的特性拥有Read和Notify的权限。

低功耗蓝牙之间的数据交互都是基于特性,以手机连接蓝牙模块为例,手机读取蓝牙模块的数据,使用的是Read方法,手机发送数据给蓝牙模块使用的是Write方法,蓝牙模块如果想要主动发送数据到手机,一般使用的是Notify方法,而且手机端还要打开Notify监听。
对于里面的一些基本概念有必要再简单说明一下。
①Service
使用蓝牙对外提供服务的设备,需要有对应的服务功能,如蓝牙耳机,他需要提供音频播放的功能,这种具体的功能,就是蓝牙服务。这Service可以理解为一个服务,提供数据的服务,比如从机设备,但是在一个 Ble 从机中,可以有多个服务。Server 和 Client 通过ATT PDU进行交互。Server通过characteristic对数据进行封装,每个service中又包含多个characteristic特征值。在服务器的数据库中,基本元素是属性,按照特定顺序排列的几条属性,可以定义一个服务,服务里包含有特性。BLE 的通信就是基于服务里的特性的读写等操作。
简单来说,服务(Service) 可以理解一个房间,当这个房间为空的时候他什么也不是,不能提供任何的服务功能,所以,房间里面至少需要有一个或多个家具,不同的家具有不同的功能,这就是特性(Characteristic)。不同的服务(Service)应该有不同的编号(UUID),用以区分不同的服务(Service)。就像我们有卧室、厨房、书房等不同的房间,为了统一,我们给这些不同功能的房间定义了各自的编号,我们假定卧室编号为0001,不管是哪家公司修建的卧室,都必须使用0001来表示这是卧室,当客人需要休息时,他只需要找到编号为0001的房间就行了。这样,不管是用英文、法文还是德语来给房间命名,我们都知道他是卧室。
②characteristic
特性(Characteristic) 是依附于某个服务(Service)的,就像前面说到卧室里的床,卧室并不能让我们睡觉休息,真正让我们睡觉休息的是床。在卧室里,我们除了放床,通常还可以放梳妆台、衣柜等相关的家具,每样家具可以提供与之相关的不同功能。同样,我们需要给每样家具分配一个编号,这就是特性(Characteristic)的UUID。我们知道,每种家具会有一个或多个不同的子功能,这个子功能就是特性(Characteristic)所包含的属性(Property) 。例如床单可以更换、床的高低可以调整。
上面讲到: Server通过characteristic对数据进行封装,每个service中又包含多个 characteristic特征值。ble主从机的通信均是通过characteristic来实现,可以理解为一个标签,通过这个标签可以获取或者写入想要的内容。一个characteristic包含三种条目:characteristic声明,characteristic的值以及characteristic的描述符。Characteristic是一个基本的存储单元,即一个attribute类型。
这3个条目需要说明的是它们是用attribute来表达的,它们每一个都是一个 attribute 。Characteristic是在GATT规范中最小的逻辑数据单元,由一个Value和多个描述特性的Desciptior组成。Properties决定了Value的访问方式,可以是多个值的组合,比如可以是(read, write)或者(Notify)等。实际上,在与蓝牙设备打交道,主要就是读写Characteristic的value来完成。
Chracteristic Declaration Chracteristic Value


Characteristic descriptor

蓝牙的数据交互都是基于一个个特性进行的,数据交互的方式主要有5种,分别是Read,Write,Write WithOutResponse,Notify,Indication。
再看一个例子:
属性句柄 属性类型 权限 属性值
39 0x2800 read FA:FF
40 0x2803 read 10:29:00:FE:FF
41 0xFFFE ntf ……
42 0x2902 read and write 0:00
43 0x2803 read 08:2c:00:FD:FF
44 0xFFFD write ……
45 0x2800 read f0:FF
里面定义了服务 0xFFFA,服务里的特性有 0xFFFD 和 0xFFFE,特性 0xFFFE 具有通知的权限,特性 0xFFFD 具有写的权限。客户端可以通过写特性 0xFFFD 的属性值,来把数据发送给服务器;服务器可以通过通知特性 0xFFFE 来把数据发送给客户端。
一般服务器不能主动发数据给客户端,都是客户端主动来读写服务器上的属性。服务器要主动发数据给客户端时,特性值就得有通知或指示的权限。首先客户端需要使能服务器上特性值的通知或指示,然后服务器在更新该特性值的数据时,就会主动把数据发给客户端。
通知和指示的区别,它们都是由服务器自发地向客户端发送属性值。通知可以在任意时刻发送;指示需要客户端回复确认信息,服务器收到确认后才发下一条指示。
总结一下,主机如果想主动发起数据传输,有两种方式:读和写。
读(Read):主机通过“读”可以读取从机的特征值和特征描述符,从而获取从机的数据。
在BLE通信中,有两种常用的写操作方式:Ble Write(带回复的写操作)和Write No Response(无回复的写操作)。
Ble Write(带回复的写操作):当一个BLE设备(称为客户端)想要向另一个BLE设备(称为服务器)发送数据时,可以使用Ble Write操作。客户端向服务器发送数据并等待服务器发送确认回复(Acknowledgment)来表示数据已经被成功接收。这种写操作是一种可靠的方式,确保数据传输的可靠性。
Write No Response(无回复的写操作):在某些情况下,客户端发送的数据并不需要服务器的确认回复,或者在时间上要求更加紧凑的传输。这时可以使用Write No Response操作。客户端向服务器发送数据后,并不会等待服务器的确认回复。这种写操作通常用于实时传输等不需要确认的数据,以减少通信延迟和增加通信吞吐量。
需要注意的是,Write No Response操作在数据传输过程中不提供任何保障机制,例如数据的可靠性、顺序性或幂等性等。因此,使用Write No Response操作时需要确保应用场景的需求和通信的可靠性。
从机如果向主动发起数据传输,只能通过两种方式:通知(Notify)和指示(Indicate)。
通知(Notify):从机发送通知后,不会关机主机有没有接收到,通知属于不可靠消息。通知需要使用客户端配置扫描符(CCCD)配置(CCCD使能/关闭通知)。
指示(Indicate):从机发送指示后,必须得到主机的应答才能发送下一条指示,指示属于可靠消息。指示同样需要使用客户端配置描述符(CCCD)配置(CCCD使能/关闭指示)。
③profile
蓝牙 Profile 是一组预定义的协议和规则,用于实现特定应用功能(如音频传输、文件共享或设备控制)。每个 Profile 对应一个具体的使用场景,并规定了设备在连接、数据传输和状态管理中的行为。Profile 可以被视为蓝牙协议栈的“应用层协议”,它定义了设备角色(如服务器或客户端)、消息格式和交互流程。例如,在音频传输场景中,A2DP Profile 会指定如何编码和传输音频数据3。
Profile 的主要目的是简化蓝牙设备的开发并确保兼容性。由于蓝牙规范庞大而复杂(包含底层射频、基带等协议),设备制造商不需要实现所有部分;Profile 允许设备只选择与自身功能相关的协议子集,从而降低成本和开发难度。这保证了不同品牌设备(如耳机和手机)能无缝协作,提升用户体验。
蓝牙规范非常复杂和庞大,大部分蓝牙设备只实现了蓝牙规范中很少一部分,那么没有实现的这些规范对这个蓝牙设备来说能不能称为规范?答案是当然不能。所谓规范或者规格,就是强制的,就必须实现。针对这种情况,profile可以很好地应对。我们把蓝牙某部分规范称为profile,这个profile如果设备要实现它,那么它就是强制的;如果设备不用它,也没关系,这就是profile。基于此,我们可以把profile翻译成子规范或者条件规范或者剖面规范。
如果上面说的 service 是一个蓝牙联盟定义的标准服务,也可以称其为profile。profile 可以理解为一种规范,一个标准的通信协议,它存在于从机中。基础Profile就有GAP(Generic Access Profile),GATT(Generic Attribute Profile)。手机首先通过GAP发现设备并建立物理连接,然后通过GATT发现服务/特征值进行数据交互。就像去图书馆,GAP是找到图书馆位置并进门,GATT是查书目和借书的具体规则。GAP 是连接层的 Profile,负责设备可见性与链路管理;GATT 是数据层的 Profile,负责结构化数据传输。二者共同构成 BLE 通信的基础。
蓝牙组织规定了一些标准的profile,比如HID/心率计/体温计/血糖仪等,都是标准蓝牙服务,因此都有相应的profile规格书。每个从机都会有一个叫做profile的东西存在,不管是的自定义的simpleprofile,还是标准的心率计profile。每个profile中会包含多个service,每个service代表从机的一种能力。
我们上面讲了很多关于蓝牙 GATT 的概念知识,接下来,介绍一些 GATT server 示例,来方便我们理解蓝牙协议栈。此处,蓝牙开发板就是一个 Server 设备,我们的手机就是 Client 设备。
当我们连接上以后,其实可以查看 Server 的信息的,为了加深对上面的概念的理解,我们使用手机连接后的图片来说明一些概念:

我们的蓝牙应用的最终目的还是数据的交互,数据收发,与蓝牙设备的数据交互,可以认为就是对 Characteristc 进行读写即可达到与其通信的目的。
其他
1.1 BLE
低功耗蓝牙(Bluetooth Low Energy,BLE,也被推广名称为Bluetooth Smart。注:本书关于术语翻译尽可能都使用英文缩写代替)被作为蓝牙4.0核心规格书的一部分开始。相对于它的老大哥——经典蓝牙,很吸引人的是BLE是一个更灵活、高度优化的版本。但实际上,BLE有着完全不同的血统和设计目标。
广播和观察
第一章和第二章介绍了BLE中隔空数据交互的基本概念,在这里可以值得简要地回顾一番。广播包无方向地按照固定间隔时间盲目发送,这建立了广播(broadcasting)(和观察 observing)和发现(discovery)的基础。一个正扫描广播包的设备可能收到一个正处于传输中的广播包,这可能仅仅时接收数据,或者开始发起连接。连接(Connections) ,另一方面来说,要求两端在定期地时间间隔下同步完成数据交互,并在数据传输和吞吐中提供保证。
广播和观察
广播和观察(Broadcast and Observation)。GAP中定义的广播模式和观察流程建立了一套设备可以无方向地发送数据的框架,即作为一个*(广播者 broadcaster)* 发送给一个或多个活动中的处于监听的端设备(观察者 observers)。很重要一点是,广播者没有办法了解数据是否到达任何观察者,因此模式和流程的结合对这术语保持了可信度:广播者无需任何确认(confirmation)或应答(acknowledgement)即可广播数据,观察者监听(暂时或者无限期)潜在的广播者也无需任何接收的数据的保证。
广播者发送的广播包包含了实际有效的用户数据,包含了链接层插入的一些元素据信息(metadata, 如蓝牙设备地址)。第二章的广播和扫描有进行描述,每一个广播包都包含至多31字节的数据(实际可用的用户数据长度将会更低,因为有包头和格式的耗用),但这一点也不一定,当通过使用扫描请求/扫描回应交互,在成功接收到观察者的广播包之后,是可产生至多每个广播事件62字节的数据。因为一个扫描回应包仅由观察者的请求而发送,最要紧的数据总是被放置到广播包自身,而不是在扫描回应包。广播者可以发送ADV_NONCONN_IND 或 ADV_SCAN_IND 广播包(见第二章表2-1)。
通过创建一个仅广播者的设备,你可以简单地将数据广播到外界,任何处于监听范围内的设备都可以接收,无论是一台还是一百台设备。这一点明显对比外围设备,其在建立一个连接之后就停止了自身的广播,并有效关闭了对监听范围内的其他中心设备的连接通道,直到该连接关闭,或者一种少见的情况,设备作为从机可以支持大量连接,直到一个另外的连接被创建。
如苹果的iBeacon(请见第九章iBeacon)使用广播模式不停向外发送一个在广播数据中的厂商指定数据区域的负载数据,以允许任何监听范围内的设备无需完全访问即可发现iBeacon设备。iBeacon结点无需担心多少设备正在监听,它只需要保持告知外界设备在这里,并交互有限的负载数据给那些关注监听的设备。
发现
设备的可发现性(discoverability)指外围设备如何向其他设备广播自身的存在,以及那些设备可以或者应该怎样处理这些信息。 不同可发现模式的区别与发现流程是关系到广播和扫描是否实际上执行完成并且考虑到广播包内数据的属性。更准确地说,一个SIG定义的广播数据的可选区域,称为Flags AD 掌控了设备的可发现模式(见表3-3)。
这些模式仅被外围设备使用,这使得中心设备在自己的监听范围内可以发现这些外围设备。发现(Discovery)大体上指监测周围出现的设备以及其基本信息。 这并不意味着有必要建立一个连接或者数据交互,尽管大部分情况是这样。在某些情况下,尤其是中心设备装有用户可见的显示屏幕, 发现功能就会被用作产生一组附近的设备从而用户可以选择进行连接。
AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。
更多推荐



所有评论(0)