蓝牙GATT专题之实战篇

蓝牙GATT中,GATT客户端和GATT服务器和蓝牙主机/蓝牙从机的关系 ?GATT客户端是否既可以作为蓝牙主机也可以作为蓝牙从机 ?

我用最清晰、不绕弯的方式给你讲透蓝牙 GATT 里这几组角色关系,保证你一次性彻底懂。

一、先把两个维度拆开

蓝牙 BLE 里有两套完全独立的角色体系

1. 物理链路角色(谁发起连接 = 链路层)

  • 主机(Master / Central):主动发起连接的一方
  • 从机(Slave / Peripheral):广播、被连接的一方

2. 数据交互角色(谁读写数据 = GATT 层)

  • GATT 服务器(Server):拥有属性表(服务、特征值),被读写的一方
  • GATT 客户端(Client):主动去读、写、订阅服务器数据的一方

二、它们的关系是什么?

一句话总结:

链路层角色(主机/从机)和 GATT 角色(客户端/服务器)完全独立,互不绑定。

  • 主机 可以是 GATT 客户端,也可以是 GATT 服务器
  • 从机 可以是 GATT 客户端,也可以是 GATT 服务器
  • 只是现实中常见搭配
    • 手机(主机) ←→ GATT 客户端
    • 手环/手表(从机) ←→ GATT 服务器

但这只是习惯,不是规则


三、核心问题:GATT 客户端是否既可以作为主机,也可以作为从机?

✅ 答案:完全可以,而且非常常见。

举例 1:手机(主机)= GATT 客户端

连手表 → 读心率、写指令
这是最常见。

举例 2:手表(从机)= GATT 客户端

手表主动去读手机上的服务特征,比如读手机电量、读消息。
此时:

  • 链路层:手表 = 从机
  • GATT 层:手表 = GATT 客户端

完全合法,标准支持。

举例 3:两个设备互相读写

A(主机)= GATT 客户端
B(从机)= GATT 客户端
两边都有服务器,互相读写。
BLE 5.0+ 非常普遍。


四、最关键结论(背会这 3 句)

  1. 主机/从机 = 谁发起连接(链路层)
  2. GATT 客户端/服务器 = 谁主动读写数据(GATT 层)
  3. GATT 客户端既可以是主机,也可以是从机,没有任何限制

五、极简记忆版

  • 主/从 = 连接方向
  • 客户端/服务器 = 数据读写方向
  • 两套角色正交、独立、可任意组合

如果你需要,我可以给你画一张角色关系图,或者给你一段NuttX / Zephyr 里双向 GATT 客户端的实际代码示例

在实际蓝牙手表中,蓝牙手表要使用GATT时,蓝牙手表是作为GATT服务端还是作为GATT客户端 ?

实际蓝牙手表里的真实角色(99% 场景都是这样)

结论先说

蓝牙手表 = 绝大多数时候是 GATT Server(服务端)
手机 = GATT Client(客户端)


一、最典型、最常见的情况

  • 蓝牙手表:GATT Server
    • 提供服务:心率、步数、电量、设备信息
    • 等着手机来读、来写、来订阅通知
  • 手机(APP):GATT Client
    • 主动去连手表
    • 主动读数据、写指令、开通知

为什么?
因为数据都存在手表里,手表是“数据提供者”,天然就是 Server。


二、那手表会不会也做 GATT Client?

会,但属于次要、辅助角色,不是主角色。

比如:

  • 手表主动去读手机的电量
  • 手表主动去读手机上的某些特征值

这时:

  • 手表 = GATT Client
  • 手机 = GATT Server

但这属于双向通信,是附加功能,不是标配。


三、一句话总结(你开发时直接按这个来)

  1. 手表主要角色:GATT Server
  2. 手机主要角色:GATT Client
  3. 手表偶尔可以兼职做 GATT Client,但不是必须

四、对应到你现在的代码场景

你在做小米 VELA / NuttX 手表开发:

  • 手表端:实现 GATT Service(服务)
    • 定义 Service UUID + Characteristic
    • 提供数据,响应手机读写
  • 手机APP:作为 GATT Client 去访问手表

这就是标准、正确、工业界通用结构。

需求:从串口操作蓝牙设备,用AT指令去操作蓝牙设备实现2个蓝牙设备的连接配对,数据收发,那么在AT指令去操作数据发送的时候,蓝牙协议栈底层是否建立GATT服务端,GATT客户端,然后用GATT的读写来实现的数据收发 ?

image

答案是yes,必须建立gatt,因为ble就是用gatt来传输数据的。

如果使用信驰达蓝牙透传模块,那么在使用透传模块收发数据时,是否经过GATT的读写 ?

