如何正确使用HCITool及代码实现Set Extended Advertising Data Command
问题描述
- 目标:重实现HCITool功能,通过
cmd命令设置携带厂商数据的蓝牙LE扩展广播数据 - 执行以下两种
hcitool命令均返回错误码1,提示命令不存在:hcitool -i hci0 cmd 0x08 0x0037 01 03 01 0C FFFF 12 34hcitool -i hci0 cmd 0x08 0x0037 01 03 01 0C FF FF 12 34 - 基于HCITool源码修改的自定义代码运行后,用
btmon未检测到扩展广播数据已正确设置,怀疑参数传递或命令构造逻辑有误
一、HCI命令格式错误分析
你调用的是LE Set Extended Advertising Data命令(OGF=0x08,OCF=0x0037),该命令对参数格式有严格要求,你的命令存在两个核心问题:
- 数据长度不匹配:参数中
0x0C代表后续数据长度为12字节,但实际仅提供了FFFF 12 34共4字节,长度不匹配导致控制器拒绝执行 - AD结构缺失:厂商数据需要遵循蓝牙AD(Advertising Data)结构,必须包含
AD Type(0xFF)字段,你的命令直接写入厂商ID,缺少该标识
正确命令示例
hcitool -i hci0 cmd 0x08 0x0037 00 00 00 05 FF FF FF 12 34
参数解释:
00:Advertising Handle(默认广播句柄)00:Operation(0x00=覆盖设置广播数据)00:Fragment Preference(0x00=允许数据分片)05:后续AD数据总长度(0xFF(AD Type) + FF FF(厂商ID) + 12 34(自定义数据)共5字节)FF:AD Type标识(厂商特定数据)FF FF:厂商ID(示例值0xFFFF)12 34:自定义厂商数据
二、自定义代码问题排查
你的代码存在三处关键错误:
- 参数数组不完整:仅定义了厂商ID和自定义数据,缺少命令必需的前4个固定参数(Advertising Handle、Operation、Fragment Preference、Data Length)
- 循环逻辑越界:
for (i = 2, len = 0; i < 12 && len < (int) sizeof(buf); i++, len++)中,arr数组仅3个元素,i从2开始后会越界读取垃圾数据,导致参数长度错误 - 命令结构错误:未按照HCI规范构造完整的命令参数,控制器无法识别错误格式的命令,因此
btmon看不到有效设置请求
三、解决思路
1. 遵循HCI规范构造命令参数
LE Set Extended Advertising Data命令的参数结构必须严格符合蓝牙核心规范:
| 偏移 | 字段 | 长度 | 说明 |
|---|---|---|---|
| 0 | Advertising Handle | 1 | 广播句柄,默认用0x00 |
| 1 | Operation | 1 | 0x00=设置数据,0x01=添加数据,0x02=删除 |
| 2 | Fragment Preference | 1 | 0x00=允许分片,0x01=禁止分片 |
| 3 | Data Length | 1 | 后续AD数据的总长度 |
| 4~n | Advertising Data | N | AD结构数据,厂商数据格式为0xFF + 厂商ID(2字节) + 自定义数据 |
2. 修正代码实现
以下是调整后的完整代码:
static void cmd_manufacturer(int dev_id) { unsigned char buf[HCI_MAX_EVENT_SIZE], *ptr = buf; struct hci_filter flt; hci_event_hdr *hdr; int len, dd; uint16_t ocf; uint8_t ogf; if (dev_id < 0) dev_id = hci_get_route(NULL); ogf = 0x08; // LE Controller命令组 ocf = 0x0037; // LE Set Extended Advertising Data命令 // 构造完整命令参数 *ptr++ = 0x00; // Advertising Handle *ptr++ = 0x00; // Operation: 覆盖设置广播数据 *ptr++ = 0x00; // Fragment Preference: 允许分片 *ptr++ = 0x05; // Data Length: 后续AD数据总长度(1+2+2=5) *ptr++ = 0xFF; // AD Type: 厂商特定数据标识 *ptr++ = 0xFF; // 厂商ID低字节(0xFFFF) *ptr++ = 0xFF; // 厂商ID高字节 *ptr++ = 0x12; // 自定义厂商数据1 *ptr++ = 0x34; // 自定义厂商数据2 len = ptr - buf; // 计算参数总长度 dd = hci_open_dev(dev_id); if (dd < 0) { perror("Device open failed"); exit(EXIT_FAILURE); } // 设置HCI事件过滤器 hci_filter_clear(&flt); hci_filter_set_ptype(HCI_EVENT_PKT, &flt); hci_filter_all_events(&flt); if (setsockopt(dd, SOL_HCI, HCI_FILTER, &flt, sizeof(flt)) < 0) { perror("HCI filter setup failed"); exit(EXIT_FAILURE); } printf("< HCI Command: ogf 0x%02x, ocf 0x%04x, plen %d\n", ogf, ocf, len); if (hci_send_cmd(dd, ogf, ocf, len, buf) < 0) { perror("Send failed"); exit(EXIT_FAILURE); } // 读取命令响应 len = read(dd, buf, sizeof(buf)); if (len < 0) { perror("Read failed"); exit(EXIT_FAILURE); } hdr = (void *)(buf + 1); ptr = buf + (1 + HCI_EVENT_HDR_SIZE); len -= (1 + HCI_EVENT_HDR_SIZE); printf("> HCI Event: 0x%02x plen %d\n", hdr->evt, hdr->plen); // 检查命令执行状态 if (hdr->evt == EVT_CMD_COMPLETE) { uint8_t status = ptr[3]; // 命令完成事件中的状态字段 if (status != 0x00) { fprintf(stderr, "Command failed with status 0x%02x\n", status); } else { printf("Extended advertising data set successfully\n"); } } hci_close_dev(dd); }
3. 验证步骤
- 确保蓝牙控制器处于LE广播模式:
hciconfig hci0 down && hciconfig hci0 up && hciconfig hci0 leadv - 运行修改后的程序执行广播数据设置
- 用
btmon捕获HCI数据包,确认LE Set Extended Advertising Data命令存在且响应状态为0x00(执行成功) - 用
bluetoothctl scan on扫描蓝牙设备,查看目标广播包中是否包含指定的厂商数据
内容的提问来源于stack exchange,提问作者ascode
相关产品推荐
相关产品推荐

