CBCharacteristic:是「特征」还是「数据通道」?
目录
- 名词困惑:两种中文译法的由来
- 官方定义 & 开发者视角
- 乐高类比:文件夹与文件
- 智能手表实例:Characteristic 长什么样?
- iOS 代码实战:读 / 写 / 订阅
- 小结 & Best Practice
1. 名词困惑:为什么有两种翻译?
英文 | 常见翻译 | 强调点 |
---|---|---|
Characteristic | 特征(直译) | 它是 Service 里的一个“特性”或“属性” |
数据通道(意译) | 它是真正承载 数据流 的最小单位 |
一句话记忆:Characteristic = 一个带权限的键值对 (UUID ➜ Data),你对它 读 / 写 / 监听,数据就在这条“管道”中流动。
2. 官方定义 & 开发者视角
“A value used by a service, plus its metadata and permitted operations.” —— Bluetooth GATT
- Value:真正的数据 (
Data
) - Metadata:UUID、描述符、属性位图
- Permitted operations:
.read .write .notify .indicate …
对 iOS 开发者而言:只有拿到 CBCharacteristic
,才能调用
peripheral.readValue(for:)
peripheral.writeValue(_:for:type:)
peripheral.setNotifyValue(true, for:)
Service 只是目录,Characteristic 才能触碰“文件内容”。
3. 乐高类比
BLE 元素 | 类比日常 | 解释 |
---|---|---|
Peripheral | 路由器 | 硬件本体 |
Service | USB 共享文件夹 | 分类功能 |
Characteristic | 文件 | 真正存/取数据 |
Property 位图 | 文件权限 | 读、写、订阅、签名… |
把 “文件” 读 / 写 / 订阅通知,就是在 Characteristic 管道 中收发字节流。
4. 智能手表实例
Service | Characteristic | 典型属性 | 用途 |
---|---|---|---|
Heart Rate (0x180D) | Measurement (0x2A37) | Notify | 实时心率流 |
Battery (0x180F) | Level (0x2A19) | Read/Notify | 电量 % |
Device Info (0x180A) | Firmware Rev (0x2A26) | Read | 显示版本 |
OTA 自定义 | Control Point (FF01) | Write | 升级指令 |
Data Packet (FF02) | Write Without Response | 升级数据块 |
一块主流手表大约 15–25 条 Characteristic,所有心率、计步、推送、升级字节都从这些“管道”进出。
5. iOS 代码实战
5.1 发现并订阅心率
// ⚑ 已连接 peripheral
let heartRateService = CBUUID(string: "180D")
let measurementChar = CBUUID(string: "2A37")peripheral.discoverServices([heartRateService])func peripheral(_ p: CBPeripheral, didDiscoverServices error: Error?) {guard let service = p.services?.first else { return }p.discoverCharacteristics([measurementChar], for: service)
}func peripheral(_ p: CBPeripheral,didDiscoverCharacteristicsFor service: CBService,error: Error?) {if let ch = service.characteristics?.first(where: { $0.uuid == measurementChar }) {p.setNotifyValue(true, for: ch) // 订阅}
}
5.2 解析 Heart Rate Measurement
func peripheral(_ p: CBPeripheral,didUpdateValueFor ch: CBCharacteristic,error: Error?) {guard let data = ch.value else { return }let flag = data[0]let bpm: Int = flag & 0x01 == 0? Int(data[1]) // 8-bit: Int(UInt16(littleEndian:data.withUnsafeBytes { $0.load(fromByteOffset: 1,as: UInt16.self) }))print("❤️ \(bpm) BPM")
}
flag & 0x01
按 GATT 规范判断 8-bit / 16-bit。
5.3 写入震动指令(自定义特征)
let vibrationCmd = Data([0x01, 0x64]) // 开启震动、强度 100
peripheral.writeValue(vibrationCmd,for: vibrationChar,type: .withResponse)
6. 小结 & Best Practice
-
翻译不重要,理解最重要:Characteristic 即“Service 中可操作的数据单元”。
-
设计协议时:
- 一功能一条 Characteristic,别塞“大杂烩”。
- 量大时拆 Control / Data 双通道。
- 能用 SIG 标准 UUID 就别自创。
-
代码层面:抽常量、集中解析
Data
,属性不符立刻抛错,避免隐式失败。
搞清楚 Characteristic 的角色,你就彻底打通 BLE 数据之路:读、写、订阅皆归一处,“特征” 即 “数据通道”。祝调试顺畅!