链路层位于BLE协议栈的控制器(Controller)部分,在物理层(Physical Layer)之上,接口层(HCI)之下。物理层比较抽象,除了BLE信道,许多地方都是通用的通信技术,不能反映BLE通信协议的特点,而HCI之上的部分,大多在软件中实现,因此,理解BLE协议栈的底层实现,一定要对链路层有所认识。链路层定义了协议栈中最为基础的状态机、数据包格式、广播和连接流程等问题。
《低功耗蓝牙开发权威指南》(Robin Heydon)这本书的第七章,对链路层做了全面的介绍。虽然全面,但是细节不足,需要结合Bluetooth Spec v4.1 Vol 6, Part B部分,对关键的概念进行更深入的理解。
状态机
链路层中定义了一个包含五种状态的状态机:
- Standby State
- Advertising State
- Scanning State
- Initiating State
- Connection State
一个状态机在同一时刻有且只能处于一个状态。但是,一个BLE设备在同一时刻可以拥有多个独立的状态机!多个状态机并存的情况有限制条件。以下为几种有效的组合状态:
- Connection + Advertising
- Connection + Initiating
- Connection(Master) + Connection(Master)
这也就意味着,BLE协议栈是支持一主多从这种连接模式的。不过要注意,不支持一从多主模式,即一个从机同时与多个主机相连。并且,一个BLE设备在同一时刻不能同时为主机和从机角色。
什么是主机(Master)?什么是从机(Slave)?
BLE设备从Initiating状态进入Connection状态以后,将负责发起连接请求,称为主机(Master)。对应的从Advertising状态进入Connection后,负责响应连接请求,称为从机(Slave)。所以主机、从机是Connection的子状态。明确这个概念,才可以看懂BLE Spec中一些奇怪的表述。如果它说Master的某些特征,就暗指Connection状态;或者在Advertising状态下,就不能称呼主机或从机,因为主从机只存在Connection状态下。在上图中,注意到Scanning到Connection之间没有直接通路,即主机在建立连接过程中,需要先进入Scanning,在进入Standby,再进入Initiating,再进入Connection,而从机则直接从Advertising进入Connection。
报文
报文是链路层的基石,是BLE通信的基础设施,它包含四个字段:序言、访问地址、协议数据单元(PDU)和循环冗余校验
(CRC)。在广播、扫描或建立连接的过程中使用广播通道PDU 传输数据包。而用于与连接器件交换数据的数据包是
通过数据通道PDU 传输的。链路层数据包的格式如图:
报文 = 前导字 + 访问地址 + PDU + CRC
前导字(Preamble)
只有两种选择:0x55(0b01010101)和0xAA(0b10101010)。假如访问地址的首位是1,则选择0xAA,反之则选择0x55,即让前9个bit是0、1交叉的序列。由于BLE信号功率强度可以从-90dBm ~ 10dBm,物理层的放大器需要去处理不同功率强度的信号,这样一串0、1序列可以帮助ADC调整增益参数,以适配不同功率的射频信号。
接入地址(Access Address)
不同于设备的物理地址(Mac Address)。访问地址分为广播接入地址和数据接入地址。广播接入地址为固定的0x8E89BED6,如果使用Sniffer检测BLE广播数据,则很容易看到这一串数字。数据接入地址则为随机数,长度仍然是4个字节(32bit)。为什么要设置为随机数呢?因为BLE的物理机制,决定了它不适合接收一长串的0或者1,这样会导致频率失锁。数据接入地址的随机性是为了避免全0(或全1)的情况发生。随机地址也使得在一个较小空间内即使存在大量BLE设备,也能保持各自通信独立。此外,它还能一定程度上提升安全特性。这里提到的BLE物理层不善于读取全0和全1的数据序列,为了避免这个情况,链路层的数据包在发送之前需要进行白化(Whiten)处理。所谓白化,就是指利用一串给定的数据与原始数据进行异或运算,目的是将一串序列打乱,不要出现全0或全1。接收端在物理层收到原始数据后,也需要先进行反白化(Dewhiten)处理。
PDU(Protocol Data Unit)
协议数据单元,数据通道PDU 包含一个16 位的头文件、一个长度可调的payload(有效负载)字段和一个可选的信息完整性检查(MIC)字段。在蓝牙4.2 规范中,数据通道PDU 中payload 字段的最大长度可从27 字节增加到251 字节,从而使数据通道的吞吐量大约提高10 倍。
广播包的报头与数据包的报头内容不同,上图显示的是广播包的报头,包含了:PDU类型、TX/RX 地址类型、净荷长度。PDU类型有几类:ADV_IND,ADV_DIRECT_IND,ADV_NONCONN_IND,ADV_SCAN_IND,SCAN_REQ,SCA_RSP,CONNECT_REQ。共七种类型。这些类型能够从名字中得知他们的含义,同时,可以发现一个信息,即扫描(SCAN)和连接请求(CONNECT_REQ)也是在广播包中进行的。广播包中的PDU数据长度最大为37字节。37 = 6 + 31。其中6是指广播设备的地址(Mac Address),31是指广播包的最大长度。广播的设备地址(Public address)数据结构值得一提,如下图:
设备地址的组成为Deivce Address = Company Id + Comany Assigned,而在上图中company_assigned在company_id的前面,即设备地址在链路层存储顺序是LSB在前,MSB在后。这也是为什么在程序代码中获取的设备地址,跟我们看到的00 A0 50 12 34 56是顺序相反的。
CRC(循环冗余校验)
常规的概念。注意一点,CRC能发现N个字节的变化,比如24位CRC能发现1、2、3、4、5个字节的变化,但是假如同时有6个字节逆转,则不能通过CRC校验出来。
信道与跳频
BLE 共有40个信道,包括3个广播信道(信道编号为37、38、39)和37个数据信道(编号为0 – 36)。广播信道传输广播数据,数据信道进行数据通信。
40个信道均匀分布在2402MHz – 2480MHz这段频率之间,每个信道占2MHz。于是,我们经常能够看到这样的信道分布图:
其中绿色的脉冲为BLE信号,红色信号分别是WIFI、微波炉和无线设备,它们形成了干扰噪声。图一中中间三块橙色为广播通道,分布在两端和中间三个位置,这样做是为了避免跟wifi的信道冲突。图二中上方三个拱形块为wifi的信道,广播通道则恰好避开了这些频率范围。
BLE设备广播一次,在三个信道发出相同的广播数据,三个信道发送完毕,称为一次广播事件。相邻事件的时间长度称为广播间隔
两个相邻的广播事件的之间的时间间隔(T_advEvent)为:
T_AdvEvent = advInterval + advDelay
其中,advInterval 必须是0.625ms的整数倍,范围是20ms-10.24s之间。对于可扫描非定向广播和不可连接非定向广播这两种广播类型,该值最好不小于100ms,即(160个0.625ms)。advDelay是Link Layer(链接层)分配的一个伪随机数,它的范围为“0 -10ms”。在查看BLE芯片功耗时,可以看到有三个紧挨着的脉冲,即三次RF发射行为。在Sniffer中也能够清晰看到在每次广播时三个信道的行为。
与广播事件类似,两个BLE设备之间进行一次数据通信称为一个连接事件,相邻事件的时间间隔为连接间隔。
每个连接事件会在某个数据信道中进行,下一次连接事件则会切换数据信道。这种切换信道的工作方式叫“跳频”。
跳频能解决wifi信道拥堵的问题。比如前面图中频率域上wifi信道1与BLE信道0-8重叠,假如某时刻这一频段的wifi信号很繁忙,则BLE信号必然受到影响,BLE跳频技术会标定0-8信道为“坏道”,将所有的通信行为都挪到其他空闲的信道中去进行。等该频段空闲,则去除坏道标记。关于信道的知识零散且有趣,讲清楚颇伤篇幅,建议阅读《权威指南》的第64页自行体会。
广播
广播有几种类型:
- 可连接定向广播(Connectable directed advertisement)
- 可连接非定向广播(Connectable undirected advertisement)
- 不可连接非定向广播(Non-onnectable undirected advertisement)
- 可发现非定向广播(Scannable undirected advertisement)
第一个为”定向“广播,比较特殊。定向广播行为每3.75ms重复一次广播,最大持续1.28秒,广播包不能被扫描,并且广播包中仅能包含自己和对端的设备地址。后面三个为”非定向“广播。”可连接“非定向广播为最常规的广播。”不可连接“非定向广播,是指不能被主机连接,也不可以携带扫描响应数据。这样的广播行为,支持对端的”被动扫描“行为。”可发现“非定向广播,是指不能被主机连接,但是可以携带扫描响应数据并被扫描到,但是不能建立连接。iBeacon可以设置为后三种广播类型之一。
主机端的扫描行为和连接请求,也使用广播信道。所谓扫描,指无线信号接收机处于工作状态,所有掠过该接收机的有效信号,都会被捕获和分析。如果是可识别的BLE广播信号,则会提取信号中的有效内容,以“发现”设备。
扫描分为两类:
- 主动扫描(Active Scan)
- 被动扫描(Passive Scan)
主动扫描是指接收了广播信号后,主机端发出扫描请求包(Scan Req Data),从机收到后进一步返回扫描响应包(Scan Response Data),如下图:
在主动扫描模式下,由于存在扫描响应包,从机可以在建立连接之前,提供更多的信息给主机端。扫描响应数据是在收到扫描请求后立即发出,应用程序无法掌控这一过程,因此扫描响应数据中适合存放一些静态数据。
被动扫描则不发扫描请求包。
每次扫描都会持续一段时间,这段时间称为“扫描窗口”(Scan Window),相邻扫描窗口之间的时间为“扫描间隔”(Scan Interval)。
每次扫描都只扫一个广播信道,下一次则扫下一个广播信道。使用示波器查看扫描时候的芯片功耗,会发现在扫描时候,芯片电流一直处于高点,即扫描是一个高功耗行为。如果芯片需要频繁的、大窗口扫描,则很难控制芯片功耗。因此在设置扫描窗口参数时候,不能太小,难以及时发现周围设备,也不能太大,增加功耗负担。一个典型的广播扫描时间参数模型如上图所示。
主机扫描到有效广播信号,即发现了周边的有效设备,停止扫描退出Scanning状态。此时进入Standby状态。如果需要建立连接,则发出连接请求(CONNECT_REQ),状态机进入Initiating状态,注意现在不是Connection状态!连接请求发送完毕,主机端就进入Connection状态。从机端一旦收到连接请求,也进入Connection状态。也就是说,Initiating状态就是发送连接请求的那一瞬。(BLE Spec在这一块讲述的不清楚,《权威指南》上压根没讲。 -_-!)
在CONNECT_REQ之后会延时1.25ms,然后在时间t(transmitWindowOffset <= t <= transmitWindowOffset + transmitWindowSize)内,主机端(Master)发出一个数据包,等待T_IFS(Inter Frame Space:帧间隔),如果从机(Slave)返回响应,则完成第一个连接事件(Connection Event)。
两个参数:transmitWindowOffset 和 transmitWindowSize是为了给从机做准备工作留的时间,也用来进行两个设备之间通信行为的时间同步(俗称:对表)。图中有个地方值得注意,主机发送连接请求后,即刻进入连接状态。从机并不回复一个响应信息。发送连接请求之前,都是使用广播信道,之后的数据都走数据信道。
连接
与广播类似,连接状态下的数据传输行为也有”连接事件“和”连接间隔“的概念。
从上图可知,一个connection event可能发多个包。每个连接事件内,都是主机发出连接请求,(又是连接请求!)从机进行相应。BLE设备之间的数据通信,比如Write/Notify,用户数据就包含在在这些连接请求和响应数据包中。
如果用户数据为空,也依然需要进行这样的“握手”行为,以维持设备之间的连接性。
假如从机每个小时只Notify一次数据,那么频繁的握手必将消耗大量的能量,这对从机而言不友好。BLE协议提供了延迟响应(Latency)这个参数,比如Latency = 10,即主机发出10次握手包,从机收都可以置之不理,主机也表示淡定。但是从第11个包开始,从机需要回复一个握手包,提示自己仍然”活着“。如果从第11个包开始仍然没有收到从机端的握手包,主机就开始焦急的等待从机的响应,后续的每一次握手会期望得到从机的回复。
通常情况下,从机端会很识趣的进行回复。但假如从机端真的断电了,失去了回复的能力,主机也不会无休止等待下去。BLE协议提供了监控超时(Supervision Timeout)参数,当主机距离上次收到从机握手包开始,等待超过了这个超时参数的时间长度,主机就认为从机”挂了“,直接终止物理连接。
由于延迟响应与监控超时这两个参数很重要,因此在”更新连接参数“这个任务中,需要处理这几个个参数:
- 连接间隔(最大值)
- 连接间隔(最小值)
- 延迟响应
- 监控超时
数据信道的包格式,与广播信道的包格式基本一致,仅仅报头(Header)不同。数据包的报头格式如下:
NESN/SN
NESN和SN各占1bit。SN全称为sequence number,表示当前发送的packet编号。NESN,next expected sequence number,用来告知对方下一个期待的packet的编号。Link layer使用SN来告知对方这个packet是新数据包还是重传包,用NESN来告诉对方你之前发我的包已经收到了(相当于ACK的作用),我现在期待下一个新的数据包了,因此BLE没有专门的ACK包,它是通过NESN/SN来实现ACK和重传双重功能的。请参考如下表格,仔细揣摩NESN和SN是如何编码的,以同时完成ACK和重传功能。
我们来分析#3数据包,#3是master发给slave的,那么#3的NESN和SN是如何确定的呢?其实#3的NESN和SN是通过比较#1和#2的NESN/SN的值来确定的,Master把#1传完之后,会把#1包的NESN和SN记录下来,即表格右边的NESNꞌ和SNꞌ。然后Master会拿SNꞌ跟#2的NESN相比,两者不等,说明slave已经收到了#1包,并期待master发一个新的包给它,此时Master会把SNꞌ增1,形成#3包的SN,表示这个数据包是一个新包,然后发出去;两者相等,说明slave没有收到#1包,此时master需要重传。Master还会拿NESNꞌ跟#2的SN相比,两者相等,说明#2包为新包,然后Master会把NESNꞌ增1,形成#3包的NESN发出去,告诉slave我已经收到#2包了并期待你的下一个包;两者不等,说明#2包为重传包。注意:大家可以从上述表格发现一个规律,就是同一方向相邻的两个数据包,他们的NESN和SN与另一个包的NESN和SN是相反的,比如#3 NESN = #1 #NESN ,#3 SN = #1 #SN ,同样#2和#4 各自的NESN和SN是相互相反的。我们可以用下面的流程图来描述上述过程。
MD(1bit),more data
用来指示对方我还有数据包要传,请继续打开射频窗口准备接收。比如Nordic nRF51822一个connection interval可以发6个包或者更多的包(也就是说,一个connection event包含多个数据包交互),用的就是MD来实现的。以notify命令为例,设备(Server)notify第一个数据包并将MD置1,Client(比如手机)收到这个notify命令后,就知道Server还有数据包要传,此时手机可以继续发一个空包给设备,以让设备把第二个notify命令发过来,详情如下所示。注:Master为手机(Client),Slave为设备(Server)。
其他
还有加密等内容,相对而言,不如上述内容关键,这里略去不讲。同时,也可以参考本论坛另一个大神的佳作BLE安全机制从入门到放弃
(完)
原作者:CY大象,原链接已失效,以及原图也已大量丢失,小编重新整理