Characteristic 属性详解:Properties、Permissions、Value 与 CCCD

这一章,我们聊聊 Characteristic 的四个核心属性。

Properties、Permissions、Value、CCCD。这四个东西,说白了就是 GATT 服务的「四根柱子」。你调联调,调来调去,最后出问题,十有八九是这四根柱子没对齐。

我刚开始做 BLE 开发那会儿,就吃过这个亏。写了一个服务,手机端死活写不进去数据。查了两天,最后发现是 Permissions 配错了。嗯,从那以后,我再也不敢小看这几个属性了。

1. Properties —— 告诉对端「我能干什么」

Properties 是一个 8 位的 bitmask。它定义了这个 Characteristic 支持哪些操作。说白了,就是告诉对端:我能读、能写、能通知、还是能指示。

Bit 属性 含义
0 Broadcast 支持广播
1 Read 支持读操作
2 Write Without Response 支持无应答写
3 Write 支持有应答写
4 Notify 支持通知(无确认)
5 Indicate 支持指示(有确认)
6 Authenticated Signed Writes 支持签名写
7 Extended Properties 扩展属性描述符存在
我的习惯:Notify 和 Indicate 不要同时开。虽然协议允许,但很多手机端 SDK 处理不好。我一般只开 Notify,除非你明确需要确认机制。

2. Permissions —— 谁可以操作

Permissions 是 GATT 层的访问控制。它决定了:谁可以读?谁可以写?需不需要加密?需不需要配对?

你想想看,Properties 只是「声明」,Permissions 才是「执行」。你 Properties 开了 Write,但 Permissions 没配,那写操作照样失败。

Permissions 通常分为三类:

  • Access Permissions:读、写、读写
  • Authentication Permissions:是否需要配对/绑定
  • Authorization Permissions:是否需要应用层授权
我曾经踩过的坑:Properties 配了 Notify,但 Permissions 没开 Read。结果手机端订阅 CCCD 时,读不到当前值。因为 CCCD 的读取,本质上是一个 Read 操作。Permissions 没开 Read,CCCD 就读不回来。

3. Value —— 真正的数据

Value 就是 Characteristic 里存的那个值。它可以是任何长度,但协议推荐不超过 20 字节(MTU 默认 23 字节,减去 3 字节头)。

当然,MTU 可以协商。我做过一个项目,MTU 协商到 512 字节,Value 直接塞了 200 字节的传感器数据。但要注意:长包传输,对端不一定支持。

Value 的读写,受 Properties 和 Permissions 双重约束。举个例子:

// 伪代码示例:定义一个 Characteristic
characteristic = {
    uuid: "0x2A37",           // 心率测量
    properties: READ | NOTIFY, // 可读、可通知
    permissions: READ,         // 只允许读
    value: [0x00, 0x00],      // 初始值
    cccd: null                 // CCCD 由协议栈自动管理
}

这里有个细节:Value 的初始值,一定要合理。我见过有人把初始值设成 0xFF,结果手机端一读,以为设备出故障了。嗯,这种问题,调试起来很烦人。

4. CCCD —— 通知/指示的开关

CCCD 的全称是 Client Characteristic Configuration Descriptor。它是一个描述符,UUID 固定为 0x2902。

它的作用很简单:客户端(手机)通过写 CCCD,告诉服务端「我要不要收通知/指示」。

CCCD 的值只有两个字节:

值 含义
0x0000 关闭通知和指示
0x0001 开启通知(Notify)
0x0002 开启指示(Indicate)
0x0003 同时开启通知和指示(不推荐)
核心逻辑:CCCD 的写操作,必须由客户端发起。服务端不能主动修改 CCCD。你想想看,如果服务端能改,那客户端就收不到它不想收的数据了。这是协议设计上的一个保护机制。

5. 四者的关系 —— 一张图说清楚

下面这张图,是我自己画的关系图。每次做联调,我都会对着它过一遍。

Characteristic 四属性关系图 Characteristic Properties Permissions Value CCCD 关键关系: 1. Properties 声明能力,Permissions 控制权限 2. Value 的读写受 Properties 和 Permissions 双重约束 3. CCCD 只对 Notify/Indicate 有效,与 Read/Write 无关 4. CCCD 的写操作必须由客户端发起,服务端不能主动修改 5. 联调时,先检查 Properties,再检查 Permissions,最后看 CCCD

6. 联调时的检查清单

每次联调前,我都会过一遍这个清单。你可以直接拿去用:

  1. Properties 配对了没有?—— 特别是 Notify 和 Indicate,别漏了
  2. Permissions 和 Properties 一致吗?—— 开了 Notify,Permissions 要开 Read
  3. Value 初始值合理吗?—— 别设成 0xFF 这种奇怪的值
  4. CCCD 有没有被正确初始化?—— 默认应该是 0x0000
  5. 手机端有没有写 CCCD?—— 很多问题出在手机端没写 CCCD
一个小技巧:用 nRF Connect 抓一下。如果手机端写了 CCCD 0x0001,但设备没发 Notify,那问题大概率在设备端。反过来,如果手机端没写 CCCD,那设备端再努力也没用。

7. 总结一下

Properties 是「声明」,Permissions 是「控制」,Value 是「数据」,CCCD 是「开关」。四者缺一不可。

联调时,按这个顺序查:Properties → Permissions → Value → CCCD。别跳步,跳步容易漏。

我见过太多人,一上来就查代码,查了两天发现是 CCCD 没配。嗯,这种问题,其实一张表就能搞定。