BLE到底一包能够收发多少数据。这是我们很多开发者都会关心的,正如我们知道BLE5.0物理层设计2Mpbs的码元率,实际我们的数据传输速率远远到不了这个级别,主要原因就是我们没有办法直接单次发送1M或者1K数据,必须按照我们知道的ATT_MTU
作为最大值进行拆包发送,不断拆包的过程中导致整体蓝牙有效数据吞吐量下降。本章节,我们实用性触发研究如何究竟蓝牙一包能够发送多少数据。
以下是GAPP Client 进行特征值写请求的完整抓包,AttValue
表示写入的值,该值的最大长度到底是多少呢,本文将详细研究并且验证。
本文的正确打开姿势,我们希望你已经仔细阅读并理解协议栈的L2CAP,和BLE4.2/5.0全新支持的 LE 数据扩展功能:
术语 | 解释 |
---|---|
PDU | Potocal Data Unit 协议数据单元,对于协议栈数据单元我们应该理解为蓝牙协议分层的消息实体的完整封装,例如LL PDU表示我们 逻辑链路层的协议消息的完整封装 |
MTU | Maximum Transmission Unit 最大传输单元,和本文研究的BLE数据包的最大长度意思接近 |
LL | Logic Link 逻辑链路层 |
PHY | physical layer 物理层 蓝牙协议的最底层 |
在 BLE PHY 包格式由以下组成,排除 前导码、访问地址、CRC地址 对于BLE4.0/BLE4.1 上层协议(LL)最大可以包含39字节长度的PDU(协议数据单元)。
但是更新后的BLE4.2/BLE5.0 直接升级硬件,将这一长度扩展到257。但是默认了兼容BLE4.0/4.1还是采用了上面的39设计。
注意:PHY 的数据包最大长度直接由硬件决定,也是限制我们BLE 数据包大小的根本原因。换句话说,PHY的硬件寄存器/存储器/FIFO 等直接决定了我们蓝牙数据包最大长度。
对于蓝牙协议分层设计来看,逻辑链路层是最底层,已经正常连接的两个设备之间通信PDU主要由LL Data PDU
组成。这里的LL Playload
作为我们上层协议的负载。
逻辑链路层再往上就是L2CAP——逻辑逻辑链路控制适配协议,如下的Basic L2CAP Header+Information Payload组成我们这里的基本帧(B-frame)。
L2CAP协议再上就是我们操作特征值的ATT层,这一层数据长度被 最大传输单元 ATT_MTU
限制,ATT_Payload由Attribute OPCode
+Attribute Parames
+Authentication Signature
(可选)组成。排除12字节可选的认证签名,和一个字节属性操作码。
对于我们这里的写属性值,其Attribute Parameters
=其Attribute Handle
(2) + Attribute Value
,Attribute Value
就是我们操作属性的值,其最大长度=ATT_MTU
-Attribute OPCode
(1)-Attribute Handle
(2)=ATT_MTU
-3。
同样地,对于读操作属性,主要包含在ReadResponse 操作码。最大长度=ATT_MTU
-Attribute OPCode
(1)=ATT_MTU
-1。
再总结下,读写属性值最大有效数据长度直接由ATT_MTU
决定,但是根本收LL PDU
大小限制
对于simpler_peripheral工程我们的开发板作为GATT Server决定 ATT_MTU
的最大值。通过以下宏配置。
simpler_peripheral默认的ATT_MTU
为23,也就是我们实际写属性值最大长度为20。GATT 服务端ATT_MTU
由以上宏定义,GATT客户端可以通过GATT_ExchangeMTU()
命令配置。GATT服务端根据当前的配置的ATT_MTU决定是否生效。例如我们这里的simple_peripheral
作为GATT服务端,以更改宏L2CAP_MTU_SIZE
为150,GATT_ExchangeMTU()
需要配置到200,我们将返回150,表示生效。
注意: 对于
GATT_ExchangeMTU()
操作,我们只能由GATT Client发起,并且GATT服务端返回实际支持最大ATT_MTU。
参考《运行第一个例程》,我们直接更改最大ATT_MTU到251,并且通过《 BTool》配置客户端ATT_MTU到251,并且更改simple_peripheral characteristic 5数组为251。属性为可读可写。
//source\ti\ble5stack\icall\inc\ble_user_config.h MAX_PDU_SIZE Line 214
// Maximum size in bytes of the BLE HCI PDU. Valid range: 27 to 255
// The maximum ATT_MTU is MAX_PDU_SIZE - 4.
#ifndef MAX_PDU_SIZE
#if defined(BLE_V42_FEATURES) && (BLE_V42_FEATURES & SECURE_CONNS_CFG)
#define MAX_PDU_SIZE 255
#else
#define MAX_PDU_SIZE 27
#endif //(BLE_V42_FEATURES & SECURE_CONNS_CFG)
#endif
我们配置L2CAP MAX_PDU_SIZE
为255, ATT_MTU
最大 为251 读写特征值最大长度应该ATT_MTU
-3=248;
//source\ti\blestack\profiles\simple_profile\simple_gatt_profile.h SIMPLEPROFILE_CHAR5_LEN Line 85
#define SIMPLEPROFILE_CHAR5_LEN 248
因为我们增加了特征值5的大小为240,对于参考特征的栈空间也得增加,对应着我们的任务栈,否则程序可能会跑飞。
//examples\rtos\CC2640R2_LAUNCHXL\blestack\simple_peripheral\src\app\simple_peripheral.c SBP_TASK_STACK_SIZE Line 180
#ifndef SBP_TASK_STACK_SIZE
#define SBP_TASK_STACK_SIZE 1024
#endif
注意:这里之所以要加大任务栈否则任务要跑飞,是因为部分任务直接分配了栈空间进行特征值拷贝。
同时为了更好测试,我们直接将特诊5的属性更改为直接读写,不需要配对连接。
//source\ti\blestack\profiles\simple_profile\cc26xx\simple_gatt_profile.c Characteristic Value 5 Line 323
// Characteristic Value 5
{
{ ATT_BT_UUID_SIZE, simpleProfilechar5UUID },
GATT_PERMIT_READ|GATT_PERMIT_WRITE,
0,
simpleProfileChar5
},
但是simple_peripheral默认没有进行Characteristic Value 5 写操作,所以我们需要更改。
//source\ti\blestack\profiles\simple_profile\cc26xx\simple_gatt_profile.c simpleProfile_WriteAttrCB Line660
case SIMPLEPROFILE_CHAR5_UUID:
if(len+offset>SIMPLEPROFILE_CHAR5_LEN) {
status = ATT_ERR_INVALID_VALUE_SIZE;
}
//Write the value
if ( status == SUCCESS ) {
uint8 *pCurVal = (uint8 *)pAttr->pValue;
for(uint8 i=0;i<len;i++) {
*(pCurVal+offset+i)=*(pValue+i);
}
if(pAttr->pValue == simpleProfileChar5) {
notifyApp = SIMPLEPROFILE_CHAR5;
}
}
break;
同时我们要在应用程序回调处理Characteristic Value 5的写入操作,方便打印到串口调试。
//examples\rtos\CC2640R2_LAUNCHXL\blestack\simple_peripheral\src\app\simple_peripheral.c SimpleBLEPeripheral_processCharValueChangeEvt Line 1183
case SIMPLEPROFILE_CHAR5:
SimpleProfile_GetParameter(SIMPLEPROFILE_CHAR5, newValue);
sprintf(sprintChar,"%s","Char 5: ");
//!< 大概没60个字符要换一行
line=7;
index=8;
for(uint8_t i=0;i<SIMPLEPROFILE_CHAR5_LEN;i++) {
sprintf(sprintChar+index, "%02X", *(newValue+i));
index=index+2;
//!< 每60个字符进行换行打印。
if(index%60==0) {
index=0;
Display_print1(dispHandle,line++ ,0,"%s",sprintChar);
}
}
if(index>0) {
Display_print1(dispHandle,line,0,"%s",sprintChar);
}
break;
交换MTU,设置到GATT 服务端编译支持的最大 251。
注意:此过程一定要发生在读取特征值之前,否则回超时。
--------------------------------------------------------------------
[26] : <Rx> - 05:28:39.401
-Type : 0x04 (Event)
-EventCode : 0x00FF (HCI_LE_ExtEvent)
-Data Length : 0x06 (6) bytes(s)
Event : 0x067F (1663) (GAP_HCI_ExtentionCommandStatus)
Status : 0x00 (0) (Success)
OpCode : 0xFD02 (ATT_ExchangeMTUReq)
DataLength : 0x00 (0)
Dump(Rx):
0000:04 FF 06 7F 06 00 02 FD 00 .........
--------------------------------------------------------------------
[27] : <Rx> - 05:28:39.511
-Type : 0x04 (Event)
-EventCode : 0x00FF (HCI_LE_ExtEvent)
-Data Length : 0x08 (8) bytes(s)
Event : 0x0503 (1283) (ATT_ExchangeMTURsp)
Status : 0x00 (0) (Success)
ConnHandle : 0x0000 (0)
PduLen : 0x02 (2)
ServerRxMTU : 0x00FB (251)
Dump(Rx):
0000:04 FF 08 03 05 00 00 00 02 FB 00 ...........
--------------------------------------------------------------------
[28] : <Rx> - 05:28:39.521
-Type : 0x04 (Event)
-EventCode : 0x00FF (HCI_LE_ExtEvent)
-Data Length : 0x08 (8) bytes(s)
Event : 0x057F (1407) (ATT_MtuUpdatedEvt)
Status : 0x00 (0) (Success)
ConnHandle : 0x0000 (0)
PduLen : 0x02 (2)
MTU : 0x00FB (251)
Dump(Rx):
0000:04 FF 08 7F 05 00 00 00 02 FB 00 ...........
--------------------------------------------------------------------
成功写入,我们将读取Attribute Value 前面和后面都改成FFFFFF03
和C6FFFF
尽管我们通过GATT 服务端配置更大MTU,通过GATT Client 交换,成功读取200字节的特征值,但是在我们抓包分析不难发现在Controller,的链路层还是被拆分为最大长度27字节发送。通过前面分析我们已经不难理解,BLE4.2/5.0为了兼容 BLE4.0/4.1设备,所以默认TX PDU 设计成了27字节。尽管我们再Host配置更了更大的ATT_MTU,其实只是L2CAP帮我们进行了拆分和重组。为了获取到更大数据吞吐量,我们继续研究 BLE4.2/5.0新增功能 LE Data Extence。
因为我们更改的GATT 服务端也就是simple_peripheral 工程的默认MTU 大小,所以正常读写特征值前我们需要进行ATT_Exchange_MTU_Req
请求。否则会直接失败。
LE Data Length Extension 文章我们详细讲解过,BLE4.2/5.0 已经硬件支持257的LL Data Payload Size。但是为了兼容BLE4.1/4.0所以默认保留该功能, 现在我们详细讲解如何使能该功能,实现真正意义的最大ATT_MTU
发送,同时体验BLE4.2/5.0全新速率。
对于BLE4.2/5.0设备,默认支持TX_PDU大小为了兼容BLE4.1/4.0 设备,默认设置为27字节/328us,同时又为了又为了兼容BLE4.2/5.0RX PDU默认设置为251字节/2152us。对于默认使能 251/2120us。只需要主从收发的任意一方设置自己的TX_PDU为251字节/2120us,那么收发双发就会直接发起协商功能。
所以这里我们直接设置Peripheral/Salve TX_PDU为251字节/2120us。在我们simple_peripheral工程 SimpleBLEPeripheral_init
加入以下代码
#define APP_SUGGESTED_PDU_SIZE 251
#define APP_SUGGESTED_TX_TIME 2120
//This API is documented in hci.h
HCI_LE_WriteSuggestedDefaultDataLenCmd(APP_SUGGESTED_PDU_SIZE ,APP_SUGGESTED_TX_TIME);
+----------------------------------------------------+----------------- - - -
| Packet sniffer frame header |
+----+-------------+-------------------------+-------+
|info| Packet nbr. | Time stamp | Length| Packet data
+----+-------------+-------------------------+-------+----------------- - - -
| 01 | E1 00 00 00 | 29 0C 05 0E 02 00 00 00 | 15 00 | 14 BD AA 6D C4 0F 09 **14** ** FB 00 48 08 1B 00 48 01 ** 75 9A 9B 2A 9A
+----+-------------+-------------------------+-------+----------------- - - -
+----------------------------------------------------+----------------- - - -
| Packet sniffer frame header |
+----+-------------+-------------------------+-------+
|info| Packet nbr. | Time stamp | Length| Packet data
+----+-------------+-------------------------+-------+----------------- - - -
| 01 | E2 00 00 00 | C2 0A 07 0E 02 00 00 00 | 15 00 | 14 BD AA 6D C4 0B 09 **14** ** FB 00 48 08 1B 00 48 01** B3 1B C9 2E 9A
+----+-------------+-------------------------+-------+----------------- - - -
+----------------------------------------------------+----------------- - - -
| Packet sniffer frame header |
+----+-------------+-------------------------+-------+
|info| Packet nbr. | Time stamp | Length| Packet data
+----+-------------+-------------------------+-------+----------------- - - -
| 01 | E3 00 00 00 | 3C 0B 85 10 02 00 00 00 | 15 00 | 14 BD AA 6D C4 03 09 **15** **FB 00 48 08 1B 00 48 01** C7 83 7F 26 82
+----+-------------+-------------------------+-------+----------------- - - -
+----------------------------------------------------+----------------- - - -
| Packet sniffer frame header |
+----+-------------+-------------------------+-------+
|info| Packet nbr. | Time stamp | Length| Packet data
+----+-------------+-------------------------+-------+----------------- - - -
| 01 | E4 00 00 00 | D7 09 87 10 02 00 00 00 | 15 00 | 14 BD AA 6D C4 07 09 **15** **FB 00 48 08 1B 00 48 01** 01 02 2D 34 82
+----+-------------+-------------------------+-------+----------------- - - -
文章所有代码、工具、文档开源。加入我们QQ群 591679055获取更多支持,共同研究CC2640R2F&BLE5.0。