当你用手机搜索附近的蓝牙设备时,那些突然出现在列表中的智能手环、耳机等设备,其实都在默默发送着一种特殊的数据包——BLE广播包。这就像是在派对上,每个人都在自我介绍:"我是健身手环,能测心率"、"我是智能灯泡,支持远程控制"。而扫描响应则是当有人对你感兴趣时,你可以进一步详细介绍自己。
广播包的数据结构非常精炼。一个标准的BLE4.x广播包最多37字节,除去6字节的设备MAC地址,实际可用仅31字节。这31字节被划分为若干个AD Structure,每个AD Structure包含三部分:
举个例子,当你看到广播数据"02 01 06"时:
Flags是广播包中唯一必须包含的AD Type,它就像设备的身份证基本信息。这个1字节的字段通过位掩码方式定义了设备的核心属性:
c复制// 典型BLE设备Flags配置示例
#define BLE_FLAGS_DEFAULT 0x06 // 二进制00000110
这个配置表示:
UUID是蓝牙服务的唯一标识,就像不同商店的营业执照编号。广播中常用的UUID类型包括:
| 类型值 | 名称 | 数据长度 | 典型用途 |
|---|---|---|---|
| 0x02 | 不完整16位UUID | 2*n字节 | 部分服务标识 |
| 0x03 | 完整16位UUID | 2*n字节 | 全部服务标识 |
| 0x06 | 不完整128位UUID | 16*n字节 | 自定义服务 |
| 0x07 | 完整128位UUID | 16*n字节 | 完整自定义服务 |
在智能手环项目中,我们这样组织UUID:
python复制# 典型UUID广播数据示例
heartrate_service_uuid = 0x180D # 心率服务标准UUID
custom_service_uuid = "0000ABCD-0000-1000-8000-00805F9B34FB"
broadcast_data = [
"02 01 06", # Flags
"03 03 0D 18", # 完整16位UUID(小端序)
"11 07" + custom_service_uuid.replace("-", "") # 完整128位UUID
]
0xFF是厂商的"自留地",可以传输任何私有数据。常见应用包括:
一个典型的厂商数据格式:
code复制[Length] 0xFF [CompanyID(2B)] [自定义数据...]
在开发智能家居网关时,我们通过0xFF字段传输设备类型和状态:
c复制// 智能灯泡厂商数据示例
uint8_t vendor_data[] = {
0x05, // Length=5 (1+2+2)
0xFF, // AD Type
0xCD,0xAB, // 公司ID 0xABCD(小端序)
0x01, // 设备类型:灯泡
0x80 // 状态:亮度50%
};
广播包和扫描响应就像简历的两页:
合理分配数据的经验法则:
在开发省电型传感器时,我们采用这些策略:
实测对比不同策略的电流消耗:
| 配置方案 | 平均电流 |
|---|---|
| 完整广播(31B) | 12.8μA |
| 精简广播(15B) | 8.2μA |
| 广播+扫描响应 | 9.7μA |
假设我们要开发一款具有以下特性的手环:
广播包(22字节):
code复制02 01 06 // Flags
03 03 0D 18 // 心率服务
05 09 48 42 61 6E 64 // 设备名"H Band"
02 0A 18 // 设备信息服务
03 FF AB CD 01 // 厂商数据:公司ID 0xABCD,固件v1
扫描响应(28字节):
code复制07 16 0D 18 00 64 00 00 // 心率服务数据:心率值100
05 16 0A 18 01 02 // 设备信息:序列号
04 FF AB CD 32 // 厂商数据:电池电量50%
在调试过程中,使用nRF Connect等工具实时查看广播数据非常有用。有一次我们发现设备在iOS上不可见,最终排查是因为Flags配置不符合iOS的严格检查。