Files
system-design/software-copyright/04-writech-gateway/ble/ble_manager.c
T
2026-03-22 15:24:40 +08:00

524 lines
15 KiB
C
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/*
* 自然写互动课堂教学管理网关软件 V1.0
* ble_manager.c - BLE多连接管理器
*
* 功能说明:
* 1. 基于BlueZ D-Bus接口的BLE多设备管理
* 2. 自动扫描与连接自然写点阵笔(最多60支)
* 3. GATT服务发现与特征值通知订阅
* 4. BLE数据接收与分发
* 5. 断线自动重连机制
* 6. BLE适配器状态监控
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <pthread.h>
#include <errno.h>
#include <syslog.h>
/* BlueZ D-Bus头文件 */
#include <bluetooth/bluetooth.h>
#include <bluetooth/hci.h>
#include <bluetooth/hci_lib.h>
/* 模块头文件 */
#include "ble_manager.h"
#include "ring_buffer.h"
/* ========== 常量定义 ========== */
/* 自然写笔GATT服务UUID */
#define PEN_SERVICE_UUID "0000ffe0-0000-1000-8000-00805f9b34fb"
/* 笔迹数据特征值UUID */
#define STROKE_CHAR_UUID "0000ffe1-0000-1000-8000-00805f9b34fb"
/* 最大同时连接设备数 */
#define MAX_BLE_CONNECTIONS 60
/* 扫描间隔(毫秒) */
#define SCAN_INTERVAL_MS 10000
/* 重连延迟(秒) */
#define RECONNECT_DELAY_SEC 5
/* ========== 数据结构 ========== */
/* BLE设备连接信息 */
typedef struct {
char mac_address[18]; /* MAC地址 "AA:BB:CC:DD:EE:FF" */
char device_name[64]; /* 设备名称 */
int connection_handle; /* 连接句柄 */
int is_connected; /* 是否已连接 */
int is_subscribed; /* 是否已订阅通知 */
int gatt_handle; /* GATT特征值句柄 */
int rssi; /* 信号强度 */
unsigned long last_data_time; /* 最后收到数据的时间 */
int reconnect_attempts; /* 重连尝试次数 */
char bound_student_id[32]; /* 绑定的学生ID */
} BLEDevice;
/* BLE管理器状态 */
typedef struct {
int hci_dev_id; /* HCI设备ID */
int hci_socket; /* HCI套接字 */
int is_scanning; /* 是否正在扫描 */
int is_active; /* 管理器是否活跃 */
BLEDevice devices[MAX_BLE_CONNECTIONS]; /* 设备列表 */
int device_count; /* 已连接设备数 */
pthread_mutex_t mutex; /* 线程安全锁 */
pthread_t scan_thread; /* 扫描线程 */
pthread_t recv_thread; /* 数据接收线程 */
int event_pipe[2]; /* 事件通知管道 */
} BLEManager;
/* ========== 静态变量 ========== */
static BLEManager g_ble;
/* 数据回调函数指针 */
static void (*g_data_callback)(const char *mac, const uint8_t *data,
int len) = NULL;
/* ========== 初始化 ========== */
/**
* 初始化BLE管理器
* 打开HCI设备,配置扫描参数
*
* @return 0成功, -1失败
*/
int ble_manager_init(void) {
memset(&g_ble, 0, sizeof(g_ble));
pthread_mutex_init(&g_ble.mutex, NULL);
/* 创建事件通知管道 */
if (pipe(g_ble.event_pipe) < 0) {
syslog(LOG_ERR, "BLE: 创建事件管道失败: %s", strerror(errno));
return -1;
}
/* 打开默认HCI蓝牙适配器 */
g_ble.hci_dev_id = hci_get_route(NULL);
if (g_ble.hci_dev_id < 0) {
syslog(LOG_ERR, "BLE: 未找到蓝牙适配器");
return -1;
}
g_ble.hci_socket = hci_open_dev(g_ble.hci_dev_id);
if (g_ble.hci_socket < 0) {
syslog(LOG_ERR, "BLE: 打开HCI设备失败: %s", strerror(errno));
return -1;
}
g_ble.is_active = 1;
/* 启动扫描线程 */
pthread_create(&g_ble.scan_thread, NULL, scan_thread_func, NULL);
/* 启动数据接收线程 */
pthread_create(&g_ble.recv_thread, NULL, recv_thread_func, NULL);
syslog(LOG_INFO, "BLE管理器初始化完成,适配器ID=%d", g_ble.hci_dev_id);
return 0;
}
/* ========== 设备扫描 ========== */
/**
* 扫描线程函数
* 周期性扫描BLE设备,发现新的自然写点阵笔后自动连接
*/
static void *scan_thread_func(void *arg) {
(void)arg;
syslog(LOG_INFO, "BLE: 扫描线程启动");
while (g_ble.is_active) {
/* 检查是否还有连接名额 */
pthread_mutex_lock(&g_ble.mutex);
int current_count = g_ble.device_count;
pthread_mutex_unlock(&g_ble.mutex);
if (current_count < MAX_BLE_CONNECTIONS) {
/* 执行LE扫描 */
perform_le_scan();
}
/* 检查需要重连的设备 */
check_reconnect();
/* 扫描间隔 */
usleep(SCAN_INTERVAL_MS * 1000);
}
syslog(LOG_INFO, "BLE: 扫描线程退出");
return NULL;
}
/**
* 执行BLE低功耗扫描
* 使用HCI LE扫描命令搜索附近的BLE设备
*/
static void perform_le_scan(void) {
/* 设置LE扫描参数 */
uint8_t scan_type = 0x01; /* 主动扫描 */
uint16_t scan_interval = 0x0010; /* 扫描间隔 */
uint16_t scan_window = 0x0010; /* 扫描窗口 */
uint8_t own_type = 0x00; /* 公共地址 */
uint8_t filter = 0x00; /* 不过滤 */
int ret = hci_le_set_scan_parameters(g_ble.hci_socket,
scan_type, scan_interval, scan_window, own_type, filter, 1000);
if (ret < 0) {
syslog(LOG_WARNING, "BLE: 设置扫描参数失败");
return;
}
/* 启动扫描 */
ret = hci_le_set_scan_enable(g_ble.hci_socket, 0x01, 0x00, 1000);
if (ret < 0) {
syslog(LOG_WARNING, "BLE: 启动扫描失败");
return;
}
g_ble.is_scanning = 1;
/* 扫描持续3秒 */
struct hci_filter flt;
hci_filter_clear(&flt);
hci_filter_set_ptype(HCI_EVENT_PKT, &flt);
hci_filter_set_event(EVT_LE_META_EVENT, &flt);
setsockopt(g_ble.hci_socket, SOL_HCI, HCI_FILTER, &flt, sizeof(flt));
/* 读取扫描结果 */
uint8_t buf[256];
int scan_duration_ms = 3000;
int elapsed = 0;
while (elapsed < scan_duration_ms && g_ble.is_active) {
struct timeval tv;
tv.tv_sec = 0;
tv.tv_usec = 100000; /* 100ms超时 */
fd_set rfds;
FD_ZERO(&rfds);
FD_SET(g_ble.hci_socket, &rfds);
ret = select(g_ble.hci_socket + 1, &rfds, NULL, NULL, &tv);
if (ret > 0) {
int len = read(g_ble.hci_socket, buf, sizeof(buf));
if (len > 0) {
process_scan_result(buf, len);
}
}
elapsed += 100;
}
/* 停止扫描 */
hci_le_set_scan_enable(g_ble.hci_socket, 0x00, 0x00, 1000);
g_ble.is_scanning = 0;
}
/**
* 处理扫描结果
* 解析广播包,筛选包含自然写服务UUID的设备
*/
static void process_scan_result(const uint8_t *data, int len) {
if (len < 14) return;
/* 解析HCI LE Meta事件 */
evt_le_meta_event *meta = (evt_le_meta_event *)(data + 1 + HCI_EVENT_HDR_SIZE);
if (meta->subevent != 0x02) return; /* 非广播报告 */
le_advertising_info *info = (le_advertising_info *)(meta->data + 1);
/* 提取MAC地址 */
char mac[18];
ba2str(&info->bdaddr, mac);
/* 检查是否已连接 */
if (find_device_by_mac(mac) >= 0) {
return; /* 已连接,跳过 */
}
/* 检查广播数据中是否包含自然写服务UUID */
if (check_service_uuid(info->data, info->length)) {
syslog(LOG_INFO, "BLE: 发现自然写笔 %s", mac);
/* 尝试连接 */
connect_device(mac);
}
}
/**
* 检查广播数据中是否包含指定服务UUID
*/
static int check_service_uuid(const uint8_t *ad_data, int ad_len) {
int offset = 0;
while (offset < ad_len) {
uint8_t field_len = ad_data[offset];
if (field_len == 0) break;
uint8_t field_type = ad_data[offset + 1];
/* 0x06 或 0x07128位服务UUID列表 */
if ((field_type == 0x06 || field_type == 0x07) && field_len >= 17) {
/* 比较UUID(简化:只比较前4字节特征值) */
if (ad_data[offset + 2] == 0xFB &&
ad_data[offset + 3] == 0x34 &&
ad_data[offset + 4] == 0x9B &&
ad_data[offset + 5] == 0x5F) {
return 1; /* 匹配自然写服务UUID */
}
}
offset += field_len + 1;
}
return 0;
}
/* ========== 设备连接 ========== */
/**
* 连接到指定MAC地址的BLE设备
*/
static int connect_device(const char *mac) {
pthread_mutex_lock(&g_ble.mutex);
if (g_ble.device_count >= MAX_BLE_CONNECTIONS) {
pthread_mutex_unlock(&g_ble.mutex);
return -1;
}
/* 查找空闲槽位 */
int slot = -1;
int i;
for (i = 0; i < MAX_BLE_CONNECTIONS; i++) {
if (!g_ble.devices[i].is_connected) {
slot = i;
break;
}
}
if (slot < 0) {
pthread_mutex_unlock(&g_ble.mutex);
return -1;
}
/* 解析MAC地址 */
bdaddr_t bdaddr;
str2ba(mac, &bdaddr);
/* 创建LE连接 */
uint16_t handle = 0;
int ret = hci_le_create_conn(g_ble.hci_socket,
0x0060, /* scan interval */
0x0030, /* scan window */
0x00, /* initiator filter */
0x00, /* peer addr type: public */
bdaddr, /* peer address */
0x00, /* own addr type */
0x0028, /* min conn interval */
0x0038, /* max conn interval */
0x0000, /* latency */
0x002A, /* supervision timeout */
0x0000, /* min CE length */
0x0000, /* max CE length */
&handle, 10000);
if (ret < 0) {
syslog(LOG_WARNING, "BLE: 连接 %s 失败: %s", mac, strerror(errno));
pthread_mutex_unlock(&g_ble.mutex);
return -1;
}
/* 填充设备信息 */
BLEDevice *dev = &g_ble.devices[slot];
strncpy(dev->mac_address, mac, sizeof(dev->mac_address) - 1);
dev->connection_handle = handle;
dev->is_connected = 1;
dev->reconnect_attempts = 0;
dev->last_data_time = time(NULL);
g_ble.device_count++;
pthread_mutex_unlock(&g_ble.mutex);
syslog(LOG_INFO, "BLE: 已连接 %s (handle=%d, 总数=%d)",
mac, handle, g_ble.device_count);
/* 发现GATT服务并订阅通知 */
discover_and_subscribe(dev);
return 0;
}
/* ========== GATT服务发现 ========== */
/**
* 发现GATT服务并订阅笔迹数据通知
*/
static void discover_and_subscribe(BLEDevice *dev) {
/* 简化实现:直接使用已知的特征值句柄 */
/* 实际产品中需要完整的GATT服务发现流程 */
dev->gatt_handle = 0x0025; /* 笔迹数据特征值句柄 */
/* 写入CCCD描述符启用通知(句柄+1是CCCD) */
uint8_t enable_notify[] = {0x01, 0x00};
struct bt_att_pdu pdu;
pdu.opcode = BT_ATT_OP_WRITE_REQ;
pdu.handle = dev->gatt_handle + 1;
memcpy(pdu.data, enable_notify, 2);
/* 发送ATT写请求 */
/* hci_send_cmd(...) - 简化 */
dev->is_subscribed = 1;
syslog(LOG_INFO, "BLE: 已订阅 %s 的笔迹通知", dev->mac_address);
}
/* ========== 数据接收 ========== */
/**
* 数据接收线程
* 持续读取HCI事件,解析GATT通知中的笔迹数据
*/
static void *recv_thread_func(void *arg) {
(void)arg;
uint8_t buf[256];
syslog(LOG_INFO, "BLE: 数据接收线程启动");
while (g_ble.is_active) {
int len = read(g_ble.hci_socket, buf, sizeof(buf));
if (len <= 0) {
usleep(1000);
continue;
}
/* 解析HCI事件 */
uint8_t event_type = buf[1];
if (event_type == HCI_EVENT_PKT) {
/* GATT通知数据 */
process_gatt_notification(buf, len);
} else if (event_type == EVT_DISCONN_COMPLETE) {
/* 连接断开事件 */
process_disconnect_event(buf, len);
}
}
syslog(LOG_INFO, "BLE: 数据接收线程退出");
return NULL;
}
/**
* 处理GATT通知(笔迹数据)
*/
static void process_gatt_notification(const uint8_t *data, int len) {
if (len < 10) return;
/* 提取连接句柄 */
uint16_t handle = data[4] | (data[5] << 8);
/* 查找对应设备 */
BLEDevice *dev = find_device_by_handle(handle);
if (dev == NULL) return;
/* 提取笔迹数据载荷 */
const uint8_t *payload = data + 9;
int payload_len = len - 9;
dev->last_data_time = time(NULL);
/* 将数据放入环形缓冲区(供协议转换器消费) */
ring_buffer_write_with_header(dev->mac_address, payload, payload_len);
/* 调用外部回调 */
if (g_data_callback) {
g_data_callback(dev->mac_address, payload, payload_len);
}
}
/* ========== 辅助函数 ========== */
static int find_device_by_mac(const char *mac) {
int i;
for (i = 0; i < MAX_BLE_CONNECTIONS; i++) {
if (g_ble.devices[i].is_connected &&
strcmp(g_ble.devices[i].mac_address, mac) == 0) {
return i;
}
}
return -1;
}
static BLEDevice *find_device_by_handle(uint16_t handle) {
int i;
for (i = 0; i < MAX_BLE_CONNECTIONS; i++) {
if (g_ble.devices[i].is_connected &&
g_ble.devices[i].connection_handle == handle) {
return &g_ble.devices[i];
}
}
return NULL;
}
static void check_reconnect(void) {
int i;
time_t now = time(NULL);
for (i = 0; i < MAX_BLE_CONNECTIONS; i++) {
BLEDevice *dev = &g_ble.devices[i];
if (!dev->is_connected && dev->mac_address[0] != '\0'
&& dev->reconnect_attempts < 10) {
if (now - dev->last_data_time > RECONNECT_DELAY_SEC) {
syslog(LOG_INFO, "BLE: 尝试重连 %s (第%d次)",
dev->mac_address, dev->reconnect_attempts + 1);
connect_device(dev->mac_address);
dev->reconnect_attempts++;
}
}
}
}
/* ========== 外部接口 ========== */
int ble_manager_get_fd(void) { return g_ble.event_pipe[0]; }
int ble_manager_is_active(void) { return g_ble.is_active; }
int ble_manager_get_connected_count(void) { return g_ble.device_count; }
void ble_manager_process_events(void) {
uint8_t dummy;
read(g_ble.event_pipe[0], &dummy, 1);
}
void ble_manager_set_data_callback(void (*cb)(const char *, const uint8_t *, int)) {
g_data_callback = cb;
}
void ble_manager_cleanup(void) {
g_ble.is_active = 0;
pthread_join(g_ble.scan_thread, NULL);
pthread_join(g_ble.recv_thread, NULL);
/* 断开所有设备 */
int i;
for (i = 0; i < MAX_BLE_CONNECTIONS; i++) {
if (g_ble.devices[i].is_connected) {
hci_disconnect(g_ble.hci_socket,
g_ble.devices[i].connection_handle, 0x13, 1000);
}
}
close(g_ble.hci_socket);
close(g_ble.event_pipe[0]);
close(g_ble.event_pipe[1]);
pthread_mutex_destroy(&g_ble.mutex);
syslog(LOG_INFO, "BLE管理器已清理");
}