赞
踩
蓝牙是一种近距离无线通信技术,运行在2.4GHz免费频段,目前已大量应用于各种移动终端,物联网,健康医疗,智能家居等行业。蓝牙4.0以后的版本分为两种模式,单模蓝牙和双模蓝牙。
如下图所示,双模蓝牙具有下图所有的特点,而单模蓝牙仅如图右侧所示。
经典蓝牙支持音频(HFP/HSP, A2DP)和数据(SPP, HID等)两大类协议,在音箱,耳机,汽车电子及传统数传行业,由于苹果对经典蓝牙数据传输接口有限制(需要过MFI认证),加上功耗偏大,因此在目前移动互联应用中慢慢地被边缘化。因此低功耗蓝牙顺势而出,由于可支持苹果4S以上及安卓4.3系统以上的数据传输,且功耗极低,目前正在被越来越多的移动互联设备所采用,但低功耗蓝牙不支持音频协议,并且受数据传输速度限制,其应用也被限制在小数据传输行业。而蓝牙双模则是综合了两者的优缺点,既可以支持音频传输,同样可支持数据传输,并且兼容性也是两者之和,在对功耗要求不苛刻的情况下,是比较理想的选择。
低功耗蓝牙瞄准多个市场,特别是移动智能终端,智能家居,互联设备等领域,主要特点包括:
蓝牙联盟沿用经典蓝牙的规范内容,为低功耗蓝牙定义了一些标准Profile
,Profile理解为数据规范,只要遵守该规范,任意厂家的蓝牙设备,均可以相互连接与通信,例如无线蓝牙键盘鼠标,无论是安卓或是iOS还是Windows,均是即插即用,这便是“标准”的力量。低功耗蓝牙支持的标准Profile有:
另外,低功耗蓝牙还可以自定义Profile,伴随着智能手机的发展和普及,低功耗蓝牙的这个特性得到了发扬光大,同时也拓宽了低功耗蓝牙的应用领域。例如,可以自定义一个开关量的Profile,数据01表示开灯,数据00表示关灯,然后手机发送数据01和00就可以控制灯的亮和灭。类似的应用案例有很多,下面总结应用特点
本节我们介绍低功耗蓝牙的基本行为状态和主从机交互过程,为后面的低功耗蓝牙协议的学习准备基础。
BLE设备角色主要分为两种角色,主机(Master或Central)
和从机(Peripheral)
,当主机和从机建立连接之后才能相互收发数据
另外还有观察者(Observer)
和广播者(Broadcaster)
,这两种角色不常使用,但也十分有用,例如iBeacon,就可以使用广播者角色来做,只需要广播特定内容即可。
蓝牙协议栈没有限制设备的角色范围,同一个BLE设备,可以作为主机,也可以作为从机,我们称之为主从一体,主从一体的好处是,每个BLE设备都是对等的,可以发起连接,也可以被别人连接,更加实用。
广播是指从机每经过一个时间间隔发送一次广播数据包
,这个时间间隔称为广播间隔
,这个广播动作叫做广播事件
,只有当从机处于广播状态时,主机才能发现该从机。
在每个广播事件中,广播包会分别在37,38和39三个信道上依次广播,如下图所示。
广播时间间隔的范围是从20ms到10.24s,广播间隔影响建立连接的时间。广播间隔越大,连接的时间越长。
另外BLE链路层会在两个广播事件之间添加一个0~10ms的随机延时,保证多个设备广播时,不会一直碰撞广播。也就是说,设置100ms的广播间隔,实际上两次广播事件的时间间隔可能是100~110ms之间的任意时间。
广播数据包最多能携带31个字节的数据,一般包含可读的设备名称,设备是否可连接等信息。
当主机收到从机广播的数据包后,它可以再发送获取更多数据包的请求,这个时候从机将广播扫描回应
数据包,扫描回应数据包和广播包一样,可以携带31个字节的数据。
扫描是主机监听从机广播数据包和发送扫描请求的过程,主机通过扫描,可以获取到从机的广播包以及扫描回应数据包,主机可以对已扫描到的从机设备发起连接请求,从而连接从机设备并通信。
扫描动作有两个比较重要的时间参数:扫描窗口
和扫描间隔
,如果扫描窗口等于扫描间隔,那么主机将一直处于扫描状态之中,持续监听从机广播包。
扫描回应
的广播包。所以,主动扫描比被动扫描,可以多收到扫描回应数据包。
在BLE连接中,使用跳频方案,两个设备在特定时间、特定频道上彼此发送和接收数据。这些设备稍后在新的通道(协议栈的链路层处理通道切换)上通过这个约定的时间相遇。这次用于收发数据的相遇称为连接事件
。如果没有要发送或接收的应用数据,则交换链路层数据来维护连接。两个连接事件之间的时间跨度称为连接间隔
,是以1.25 ms为单位,范围从最小值7.5 ms到最大值4.0 s
Connection Interval连接间隔,两次连接事件之间的时间间隔称为连接间隔。1.25 ms为单位,范围从最小值7.5 ms到最大值4.0 s
Slave Latency从机延迟,如果从机没有要发送的数据,则可以跳过连接事件,继续保持睡眠节省电量。
Supervision Time-out监控超时,是两次成功连接事件之间的最长时间。如果在此时间内没有成功的连接事件,设备将终止连接并返回到未连接状态。该参数值以10 ms为单位,监控超时值可以从最小值10(100 ms)到3200(32.0 s)。超时必须大于有效的连接间隔。
连接参数由主机发起连接的时候提供,如果从机对连接参数有自己的要求,例如要求更低的功耗,或者更高的通信速率等,从机可以向主机发送连接参数更新请求。
从机可以在连接后的任何时候发起连接参数更新请求,但最好不要在主从建立连接后立刻发起,建议延迟5s左右再发送请求。
连接参数更新请求可以修改:Connection Interval连接间隔,Slave Latency从机延迟,Supervision Time-out监控超时。
Effective Connection Interval有效连接间隔等于两个连接事件之间的时间跨度,假设从机跳过最大数量的连接事件,且允许从机延迟(如果从机延迟设置为0,则有效连接间隔等于实际连接间隔,)。
从机延迟表示可以跳过的最大事件数。该数字的范围可以从最小值0(意味着不能跳过连接事件)到最大值499。最大值不能使有效连接间隔(见下列公式)大于16秒。间隔可以使用以下公式计算:
Effective Connection Interval = (Connection Interval) × (1 + [Slave Latency])
Consider the following example:
当没有数据从从机发送到主机时,从机每500ms一个连接事件交互一次。
在许多应用中,从机跳过最大连接事件数。选择正确的连接参数组在低功耗蓝牙设备的功率优化中起重要作用。以下列表给出了连接参数设置中权衡的总体概述。
减少连接间隔如下:
增加连接间隔如下:
减少从机延迟(或将其设置为零)如下:
增加从机延迟如下:
通俗的说,我们将从机具有的数据或者属性特征,称之为Profile,Profile可翻译为:配置文件。
从机中添加Profile配置文件(定义和存储Profile),作为GATT的Server端,主机作为GATT的Client端。
Profile包含一个或者多个Service,每个Service又包含一个或者多个Characteristic。主机可以发现和获取从机的Service和Characteristic,然后与之通信。Characteristic是主从通信的最小单元。
注意,这里引用了服务 Service
和 特征值 Characteristic
的概念。每个服务和特征值都有自己的唯一标识 UUID
,标准UUID为128位,蓝牙协议栈中一般采用16位,也就是两个字节的UUID格式。
一个从机设备包括一个或者多个服务;一个服务中又可以包括一条或者多条特征值,每个特征值都有自己的属性 Property
,属性的取值有:可读 Read
,可写 Write
以及 通知 Notify
。
下图是一个典型的从机设备,该从机包含有一个Profile,两个个Service和五个Characteristic。我们先来介绍这些特征值的作用,然后介绍如何通过特征值通信。
服务0x180A
180A是蓝牙协议里标准的服务UUID,用来描述设备信息 Device Information
,可以通过该服务,来提供从机设备的相关说明,例如硬件版本,软件版本,序列号等信息。这样,主机就可以获取从机的设备信息。上图中我们添加了三个提供具体设备信息的特征值,他们分别是:
Model Number String
,例如某智能锁的产品型号为:“DSL-C07”。Serial Number String
,例如某智能锁的产品序列号为:“lkjl0016190502500269”上述特征值仅有Read属性,因此主机只能读,不能执行写操作。
服务0xFFF0
FFF0是我们自定义的服务UUID,它包含两个特征值,用来发送和接收数据。
假设主机写特征值的协议栈函数原型为 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)
主机或从机都可以发起断开连接请求,对方会收到该请求,然后断开连接恢复连接前的状态。
现在我们总结一下BLE的工作流程,使用两个虚拟的BLE硬件来模拟主从机的交互过程。
假设有两个BLE设备,使用的是BLE261低功耗蓝牙模块(假设已经下载了用于交互演示的功能固件),一个是主机,名称为:BleCentral,另一个是从机,名称为:BlePeripheral,如下图所示。
3.7.1 步骤1:上电初始化
主机、从机上电后(不分先后顺序),首先进行协议栈初始化和相关功能调用,如下图所示。
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通知,数据内容为:1234
3.7.10 步骤10:发送断开请求
主机和从机任何一方均可以发起断开连接的请求,对方收到后,状态将变为已断开。
3.7.11 步骤11:成功断开连接
从机收到主机发来的断开请求,此刻状态变为已断开。
BLE协议栈一般是指芯片厂家,依据 Bluetooth SIG 发布的 Bluetooth Core Specification
核心协议的实现的代码固件,并提供函数接口,由芯片内部程序调用,可实现上节BLE工作流程等相关功能。
常见的协议栈有德州仪器 TI 的 ble-stack
和 Nordic 的 SoftDevice
。
在本节中,我们列举两家典型的蓝牙芯片厂家:TI和Noridc,来深入了解低功耗蓝牙协议栈。
下图是TI的CC26系列芯片协议栈结构图,
下图是Nordic的nRF52系列芯片的协议栈结构图。
从上节的两张协议栈功能框图中可以看出,无论是哪个芯片厂商实现的BLE协议栈,其结构都非常的相似,均三个部分:
然后每一层又分成若干个子模块。我们现在由下而上,逐层介绍。
Physical 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的功能接口, 主要用于 2 颗芯片实现 BLE 协议栈 的场合,用来规范两者之间的通信协议和通信命令等, 所以称作Host Controller Interface。Logical 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进行分类管理。没有GATT,BLE协议栈也能跑,但互联互通就会出问题,也正是因为有了GATT和各种各样的应用profile,BLE摆脱了ZigBee等无线协议的兼容性困境,成了出货量最大的2.4G无线通信产品。应用层是用户开发实际蓝牙应用的地方,包含必要的协议栈参数设置,以及各种功能函数的调用。我们分别从蓝牙从机和蓝牙主机两种设备来分析。
蓝牙协议栈分为两类结构:控制器(Controller)和主机(Host)。每个类别都有子类别,这些子类别执行特定的角色。我们将要研究的两个子类别是 通用访问配置文件 (GAP)和 通用属性配置文件 (GATT)。
区分GAP和GATT很重要。
GATT特别关注如何根据其描述的规则格式化打包和发送数据。在BLE网络堆栈中,属性协议(ATT)与GATT紧密对齐,GATT直接位于ATT的顶部。GATT实际上使用ATT来描述如何从两个连接的设备交换数据。
GAP 定义了设备如何彼此发现、建立连接、以及如何实现绑定,同时描述了设备如何成为广播者和观察者,并且实现无需连接的数据传输,最后,其定义了如何用不同类新的地址来实现隐私性和可解析性。BLE设备可以使用两种机制与外界通信:广播或连接。这些机制受通用访问配置文件(GAP)准则的约束。GAP定义了启用BLE的设备如何使其自身可用,以及两个设备如何直接相互通信。
通用访问规范GAP(Generic Access Profile)是BLE设备内部功能对外的接口层,它规定了三个方面:GAP角色、模式和规程、安全问题。
GAP层将设备分为四种角色,分别是外围设备,中央设备,播报设备和观察设备。这些设备围绕着广播和连接的差异性而区分,外围设备和播报设备对外发出广播数据,中央设备和观察设备扫描外部广播数据,播报设备和观察设备通常不建立连接,而外围设备和中央设备可以建立连接。
围绕着广播和连接,GAP层定义了许多不同的模式(主要是从设备所处的状态),在不同模式下的操作称为“规程”(主要是主设备所做的操作)。
对于安全问题,GAP层提供了BLE安全管理器的一些列参数设置接口,包括:设备的安全模式、IO能力、安全级别和密钥长度等参数。设备需要根据实际能力设置GAP的安全管理器参数,从而使用合适的配对方法。
GAP层可以与L2CAP建立联系,设置自定义的MTU值。
GAP层定义了四种角色:
A、广播(Broadcasting):这些角色不必显式地相互连接即可传输数据。
B、连接(Connecting):这些角色必须显式连接和握手才能传输数据。这些角色比广播角色更常用。
由于链路层支持同时拥有多个状态机,GAP层也支持一个设备同时具有多个GAP角色,比如在一个连接中充当中央设备,同时对外发出广播充当外围设备。
GAP定义了几个与用户操作密切相关的参数:设备地址,设备名,PIN码和设备外观。
设备地址在协议栈内部指BD_ADDR,在用户界面显示为“Bluetooth Device Address”。
设备地址为一个6字节的整形数组成,可以用冒号作为分隔符,比如00:0C:3E:3A:4B:69。
在用户界面,设备地址以自然顺序显示,而内部的BD_ADDR则以逆序保存,对于上述地址,BD_ADDR[0]等于0x69而不是0x00。
设备地址分为共有地址和随机地址,随机地址分为静态随机地址和私有地址,私有地址进一步分为可解析的私有地址和不可解析的私有地址。常用的地址为共有地址和可解析的私有地址两种类型。
关于设备地址类型分析,参见链路层的文章介绍。
设备名称仅起识别设备的作用,在用户界面显示为“Bluetooth Device Name”。
设备名最长可达248个字节,但是对端设备可能并不能显示这么长的名称。
设备名支持UTF-8编码,因此设备名可以使用中文。
PIN码指两个设备配对时使用的passkey密码,在用户界面显示为“Bluetooth Passkey”。
PIN码为6位十进制整形数,因此它的有效范围为000000-999999(0x00000000 – 0x000F423F)。使用时必须显示全部6位数字,包括前导0。
设备外观仅起识辅助别设备的作用,在用户界面显示为一个图标或一个字符串。
设备外观为一个2字节数,扫描设备可以通过设备外观值为设备分配一个合适的图标或描述。
模式表示一种工作状态,规程是针对模式实现的一套操作方法。模式和规程成对出现,GAP规定了五种模式和规程,如下:
模式 | 规程 |
---|---|
Broadcast Mode | Observer Procedure |
Discovery Mode | Discovery Procedure |
Connection Mode | Connection Procedure |
Bonding Mode | Bonding Procedure |
Periodic Advertising Mode | Periodic Advertising Procedure |
播报模式并不等同于普通的广播状态。
在播报模式下,设备在广播事件中发送不可连接的广播数据,播报设备可以响应外部的扫描请求。
播报设备的广播数据格式与普通的广播数据相同,但是它不设置LE Limited Discoverable Mode和LE General Discoverable Mode这两个标志位,即播报设备是Non-discovery设备,这意味着中央设备扫描到该广播数据,应该选择忽略。
观察规程用于监听播报设备的广播数据和扫描响应数据,也可以监听普通广播设备。
可发现模式分为三种,如下:
可发现模式 | 描述 |
---|---|
Non-Discoverable mode | 不可发现模式,扫描设备应该选择忽略这类广播数据。 |
Limited Discoverable mode | 有限发现模式,广播数据仅工作有限时间内是可被发现的。 |
General Discoverable mode | 普通发现模式,没有额外限制。 |
有限发现模式将广播数据的LE Limited Discoverable Mode位置为1,普通发现模式将广播数据的LE General Discoverable Mode位置为1,如果这两个标志位均不设置,就是不可发现模式。
不可发现模式的广播数据与其他两种模式相同,所以其广播数据仍然能够被扫描设备正确读取,但由于没有设置相应的标志位,扫描设备在解析广播数据时应该尊重其不愿意被发现的意图,主动忽略该广播数据。
使用观察规程的观察设备,则不会忽略不可发现模式的广播数据。
有限发现模式通常用于用户指定的行为让设备临时进入可发现状态,可发现状态持续时间为T_GAP[lim_adv_timeout]。
普通发现模式是默认模式,它没有时间限制。
发现规程分为两种,如下:
发现规程 | 描述 |
---|---|
Limited Discovery Procedure | 有限发现规程,仅能发现有限发现模式的广播数据。 |
General Discovery Procedure | 普通发现规程,没有额外限制。 |
有限发现规程,仅处理有限发现模式下的广播数据,包括设备地址和广播数据,忽略其他发现模式下的广播设备。
常规发现规程,能普通发现模式和有限发现模式下的广播数据。
此外还有一种发现规程,专用于发现设备名称,如下:
发现规程 | 描述 |
---|---|
Name Discovery Procedure | 设备名发现规程,用于发现广播设备的设备名称。 |
设备名发现规程,可以发现普通发现模式和有限发现模式下的广播设备名称。
发现设备名称的步骤如下:
可连接模式分三种,如下:
可连接模式 | 描述 |
---|---|
Non-Connectable Mode | 不可连接模式,无法与其他设备建立连接。 |
Directed Connectable Mode | 定向可连接模式,可以与指定的中央设备建立连接。 |
Undirected Connectable Mode | 非定向可连接模式,可以与任何中央设备建立连接,这是默认的可连接模式。 |
而相关的连接规程则由四种,如下:
连接规程 | 描述 |
---|---|
Auto Connection Establishment Procedure | 自动连接建立规程,利用中央设备的设备地址白名单,一旦地址匹配就自动建立连接。 |
General Connection Establishment Procedure | 普通连接建立规程,这是默认的连接规程,没有额外条件。 |
Selective Connection Establishment Procedure | 可选连接建立规程,利用中央设备的设备地址白名单,只有地址匹配的设备才能建立连接。 |
Direct Connection Establishment Procedure | 定向连接建立规程,与指定地址的外围设备建立连接。 |
此外,还有两个与连接相关的规程,如下:
规程 | 描述 |
---|---|
Connection Parameter Update Procedure | 连接参数更新规程,更新连接参数信息。 |
Terminate Connection Procedure | 终止连接规程,终止当前连接。 |
可绑定模式分为:
可绑定模式 | 描述 |
---|---|
Non-Bondable mode | 不可绑定模式,设备不支持配对操作,在配对请求命令中清除Bonding_Flags标志位。 |
Bondable mode | 可绑定模式,设备将设置认证请求命令中的Bonding_Flags标志位,并且保存绑定信息。 |
两个未绑定的设备,在访问需要绑定权限的数据时,执行绑定规程。
周期广播模式分为:
模式 | 描述 |
---|---|
Periodic Advertising Synchronizability mode | 周期广播同步模式,发送周期广播事件的同步信息,适用于播报设备, |
Periodic Advertising mode | 周期广播模式,发送周期广播数据,适用于播报设备 |
周期广播规程为:
规程 | 描述 |
---|---|
Periodic Advertising Synchronization Establishment procedure | 周期广播同步建立规程,接收周期广播事件的同步信息并同步周期广播事件,适用于观察设备。 |
共有两种安全模式:
安全模式 | 描述 |
---|---|
LE Security mode 1 | 安全模式1,使用认证信息保证安全。 |
LE Security mode 2 | 安全模式2,使用数字签名保证安全。 |
安全模式1下有四种安全级别:
四种安全级别围绕着认证和加密进行,安全级别依次增加,第1种安全级别没有认证和加密, 第2种安全基本提供未认证的加密,第3、4种安全级别能够提供认证和加密。
安全模式2下有两种安全级别:
假如设备同时要求加密和数字签名,将视认证需求选择合适的安全模式,比如需要认证则选择模式1.3,不需要认证则选择模式1.2,如果需要安全连接则选择模式1.4。
共有四种安全规程,如下:
规程 | 描述 | 适用安全模式 |
---|---|---|
Authentication procedure | 认证规程,执行认证和加密操作。 | 安全模式1 |
Authorization procedure | 授权规程,用户行为确认是否为某个操作提供授权。 | 安全模式1 |
Connection data signing procedure | 连接数据签名规程,在未加密的连接中传输认证的数据。 | 安全模式2 |
Authenticate signed data procedure | 认证已签名的数据规程,校验带有前面的数据是否有效。 | 安全模式2 |
Encryption procedure | 加密规程,对连接和数据进行加密。 | 安全模式1 |
隐私与私有地址有密切关系,跟私有地址相关的规程如下:
规程 | 描述 |
---|---|
Non-resolvable private address generation procedure | 不可解析私有地址生成规程 |
Resolvable private address generation procedure | 可解析私有地址生成规程 |
Resolvable private address resolution procedure | 可解析私有地址解析规程 |
广播包和扫描响应使用相同的数据格式,如下:
一个广播包由多个AD Structure组成,传统广播包的最大长度为31字节,扩展广播包的最大长度为255字节,未占用的数据则补零。
一个AD Structure中包含三个元素:长度、广播数据类型和广播数据。
其中长度指广播数据类型加上广播数据的总长度,广播数据类型决定了广播数据的属性,可以代表设备名、设备地址或服务的UUID。完整的广播数据类型可以在官方网站检索(链接)。
动态的广播数据适合放在广播包中发送,静态的广播数据适合放在扫描响应包中发送。
每个BLE设备的GATT均包含必要的GAP服务项,GAP服务项包含以下特征项:
特征项 | UUID | 描述 |
---|---|---|
Device Name | 0x2A00 | 读取设备名称 |
Appearance | 0x2A01 | 读取设备外观 |
Peripheral Preferred Connection Parameters | 0x2A04 | 读取期望的连接参数 |
Central Address Resolution | 0x2AA6 | 中央设备支持解析地址,供外围设备读取以确定中央设备能否使用地址解析,仅在使能了隐私功能时使用,否则应删除 |
Resolvable Private Address Only | 0x2AC9 | 设备仅使用可解析的随机地址,供对端设备读取以确定该设备在绑定后是否仅使用可解析的随机地址,仅在使能了隐私功能时使用,否则应删除 |
GATT分为两种类型,注意与从机或主机无关,即主机可能是客户端也可能是服务端,从机可能是服务端也可能是客户端,其重点主要是看谁提供数据,谁使用数据,提供数据一方为服务端,接收数据一端为客户端。
客户端(Client):客户端可以发送请求给GATT服务端,客户端可以读(Read)/写(Write)服务端的属性(Attributes ),通过属性可以通信数据。
服务端(Server):服务端是用来存储属性(Attributes )的,每当客户端发送请求时,服务端会相应这些请求。
一个示例如下:手环采集了心跳信息,希望计算机读取该信息。手环充当服务端并提供信息。手机充当客户端,读取该信息。
GAP和GATT模型角色基本上彼此独立从机设备或主机设备都可以充当服务端或客户端,这取决于数据的流动方式。
在一般的主从机通信时,主机可以通过读写从机的属性,实现接收和发送数据给从机,从机可以通过发送通知的方式实现与主机的通信。因此,一般从机是作为GATT的服务端,主机作为GATT的客户端。
1. GATT是低功耗蓝牙属性应用规范,应用于主机和从设备之间的数据传输。其与GAP并列为BLE两大profile。
Attribute是属性的意思。何为属性?在各蓝牙单芯片平台的SDK实际使用中,属性是指一条带有标签的、可以被寻址的数据。在蓝牙实际的规范中,寻址即用handle句柄来表示。每个属性都对应一个唯一的handle。
2. 对属性协议需求的思考
蓝牙是无线通信,BLE利用属性协议进行传输,其如此重要,如果我们不理解其需求,那么我们也很难从真正去理解其规范。尽管在实际的蓝牙单芯片SDK中很容易通过模仿的方法进行应用,但是如果想深入地理解其为什么要设计呢?
1) 连接的参数是一个设备的固有参数,一般会作为一个服务来提供,如GAP服务;而假设这个设备是一个温度采集器,那么这个温度采集明显跟设备的参数不属于一类,因此可以再作为一个服务。所以属性协议应该支持多个服务。如何来区分这些不同的服务?这即对应蓝牙标准规范规定的UUID。我们可以认为不同的UUID对应不同的确定的服务。
2) 连接的参数可以有多个,如Connection Interval、Slave Latency等等,我们如何区分这是一个服务,又如何区分服务包含了这些特性参数。我们可以认为一个服务包含了多个特性(参数)。在蓝牙标准里面,同样是用不同的UUID来区分服务类型、特性类型等等。
3) 对于每个特性characteristic,要让对方获取这个特性,就必须要分别告诉对方这个特性的长度是多少,值是多少,而不能只给数值。除此之外,特性还可能有描述值(说明特性名称或者作用等)、特性单位等(国际单位,如米是公里/每小时还是米/秒)。后面这两个是非必选的。
4) 属性还应该有一个访问控制,如可读可写还是读写、或者是通知notify/indicate等等,这是数据通信必须具有的权限控制,不管是服务还是特性,它都具有访问控制属性。
3. 属性和属性类型
属性由属性句柄、属性类型、属性值组成。如下图:
1) 属性句柄在实际的运用中可以认为是属性在属性数组中的下标。我们都知道在实际的编程中,下标并不需要专门存储,而只是通过元素的结构体来进行索引即可。因此可以认为属性句柄是一个无形的东西,它只能被所在的设备程序所认识,而不能用于无线传输。
2) 属性类型是真实存在的,其和属性值都会被实际存储。属性类型是由蓝牙标准组织所规范,其一般通过128位的UUID来表征一个具体的属性。由于BLE的GATT可以认为是蓝牙标准规范的精简版,所以BLE被允许只传输前面2字节(16位)的UUID,所有的BLE的UUID的基数都是一样的,如下,只有前面两字节不同。
利用2字节(16位)也可以定义65536种属性了。事实上,蓝牙标准组织对这些UUID进行了分类。如下:
属性类型即是0x2800~0x28ff,在实际的应用中,属性类型主要包括:我们主要使用服务和特性定义两种,其他两个很少用到。
3) 蓝牙标准不仅通过UUID来进行属性分类,而且还用UUID来确定各种具体的服务和特性。所以我们会看到UUID可能会出现在属性的属性类型和属性值两个地方。
4) 蓝牙标准组织规定两个ATT_DECL_PRIMARY_SERVICE服务之间的特性都隶属于第一个服务。这样可以理解在蓝牙服务发现协议中先通过UUID找到目标服务,然后通过ATT_DECL_PRIMARY_SERVICE这个属性类型找到下一个服务,接着即可以在这两个服务中进行特性的遍历,遍历的结果即是目标服务的所有特性。
4. 属性值
属性值的长度可以最长到512字节,但对于某些属性,其长度是固定的。对于蓝牙标准里面规定的UUID所对应的属性(包括服务、特性定义、特性值、特性描述等等),服务、特性定义的长度是确定的,而特性值则是不固定长度的。
所以,对于不同的属性,其属性值是不一样的。也即对于以上五类(通用服务、单位、属性类型、特性描述和区分特性类型)等属性,其属性值的规范是不一样,具体到不同的特性类型,其属性值也是不同的。
BLE蓝牙的UUID列表(服务和特性):蓝牙UUID | BLE蓝牙的UUID列表_迈微AI研习社 · 号主-CSDN博客
1)通用服务类通过唯一的UUID(0x1800~0x26ff)来标识一种明确的服务。好比,0x180f代表电池电量服务。
2)计量单位类通过唯一的UUID来标识一种单位。
3)区分属性类型类通过唯一的UUID来标识该属性是首要服务定义、次要服务、包含服务还是特性定义等。其好比程序中的变量的类型,是整型、字节型、还是确定的结构体。
4)特性描述类除了描述特性的名称、作用之外,还有一个非常重要的配置作用。例如如果提供的特性服务需要主动告知对方,那么对方就必须在连接时进行订阅配置。这样在该特性的数据值发生变更时能够主动地进行notify或者indicate。
5)区分特性类型用于用户定义不同的特性,用于区分该设备里面所有的特性。
5. 特性
把特性理解为一个程序中的一个变量是最好理解的。变量有变量类型和值,变量类型有int整型、字节型等等(其实就是变量的存储长度),值即具体的数值。相应地,而特性则有值和存储值的长度的概念。如同变量的声明和定义,特性characteristic也有声明和定义(赋值)的概念。
一般地,在蓝牙标准里面,特性一般包括三个要素:声明、数值和描述。前两者都是必须的。作为通信交互,一个特性必须要告诉对方声明(存储长度和访问控制)、定义(具体赋值)。在某些特性(如notify或者indicate)里面,特性还需要告知对方附加的配置属性(提供订阅等)。
特性声明必须作为服务属性之后的第一条属性,而数值必须紧随其后。
1) 特性声明
性质为一个8位字段,指示访问控制权限,包括读、写、notify或者indicate等。对于特性声明而言,其一般是只读的(这里只针对声明这条属性本身,而不是针对对应的特性数值)。数值句柄即用于直接寻址接下来的特性数值。其对于通信的对方是很有好处的,因为对方只需要记录该句柄即可在后续的访问中直接寻址,否则每次通信都要遍历。在实际的编程应用中,我们往往在初始化时填入0,代表由底层逻辑来自动更新该handle。而属性UUID和接下来的特性数值属性的区分特性类型值是一致的。
2)特性数值
特性数值也是一个属性,其属性类型填入特性声明的属性UUID。属性值要填入特性数值的访问权限、长度和数值。
3)特性描述
其可以是字符串表示的特性名称,或者是notify/indicate要求的配置等等。
例如一个电池服务包括一个特性(电池电量),那么其至少包括以下属性:首要服务定义(电池服务)、当前电量的特性定义(定义值长度)、当前电量的特性值。如果希望电池电量在低于某个水平时主动告知对方,那么这个电量特性值不仅应该是可读的,还应该是能够notify的。由于有主动告知,因此该特性还需要包括配置要素,用于对方来订阅。
1.对于电池服务这个属性,其属性类型是ATT_DECL_PRIMARY_SERVICE(0x2800),属性值是访问可读和蓝牙标准组织规定的0x180f(位于0x1800到0x26ff之间);
2.电池当前电量的特性定义这个属性,其属性类型是ATT_DECL_CHARACTERISTIC(0x2803),属性值是可读、特性值句柄和特性值的UUID(0x2A19)。
3.对于当前电量的特性值这个属性,其属性类型是0x2A19(0x2A00~0x7fff之间,区分特性类型),其用于区分多种不同的特性(如一个温度采集器可能要采集多个温度,这里就要用户通过不同的UUID来区分不同的特性了),属性值即访问控制(可读/indicate)、长度(1字节)、数值。
4.配置属性,用于notify的订阅配置。其属性类型是0x2902,属性值是可读/可写(要能写入订阅方的handle)、长度(2个字节)、handle值。
根据以上分析,我们来重构这个蓝牙的数据底层数据库。
下面以如何发送一个无线数据包的例子来简单阐述协议栈中各分层的作用和必要性。实际上,协议栈的实现可能更加负责,它需要考虑方方面面的因素。
广播(Advertising),之所谓称之为广播,最初的含义(BLE 4.2)是为了让其他设备发现自己的存在,也就是告诉空中的其他设备:“我在这里啊~~,这是我的地址 0xAABBCCDDEEFF ”(BD Address 我瞎写的),其他的处于 Scanning 状态的设备,就能够发现广播者了。
广播(后面称为 ADV 或者 Adv)也分为,可连接/不可连接 ADV,可扫描/不可扫描 ADV,定向/不定向 ADV,等以及他们的各种组合。(唐僧给悟空啰嗦这些,肯定要被活活 K 死)。好了,现在不管那么多,咱们先来介绍各种 ADV 包的格式以及用法(请看官耐心)。
广播包的组成呢,也遵循上一节讲的数据包的基本组成方式(BLE(3)—— 空口数据包组成),只不过 AA 变为了特定值(0x8E89BED6),PDU 赋予了它新的生命:
广播的 PDU 组成主要由 Header 和 Payload 构成:
Header 由 16bits 构成,Payload 长度为 1~255 字节。
我们一步一步来,先看看 Header 域。
Header 包含的内容比较丰富:
1、PDU Type:标识这种 ADV 是什么类型的 ADV(后面详细介绍)
2、RFU : Reserved For Further 暂时不用,为后续预留
3、ChSel:如果本机支持跳频(Hopping)算法 #2,这设置成为 1
4、TxAdd:如果为 0 代表 ADV 是 public 类型的 Address,否则为 1,是 random 类型的 Address
5、RxAdd:如果为 0 代表期望的对端地址类型为 public,否则为1,代表期望对端的 Target Address 为 random(在指向性广播中使用,因为指向性广播,携带了对端地址,其他类型广播,这个 bit 没用)
6、Length:代表了后面的 Payload 的长度,以直接为单位,因为是 8bits,所以最大的长度为 255 个字节
好了,这里就剩下 PDU Type 没用详细介绍了,在介绍这个之前呢,需要给大家插播一条 BLE4.2 Vs BLE 5.x,这样理解这个玩意会更加容易。
---------------------------------------------------- 插播内容开始 ---------------------------------------------------
在 BLE 4.2 时代,所有的 ADV 都在 37、38、39上进行发送和接收交互,这里我们称 37、38、39 为 Primary Advertising Physical Channel 并且呢,ADV 携带的数据最大是 31 个字节。
到了 BLE 5.0 时代,SIG 说,ADV 你们几爷子也可以在其他频道上搞事情了,好了,这个时候,SIG 对 ADV 进行了扩展,Core Spec 管这些新来的 ADV 大爷叫做 Extended ADV,也就是扩展广播(名字简单粗暴吧)。扩展广播是怎么个扩展法子呢?就是在 Primary Advertising Physical Channel 上,还是会发一个叫 EXT_ADV 的包,这个包呢,携带了一些信息,信息中包含了下一个和他关联的包的所在地(Secondary Advertising Physical Channel),这个所在地,就不是 37、38、39了,而是其他的 37 个通道中的一个,具体是哪个,由这个 EXT_ADV 的包来决定。
---------------------------------------------------- 插播内容结束 ---------------------------------------------------
好了,这里主要插播的目的是引出,Extended ADV 的概念和 Secondary Advertising Physical Channel 的概念。OK,我们继续包格式的分析之旅。
1、ADV PDU Header PDU Type
Alright,下图表示了 PDU Type 不同,所对应的包的不同,以及他们的 Physical Channel,甚至于支持的 PHYs:
我觉得已经非常清楚了(可能是我已经知道了后面的内容的缘故),如果觉得不清楚呢,没事,看完 ADV 的所有分析后,在回头来看这个表,客官您一定会觉得豁然开朗。
远古时代,就已经支持个一些种类的 ADV PDUs,咱们称之为,Legcay ADV:
• ADV_IND
• ADV_DIRECT_IND
• ADV_NONCONN_IND
• ADV_SCAN_IND
接下来一个一个分析呗。
这个比较经典和常用的 ADV PDU 了,它代表了咱们发出去的这个 ADV 包,是一个可连接的,并且可扫描的广播包。什么是可连接呢?意思是,我发出去这个包,别人想连接我,OK,没问题。可扫描指的是,有人处于 Scanning 状态,收到我的这个 ADV_IND 后,对端发起 SCAN_REQ,咱们回复他 SCAN_RSP。
ADV_IND 的 包体为:
AdvA:本机地址 48bits
AdvData:携带的数据 0 - 31 字节
顾名思义,咱们可以知道这种类型的数据包,是可连接的并且带指向性的(不可扫描),什么叫指向性呢,也就是,我指着对端的鼻子说,我这个包是专门给你准备的(来连接我啊)。所以这个包携带了本机地址和指着的那个地址。同时不允许携带数据:
AdvA:本机地址 48bits
TargetA:对端地址 48bit
这种类型的 ADV PDU,属于不可连接,不可扫描,不定向的 ADV 包,包含了本机地址和数据(类似于,在空中散布谣言类型)
AdvA:本机地址 48bits
AdvData:携带的数据 0 - 31 字节
这种类型的 ADV PDU 是只能扫描的不定向的,不连接的 ADV
AdvA:本机地址 48bits
AdvData:携带的数据 0 - 31 字节
假设有设备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两者之间的连接。
我们先来看一下简单的广播情况,这种情况下,我们把设备A叫advertiser(广播者),设备B叫scanner或者observer(扫描者)。广播状态下设备A的LL层API将变成send_LL(0x53,2402M, 0x8E89BED6)
。由于设备B可以同时接收到很多设备的广播,因此数据包还必须包含设备A的device address(0xE1022AAB753B)以确认该广播包来自设备A,为此send_LL参数需要变成send_LL(0x53,2402M, 0x8E89BED6, 0xE1022AAB753B)
。LL层还要检查数据的完整性,即数据在传输过程中有没有发生窜改,为此引入CRC24对数据包进行检验 (假设为0xB2C78E) 。同时为了调制解调电路工作更高效,每一个数据包的最前面会加上1个字节的preamble(前导帧),preamble一般为0x55或者0xAA。这样,整个空中包就变成(注:空中包用小端模式表示!):
上面这个数据包还有如下问题:
最终空中传输的数据包将变成:
AAD6BE898E600E3B75AB2A02E102010504FF5900538EC7B2
AA
– 前导帧(preamble)D6BE898E
– 访问地址(access address)60
– LL帧头字段(LL header)0E
– 有效数据包长度(payload length)3B75AB2A02E1
– 广播者设备地址(advertiser address)02010504FF590053
– 广播数据8EC7B2
– CRC24值有了PHY,LL和GAP,就可以发送广播包了,但广播包携带的信息极其有限,而且还有如下几大限制:
而连接则可以很好解决上述问题,下面我们就来看看连接是如何将0x53发送出去的。
到底什么叫连接(connection)?像有线UART,很容易理解,就是用线(Rx和Tx等)把设备A和设备B相连,即为连接。用“线”把两个设备相连,实际是让2个设备有共同的通信媒介,并让两者时钟同步起来。蓝牙连接有何尝不是这个道理,所谓设备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发送出去的,从中大家可以体会到蓝牙协议栈分层的妙处。
AA
– 前导帧(preamble)0x50655DAB
– 访问地址(access address)1E
– LL帧头字段(LL header)08
– 有效数据包长度(payload length)04000400
– ATT数据长度,以及L2CAP通道编号1B
– notify command0x0013
– 电量数据handle0x53
– 真正要发送的电量数据0xF650D5
– CRC24值虽然开发者只调用了 send(0x53),但由于低功耗蓝牙协议栈层层打包,最后空中实际传输的数据将变成下图所示的模样,这就既满足了低功耗蓝牙通信的需求,又让用户API变得简单,可谓一箭双雕!
希望通过这个例子,让大家对协议栈的各层作用有个初步的印象。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。