蓝牙低功耗(Bluetooth Low Energy,简称BLE)是蓝牙技术联盟在2010年推出的4.0版本中引入的重要特性。与传统蓝牙相比,BLE在保持类似通信范围的同时大幅降低了功耗,使其成为物联网设备的理想选择。理解BLE的服务(Service)、特征(Characteristic)和描述(Descriptor)这三个核心概念,是开发BLE应用的基础。
在BLE架构中,每个设备都遵循客户端-服务器模型。外围设备(Peripheral)作为服务器提供数据,中央设备(Central)作为客户端访问这些数据。这种数据交换通过GATT(通用属性协议)规范定义的层级结构实现,而服务、特征和描述正是构成这个层级结构的三大要素。
服务是BLE设备功能的基本组织单元,可以理解为一个功能模块或数据集合。每个服务都有一个唯一的UUID(通用唯一标识符)来标识其类型。UUID有16位和128位两种格式,16位UUID由蓝牙技术联盟标准化,而128位UUID通常由开发者自定义。
例如,心率服务使用0x180D这个16位UUID,设备信息服务使用0x180A。一个BLE设备可以同时提供多个服务,比如一个智能手环可能同时提供心率服务、电池服务和设备信息服务。
蓝牙技术联盟定义了一系列标准服务,这些服务有预定义的功能和数据格式。使用标准服务的好处是不同厂商的设备可以互操作。常见的标准服务包括:
当标准服务无法满足需求时,开发者可以创建自定义服务,使用128位UUID。自定义服务提供了极大的灵活性,但也意味着需要自行定义数据格式和通信协议。
提示:在设计自定义服务时,建议参考现有标准服务的数据结构,这有助于保持一致性并简化开发。
特征是服务的下一级组成单元,实际承载着设备的数据和功能。每个特征也有自己的UUID,同样分为标准和自定义两类。特征比服务复杂得多,因为它包含多个属性:
特征的属性决定了客户端可以执行的操作,主要包括:
例如,心率测量特征通常设置为可通知,这样心率监测器可以在检测到新心率时主动通知连接的设备,而不需要设备不断轮询。
特征值的数据格式由特征类型决定。标准特征有明确定义的格式,如心率测量特征的第1字节包含标志位,指示后续数据的格式和内容。自定义特征需要开发者自行定义数据格式,建议采用TLV(类型-长度-值)等结构化格式以提高可扩展性。
描述是特征的元数据,提供关于特征的附加信息。最常见的描述是客户端特征配置描述(CCCD),它用于启用或禁用特征的通知和指示功能。其他常见描述包括:
CCCD是一个2字节的值,当客户端希望接收某个特征的通知或指示时,需要向对应的CCCD写入适当的配置:
例如,要接收心率测量特征的通知,客户端需要向心率测量特征的CCCD写入0x0001。这是一个非常容易出错的地方,很多BLE通信问题都源于CCCD配置不当。
注意:在Android开发中,启用通知需要两个步骤:先设置特征的通知属性,再写入CCCD。漏掉任何一步都会导致通知无法正常工作。
BLE设备的GATT层级结构可以表示为:
code复制设备(Device)
└── 服务(Service)
└── 特征(Characteristic)
├── 值(Value)
└── 描述(Descriptor)
一个设备包含多个服务,每个服务包含多个特征,每个特征又可能包含多个描述。客户端通过发现过程获取这个层级结构,然后根据需要与特定特征交互。
一个完整的BLE交互通常包括以下步骤:
不同平台(iOS/Android/嵌入式系统)对BLE的实现有细微差别,常见问题包括:
解决方案:
当特征值超过单个数据包大小时(通常是20字节),需要进行分包传输。处理这种情况的关键点:
BLE连接参数(连接间隔、从机延迟、监督超时)直接影响功耗和响应速度。优化建议:
在复杂应用中,多个特征可以协同工作。例如,一个环境监测设备可能包含:
这种设计实现了数据采集、配置和控制的功能分离,提高了系统的模块化和可维护性。
BLE通信安全要点:
对于电池供电的BLE设备,功耗优化至关重要:
| 问题现象 | 可能原因 | 解决方案 |
|---|---|---|
| 无法发现服务 | 服务UUID不正确 | 检查UUID格式和值 |
| 读取特征失败 | 特征不可读 | 检查特征属性 |
| 通知不工作 | CCCD未配置 | 正确写入CCCD |
| 连接频繁断开 | 连接参数不当 | 调整连接参数 |
| 数据传输错误 | MTU大小限制 | 协商更大的MTU或分包 |
在实际项目中,我发现最常被忽视的是CCCD的配置和连接参数的优化。很多开发者花费大量时间调试通信问题,最终发现只是忘记正确配置CCCD。另一个常见误区是过度依赖自定义UUID,其实在很多情况下,使用或扩展现有标准服务是更好的选择。