2. 配置蓝牙地址
任务说明
本案例实现蓝牙设备地址(BD_ADDR)的设置与读取功能。BD_ADDR 是蓝牙设备的唯一 48 位标识符,分为公共地址(Public Address)和随机地址(Random Address)两类。我们将基于 Linux 蓝牙协议栈(BlueZ)提供的 HCI 接口,通过发送 HCI 命令完成地址配置,并读取当前生效地址进行验证。
实现方案
使用 BlueZ 的 HCI 用户态库(libbluetooth),通过 HCI 套接字直接与蓝牙控制器通信。核心操作包括:
- 打开 HCI 套接字并绑定指定蓝牙设备(如 hci0)
- 发送 HCI 命令
HCI_OP_WRITE_BD_ADDR(Opcode 0x0006)写入新地址 - 发送 HCI 命令
HCI_OP_READ_BD_ADDR(Opcode 0x0009)读取当前地址 - 解析返回事件,提取 BD_ADDR 并格式化输出
代码实现
// bd_addr_config.c
// 编译: gcc -o bd_addr_config bd_addr_config.c -lbluetooth
// 运行: sudo ./bd_addr_config hci0 00:11:22:33:44:55 (设置地址)
// sudo ./bd_addr_config hci0 (读取地址)
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/socket.h>
#include <bluetooth/bluetooth.h>
#include <bluetooth/hci.h>
#include <bluetooth/hci_lib.h>
// 将 "XX:XX:XX:XX:XX:XX" 格式字符串转换为 6 字节数组
static int parse_bdaddr(const char *str, uint8_t *addr) {
unsigned int b[6];
if (sscanf(str, "%02x:%02x:%02x:%02x:%02x:%02x",
&b[0], &b[1], &b[2], &b[3], &b[4], &b[5]) != 6)
return -1;
for (int i = 0; i < 6; i++)
addr[i] = (uint8_t)b[i];
return 0;
}
// 读取 BD_ADDR
static int read_bdaddr(int dd) {
unsigned char buf[HCI_MAX_EVENT_SIZE];
hci_command_hdr *cmd = (hci_command_hdr *)buf;
uint16_t opcode = HCI_OP_READ_BD_ADDR;
cmd->opcode = htobs(opcode);
cmd->plen = 0;
if (write(dd, buf, HCI_COMMAND_HDR_SIZE) != HCI_COMMAND_HDR_SIZE) {
perror("write command");
return -1;
}
// 等待命令完成事件
int len = read(dd, buf, sizeof(buf));
if (len < 0) {
perror("read event");
return -1;
}
// 解析 HCI 事件
hci_event_hdr *evt = (hci_event_hdr *)(buf + 1); // 跳过包类型字节
if (evt->evt != EVT_CMD_COMPLETE) {
fprintf(stderr, "Unexpected event: 0x%02x\n", evt->evt);
return -1;
}
evt_cmd_complete *cc = (evt_cmd_complete *)(buf + 1 + HCI_EVENT_HDR_SIZE);
if (cc->opcode != htobs(opcode)) {
fprintf(stderr, "Opcode mismatch\n");
return -1;
}
uint8_t status = cc->data[0];
if (status != 0) {
fprintf(stderr, "HCI command failed, status=0x%02x\n", status);
return -1;
}
// BD_ADDR 在 data[1] 到 data[6]
uint8_t *bdaddr = &cc->data[1];
printf("Current BD_ADDR: %02X:%02X:%02X:%02X:%02X:%02X\n",
bdaddr[0], bdaddr[1], bdaddr[2],
bdaddr[3], bdaddr[4], bdaddr[5]);
return 0;
}
// 写入 BD_ADDR
static int write_bdaddr(int dd, uint8_t *new_addr) {
unsigned char buf[HCI_MAX_EVENT_SIZE];
hci_command_hdr *cmd = (hci_command_hdr *)buf;
uint16_t opcode = HCI_OP_WRITE_BD_ADDR;
cmd->opcode = htobs(opcode);
cmd->plen = 6;
memcpy(buf + HCI_COMMAND_HDR_SIZE, new_addr, 6);
if (write(dd, buf, HCI_COMMAND_HDR_SIZE + 6) != HCI_COMMAND_HDR_SIZE + 6) {
perror("write command");
return -1;
}
// 等待命令完成事件
int len = read(dd, buf, sizeof(buf));
if (len < 0) {
perror("read event");
return -1;
}
hci_event_hdr *evt = (hci_event_hdr *)(buf + 1);
if (evt->evt != EVT_CMD_COMPLETE) {
fprintf(stderr, "Unexpected event: 0x%02x\n", evt->evt);
return -1;
}
evt_cmd_complete *cc = (evt_cmd_complete *)(buf + 1 + HCI_EVENT_HDR_SIZE);
if (cc->opcode != htobs(opcode)) {
fprintf(stderr, "Opcode mismatch\n");
return -1;
}
uint8_t status = cc->data[0];
if (status != 0) {
fprintf(stderr, "HCI command failed, status=0x%02x\n", status);
return -1;
}
printf("BD_ADDR write successful\n");
return 0;
}
int main(int argc, char *argv[]) {
if (argc < 2 || argc > 3) {
fprintf(stderr, "Usage: %s <hciX> [new_bdaddr]\n", argv[0]);
fprintf(stderr, " hciX - Bluetooth device, e.g. hci0\n");
fprintf(stderr, " new_bdaddr - Address in XX:XX:XX:XX:XX:XX format (optional)\n");
return 1;
}
// 打开 HCI 设备
int dd = hci_open_dev(hci_devid(argv[1]));
if (dd < 0) {
perror("hci_open_dev");
return 1;
}
int ret = 0;
if (argc == 3) {
// 设置模式
uint8_t addr[6];
if (parse_bdaddr(argv[2], addr) != 0) {
fprintf(stderr, "Invalid BD_ADDR format: %s\n", argv[2]);
hci_close_dev(dd);
return 1;
}
ret = write_bdaddr(dd, addr);
} else {
// 读取模式
ret = read_bdaddr(dd);
}
hci_close_dev(dd);
return ret;
}
代码说明
程序通过 hci_open_dev() 打开指定蓝牙设备,然后根据命令行参数决定执行写入或读取操作。写入时使用 HCI_OP_WRITE_BD_ADDR 命令携带 6 字节新地址;读取时使用 HCI_OP_READ_BD_ADDR 命令。两者均通过 write() 发送命令,再 read() 等待控制器返回 EVT_CMD_COMPLETE 事件,从事件参数中提取状态码和地址数据。地址字节序遵循蓝牙规范,低字节在前。
HCI 命令交互流程
编译与运行
# 安装依赖(Debian/Ubuntu)
sudo apt-get install libbluetooth-dev
# 编译
gcc -o bd_addr_config bd_addr_config.c -lbluetooth
# 读取当前地址(需要 root 权限)
sudo ./bd_addr_config hci0
# 设置新地址
sudo ./bd_addr_config hci0 00:11:22:33:44:55
验证方法
执行读取命令后,程序输出 Current BD_ADDR: XX:XX:XX:XX:XX:XX。执行设置命令后,程序输出 BD_ADDR write successful,随后再次运行读取命令,确认输出的地址与刚设置的地址一致,即表示设置成功。注意:部分蓝牙控制器(尤其是 USB 适配器)可能不支持修改 BD_ADDR,此时 HCI 命令会返回非零状态码,程序将打印错误信息。若需恢复原始地址,请使用原厂工具或重新插拔适配器。