BLE是低功耗蓝牙的英文缩写Bluetooth Low Energy是蓝牙4.0版本起开始支持的新的、低功耗版本的蓝牙技术规范。蓝牙技术联盟Bluetooth SIG在2010年发布了跨时代的蓝牙4.0它并不是蓝牙3.0的简单升级版本而是全新的技术架构蓝牙4.0版本分两种模式单模蓝牙和双模蓝牙。常见的蓝牙音箱是典型的双模蓝牙它需要传输大量的音频数据。而小米手环蓝牙温度计则属于单模蓝牙。行业里一般不讲单模蓝牙而是统一称为低功耗蓝牙。如今蓝牙5.0已经发布和应用4倍通信速度、2倍的通信距离以及Mesh组网特性将使蓝牙成为物联网领域的重要的技术之一。本文我们将由表及里由浅入深全方位的揭秘低功耗蓝牙技术。1 蓝牙简介蓝牙是一种近距离无线通信技术运行在2.4GHz免费频段目前已大量应用于各种移动终端物联网健康医疗智能家居等行业。蓝牙4.0以后的版本分为两种模式单模蓝牙和双模蓝牙。单模蓝牙即低功耗蓝牙模式是蓝牙4.0中的重点技术低功耗快连接长距离。双模蓝牙支持低功耗蓝牙的同时还兼容经典蓝牙经典蓝牙的特点是大数据高速率例如音频、视频等数据传输。如下图所示双模蓝牙具有下图所有的特点而单模蓝牙仅如图右侧所示。经典蓝牙支持音频HFP/HSP, A2DP和数据SPP, HID等两大类协议在音箱耳机汽车电子及传统数传行业由于苹果对经典蓝牙数据传输接口有限制需要过MFI认证加上功耗偏大因此在目前移动互联应用中慢慢地被边缘化。因此低功耗蓝牙顺势而出由于可支持苹果4S以上及安卓4.3系统以上的数据传输且功耗极低目前正在被越来越多的移动互联设备所采用但低功耗蓝牙不支持音频协议并且受数据传输速度限制其应用也被限制在小数据传输行业。而蓝牙双模则是综合了两者的优缺点既可以支持音频传输同样可支持数据传输并且兼容性也是两者之和在对功耗要求不苛刻的情况下是比较理想的选择。2 BLE特点低功耗蓝牙瞄准多个市场特别是移动智能终端智能家居互联设备等领域主要特点包括低功耗使用纽扣电池就可以运行数月至数年。快连接毫秒级的连接速度传统蓝牙甚至长达数分钟。远距离长达数百米的通信距离而传统蓝牙通常10米左右。蓝牙联盟沿用经典蓝牙的规范内容为低功耗蓝牙定义了一些标准ProfileProfile理解为数据规范只要遵守该规范任意厂家的蓝牙设备均可以相互连接与通信例如无线蓝牙键盘鼠标无论是安卓或是iOS还是Windows均是即插即用这便是“标准”的力量。低功耗蓝牙支持的标准Profile有HID用于无线鼠标键盘或其他遥控设备。BatteryServices电池状态服务用于告知电池电量状态。HRP心率计Profile用于心率采集。等等。另外低功耗蓝牙还可以自定义Profile伴随着智能手机的发展和普及低功耗蓝牙的这个特性得到了发扬光大同时也拓宽了低功耗蓝牙的应用领域。例如可以自定义一个开关量的Profile数据01表示开灯数据00表示关灯然后手机发送数据01和00就可以控制灯的亮和灭。类似的应用案例有很多下面总结应用特点支持自定义Profile可以收发任意格式的数据如01和00支持自定义设备支持任意设备的连接和通信例如智能蓝牙插座等。提示低功耗蓝牙的Profile均基于GATT通用属性规范后面会详解之上如HID over GATT。也就是说经典蓝牙中的HID规范与低功耗蓝牙中的HID规范用的是两个不同的通道。3 BLE工作流程本节我们介绍低功耗蓝牙的基本行为状态和主从机交互过程为后面的低功耗蓝牙协议的学习准备基础。3.1 角色BLE设备角色主要分为两种角色主机Master或Central和从机Peripheral当主机和从机建立连接之后才能相互收发数据主机主机可以发起对从机的扫描连接。例如手机通常作为BLE的主机设备从机从机只能广播并等待主机的连接。例如智能手环是作为BLE的从机设备另外还有观察者Observer和广播者Broadcaster这两种角色不常使用但也十分有用例如iBeacon就可以使用广播者角色来做只需要广播特定内容即可。观察者观察者角色监听空中的广播事件和主机唯一的区别是不能发起连接只能持续扫描从机。广播者广播者可以持续广播信息和从机的唯一区别是不能被主机连接只能广播数据蓝牙协议栈没有限制设备的角色范围同一个BLE设备可以作为主机也可以作为从机我们称之为主从一体主从一体的好处是每个BLE设备都是对等的可以发起连接也可以被别人连接更加实用。3.2 广播广播是指从机每经过一个时间间隔发送一次广播数据包这个时间间隔称为广播间隔这个广播动作叫做广播事件只有当从机处于广播状态时主机才能发现该从机。在每个广播事件中广播包会分别在37,38和39三个信道上依次广播如下图所示。广播时间间隔的范围是从20ms到10.24s广播间隔影响建立连接的时间。广播间隔越大连接的时间越长。另外BLE链路层会在两个广播事件之间添加一个0~10ms的随机延时保证多个设备广播时不会一直碰撞广播。也就是说设置100ms的广播间隔实际上两次广播事件的时间间隔可能是100~110ms之间的任意时间。广播数据包最多能携带31个字节的数据一般包含可读的设备名称设备是否可连接等信息。当主机收到从机广播的数据包后它可以再发送获取更多数据包的请求这个时候从机将广播扫描回应数据包扫描回应数据包和广播包一样可以携带31个字节的数据。提示蓝牙4.x广播有效载荷最多是31个字节。而在蓝牙5.0中通过添加额外的广播信道和新的广播PDU将有效载荷增加到了255个字节3.3 扫描扫描是主机监听从机广播数据包和发送扫描请求的过程主机通过扫描可以获取到从机的广播包以及扫描回应数据包主机可以对已扫描到的从机设备发起连接请求从而连接从机设备并通信。扫描动作有两个比较重要的时间参数扫描窗口和扫描间隔如果扫描窗口等于扫描间隔那么主机将一直处于扫描状态之中持续监听从机广播包。被动扫描主机监听广播信道的数据当接收到广播包时协议栈将向上层也就是应用层用户可编程传递广播包。主动扫描主动扫描除了完成被动扫描的动作外还会向从机发送一个扫描请求从机收到该请求时会再次发送一个称作扫描回应的广播包。所以主动扫描比被动扫描可以多收到扫描回应数据包。3.4 连接在BLE连接中使用跳频方案两个设备在特定时间、特定频道上彼此发送和接收数据。这些设备稍后在新的通道协议栈的链路层处理通道切换上通过这个约定的时间相遇。这次用于收发数据的相遇称为连接事件。如果没有要发送或接收的应用数据则交换链路层数据来维护连接。两个连接事件之间的时间跨度称为连接间隔是以1.25 ms为单位范围从最小值7.5 ms到最大值4.0 s3.4.1 连接参数Connection Interval连接间隔两次连接事件之间的时间间隔称为连接间隔。1.25 ms为单位范围从最小值7.5 ms到最大值4.0 sSlave Latency从机延迟如果从机没有要发送的数据则可以跳过连接事件继续保持睡眠节省电量。Supervision Time-out监控超时是两次成功连接事件之间的最长时间。如果在此时间内没有成功的连接事件设备将终止连接并返回到未连接状态。该参数值以10 ms为单位监控超时值可以从最小值10100 ms到320032.0 s。超时必须大于有效的连接间隔。3.4.2 连接参数更新请求连接参数由主机发起连接的时候提供如果从机对连接参数有自己的要求例如要求更低的功耗或者更高的通信速率等从机可以向主机发送连接参数更新请求。从机可以在连接后的任何时候发起连接参数更新请求但最好不要在主从建立连接后立刻发起建议延迟5s左右再发送请求。连接参数更新请求可以修改Connection Interval连接间隔Slave Latency从机延迟Supervision Time-out监控超时。3.4.3 有效连接间隔Effective Connection Interval有效连接间隔等于两个连接事件之间的时间跨度假设从机跳过最大数量的连接事件且允许从机延迟如果从机延迟设置为0则有效连接间隔等于实际连接间隔。从机延迟表示可以跳过的最大事件数。该数字的范围可以从最小值0意味着不能跳过连接事件到最大值499。最大值不能使有效连接间隔见下列公式大于16秒。间隔可以使用以下公式计算Effective Connection Interval(Connection Interval)×(1[Slave Latency])Consider the following example:Connection Interval:80(100 ms)Slave Latency:4Effective Connection Interval:(100 ms)×(14)500 ms当没有数据从从机发送到主机时从机每500ms一个连接事件交互一次。3.4.4 iOS对连接参数的要求不同的平台对有连接间隔有着不同的要求例如iOS系统对ble的连接间隔有着如下的要求。Interval Max * (Slave Latency 1) 2sInterval Min 20msInterval Min 20 ms Interval MaxSlave Latency 4SupervisionTimeout 6 sInterval Max * ( Slave Latency 1) * 3 SupervisionTimeout3.4.5 连接参数的优化考量在许多应用中从机跳过最大连接事件数。选择正确的连接参数组在低功耗蓝牙设备的功率优化中起重要作用。以下列表给出了连接参数设置中权衡的总体概述。减少连接间隔如下增加两个设备的功耗增加双向吞吐量减少任一方向发送数据的时间增加连接间隔如下降低两个设备的功耗降低双向吞吐量增加任一方向发送数据的时间减少从机延迟或将其设置为零如下增加外围设备的功耗减少外围设备接收从中央设备发送的数据的时间增加从机延迟如下在周边没有数据发送期间可以降低外设的功耗到主机设备增加外设设备接收从主机设备发送的数据的时间3.5 通信通俗的说我们将从机具有的数据或者属性特征称之为ProfileProfile可翻译为配置文件。从机中添加Profile配置文件定义和存储Profile作为GATT的Server端主机作为GATT的Client端。Profile包含一个或者多个Service每个Service又包含一个或者多个Characteristic。主机可以发现和获取从机的Service和Characteristic然后与之通信。Characteristic是主从通信的最小单元。主机可主动向从机Write写入或Read读取数据。从机可主动向主机Notify通知数据。注意这里引用了服务 Service和特征值 Characteristic的概念。每个服务和特征值都有自己的唯一标识UUID标准UUID为128位蓝牙协议栈中一般采用16位也就是两个字节的UUID格式。一个从机设备包括一个或者多个服务一个服务中又可以包括一条或者多条特征值每个特征值都有自己的属性 Property属性的取值有可读 Read可写 Write以及通知 Notify。可读可写的字面意思容易理解表示该特征值可以被主机读取和写入数据而通知则表示从机可以主动向主机发送通知数据。这便是主从机之间两个典型的通信方式。下图是一个典型的从机设备该从机包含有一个Profile两个个Service和五个Characteristic。我们先来介绍这些特征值的作用然后介绍如何通过特征值通信。服务0x180A180A是蓝牙协议里标准的服务UUID用来描述设备信息 Device Information可以通过该服务来提供从机设备的相关说明例如硬件版本软件版本序列号等信息。这样主机就可以获取从机的设备信息。上图中我们添加了三个提供具体设备信息的特征值他们分别是特征值0x2A24描述产品型号Model Number String例如某智能锁的产品型号为“DSL-C07”。特征值0x2A25描述产品序列号Serial Number String例如某智能锁的产品序列号为“lkjl0016190502500269”特征值0x2A26描述产品固件版本号 Firmaware Revision String例如某智能锁的固件号为“2.7.2.0”上述特征值仅有Read属性因此主机只能读不能执行写操作。服务0xFFF0FFF0是我们自定义的服务UUID它包含两个特征值用来发送和接收数据。特征值0xFFF1自定义的数据发送通道具有Read和Write属性主机可以通过该特征值向从机发送数据至于发送的数据最大长度可以在Profile中配置。特征值0xFFF2自定义的数据接收通道具有Notify属性从机可以通过该特征值主动向主机发送数据。假设主机写特征值的协议栈函数原型为int GATT_WriteCharValue(uuid_t UUID, uint8 *pValue, uint8 len)假设从机发送通知的协议栈函数原型为int GATT_Notification(uuid_t UUID, uint8 *pValue, uint8 len)那么主机向从机发送Hello可以这样调用协议栈的函数GATT_WriteCharValue(0xFFF1,Hello,5)那么从机向主机发送1234可以这样调用协议栈的函数GATT_Notification(0xFFF2,1234,4)3.6 断开主机或从机都可以发起断开连接请求对方会收到该请求然后断开连接恢复连接前的状态。3.7 过程演示现在我们总结一下BLE的工作流程使用两个虚拟的BLE硬件来模拟主从机的交互过程。假设有两个BLE设备使用的是BLE261低功耗蓝牙模块假设已经下载了用于交互演示的功能固件一个是主机名称为BleCentral另一个是从机名称为BlePeripheral如下图所示。3.7.1步骤1上电初始化主机、从机上电后不分先后顺序首先进行协议栈初始化和相关功能调用如下图所示。主机设备主机初始化时需要设置设备类型设置用于扫描的相关参数初始化GATT等协议相关的参数。下一章节详细介绍何为GATT从机设备从机初始化时需要设置设备名称广播相关参数从机Profile等。从机一般会立即开启广播也可以等待一个事件来触发广播例如按键触发。3.7.2步骤2主机扫描从机按键按下触发主机扫描从机此时主机显示屏打印Scanning正在扫描。此刻的从机仍然处于广播状态。3.7.3 步骤3发现从机设备当主机扫描到从机时可以返回已扫描到的从机相关信息例如可以提取到下图中的从机设备名称从机MAC地址从机的RSSI信号值等数据。因此有些应用在从机的广播包或者扫描回应包中添加自定义字段这样就可以被主机通过扫描的方式拿到数据。3.7.4 步骤4发送连接请求当主机扫描到从机后通过MAC地址向从机发送连接请求。低功耗蓝牙的连接速度非常快100ms左右即可成功连接上。如果从机的广播比较大则会影响连接的速度。从机在未收到连接请求之前仍然处于自由的广播状态。3.7.5 步骤5成功连接从机当从机收到连接请求后双方成功建立连接此时双方的状态均变为已连接状态。然后主机可以调用协议栈提供的接口函数来获取从机的服务。3.7.6 步骤6获取从机服务获取从机服务通常是在连接成功后就立即执行的因为只有获取从机的服务后才能与其通信。下图是主机向从机发送获取服务的请求。此刻从机处于已连接状态。响应服务获取请求是在底层自动完成上层无需理会。3.7.7 步骤7成功获取服务如下图所示主机成功获取到从机的服务例如获取到UUID为0xFFF0的Services该Service有两个特征值分别是具有读写属性的0xFFF1以及具有通知属性的0xFFF2。读写属性是指主机可以读写该特征值的内容。而通知属性是指从机可以通过该特征值向主机发送数据。3.7.8 步骤8主机向从机发送数据主机通过特征值0xFFF1主动向从机发送自定义数据Hello当数据成功发送后主机状态变为数据已发送。从机将收到主机发来的数据从机状态变为收到数据。3.7.9 步骤9从机向主机发送数据从机可以通过Norify的方式主动向主机发送数据例如下图从机通过特征值0xFFF2发送了一条Notify通知数据内容为12343.7.10 步骤10发送断开请求主机和从机任何一方均可以发起断开连接的请求对方收到后状态将变为已断开。3.7.11 步骤11成功断开连接从机收到主机发来的断开请求此刻状态变为已断开。4 BLE协议栈BLE协议栈一般是指芯片厂家依据 Bluetooth SIG 发布的Bluetooth Core Specification核心协议的实现的代码固件并提供函数接口由芯片内部程序调用可实现上节BLE工作流程等相关功能。常见的协议栈有德州仪器 TI 的ble-stack和 Nordic 的SoftDevice。4.1 功能框图在本节中我们列举两家典型的蓝牙芯片厂家TI和Noridc来深入了解低功耗蓝牙协议栈。下图是TI的CC26系列芯片协议栈结构图下图是Nordic的nRF52系列芯片的协议栈结构图。4.2 协议栈结构从上节的两张协议栈功能框图中可以看出无论是哪个芯片厂商实现的BLE协议栈其结构都非常的相似均三个部分底层Controller中层Host顶层Application然后每一层又分成若干个子模块。我们现在由下而上逐层介绍。提示我们将位于顶层的应用层Application也归到协议栈中描述其实应用层Application不属于协议栈它是用来调用协议栈提供的接口然后实现蓝牙的功能。4.2.1 控制器ControllerPhysical Layer简称PHY物理层。PHY层用来指定BLE所用的无线频段调制解调方式和方法等。PHY层做得好不好直接决定整个BLE芯片的功耗灵敏度以及selectivity等射频指标。Link Layer简称LL链路层。LL层是整个BLE协议栈的核心也是BLE协议栈的难点和重点。像Nordic的BLE协议栈能同时支持20个link连接就是LL层的功劳。LL层要做的事情非常多比如具体选择哪个射频通道进行通信怎么识别空中数据包具体在哪个时间点把数据包发送出去怎么保证数据的完整性ACK如何接收如何进行重传以及如何对链路进行管理和控制等等。LL层只负责把数据发出去或者收回来对数据进行怎样的解析则交给上面的GAP或者ATTHost Controller Interface简称HCI。协议栈应用开发中我们会经常看到HCI的身影它对上Host提供Controller的功能接口所以称作Host Controller Interface。4.2.2 主控HostLogical Link Control Adaptation Protocol简称L2CAP。L2CAP对LL进行了一次简单封装LL只关心传输的数据本身L2CAP就要区分是加密通道还是普通通道同时还要对连接间隔进行管理。Attribute Protocol简称ATT。ATT层用来定义用户命令及命令操作的数据比如读取某个数据或者写某个数据。BLE协议栈中开发者接触最多的就是ATT。BLE引入了attribute概念用来描述一条一条的数据。Attribute除了定义数据同时定义该数据可以使用的ATT命令因此这一层被称为ATT层。Security Manager简称SM。SMP用来管理BLE连接的加密和安全的如何保证连接的安全性同时不影响用户的体验这些都是SMP要考虑的工作。Generic Access Profile简称GAP。GAP是对LL层payload有效数据包如何进行解析的两种方式中的一种而且是最简单的那一种。GAP简单的对LL payload进行一些规范和定义因此GAP能实现的功能极其有限。GAP目前主要用来进行广播扫描和发起连接等。Generic Attribute Profile简称GATT。GATT用来规范attribute中的数据内容并运用group分组的概念对attribute进行分类管理。没有GATTBLE协议栈也能跑但互联互通就会出问题也正是因为有了GATT和各种各样的应用profileBLE摆脱了ZigBee等无线协议的兼容性困境成了出货量最大的2.4G无线通信产品。4.2.3 应用Application应用层是用户开发实际蓝牙应用的地方包含必要的协议栈参数设置以及各种功能函数的调用。我们分别从蓝牙从机和蓝牙主机两种设备来分析。蓝牙从机相关硬件和基础服务初始化设置广播参数广播数据广播间隔扫描回应等参数或者数据。设置Profile添加从机服务、特征是还有设置回调函数用于接收主机数据等。设置绑定管理参数可选启动广播开始运行。等待相关事件及事件处理例如收到主机发来的数据被链接等等。蓝牙主机相关硬件和基础服务初始化设置扫描参数。设置连接参数。设置绑定管理参数可选启动协议栈开始运行。等待相关事件及事件处理例如扫描事件从机的Notify事件等等。5 GAP和GATT蓝牙协议栈分为两类结构控制器Controller和主机Host。每个类别都有子类别这些子类别执行特定的角色。我们将要研究的两个子类别是 通用访问配置文件 GAP和 通用属性配置文件 GATT。GAP是Generic Access Profile的缩写中文含义是通用访问配置文件。GATT是Generic Attribute Profile的缩写中文含义是通用属性配置文件。5.1 GAP和GATT区别区分GAP和GATT很重要。GAP 定义了 BLE网络堆栈的一般拓扑。GATT 详细描述了一旦设备建立连接后如何传输属性数据。GATT特别关注如何根据其描述的规则格式化打包和发送数据。在BLE网络堆栈中属性协议ATT与GATT紧密对齐GATT直接位于ATT的顶部。GATT实际上使用ATT来描述如何从两个连接的设备交换数据。5.2 通用访问配置文件GAPBLE设备可以使用两种机制与外界通信广播或连接。这些机制受通用访问配置文件GAP准则的约束。GAP定义了启用BLE的设备如何使其自身可用以及两个设备如何直接相互通信。5.2.1 建立联系Connecting设备可以通过采用GAP中指定的以下角色来加入BLE网络A、广播Broadcasting这些角色不必显式地相互连接即可传输数据。广播者Broadcaster广播公共数据包的设备例如可以广播按下按钮的时间。观察者Observer侦听广播者发送的广告包中数据的设备。广播者和观察者之间没有任何连接。/2、/2B、连接Connecting这些角色必须显式连接和握手才能传输数据。这些角色比广播角色更常用。从机设备Peripheral 通过广播告知其他设备自己的存在以便主机设备可以建立连接。连接后从机设备不再向其他主机设备广播数据而是保持与主机设备的连接。从机设备功耗低因为它们只需要定期发送信标即可。主机设备负责开始与从机设备的通信。手环是BLE外设的一个示例。主机设备Central一种通过侦听广播包来启动与从机设备的连接的设备。主机设备可以连接到许多其他从机设备。当主机设备要连接时它将请求连接数据包发送到从机设备。如果从机设备接受来自主机设备的请求则建立连接。当您的手机连接到手环时就是BLE Central设备的一个示例。5.2.2 连接后Connected主机设备可以更新连接参数 主机设备通常在设备与其自身之间建立连接参数。只有主机设备能修改连接参数。但是从机设备可以要求主机设备更改连接参数及从机发送更新参数请求。从机设备或主机设备可以终止连接连接可能由于多种原因而终止例如设备的电池可能耗尽或网络干扰可能导致连接失败。设备还可以主动与对等设备断开连接。5.3 通用属性配置文件GATT5.3.1 模型角色GATT分为两种类型注意与从机或主机无关。客户端Client客户端可以发送请求给GATT服务端客户端可以读Read/写Write服务端的属性Attributes 通过属性可以通信数据。服务端Server服务端是用来存储属性Attributes 的每当客户端发送请求时服务端会相应这些请求。5.3.2 客户端与服务端的关系一个示例如下手环采集了心跳信息希望计算机读取该信息。手环充当服务端并提供信息。手机充当客户端读取该信息。GAP和GATT模型角色基本上彼此独立从机设备或主机设备都可以充当服务端或客户端这取决于数据的流动方式。在一般的主从机通信时主机可以通过读写从机的属性实现接收和发送数据给从机从机可以通过发送通知的方式实现与主机的通信。因此一般从机是作为GATT的服务端主机作为GATT的客户端。6 协议栈分层协作下面以如何发送一个无线数据包的例子来简单阐述协议栈中各分层的作用和必要性。实际上协议栈的实现可能更加负责它需要考虑方方面面的因素。6.1 发送数据包假设有设备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两者之间的连接。6.2 广播方式我们先来看一下简单的广播情况这种情况下我们把设备A叫advertiser广播者设备B叫scanner或者observer扫描者。广播状态下设备A的LL层API将变成send_LL(0x53,2402M, 0x8E89BED6)。由于设备B可以同时接收到很多设备的广播因此数据包还必须包含设备A的device address0xE1022AAB753B以确认该广播包来自设备A为此send_LL参数需要变成send_LL(0x53,2402M, 0x8E89BED6, 0xE1022AAB753B)。LL层还要检查数据的完整性即数据在传输过程中有没有发生窜改为此引入CRC24对数据包进行检验 (假设为0xB2C78E) 。同时为了调制解调电路工作更高效每一个数据包的最前面会加上1个字节的preamble前导帧preamble一般为0x55或者0xAA。这样整个空中包就变成注空中包用小端模式表示上面这个数据包还有如下问题没有对数据包进行分类组织设备B无法找到自己想要的数据0x53。为此我们需要在access address之后加入两个字段LL header和长度字节。LL header用来表示数据包的LL类型长度字节用来指明payload的长度设备B什么时候开启射频窗口以接收空中数据包如上图case1所示当设备A的数据包在空中传输的时候设备B把接收窗口关闭此时通信将失败同样对case2来说当设备A没有在空中发送数据包时设备B把接收窗口打开此时通信也将失败。只有case3的情况通信才能成功即设备A的数据包在空中传输时设备B正好打开射频接收窗口此时通信才能成功换句话说LL层还必须定义通信时序。当设备B拿到数据0x53后该如何解析这个数据呢它到底表示湿度还是电量还是别的意思这个就是GAP层要做的工作GAP层引入了LTVLength-Type-Value结构来定义数据比如02010502-长度01-类型强制字段表示广播flag广播包必须包含该字段05-值。由于广播包最大只能为31个字节它能定义的数据类型极其有限像这里说的电量GAP就没有定义因此要通过广播方式把电量数据发出去只能使用供应商自定义数据类型0xFF即04FF590053其中04表示长度FF表示数据类型自定义数据0x0059是供应商ID自定义数据中的强制字段0x53就是我们的数据(设备双方约定0x53就是表示电量而不是其他意思)。最终空中传输的数据包将变成AAD6BE898E600E3B75AB2A02E102010504FF5900538EC7B2AA– 前导帧(preamble)D6BE898E– 访问地址(access address)60– LL帧头字段(LL header)0E– 有效数据包长度(payload length)3B75AB2A02E1– 广播者设备地址(advertiser address)02010504FF590053– 广播数据8EC7B2– CRC24值有了PHYLL和GAP就可以发送广播包了但广播包携带的信息极其有限而且还有如下几大限制无法进行一对一双向通信 广播是一对多通信而且是单方向的通信由于不支持组包和拆包因此无法传输大数据通信不可靠及效率低下。广播信道不能太多否则将导致扫描端效率低下。为此BLE只使用37(2402MHz) /38(2426MHz) /39(2480MHz)三个信道进行广播和扫描因此广播不支持跳频。由于广播是一对多的所以广播也无法支持ACK。这些都使广播通信变得不可靠。扫描端功耗高。由于扫描端不知道设备端何时广播也不知道设备端选用哪个频道进行广播扫描端只能拉长扫描窗口时间并同时对37/38/39三个通道进行扫描这样功耗就会比较高。而连接则可以很好解决上述问题下面我们就来看看连接是如何将0x53发送出去的。6.3 连接方式到底什么叫连接(connection)像有线UART很容易理解就是用线Rx和Tx等把设备A和设备B相连即为连接。用“线”把两个设备相连实际是让2个设备有共同的通信媒介并让两者时钟同步起来。蓝牙连接有何尝不是这个道理所谓设备A和设备B建立蓝牙连接就是指设备A和设备B两者一对一“同步”成功其具体包含以下几方面设备A和设备B对接下来要使用的物理信道达成一致设备A和设备B双方建立一个共同的时间锚点也就是说把双方的时间原点变成同一个点设备A和设备B两者时钟同步成功即双方都知道对方什么时候发送数据包什么时候接收数据包连接成功后设备A和设备B通信流程如下所示如上图所示一旦设备A和设备B连接成功此种情况下我们把设备A称为Master或者Central把设备B称为Slave或者Peripheral设备A将周期性以CIconnection interval为间隔向设备B发送数据包而设备B也周期性地以CI为间隔打开射频接收窗口以接收设备A的数据包。同时按照蓝牙spec要求设备B收到设备A数据包150us后设备B切换到发送状态把自己的数据发给设备A设备A则切换到接收状态接收设备B发过来的数据。由此可见连接状态下设备A和设备B的射频发送和接收窗口都是周期性地有计划地开和关而且开的时间非常短从而大大降低系统功耗并大大提高系统效率。现在我们看看连接状态下是如何把数据0x53发送出去的从中大家可以体会到蓝牙协议栈分层的妙处。对开发者来说很简单他只需要调用send(0x53)GATT层定义数据的类型和分组方便起见我们用0x0013表示电量这种数据类型这样GATT层把数据打包成130053小端模式ATT层用来选择具体的通信命令比如读/写/notify/indicate等这里选择notify命令0x1B这样数据包变成了1B130053L2CAP用来指定connection interval连接间隔比如每10ms同步一次CI不体现在数据包中同时指定逻辑通道编号0004表示ATT命令最后把ATT数据长度0x0004加在包头这样数据就变为040004001B130053LL层要做的工作很多首先LL层需要指定用哪个物理信道进行传输物理信道不体现在数据包中然后再给此连接分配一个Access address0x50655DAB以标识此连接只为设备A和设备B直连服务然后加上LL header和payload length字段LL header标识此packet为数据packet而不是control packet等payload length为整个L2CAP字段的长度最后加上CRC24字段以保证整个packet的数据完整性所以数据包最后变成AAAB5D65501E08040004001B130053D550F6AA– 前导帧(preamble)0x50655DAB– 访问地址(access address)1E– LL帧头字段(LL header)08– 有效数据包长度(payload length)04000400– ATT数据长度以及L2CAP通道编号1B– notify command0x0013– 电量数据handle0x53– 真正要发送的电量数据0xF650D5– CRC24值虽然开发者只调用了 send(0x53)但由于低功耗蓝牙协议栈层层打包最后空中实际传输的数据将变成下图所示的模样这就既满足了低功耗蓝牙通信的需求又让用户API变得简单可谓一箭双雕希望通过这个例子让大家对协议栈的各层作用有个初步的印象。