<think>我们已经在之前的回答中分别提供了传统蓝牙和BLE设备的扫描、连接和通信的C代码示例。现在,用户希望有一个统一的程序,能够同时处理传统蓝牙和BLE设备。然而,由于传统蓝牙和BLE使用不同的协议栈和API,通常需要分别处理。我们可以设计一个程序,包含两种设备的处理模块,并允许用户选择模式。
但是,请注意:
1. 传统蓝牙使用RFCOMM或L2CAP进行数据传输,而BLE使用GATT协议(基于ATT)。
2. 扫描方式也不同:传统蓝牙使用`hci_inquiry`,而BLE使用LE扫描命令。
因此,我们可以设计一个程序,让用户通过命令行参数选择模式(经典或BLE),然后执行相应的操作。
以下是一个综合示例,包含两种模式的选择:
### 综合示例:蓝牙设备管理程序
```c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <bluetooth/bluetooth.h>
#include <bluetooth/hci.h>
#include <bluetooth/hci_lib.h>
#include <bluetooth/rfcomm.h>
#include <gattlib.h> // 需要安装gattlib
// 传统蓝牙扫描
void scan_classic_bt(int duration) {
// ... 使用之前提供的传统蓝牙扫描代码 ...
}
// BLE扫描
void scan_ble(int duration) {
// ... 使用之前提供的BLE扫描代码(注意:需要事件处理回调来解析广播数据)...
// 由于BLE扫描需要事件处理,我们这里简化处理,只扫描一段时间并打印设备地址
// 实际应用中需要实现事件处理函数
}
// 连接传统蓝牙设备
int connect_classic_bt(const char *addr, int channel) {
// ... 使用之前提供的RFCOMM连接代码 ...
}
// 连接BLE设备
gatt_connection_t* connect_ble(const char *addr) {
// ... 使用之前提供的gattlib连接代码 ...
}
// 传统蓝牙数据传输
void classic_bt_transfer(int sock) {
// ... 使用之前提供的传统蓝牙数据传输代码 ...
}
// BLE数据传输(通过GATT特性读写)
void ble_transfer(gatt_connection_t* connection, const char* uuid_str) {
uuid_t uuid;
if (gattlib_string_to_uuid(uuid_str, strlen(uuid_str), &uuid) < 0) {
fprintf(stderr, "Invalid UUID format\n");
return;
}
// 读取特性
uint8_t buffer[100];
size_t len = sizeof(buffer);
if (gattlib_read_char_by_uuid(connection, &uuid, buffer, &len) == 0) {
printf("Read value: ");
for (int i = 0; i < len; i++) printf("%02X ", buffer[i]);
printf("\n");
} else {
perror("Read failed");
}
// 写入特性(示例数据)
uint8_t data[] = {0x01, 0x02, 0x03};
if (gattlib_write_char_by_uuid(connection, &uuid, data, sizeof(data)) == 0) {
printf("Write successful\n");
} else {
perror("Write failed");
}
}
int main(int argc, char **argv) {
if (argc < 2) {
fprintf(stderr, "Usage: %s [classic|ble] [scan|connect] [options]\n", argv[0]);
fprintf(stderr, "Examples:\n");
fprintf(stderr, " %s classic scan 10\n", argv[0]);
fprintf(stderr, " %s classic connect 00:11:22:33:44:55 1\n", argv[0]);
fprintf(stderr, " %s ble scan 5\n", argv[0]);
fprintf(stderr, " %s ble connect 00:11:22:33:44:55\n", argv[0]);
return 1;
}
const char *mode = argv[1];
const char *command = argv[2];
if (strcmp(mode, "classic") == 0) {
if (strcmp(command, "scan") == 0) {
if (argc < 4) {
fprintf(stderr, "Missing duration argument\n");
return 1;
}
int duration = atoi(argv[3]);
scan_classic_bt(duration);
} else if (strcmp(command, "connect") == 0) {
if (argc < 5) {
fprintf(stderr, "Missing address or channel\n");
return 1;
}
const char *addr = argv[3];
int channel = atoi(argv[4]);
int sock = connect_classic_bt(addr, channel);
if (sock < 0) {
return 1;
}
// 示例:传输数据
classic_bt_transfer(sock);
close(sock);
}
} else if (strcmp(mode, "ble") == 0) {
if (strcmp(command, "scan") == 0) {
if (argc < 4) {
fprintf(stderr, "Missing duration argument\n");
return 1;
}
int duration = atoi(argv[3]);
scan_ble(duration);
} else if (strcmp(command, "connect") == 0) {
if (argc < 4) {
fprintf(stderr, "Missing address\n");
return 1;
}
const char *addr = argv[3];
gatt_connection_t* connection = connect_ble(addr);
if (!connection) {
return 1;
}
// 示例:传输数据(需要知道具体的UUID)
// 这里假设用户提供了一个UUID,实际应用中可能需要发现服务
if (argc >= 5) {
const char *uuid_str = argv[4];
ble_transfer(connection, uuid_str);
} else {
printf("Connected but no UUID provided for transfer\n");
}
gattlib_disconnect(connection);
}
} else {
fprintf(stderr, "Unknown mode: %s\n", mode);
return 1;
}
return 0;
}
```
### 编译指令
```bash
# 安装依赖
sudo apt install libbluetooth-dev libgattlib-dev
# 编译
gcc -o bt_manager bt_manager.c -lbluetooth -lgattlib
```
### 使用示例
1. **扫描传统蓝牙设备(10秒)**:
```bash
sudo ./bt_manager classic scan 10
```
2. **连接传统蓝牙设备**:
```bash
sudo ./bt_manager classic connect 00:11:22:33:44:55 1
```
3. **扫描BLE设备(5秒)**:
```bash
sudo ./bt_manager ble scan 5
```
4. **连接BLE设备并读写特性**:
```bash
sudo ./bt_manager ble connect 00:11:22:33:44:55 00002a00-0000-1000-8000-00805f9b34fb
```
### 注意事项
1. **权限问题**:运行程序需要蓝牙管理权限,可以通过`setcap`或`sudo`解决。
2. **BLE扫描事件处理**:上面的简化BLE扫描没有实现事件处理,实际应用中需要注册回调函数来解析广播数据。这需要更复杂的代码,使用`hci_le_set_scan_enable`和`hci_le_add_white_list`等函数,并处理`HCI_EVENT_LE_META`事件。
3. **传统蓝牙通道**:传统蓝牙连接通常需要知道RFCOMM通道号。可以通过SDP查询获取,但示例中直接指定(如通道1)。
### 增强建议
- **自动通道发现**:为传统蓝牙设备添加SDP查询功能,自动获取RFCOMM通道号。
- **BLE扫描回调**:实现BLE扫描的完整事件处理,打印设备名称和广播数据。
- **错误处理**:增加更详细的错误处理逻辑。
### 引用说明
本文中引用的API和库来自BlueZ和gattlib[^1][^3]。
§§相关问题§§
1. 如何自动发现传统蓝牙设备的RFCOMM通道号?
2. 如何实现BLE扫描的广播数据解析回调?
3. 在Linux中如何同时扫描传统蓝牙和BLE设备?
4. 如何处理蓝牙连接中的超时和重连?</think>### Linux蓝牙(BR/EDR)和BLE设备综合操作代码
以下是支持传统蓝牙和BLE设备的完整C代码实现,包含扫描、连接和数据传输功能:
```c
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <bluetooth/bluetooth.h>
#include <bluetooth/hci.h>
#include <bluetooth/hci_lib.h>
#include <bluetooth/rfcomm.h>
#include <gattlib.h> // 需要安装gattlib: sudo apt install libgattlib-dev
// 扫描所有蓝牙设备(传统+BLE)
void scan_all_devices(int duration) {
int dev_id = hci_get_route(NULL);
int sock = hci_open_dev(dev_id);
if (sock < 0) {
perror("Failed to open HCI device");
return;
}
// 扫描传统蓝牙设备
inquiry_info *ii = NULL;
int max_rsp = 255, num_rsp;
num_rsp = hci_inquiry(dev_id, duration, max_rsp, NULL, &ii, IREQ_CACHE_FLUSH);
if (num_rsp >= 0) {
printf("\n=== Classic Bluetooth Devices ===\n");
char addr[19] = {0};
for (int i = 0; i < num_rsp; i++) {
ba2str(&(ii+i)->bdaddr, addr);
printf("Classic: %s\n", addr);
}
free(ii);
}
// 扫描BLE设备
printf("\n=== BLE Devices ===\n");
le_set_scan_parameters_cp scan_params = {
.scan_type = 0x01, // 主动扫描
.interval = htobs(0x0010),
.window = htobs(0x0010),
.own_bdaddr_type = LE_PUBLIC_ADDRESS,
.filter = 0x01 // 过滤重复
};
struct hci_request ble_req;
memset(&ble_req, 0, sizeof(ble_req));
ble_req.ogf = OGF_LE_CTL;
ble_req.ocf = OCF_LE_SET_SCAN_PARAMETERS;
ble_req.cparam = &scan_params;
ble_req.clen = LE_SET_SCAN_PARAMETERS_CP_SIZE;
if (hci_send_req(sock, &ble_req, 1000) < 0) {
perror("Set BLE scan params failed");
}
le_set_scan_enable_cp scan_enable = {.enable = 0x01, .filter_dup = 0x01};
ble_req.ocf = OCF_LE_SET_SCAN_ENABLE;
ble_req.cparam = &scan_enable;
ble_req.clen = LE_SET_SCAN_ENABLE_CP_SIZE;
if (hci_send_req(sock, &ble_req, 1000) < 0) {
perror("Enable BLE scan failed");
}
// 处理BLE扫描结果
unsigned char buf[HCI_MAX_EVENT_SIZE];
struct hci_filter nf;
hci_filter_clear(&nf);
hci_filter_set_ptype(HCI_EVENT_PKT, &nf);
hci_filter_set_event(EVT_LE_META_EVENT, &nf);
setsockopt(sock, SOL_HCI, HCI_FILTER, &nf, sizeof(nf));
time_t start = time(NULL);
while (time(NULL) - start < duration) {
int len = read(sock, buf, sizeof(buf));
if (len < 0) continue;
if (buf[0] == HCI_EVENT_PKT && buf[1] == EVT_LE_META_EVENT) {
evt_le_meta_event *meta = (void *)(buf + 2);
if (meta->subevent == EVT_LE_ADVERTISING_REPORT) {
le_advertising_info *info = (void *)(meta->data + 1);
char addr[18];
ba2str(&info->bdaddr, addr);
printf("BLE: %s\n", addr);
}
}
}
// 停止扫描
scan_enable.enable = 0x00;
hci_send_req(sock, &ble_req, 1000);
close(sock);
}
// 连接传统蓝牙设备
int connect_classic_bt(const char *addr, int channel) {
struct sockaddr_rc addr_rc = {0};
int sock = socket(AF_BLUETOOTH, SOCK_STREAM, BTPROTO_RFCOMM);
addr_rc.rc_family = AF_BLUETOOTH;
addr_rc.rc_channel = (uint8_t)channel;
str2ba(addr, &addr_rc.rc_bdaddr);
if (connect(sock, (struct sockaddr *)&addr_rc, sizeof(addr_rc)) < 0) {
perror("RFCOMM connect failed");
close(sock);
return -1;
}
return sock;
}
// 连接BLE设备
gatt_connection_t* connect_ble(const char *addr) {
gatt_connection_t* connection = gattlib_connect(
NULL, addr, BDADDR_LE_PUBLIC, BT_SEC_LOW, 0, 0);
if (!connection) {
fprintf(stderr, "Failed to connect to BLE device\n");
return NULL;
}
return connection;
}
// 传统蓝牙数据传输
void classic_bt_transfer(int sock, const char *message) {
// 发送数据
if (write(sock, message, strlen(message)) < 0) {
perror("Write failed");
return;
}
// 接收数据
char buf[1024] = {0};
ssize_t len = read(sock, buf, sizeof(buf)-1);
if (len < 0) {
perror("Read failed");
} else {
printf("Received: %s\n", buf);
}
}
// BLE数据传输
void ble_transfer(gatt_connection_t* connection,
const char* read_uuid,
const char* write_uuid,
const uint8_t* data,
size_t data_len) {
uuid_t uuid_read, uuid_write;
gattlib_string_to_uuid(read_uuid, strlen(read_uuid), &uuid_read);
gattlib_string_to_uuid(write_uuid, strlen(write_uuid), &uuid_write);
// 读取特征值
uint8_t buffer[100];
size_t len = sizeof(buffer);
if (gattlib_read_char_by_uuid(connection, &uuid_read, buffer, &len) == 0) {
printf("Read value: ");
for (size_t i = 0; i < len; i++) printf("%02X ", buffer[i]);
printf("\n");
}
// 写入特征值
if (gattlib_write_char_by_uuid(connection, &uuid_write, data, data_len) == 0) {
printf("Write successful\n");
}
}
int main() {
printf("Scanning all Bluetooth devices for 5 seconds...\n");
scan_all_devices(5);
// 传统蓝牙示例
const char *classic_addr = "00:11:22:33:44:55";
int classic_sock = connect_classic_bt(classic_addr, 1);
if (classic_sock >= 0) {
classic_bt_transfer(classic_sock, "Hello Classic BT!");
close(classic_sock);
}
// BLE示例
const char *ble_addr = "AA:BB:CC:DD:EE:FF";
gatt_connection_t* ble_conn = connect_ble(ble_addr);
if (ble_conn) {
uint8_t data[] = {0x01, 0x02, 0x03};
ble_transfer(ble_conn,
"00002a00-0000-1000-8000-00805f9b34fb", // 标准设备名称UUID
"0000ffe1-0000-1000-8000-00805f9b34fb", // 示例自定义UUID
data, sizeof(data));
gattlib_disconnect(ble_conn);
}
return 0;
}
```
### 关键功能说明
1. **统一扫描**:
- 使用`hci_inquiry()`扫描传统蓝牙设备
- 使用LE扫描命令和事件处理扫描BLE设备
- 同时显示两种设备类型的结果
2. **连接机制**:
- 传统蓝牙:通过RFCOMM套接字连接
- BLE:使用gattlib库建立GATT连接
- 自动处理不同协议栈的连接参数
3. **数据传输**:
```c
// 传统蓝牙:基于流的套接字通信
write(sock, data, len);
read(sock, buffer, size);
// BLE:GATT特征值读写
gattlib_read_char_by_uuid(conn, &uuid, buffer, &len);
gattlib_write_char_by_uuid(conn, &uuid, data, data_len);
```
### 编译与运行
```bash
# 安装依赖
sudo apt install libbluetooth-dev libgattlib-dev
# 编译
gcc -o bt_manager bt_manager.c -lbluetooth -lgattlib
# 设置权限(避免sudo)
sudo setcap 'cap_net_admin,cap_net_raw+eip' ./bt_manager
# 运行
./bt_manager
```
### 关键注意事项
1. **设备区分**:
- 传统蓝牙:使用BR/EDR协议,设备类(class)字段通常非零
- BLE设备:广播数据包含LE特定标志(0x01)[^1]
```c
// 在扫描回调中判断设备类型
if (info->bdaddr_type == LE_PUBLIC_ADDRESS ||
info->bdaddr_type == LE_RANDOM_ADDRESS) {
// BLE设备
} else {
// 传统蓝牙设备
}
```
2. **错误处理**:
- 检查所有蓝牙API调用的返回值
- 使用`perror()`输出系统错误信息
- 实现超时重连机制:
```c
int retries = 0;
while ((sock = connect_classic_bt(addr, channel)) < 0 && retries < 3) {
sleep(1);
retries++;
}
```
3. **资源管理**:
- 确保关闭所有打开的套接字和连接
- 使用`free()`释放动态分配的内存
- BLE操作后调用`gattlib_disconnect()`
4. **UUID处理**:
- BLE服务使用标准UUID(16位或128位)
- 常用标准UUID参考:
- 设备信息: `0000180a-0000-1000-8000-00805f9b34fb`
- 电池服务: `0000180f-0000-1000-8000-00805f9b34fb`
- 自定义服务通常使用128位UUID
### 性能优化建议
1. **并行扫描**:
```c
// 使用线程同时执行传统和BLE扫描
pthread_t classic_thread, ble_thread;
pthread_create(&classic_thread, NULL, classic_scan_func, NULL);
pthread_create(&ble_thread, NULL, ble_scan_func, NULL);
pthread_join(classic_thread, NULL);
pthread_join(ble_thread, NULL);
```
2. **连接池管理**:
- 维护活跃连接列表
- 实现心跳机制保持连接
- 自动回收空闲连接
3. **数据缓存**:
- 为高频数据传输设计环形缓冲区
- 使用批处理减少小数据包传输
### 调试技巧
```bash
# 实时监控蓝牙流量
sudo btmon
# 查看已连接设备
bluetoothctl devices Connected
# 测试RFCOMM连接
sdptool browse 00:11:22:33:44:55
```
> 此实现结合了Linux BlueZ栈的底层API和gattlib库的高级抽象,完整覆盖了蓝牙双模设备的操作需求[^1][^3]。对于需要深度优化的场景,可参考BlueZ官方文档调整HCI参数[^1]。