OpenHaystack广播格式解析:advertisement_template结构详解
OpenHaystack通过构建符合Apple Find My网络规范的蓝牙广播包,实现了利用苹果庞大网络进行设备追踪的功能。本文将深入解析其核心广播格式advertisement_template的结构组成,帮助开发者理解设备如何被Find My网络识别和定位。
广播包整体架构
OpenHaystack的蓝牙广播包采用非连接不可发现模式(ADV_TYPE_NONCONN_IND),通过特定的数据结构模拟Apple的离线查找协议。广播数据主要由三部分构成:头部标识区、设备公钥区和状态控制区,总长度严格控制在31字节(蓝牙广播最大载荷)。
数据模板定义
在ESP32固件实现中,广播模板定义于Firmware/ESP32/main/openhaystack_main.c第27-38行:
static uint8_t adv_data[31] = {
0x1e, /* Length (30) */
0xff, /* Manufacturer Specific Data (type 0xff) */
0x4c, 0x00, /* Company ID (Apple) */
0x12, 0x19, /* Offline Finding type and length */
0x00, /* State */
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, /* First two bits */
0x00, /* Hint (0x00) */
};
字段详细解析
1. 头部标识区(0-6字节)
| 偏移 | 长度 | 数值 | 含义 |
|---|---|---|---|
| 0 | 1 | 0x1e | 有效数据长度(30字节) |
| 1 | 1 | 0xff | 数据类型:厂商特定数据 |
| 2-3 | 2 | 0x4c00 | 公司ID:Apple(小端序) |
| 4 | 1 | 0x12 | 子类型:离线查找协议 |
| 5 | 1 | 0x19 | 子类型数据长度(25字节) |
| 6 | 1 | 0x00 | 状态位:0x00表示未配对 |
2. 设备公钥区(7-28字节)
这部分存储设备的22字节公钥,对应代码中的公钥注入逻辑(Firmware/ESP32/main/openhaystack_main.c第118行):
memcpy(&payload[7], &public_key[6], 22);
公钥通过安全分区加载,与设备随机地址存在关联映射关系,具体生成逻辑见set_addr_from_key函数。
3. 状态控制区(29-30字节)
| 偏移 | 长度 | 含义 |
|---|---|---|
| 29 | 1 | 公钥高位2比特(用于地址计算) |
| 30 | 1 | 提示位:0x00表示标准广播 |
关键验证逻辑
在接收端,OpenHaystack/OpenHaystack/HaystackApp/Bluetooth/Advertisement.swift实现了严格的格式校验:
static func extractPublicKeyFromPayload(_ payload: Data) -> Data? {
guard payload.count == 29 else { return nil }
guard payload.subdata(in: 0..<2) == Data([0x4c, 0x00]) else { return nil } // Apple公司ID
guard payload.subdata(in: 2..<3) == Data([0x12]) else { return nil } // 离线查找类型
guard payload.subdata(in: 3..<4) == Data([0x19]) else { return nil } // 数据长度
let publicKey = payload.subdata(in: 5..<5 + publicKeyPayloadLength)
guard publicKey.count == publicKeyPayloadLength else { return nil }
return publicKey
}
这段代码确保只有符合格式的广播包才会被解析为公钥,有效过滤无效数据。
设备地址生成机制
广播设备使用随机蓝牙地址,由公钥前6字节生成(Firmware/ESP32/main/openhaystack_main.c第107-114行):
void set_addr_from_key(esp_bd_addr_t addr, uint8_t *public_key) {
addr[0] = public_key[0] | 0b11000000; // 设置随机地址标志位
addr[1] = public_key[1];
addr[2] = public_key[2];
addr[3] = public_key[3];
addr[4] = public_key[4];
addr[5] = public_key[5];
}
通过将公钥前6字节与0xC0(0b11000000)按位或运算,确保生成符合蓝牙规范的随机静态地址。
广播参数配置
广播间隔直接影响设备功耗和定位频率,在Firmware/ESP32/main/openhaystack_main.c中配置:
.adv_int_min = 0x0640, // 1秒 (0x0640 * 0.625ms = 1000ms)
.adv_int_max = 0x0C80, // 2秒 (0x0C80 * 0.625ms = 2000ms)
.channel_map = ADV_CHNL_ALL, // 全信道广播
默认配置下设备每1-2秒广播一次,平衡了定位实时性与电池续航。
实际应用示例
当公钥为00 11 22 33 44 55 66 77 88 99 AA BB CC DD EE FF 00 11 22 33 44 55 66 77 88 99 AA BB CC时,生成的广播包结构如下:
| 偏移 | 内容 | 说明 |
|---|---|---|
| 0-6 | 1e ff 4c 00 12 19 00 | 头部标识 |
| 7-28 | 66 77 88 99 AA BB CC DD EE FF 00 11 22 33 44 55 66 77 88 99 AA BB | 公钥后22字节 |
| 29 | 00 | 公钥高位2比特 |
| 30 | 00 | 提示位 |
此广播包将被Find My网络节点接收并上传至定位服务器,实现设备追踪功能。
通过深入理解advertisement_template结构,开发者可以基于OpenHaystack构建自定义追踪设备,或优化现有设备的广播效率与兼容性。完整实现可参考项目中的ESP32固件代码和iOS解析逻辑。
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考



