引入-如何发送一个数据包
假设有两个设备A和B,A想把自己当前的电量
83%
(十六进制下0x53)发送给B。那么肯定是越简单越好,比如调用一个简单的API:
send(0x53)
。这正是BLE
协议栈的设计。但其并不是简单的在物理层直接发送0x53
就可以解决的:这样会有很多问题,比如,没有考虑用哪个射频信道来进行传输
再不改变原API
send(0x53)
的情况下,只能对协议栈分层并引入LL
层,即Link Layer
链路层。即send(0x53)
会调用send_LL(0x53,2402M)
来指定发送的信道频率另一个问题则是设备B怎样知道这个数据包是给自己的。为此
BLE
引入了access address
来指明接收者身份。其中地址0x8E89BED6
用于表示要发给周边所有设备,即广播如果要进行一对一通信,则必须生成一个独特随机的
access address
来标识AB
两者之间的连接广播
在广播的情况下,记A为
Advertiser
,B为scanner/observer
。这种状态下A的LL层API为send_LL(0x53,2402M,0x8E89BED6)
而由于设备B可以同时接收到很多设备的广播,A发送的数据包还需要包含其自身的
device address (0xE1022AAB753B)
来确认该广播包来自A,那么API则是send_LL(0x53,2402M,0x8E89BED6,0xE1022AAB753B)
LL层还需要检查数据完整性,所以引入
crc24
,假设结果为0xB2C78E
同时为了调制解调电路工作更高效,每个数据包最前面会加上一个字节的
preamble
前导帧,其一般为0x55
或0xAA
那么整个包即为(注意是小端模式):
而这个包还有一些问题:
- 没有对数据包分类组织,设备B难以找到发送过来的数据
0x53
,所以要在access address
后面加入两个字段:LL header
和lenth
,分别表示数据包的LL
类型和payload的长度 - 设备B开启射频窗口来接收空中数据包的时间未知。如图,
case1
时A的数据包在空中传输而B的接收窗口关闭,case2
时B接收窗口打开但A没有发送数据包。这两种情况下通信都失败。只有case3
的情况下能成功通信。也就是说LL
层必须定义通信时序 设备如何解析
0x53
这个数据,如何确定其表示的是电量而不是其他的东西。这是GAP
层(通用访问规范)的工作内容。其引入了LTV(Length-Type-Value)
结构来定义数据, 例如020105
,其中长度为 $2$ ,类型为 $1$ (强制字段,表示广播flag,广播包必须包含该字段),值为 $5$。而由于广播包最大 $31$ 个字节,其能定义的数据类型很有限,例如这里的电量就没有被GAP
定义。如果仍要发送电量数据就必须使用供应商自定义数据类型0xFF
,即04FF590053
,0x0059(小端)
是供应商ID,其是自定义数据中的强制字段,0x53
就是数据,双方约定其表示电量。最终广播包如图:
而广播具有很多缺点:
无法进行一对一的双向通信
- 不支持组包和拆包所以无法传输大数据
- 通信不可靠,效率低。广播信道不能太多否则会导致扫描端效率低,所以
BLE
只使用37(2402MHz)/38(2426MHz)/39(2480MHz)
三个信道来进行广播和扫描,因此广播不支持调频。由于其一对多导致不支持ACK - 由于扫描端不知道设备端何时、哪个频道广播,所以只能拉长扫描窗口时间并扫描三个通道
- 没有对数据包分类组织,设备B难以找到发送过来的数据
连接
让设备A与B连接包含以下几方面:
- 设备A和设备B对接下来要使用的物理信道达成一致
- 设备A和设备B双方建立一个共同的时间锚点,也就是说,把双方的时间原点变成同一个点
设备A和设备B两者时钟同步成功,即双方都知道对方什么时候发送数据包什么时候接收数据包
在AB连接成功后,A称为
Central
,B称为Peripheral
。A将周期性以CI(connection interval)连接间隔
为间隔向B发送数据包,同时B周期性以CI
为间隔打开射频接收窗口以接收设备A的数据包。同时在接收到A数据包 $150$ us 后B切换到发送状态将自己的数据发送给A,同时A切换到接收状态。其数据包打包发送情况如下:(注意小端)
开发者调用
send(0x53)
GATT
层定义数据类型和分组,这里定义0x0013
表示电量这一数据类型,那么其将数据打包为130053
ATT
层用来选择具体的通信命令,比如读、写、notify,这里选择notify
0x1B
,数据包打包为1B130053
L2CAP
层用来指定CI
,但其不体现在数据包中。同时指定逻辑通道编号0004
(表示ATT命令),并且把ATT数据长度0x0004
加到包头,数据打包为040004001B130053
LL
层需要指定哪个物理信道进行传输,其不体现在数据包中,然后给此连接分配一个access address:0x50655DAB
,然后加上LLheader:0x1E
表示此包为数据包而非控制包,payload length
为整个L2CAP
字段长度,在最后加上CRC24
字段,在开头加上前导帧:AA AB5D6550 1E 08 0400 0400 1B 1300 53 D550F6
noone的ble协议学习
- 本文链接: http://noone40404.github.io/2025/07/02/noone的ble协议学习/
- 版权声明: 本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!