4. GATT服务与特征:Service/UUID定义、Characteristic属性、CCC配置、Handle机制
好,咱们进入正题。GATT,全称是通用属性协议(Generic Attribute Profile)。说白了,它就是蓝牙设备之间怎么交换数据的规矩。你想想看,血糖仪测出个数值,总得告诉手机吧?这个“告诉”的过程,就是GATT在管。
我个人习惯,把GATT想象成一个文件柜。柜子里有抽屉(Service),抽屉里有文件夹(Characteristic),文件夹里装着具体的数据。嗯,这个比喻虽然简单,但很管用。
4.1 Service与UUID定义
Service,就是服务。一个蓝牙设备可以提供多个服务。比如血糖仪,可能有“血糖测量服务”、“设备信息服务”、“电池服务”等等。
每个Service都有一个唯一的ID,叫UUID(通用唯一标识符)。UUID有16位的,也有128位的。16位的是蓝牙联盟定义的标准服务,比如血糖服务就是0x1808。128位的是厂商自定义的,比如你开发个私有协议,就得自己生成一个。
我在项目中遇到过,有些工程师图省事,随便写个“0001”当UUID。结果呢?跟别的设备冲突了,数据乱套。所以,自定义服务一定要用128位UUID,别偷懒。
标准设备信息服务UUID: 0x180A
自定义服务示例: 0000XXXX-0000-1000-8000-00805F9B34FB
定义Service的时候,要注意层级关系。一个Service可以包含多个Characteristic,但Service之间是平级的,不能嵌套。
4.2 Characteristic属性
Characteristic,就是特征。它是实际存放数据的地方。每个Characteristic也有自己的UUID,也有自己的属性。
属性有哪些呢?我列一下常用的:
- Read(读):手机可以读取这个特征的值。比如读取血糖仪的序列号。
- Write(写):手机可以写入数据。比如设置血糖仪的时间。
- Notify(通知):设备主动给手机发数据。比如血糖测量完成,自动把结果推给手机。
- Indicate(指示):跟Notify类似,但需要手机确认收到。可靠性更高,但速度慢一点。
你想想看,血糖仪测完血糖,数值是自动上报的,还是手机去读的?当然是自动上报。所以血糖测量特征,必须要有Notify属性。
Characteristic还有权限控制。比如有些特征只能读不能写,有些需要配对后才能访问。这些都是在定义时设置好的。
4.3 CCC配置
CCC,全称是Client Characteristic Configuration,客户端特征配置。它是个描述符(Descriptor),专门用来开关Notify和Indicate的。
为什么需要CCC?因为设备不能随便给手机发通知。手机得先告诉设备:“嘿,你可以给我发通知了。”这个“告诉”的过程,就是写CCC。
CCC的值很简单:
- 0x0000:关闭通知和指示
- 0x0001:开启通知
- 0x0002:开启指示
我记得刚开始做蓝牙开发时,忘了配置CCC,结果手机死活收不到数据。查了两天,才发现是CCC没写。嗯,这个坑我替你们踩过了。
CCC的Handle是固定的。每个支持Notify或Indicate的Characteristic,都会自动生成一个CCC描述符。它的Handle通常是Characteristic的Handle+1或+2,具体看协议栈实现。
4.4 Handle机制
Handle,就是句柄。它是GATT协议里每个属性(Service、Characteristic、Descriptor)的编号。从0x0001开始,依次递增。
手机跟设备通信时,不靠名字,靠Handle。比如手机说:“我要读Handle 0x0010的数据。”设备就知道,哦,这是要读血糖值。
Handle是固定的吗?是的。一旦设备启动,所有属性的Handle就确定了。但不同品牌的芯片,Handle分配方式可能不同。比如Nordic的芯片,Service的Handle从0x0001开始,Characteristic的Handle紧随其后。
我举个例子,一个简单的血糖仪GATT表:
| Handle | 类型 | UUID | 说明 |
|---|---|---|---|
| 0x0001 | Service | 0x1808 | 血糖服务 |
| 0x0002 | Characteristic | 0x2A18 | 血糖测量特征 |
| 0x0003 | Value | 0x2A18 | 血糖值数据 |
| 0x0004 | Descriptor | 0x2902 | CCC配置 |
你看,Handle是连续的。手机要读血糖值,先通过Handle 0x0002找到特征,再读Handle 0x0003的数据。要开启通知,就写Handle 0x0004的CCC。
为什么不用UUID直接访问?因为UUID太长,128位呢,每次通信都带UUID太浪费带宽。Handle只有16位,效率高得多。
- Service是服务的容器,用UUID标识
- Characteristic是数据的载体,有读写通知等属性
- CCC是通知/指示的开关,重连后必须重新配置
- Handle是属性的编号,通信时用Handle代替UUID
好了,这一章的内容就这些。说白了,GATT就是一套规则,告诉你怎么组织数据、怎么访问数据。你只要把Service、Characteristic、CCC、Handle这四个概念搞明白,蓝牙数据传输这块就通了八成。
下一章,咱们聊聊实际的数据格式。血糖值怎么编码?时间戳怎么传?嗯,那才是真正动手的地方。