引入-如何发送一个数据包
假设有两个设备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表示电量这一数据类型,那么其将数据打包为130053ATT层用来选择具体的通信命令,比如读、写、notify,这里选择notify0x1B,数据包打包为1B130053L2CAP层用来指定CI,但其不体现在数据包中。同时指定逻辑通道编号0004(表示ATT命令),并且把ATT数据长度0x0004加到包头,数据打包为040004001B130053LL层需要指定哪个物理信道进行传输,其不体现在数据包中,然后给此连接分配一个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 许可协议。转载请注明出处!