是的,使用信驰达蓝牙透传模块收发数据时,数据底层是经过 GATT(通用属性协议)的读写操作来完成的。

虽然从你(用户)的角度看,只是在通过串口发送和接收原始数据,但模块内部已经自动帮你完成了复杂的蓝牙协议转换。整个数据流向可以分为“用户视角”和“底层技术视角”两个层面:

image

1. 用户视角:简单的串口“透传”

从开发者的角度看,整个过程被大大简化了:

  • MCU发数据:你的MCU(如STM32)只需要把要发的数据通过串口(UART)发给信驰达模块。
  • 手机收数据:手机APP就能收到这个数据。
  • 反之亦然:手机APP发来的数据,也会从模块的串口原封不动地输出,被你的MCU接收。

你完全不需要关心蓝牙协议栈内部的细节,就像使用一根无线串口线一样。

2. 技术视角:自动完成的GATT读写

在模块内部,为了让数据在蓝牙无线通道上传输,必须遵循BLE的GATT规范。信驰达透传模块的固件预设了一个“透明传输服务”,这个服务通常定义了3个特定的通道(UUID,即通用唯一标识符)来完成数据的收发:

方向 使用的GATT通道 (UUID) 作用
MCU → 手机 Notify (如 0xFFF1) 模块主动“通知”手机,把从串口收到的数据发出去。
手机 → MCU Write (如 0xFFF2) 手机“写入”数据到这个通道,模块接收后转发到串口。
服务定义 Service (如 0xFFF0) 用于声明这是一个提供透传服务的“容器”。

数据接收和发送的具体过程如下:

  • MCU发送数据,手机接收(使用GATT Notify操作)

    1. 你的MCU通过串口发送数据“ABC”给信驰达模块。
    2. 模块内部的固件自动将这个数据打包,并通过GATT协议中的 Notify 操作发送出去。
    3. 手机APP需要先订阅(Enable Notification)这个Notify通道,之后就能收到“ABC”了。
  • 手机发送数据,MCU接收(使用GATT Write操作)

    1. 手机APP将数据“123”通过GATT的 Write 操作,写入到模块预设的Write通道(如0xFFF2)。
    2. 模块收到这条写入请求后,会提取出里面的有效数据“123”。
    3. 最后,模块通过串口将“123”发送给你的MCU。

重要概念澄清:这里提到的 ServiceCharacteristic 是GATT协议的核心概念。Service是一个服务容器,Characteristic是这个服务下具体的数据通道。透传模块正是利用Notify类型的Characteristic来实现模块→手机的高速数据推送,避免了手机不停地发起查询请求。

总结

总而言之,信驰达透传模块的核心价值就是“封装复杂性”。它将底层的 GATT 协议读写操作(如WriteNotify)与上层的 用户串口数据 做了一个无缝的“翻译”和“转发”。

所以,在使用时你可以完全忽略GATT的存在,像操作普通串口设备一样进行开发。只有当需要自己开发手机APP时,才需要按照模块手册中规定的Service UUID和Characteristic UUID(如0xFFF0, 0xFFF2, 0xFFF1)进行对应的GATT读写编程。

ESP32的官方教程
https://docs.espressif.com/projects/esp-idf/zh_CN/stable/esp32/api-guides/c.html

ble测试吞吐时,MTU会影响吞吐吗

非常明确、直接的结论:

MTU 对 BLE 吞吐率影响巨大,而且是决定性因素之一。

简单一句话:
MTU 越大,单次能发的数据越多,发包次数越少,吞吐率越高。


1. 为什么 MTU 会影响吞吐?

BLE 并不是“一直发”,而是按数据包发送

  • 默认 MTU = 23 字节
    有效应用数据只有 20 字节
  • MTU = 247 字节
    有效应用数据可达 244 字节

同样发 1024 字节数据:

  • MTU23:要发 52 包
  • MTU247:只需要 5 包

发包次数越少,空包、间隔、交互开销占比越低 → 吞吐率大幅上升


2. 真实吞吐大概是什么水平?(直观感受)

  • MTU 23(默认):
    吞吐 ≈ 1~2 KB/s
  • MTU 128:
    吞吐 ≈ 8~12 KB/s
  • MTU 247(最大):
    吞吐 ≈ 15~25 KB/s(取决于连接间隔、Phy 等)

可以看到:
MTU 从 23 → 247,吞吐能翻 10 倍以上。


3. 为什么不是 MTU 越大越好?

有上限:

  • BLE 最大 ATT MTU = 247 字节
  • 再大协议不支持

