3. 扫描设备
任务说明
本案例实现经典蓝牙(BR/EDR)的设备发现过程,即Inquiry扫描。目标是通过HCI命令向蓝牙控制器发起查询,接收查询结果事件,并解析出周围可发现设备的蓝牙地址(BD_ADDR)与设备类别(CoD),最终输出设备列表。代码基于Linux BlueZ的HCI套接字接口,直接与控制器交互,不依赖上层BlueZ守护进程。
实现原理
设备发现分为三个步骤:首先发送HCI_Inquiry命令,控制器进入查询状态并广播ID数据包;随后周围设备响应FHS包,控制器通过HCI_Inquiry_Result事件上报发现的设备;最后发送HCI_Inquiry_Cancel命令结束查询。本案例使用原始HCI套接字,通过ioctl绑定指定蓝牙适配器,以同步方式发送命令并阻塞等待事件,解析事件参数中的设备地址与类别字段。
代码实现
// scan_devices.c - 经典蓝牙设备发现(Inquiry)
// 编译: gcc -o scan_devices scan_devices.c
// 运行: sudo ./scan_devices [hciX] (默认 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>
#define INQUIRY_TIMEOUT 8 // 查询时长(1.28秒单位),8 = 10.24秒
int main(int argc, char *argv[]) {
int dev_id = 0;
if (argc > 1) {
dev_id = hci_devid(argv[1]); // 解析 "hci0" 格式
if (dev_id < 0) {
fprintf(stderr, "无效设备: %s\n", argv[1]);
return 1;
}
}
// 打开HCI套接字并绑定设备
int sock = socket(AF_BLUETOOTH, SOCK_RAW, BTPROTO_HCI);
if (sock < 0) {
perror("socket");
return 1;
}
if (hci_devba(dev_id, &(struct sockaddr_hci){ .hci_family = AF_BLUETOOTH,
.hci_dev = dev_id }) < 0) {
perror("bind");
close(sock);
return 1;
}
// 发送 Inquiry 命令
inquiry_info *info = malloc(255 * sizeof(inquiry_info));
int num_rsp = hci_inquiry(dev_id, INQUIRY_TIMEOUT, 255, NULL, &info,
IREQ_CACHE_FLUSH);
if (num_rsp < 0) {
perror("hci_inquiry");
free(info);
close(sock);
return 1;
}
// 解析并打印结果
printf("发现 %d 个设备:\n", num_rsp);
printf("%-18s %-10s %s\n", "BD_ADDR", "CoD(hex)", "Clock Offset");
for (int i = 0; i < num_rsp; i++) {
char addr[18];
ba2str(&info[i].bdaddr, addr);
uint32_t cod = info[i].dev_class[0] |
(info[i].dev_class[1] << 8) |
(info[i].dev_class[2] << 16);
printf("%-18s 0x%06x 0x%04x\n", addr, cod,
btohs(info[i].clock_offset));
}
free(info);
close(sock);
return 0;
}
关键说明
hci_inquiry()是BlueZ提供的封装函数,内部依次完成:发送HCI_Inquiry命令、等待HCI_Inquiry_Result事件、在超时后发送HCI_Inquiry_Cancel。第三个参数255表示最大响应数,IREQ_CACHE_FLUSH强制刷新缓存以确保获取实时结果。返回的inquiry_info数组每个元素包含6字节BD_ADDR、3字节设备类别和2字节时钟偏移,代码将其转换为可读格式输出。
验证方法
确保蓝牙适配器可用且周围有可发现模式(通常为可见模式)的蓝牙设备。执行以下命令验证:
# 查看适配器状态
hciconfig hci0 up
# 编译并运行(需要root权限访问HCI套接字)
gcc -o scan_devices scan_devices.c -lbluetooth
sudo ./scan_devices hci0
判定标准:程序在约10秒内完成扫描,输出至少包含表头行。若周围存在可发现设备,则每行显示一个设备的BD_ADDR(格式如AA:BB:CC:DD:EE:FF)、设备类别十六进制值和时钟偏移。若周围无设备,则输出发现 0 个设备且仅显示表头。程序正常退出且无错误信息即视为通过。注意:部分手机需在蓝牙设置中开启“可被检测”选项才能被发现。