摘要ATT MTU 大小直接决定 BLE 单包有效载荷上限。默认 23 字节的 ATT_MTU 仅能承载 20 字节应用数据严重制约吞吐量。本文系统阐述 Zephyr 平台下 Peripheral 端 MTU 更新的实现方法涵盖 MTU 语义辨析、协商机制、Kconfig 配置、运行时 API 调用及回调验证。研究表明Peripheral 端提升 MTU 需同时满足链路层缓冲区扩容与ATT 层 MTU 配置两项前提并通过bt_gatt_exchange_mtu()或CONFIG_BT_GATT_AUTO_UPDATE_MTU触发协商最终以bt_gatt_get_mtu()验证协商结果。关键词BLEATT MTUZephyrPeripheralGATT吞吐量优化2 MTU 语义辨析在讨论实现之前需厘清三个易混淆的概念表 1 BLE 中三种 MTU 的语义与配置方式概念定义默认值Zephyr 配置符号ATT_MTUATT PDU 最大长度23CONFIG_BT_L2CAP_TX_MTUL2CAP MTUL2CAP SDU 最大长度随 ATT_MTU无独立符号由 ACL 缓冲区推导LL PDU链路层射频包载荷27由 DLE 控制非 MTU 配置三者中ATT_MTU 是协商的对象链路层 PDU 大小由 DLE 独立管理与 ATT_MTU 无直接耦合。但 ATT_MTU 的提升必须以足够的链路层缓冲区为前提否则协商会因缓冲区不足而失败或被截断。3 Peripheral 端 MTU 更新机制3.1 协商方向与对称性约束ATT MTU 协商通过一次 Exchange MTU Request/Response 完成。协商结果为min(client_rx_mtu, server_rx_mtu)双方在响应发出后立即应用该值。两条关键规则需特别注意每连接一次交换。先发起方锁定 MTU 值后续再发 Exchange MTU Request 将返回 “Request Not Supported”。因此 Peripheral 端应在 Central 尚未发起交换的窗口期内主动触发否则将失去发起机会。对称性强制要求。蓝牙规范规定 ATT MTU 必须对称即使仅需发送大包而不接收也必须同时配置接收方向的 ACL 缓冲区。3.2 Central 优先发起的现实iOS 与 Android 的 BLE 协议栈均在连接建立后自动发起 MTU 交换。这意味着 Peripheral 端在多数场景下是 MTU 协商的被动响应方而非发起方。Peripheral 端提升 MTU 的正确策略是配置足够大的本地 MTU 上限使 Central 发起交换时能够协商出更大的值。4 Kconfig 配置Peripheral 端支持 247 字节 ATT_MTU 的最小配置集如下CONFIG_BT_L2CAP_TX_MTU247 CONFIG_BT_BUF_ACL_RX_SIZE251 CONFIG_BT_BUF_ACL_TX_SIZE251各参数的含义与依赖关系CONFIG_BT_L2CAP_TX_MTU247设置本地 ATT TX MTU。Zephyr 中CONFIG_BT_L2CAP_TX_MTU决定了发送方向可协商的最大 ATT_MTU 值。CONFIG_BT_BUF_ACL_RX_SIZE251ACL 接收缓冲区大小。需为 ATT_MTU 4 字节 L2CAP 头部。251 247 4。CONFIG_BT_BUF_ACL_TX_SIZE251ACL 发送缓冲区大小同样需为 ATT_MTU 4。若使用 EATTEnhanced ATTCONFIG_BT_BUF_ACL_RX_SIZE还需额外增加 SDU 长度字段共需 ATT_MTU 6。关键陷阱仅设置CONFIG_BT_L2CAP_TX_MTU而不增大 ACL 缓冲区是 Zephyr BLE 项目中最常见的静默失败模式——协商看似成功但数据发送时因缓冲区不足而被截断或触发断言。如需将有效载荷提升至 260 字节应配置CONFIG_BT_L2CAP_TX_MTU263260 3 字节 ATT 头部同时将 ACL 缓冲区设为 267。5 运行时实现5.1 方案 A自动更新推荐Zephyr 提供 Kconfig 选项在连接建立后自动发起 MTU 交换CONFIG_BT_GATT_AUTO_UPDATE_MTUy该选项在每次连接后自动调用bt_gatt_exchange_mtu()使用本地配置的最大 MTU 值。适用于 Perpheral 端需主动发起交换的场景配置最为简洁。但该方式为连接后自动触发无法根据业务逻辑动态控制且若 Central 已抢先完成交换自动更新调用将返回-EALREADY。5.2 方案 B手动调用灵活控制若需在特定时机如收到某特征写入后再触发 MTU 交换可手动调用bt_gatt_exchange_mtu()。注意 Peripheral 端手动发起需先启用CONFIG_BT_GATT_CLIENTy因为 Exchange MTU 属于 GATT 客户端过程。以下为多连接场景下使用独立交换参数结构体的实现#include zephyr/bluetooth/gatt.h static struct bt_gatt_exchange_params mtu_exchange_params[CONFIG_BT_MAX_CONN]; static void mtu_exchange_cb(struct bt_conn *conn, uint8_t err, struct bt_gatt_exchange_params *params) { if (err 0U) { uint16_t mtu bt_gatt_get_mtu(conn); printk(MTU exchange successful, new MTU: %u\n, mtu); } else { printk(MTU exchange failed (err %u)\n, err); } } static int mtu_exchange(struct bt_conn *conn) { uint8_t conn_index bt_conn_index(conn); int err; mtu_exchange_params[conn_index].func mtu_exchange_cb; err bt_gatt_exchange_mtu(conn, mtu_exchange_params[conn_index]); if (err) { printk(bt_gatt_exchange_mtu failed (err %d)\n, err); } return err; }调用bt_gatt_exchange_mtu()需注意以下约束每连接仅可调用一次重复调用返回-EALREADYparams结构体必须在回调执行前保持有效若 ATT 请求队列已满函数可能阻塞除非在 BT RX 线程中调用此时返回-ENOMEM回调在CONFIG_BT_RECV_CONTEXT指定的上下文中执行。在连接回调中触发交换static void connected(struct bt_conn *conn, uint8_t err) { if (err) return; mtu_exchange(conn); }多连接场景中mtu_exchange_params必须为每个连接维护独立实例。共用同一结构体会导致并发交换相互干扰。5.3 协商结果验证无论采用哪种方案均不应假设协商结果而应通过回调或 API 验证。Zephyr 提供两种验证途径方法一bt_gatt_get_mtu()在交换回调中读取协商后的 ATT_MTUuint16_t att_mtu bt_gatt_get_mtu(conn); uint16_t payload_mtu att_mtu - 3; /* 扣除 ATT 头部 */方法二bt_gatt_cb回调注册 ATT MTU 更新回调无论交换由哪一方发起均可收到通知static void mtu_updated(struct bt_conn *conn, uint16_t tx, uint16_t rx) { printk(Updated MTU: TX: %u RX: %u\n, tx, rx); } static struct bt_gatt_cb gatt_callbacks { .att_mtu_updated mtu_updated, }; /* 在 bt_enable() 之后注册 */ bt_gatt_cb_register(gatt_callbacks);该方法的优势在于不依赖应用层主动发起交换Central 自动完成交换后 Peripheral 端仍能获知结果。6 典型问题与处理6.1 MTU 停留在 23 不更新若 Central 端如 Android 手机自动发起 MTU 交换但 Peripheral 端未配置足够大的CONFIG_BT_L2CAP_TX_MTU协商结果将受限于 Peripheral 端的接收 MTU即CONFIG_BT_BUF_ACL_RX_SIZE - 4。例如若 ACL RX 缓冲区仍为默认值 27 字节则 L2CAP RX MTU 仅为 23协商结果无法提升。6.2 协商成功但发送大包失败常见原因是 ACL 缓冲区配置不足。即使CONFIG_BT_L2CAP_TX_MTU设为 247若CONFIG_BT_BUF_ACL_TX_SIZE仍为默认值发送 244 字节载荷时将触发截断或返回-EMSGSIZE。务必确认 ACL TX 缓冲区 ≥ ATT_MTU 4。6.3 重复调用导致 “Request Not Supported”bt_gatt_exchange_mtu()每连接仅可成功调用一次。若CONFIG_BT_GATT_AUTO_UPDATE_MTUy已启用同时在连接回调中手动调用第二次调用将返回-EALREADY。应在启动手动交换前确认自动更新已关闭。6.4 MTU 协商后连接异常nRF54L15 上报告过 Windows 主机发起 Exchange MTU RequestClient MTU 527后连接无响应的案例。此类问题的根因通常为缓冲区溢出或控制器资源不足需确认CONFIG_BT_BUF_ACL_RX_SIZE是否足以容纳 Central 端请求的 MTU 值。7 结论Zephyr Peripheral 端 MTU 更新的核心在于配置先行、协商触发、结果验证三步闭环。Kconfig 层面需同时提升CONFIG_BT_L2CAP_TX_MTU与 ACL 缓冲区大小二者缺一不可运行时通过CONFIG_BT_GATT_AUTO_UPDATE_MTU或bt_gatt_exchange_mtu()触发协商最终以bt_gatt_get_mtu()或bt_gatt_cb.att_mtu_updated验证协商结果。247 字节 ATT_MTU244 字节应用载荷是与链路层 DLE 对齐的推荐目标值。对于从 nRF5 SDK 迁移的项目应注意 Zephyr 中 MTU 配置已完全 Kconfig 化不再提供运行时直接设置 MTU 的 API。
阅读完成 · 觉得有帮助?