另外:

  • MTU 过大会增加 单次传输延迟
  • 对低功耗不太友好
  • 部分老旧设备不支持大 MTU

测吞吐时,一定开到 247


4. 除了 MTU,还有谁影响吞吐?

按重要程度排序:

  1. MTU 大小(你问的这个)
  2. 连接间隔 Connection Interval
  3. PHY 速率(2M PHY)
  4. 是否打开 DLE(数据长度扩展)
  5. 从机 latency / 监督超时

5. 最终总结(最精炼版)

  • MTU 直接决定 BLE 吞吐上限
  • MTU 越大,吞吐越高
  • 默认 MTU23 吞吐极低,测吞吐量必须 协商到 247
  • 信驰达、汇顶、泰凌微等 BLE 模块都一样

如果你需要,我可以给你:

  • 一套测 BLE 吞吐的最优参数组合(MTU+Interval+PHY)
  • 或者GATT 吞吐测试的简单计算公式

实现一个GATT客户端测吞吐的服务,请解释出你的详细设计
GATT流程中,从GATT注册属性表,到GATT读写数据,完整的流程是什么样的 ?

蓝牙的各种包,广播包,不同的广播包,数据包长什么样子?用抓包软件抓出来的包长什么样子 ?
在蓝牙连接成功后,用GATT交互的成功中的抓包是什么样的 ?

小米使用NimBLE蓝牙协议栈
乐鑫esp32使用的蓝牙协议栈是NimBLE吗---乐鑫ESP-IDF 同时提供两套 BLE 主机协议栈:Bluedroid(默认)和 NimBLE(可选)

怎么理解特征?怎么理解描述符 ?怎么理解属性 ?

1,特征,到底什么是特征,是存储实际业务数据内容的载体。比如:心率/血氧/电量。
2,首先,概念混淆,BLE中没有描述符这个概念,有特征描述符,特征描述符是专门针对特征的配置/额外信息。
那么,特征描述符配置了特征的哪些功能 ?---特征描述符配置通知/指示功能的打开和关闭。
3,属性是什么,初始化时注册的属性表,就是注册了特征,描述符,所以属性是特征/特征描述符的集合。

蓝牙的服务有16bit的UUID和128bit的UUID,什么场景下用16bit的UUID,什么场景下使用128bit的UUID ?

超清晰区分:16bit UUID vs 128bit UUID 使用场景

一句话结论

  • 16bit UUID:只用于 蓝牙 SIG 官方定义的标准服务(你不能自己定义)
  • 128bit UUID:用于 厂商自定义服务、私有协议、你自己写的业务(你随便用)

1. 什么时候用 16bit UUID

满足两个条件才用:

  1. 这个服务/特征是 蓝牙 SIG 官方规定
  2. 你要实现的是标准蓝牙 Profile

典型场景:

  • 设备信息服务:0x180A
  • 电池服务:0x180F
  • 心率服务:0x180D
  • 串口透传标准 SPP 对应 BLE 标准服务(如果有的话)
  • 血糖、体重、温度计等行业标准 Profile

特点:

  • 短、省空间、协议效率高
  • 你不能自己申请、不能自己瞎编
  • 只有 SIG 有权分配

2. 什么时候用 128bit UUID

只要不是官方标准服务,全部用 128bit

典型场景:

  • 智能手表/手环 自定义协议(步数、控制、升级、消息)
  • 你和手机APP之间 私有通信协议
  • 厂商自己的透传服务(信驰达、安信可等模块的串口透传)
  • 任何你自己发明的业务功能
  • 不想和别人冲突、需要全局唯一的服务

特点:

  • 极长,几乎不可能重复
  • 你可以自己随便生成
  • 厂商私有业务标配

3. 实际开发中的真实规则(非常重要)

你的蓝牙手表场景:

  • 系统服务、电池、设备信息 → 16bit UUID
  • 你的APP和手表之间的自定义通信 → 128bit UUID

信驰达 BLE 模块透传:

  • 它用的就是 128bit UUID
    例如:6E400001-B5A3-F393-E0A9-E50E24DCCA9E
  • 因为这是厂商自定义串口服务,不是SIG标准

4. 极简记忆口诀

  • 官方标准 → 16bit
  • 自己写的 → 128bit
  • 私有业务、透传、自定义协议 → 一律 128bit

如果你愿意,我可以
直接给你生成一个不重复、可用于你手表项目的 128bit UUID,拿来就能用。

蓝牙测吞吐时,客户端使用的是GATT的写属性,还是读属性,还是通知或者指示 ?

测吞吐用 Write Without Response(ATT Write Command),配合 LE 2M PHY + 517 MTU + 短连接间隔,可达到蓝牙 LE 最大吞吐(约 1.4 Mbps 应用层净荷)
image

