<think>我们被要求使用C语言在Linux下实现蓝牙的扫描、连接和数据读写。根据引用[1]中的示例代码,我们可以看到一个基本的RFCOMM连接和读写操作。但是,引用[1]中并没有提供扫描设备的代码。另外,引用[2]提到了btstack这个第三方库,但我们这里要求使用Linux自带的BlueZ库。
在Linux中,蓝牙开发通常有两种方式:
1. 使用BlueZ提供的DBus接口(高级,推荐)
2. 使用BlueZ提供的socket接口(低级,如RFCOMM,L2CAP等)
用户要求使用C语言,并且希望有完整的示例代码。由于DBus接口比较复杂,而引用[1]中使用了socket接口(RFCOMM),我们可以基于此进行扩展。
但是,扫描设备通常使用DBus或者通过hci工具。在C语言中,我们可以使用hci设备相关的系统调用来扫描蓝牙设备。
以下是一个完整的示例,包括扫描、连接和数据读写:
步骤:
1. 扫描蓝牙设备:使用hci设备发送HCI指令来扫描。
2. 连接:使用RFCOMM socket连接。
3. 读写:使用read/write系统调用。
注意:需要安装libbluetooth-dev,编译时链接-lbluetooth。
由于代码较长,我们分部分实现。
### 扫描蓝牙设备
```c
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/socket.h>
#include <bluetooth/bluetooth.h>
#include <bluetooth/hci.h>
#include <bluetooth/hci_lib.h>
void scan_devices(int dev_id, int duration) {
inquiry_info *ii = NULL;
int max_rsp, num_rsp;
int sock;
int flags = IREQ_CACHE_FLUSH;
char addr[19] = {0};
char name[248] = {0};
// 打开HCI socket
sock = hci_open_dev(dev_id);
if (sock < 0) {
perror("hci_open_dev");
return;
}
// 设置最大响应数
max_rsp = 255;
ii = (inquiry_info*)malloc(max_rsp * sizeof(inquiry_info));
if (!ii) {
perror("malloc");
close(sock);
return;
}
// 开始扫描
num_rsp = hci_inquiry(dev_id, duration, max_rsp, NULL, &ii, flags);
if (num_rsp < 0) {
perror("hci_inquiry");
free(ii);
close(sock);
return;
}
printf("Found %d devices:\n", num_rsp);
for (int i = 0; i < num_rsp; i++) {
ba2str(&(ii+i)->bdaddr, addr);
memset(name, 0, sizeof(name));
if (hci_read_remote_name(sock, &(ii+i)->bdaddr, sizeof(name), name, 0) < 0) {
strcpy(name, "[unknown]");
}
printf("%s %s\n", addr, name);
}
free(ii);
close(sock);
}
```
### 连接与数据读写
这部分与引用[1]中的代码类似,但我们将它封装成一个函数。
```c
void connect_and_communicate(char *dest_addr, int channel) {
struct sockaddr_rc addr = {0};
int sock, status;
char buf[1024] = {0};
// 分配socket
sock = socket(AF_BLUETOOTH, SOCK_STREAM, BTPROTO_RFCOMM);
if (sock < 0) {
perror("socket");
return;
}
// 设置连接参数
addr.rc_family = AF_BLUETOOTH;
addr.rc_channel = (uint8_t) channel;
str2ba(dest_addr, &addr.rc_bdaddr);
// 连接
status = connect(sock, (struct sockaddr *)&addr, sizeof(addr));
if (status) {
perror("connect");
close(sock);
return;
}
printf("Connected to %s\n", dest_addr);
// 读写循环
while (1) {
printf("Enter message (or 'exit' to quit): ");
fgets(buf, sizeof(buf), stdin);
buf[strlen(buf)-1] = '\0'; // 去掉换行符
if (strcmp(buf, "exit") == 0) {
break;
}
// 发送数据
if (write(sock, buf, strlen(buf)) < 0) {
perror("write");
break;
}
// 读取响应
memset(buf, 0, sizeof(buf));
int len = read(sock, buf, sizeof(buf)-1);
if (len > 0) {
buf[len] = '\0';
printf("Received: %s\n", buf);
} else {
perror("read");
break;
}
}
close(sock);
}
```
### 主函数
```c
int main(int argc, char **argv) {
int dev_id = hci_get_route(NULL); // 获取第一个可用的蓝牙设备
if (dev_id < 0) {
perror("hci_get_route");
return -1;
}
// 扫描设备
scan_devices(dev_id, 8); // 扫描8秒
char dest[18] = {0};
printf("Enter device address to connect (e.g., 00:11:22:33:44:55): ");
scanf("%s", dest);
int channel = 1;
printf("Enter RFCOMM channel (default 1): ");
scanf("%d", &channel);
connect_and_communicate(dest, channel);
return 0;
}
```
### 编译与运行
编译命令:
```bash
gcc -o bt_example bt_example.c -lbluetooth
```
运行:
```bash
sudo ./bt_example
```
注意:需要以root权限运行,或者给予程序相应的蓝牙操作权限。
### 说明
1. 扫描部分使用`hci_inquiry`函数,它会扫描附近的蓝牙设备并返回设备地址和名称。
2. 连接部分使用RFCOMM socket,需要指定设备的蓝牙地址和RFCOMM通道(通常为1)。
3. 数据读写使用标准的read/write系统调用。
### 注意事项
- 扫描时可能需要一定时间(示例中为8秒)。
- 连接时,目标设备必须处于可发现和可连接状态。
- RFCOMM通道通常为1,但有些设备可能使用其他通道,可以通过SDP查询获取。
### 引用
[^1]: 提供了RFCOMM连接和读写的基本代码。
[^2]: 提到了btstack,但我们这里使用的是BlueZ自带的库。
[^3]: 展示了使用命令行工具进行BLE操作,但我们的代码是经典蓝牙(RFCOMM)。
[^4]: 提到了QT5的蓝牙开发,但这里我们使用纯C。
### 相关问题
§§相关问题§§
1. 如何查询蓝牙设备的RFCOMM通道号?
2. 在Linux下如何通过C语言实现BLE(低功耗蓝牙)通信?
3. 如何通过DBus接口实现蓝牙设备管理?
4. 如何实现蓝牙数据的异步读写?</think>### Linux下C语言蓝牙开发完整示例
以下是在Linux平台使用C语言实现蓝牙扫描、连接和数据读写的完整解决方案,基于BlueZ库和标准Linux蓝牙API。
#### 一、环境配置
首先安装开发依赖:
```bash
sudo apt update
sudo apt install libbluetooth-dev bluez bluez-tools
```
#### 二、完整示例代码
```c
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/socket.h>
#include <bluetooth/bluetooth.h>
#include <bluetooth/hci.h>
#include <bluetooth/hci_lib.h>
#include <bluetooth/rfcomm.h>
// 蓝牙设备扫描
void scan_bt_devices() {
inquiry_info *ii = NULL;
int max_rsp, num_rsp;
int dev_id, sock, len, flags;
char addr[19] = {0};
char name[248] = {0};
// 获取第一个可用蓝牙适配器
dev_id = hci_get_route(NULL);
if (dev_id < 0) {
perror("No Bluetooth adapter found");
exit(1);
}
// 打开HCI socket
sock = hci_open_dev(dev_id);
if (sock < 0) {
perror("Open HCI socket failed");
exit(1);
}
len = 8; // 扫描持续时间 (1.28 * len 秒)
max_rsp = 255; // 最大响应设备数
flags = IREQ_CACHE_FLUSH;
ii = (inquiry_info*)malloc(max_rsp * sizeof(inquiry_info));
// 开始扫描
num_rsp = hci_inquiry(dev_id, len, max_rsp, NULL, &ii, flags);
if (num_rsp < 0) perror("Scan failed");
printf("Found %d devices:\n", num_rsp);
// 打印设备信息
for (int i = 0; i < num_rsp; i++) {
ba2str(&(ii+i)->bdaddr, addr);
memset(name, 0, sizeof(name));
if (hci_read_remote_name(sock, &(ii+i)->bdaddr, sizeof(name), name, 0) < 0) {
strcpy(name, "[unknown]");
}
printf(" %s %s\n", addr, name);
}
free(ii);
close(sock);
}
// 蓝牙连接与数据通信
void bt_connect_communicate(char *dest) {
struct sockaddr_rc addr = {0};
int sock, status;
char buf[1024] = {0};
// 创建RFCOMM socket
sock = socket(AF_BLUETOOTH, SOCK_STREAM, BTPROTO_RFCOMM);
if (sock < 0) {
perror("Socket creation failed");
exit(1);
}
// 设置连接参数
addr.rc_family = AF_BLUETOOTH;
addr.rc_channel = (uint8_t) 1; // RFCOMM通道
str2ba(dest, &addr.rc_bdaddr);
// 连接到设备
status = connect(sock, (struct sockaddr *)&addr, sizeof(addr));
if (status < 0) {
perror("Connection failed");
close(sock);
exit(1);
}
printf("Connected to %s\n", dest);
// 数据通信循环
while (1) {
printf("Send > ");
fgets(buf, sizeof(buf), stdin);
buf[strlen(buf)-1] = '\0'; // 移除换行符
if (strcmp(buf, "exit") == 0) break;
// 发送数据
if (write(sock, buf, strlen(buf)) < 0) {
perror("Write failed");
break;
}
// 接收数据
memset(buf, 0, sizeof(buf));
int bytes_read = read(sock, buf, sizeof(buf)-1);
if (bytes_read > 0) {
printf("Received: %s\n", buf);
} else if (bytes_read == 0) {
printf("Connection closed\n");
break;
} else {
perror("Read failed");
break;
}
}
close(sock);
}
int main(int argc, char **argv) {
if (argc < 2) {
printf("Usage:\n");
printf(" %s scan - Scan for devices\n", argv[0]);
printf(" %s connect <MAC> - Connect to device\n", argv[0]);
return 1;
}
if (strcmp(argv[1], "scan") == 0) {
scan_bt_devices();
}
else if (strcmp(argv[1], "connect") == 0 && argc == 3) {
bt_connect_communicate(argv[2]);
}
else {
printf("Invalid command\n");
return 1;
}
return 0;
}
```
#### 三、编译与使用
1. 编译程序:
```bash
gcc bt_example.c -o bt_example -lbluetooth
```
2. 扫描设备:
```bash
sudo ./bt_example scan
# 输出示例:
# Found 3 devices:
# 00:1A:7D:DA:71:13 MyHeadset
# 5C:F3:70:9A:2C:25 MyPhone
```
3. 连接设备:
```bash
sudo ./bt_example connect 00:1A:7D:DA:71:13
# 连接成功后输入文本进行双向通信
# 输入"exit"退出
```
#### 四、关键API说明
1. **设备扫描**:
- `hci_get_route()`:获取蓝牙适配器ID
- `hci_open_dev()`:打开HCI设备
- `hci_inquiry()`:执行设备扫描
- `hci_read_remote_name()`:获取设备名称
2. **连接通信**:
- `socket(AF_BLUETOOTH, SOCK_STREAM, BTPROTO_RFCOMM)`:创建RFCOMM socket
- `str2ba()`:将字符串MAC地址转换为蓝牙地址结构
- `connect()`:建立蓝牙连接
- `read()`/`write()`:数据传输
#### 五、常见问题解决
1. **权限问题**:
```bash
sudo setcap 'cap_net_raw,cap_net_admin+eip' ./bt_example
```
2. **连接失败**:
- 确认设备已配对:`bluetoothctl pair <MAC>`
- 确认RFCOMM通道:使用`sdptool browse <MAC>`查询
3. **数据收发异常**:
- 使用`btmon`监控HCI数据包:
```bash
sudo btmon -w log.bin
```
#### 六、进阶功能
1. **自动配对**:
```c
// 在connect前添加
struct pin_code_reply pincode;
memset(&pincode, 0, sizeof(pincode));
strcpy(pincode.pin_code, "0000");
pincode.pin_len = 4;
hci_send_req(sock, OGF_LINK_CTL, OCF_PIN_CODE_REPLY,
PIN_CODE_REPLY_CP_SIZE, &pincode, 0);
```
2. **多通道支持**:
```c
// 修改连接通道
addr.rc_channel = (uint8_t) atoi(argv[3]); // 从参数获取通道号
```
> **注意**:此示例基于经典蓝牙(RFCOMM),如需低功耗蓝牙(BLE)开发,需使用GATT API[^1][^3]。