GATT中通知和指示的区别?
之前一看概念,一个有响应,一个无响应,但具体哪个有响应,哪个无响应,这个忘记了。
image

image

第二次看这个区别的时候,发现通知是在BLE有数据变化时才发送给对端的动作,知道使用具体场景了。

GATT中读写操作是都开启的吗?还是可以只开其中一个操作?
通知和指示是怎么使用的 ?是必须使用的吗 ?
用心率服务来解释下GATT的流程中上面问题是怎么处理的?
不是都开启,不用读写操作就可以不开启,直接用通知就行,心率上报给手机就是直接用通知来实现的。
通知和指示不是必须使用的,是可以在特征描述符中进行配置,是否打开或者关闭通知/指示。
如果只是心率数据的话,直接用GATT中的通知就行,不需要数据读写。

BLE中的连接间隔对吞吐的影响是什么
BLE(蓝牙低功耗)中的连接间隔(Connection Interval) 对吞吐量有直接影响,这是一个核心的性能调优参数。

连接间隔与吞吐量的关系

基本公式

理论最大吞吐量 = 每包数据字节 × 每连接事件包数 × (1000ms / 连接间隔)
连接间隔 每秒连接事件数 典型吞吐趋势
7.5 ms(最小) ~133 次 最高吞吐、最高功耗
50 ms 20 次 中等吞吐、平衡功耗
100 ms 10 次 较低吞吐、较低功耗
4000 ms(最大) 0.25 次 最低吞吐、最低功耗

关键机制

1. 连接事件(Connection Event)

时间线:
|---[连接事件]---|---[连接事件]---|---[连接事件]---|
    ↑ 数据交换      ↑ 数据交换       ↑ 数据交换
    间隔 = 连接间隔
  • 每个连接事件是主从设备短暂通信的时间窗口
  • 间隔越短 → 通信窗口越频繁 → 吞吐越高
  • 间隔越长 → 通信窗口越稀疏 → 吞吐越低

2. 每事件多包传输

在一个连接事件内,设备可以连续发送多个数据包:

  • BLE 4.2/5.0+ 支持 6 个包/连接事件(甚至更多)
  • 有效吞吐 = 单包载荷 × 每事件包数 × 每秒事件数

实际数据对比

参数配置 理论数据速率 适用场景
7.5ms 间隔 + 251 字节 PDU ~100+ kbps OTA 升级、大批量数据传输
50ms 间隔 + 251 字节 PDU ~20 kbps 传感器数据上报
100ms 间隔 + 27 字节 PDU ~2 kbps 低功耗心跳包

代码配置示例

// 从机请求的连接参数(以 Nordic SDK 为例)
ble_gap_conn_params_t conn_params = {
    .min_conn_interval = MSEC_TO_UNITS(7.5, UNIT_1_25_MS),   // 最小 7.5ms
    .max_conn_interval = MSEC_TO_UNITS(20, UNIT_1_25_MS),    // 最大 20ms
    .slave_latency     = 0,                                    // 从机延迟
    .conn_sup_timeout  = MSEC_TO_UNITS(4000, UNIT_10_MS),    // 监督超时
};

// 主机(手机)可能接受或拒绝,最终值在 min~max 之间协商

权衡:吞吐量 vs 功耗

高吞吐 ←————————————————→ 低功耗
  短间隔                    长间隔
  7.5ms                     100ms+
  频繁唤醒                  深度睡眠
  高功耗                    电池友好

优化建议

场景 推荐策略
需要高吞吐(文件传输、OTA) 协商最短连接间隔(7.5-20ms),使用 Data Length Extension(251 字节 PDU)
平衡模式(一般传感器) 50-100ms 间隔,根据数据量动态调整
超低功耗(信标、门锁) 延长到 500ms-4s,牺牲吞吐换取续航

其他影响因素

连接间隔只是吞吐的一个维度,还需配合:

  • MTU 大小:更大的 MTU 减少包头开销
  • Data Length Extension:BLE 4.2+ 支持 251 字节 PDU(vs 经典 27 字节)
  • PHY 速率:BLE 5.0 的 2M PHY 直接翻倍吞吐
  • 从机延迟(Slave Latency):允许从机跳过 N 个事件,节能但不影响峰值吞吐

总结:连接间隔是吞吐与功耗的核心权衡参数,缩短间隔能线性提升吞吐,但会显著增加功耗。实际应用中应根据业务场景协商最优值。

posted @ 2026-04-15 15:10  wzm888  阅读(38)  评论(0)    收藏  举报