下面是一个完整的 Ubuntu C++ 项目,使用 gattlib-0.7.2 库实现 BLE 设备的扫描、连接和通知功能,并使用 CMake 进行编译——AI生成,并不可用,程序代码必须建立在glib开发,所有事件在gattlib-main-loop中完成。。。还是看自带的例子吧。。。
项目结构
ble_project/ ├── CMakeLists.txt ├── include/ │ └── ble_scanner.h ├── src/ │ ├── ble_scanner.cpp │ └── main.cpp └── third_party/ └── gattlib/ └── (gattlib 头文件和库文件)
1. 安装依赖
首先安装必要的依赖包:
sudo apt-get updatesudo apt-get install -y \ cmake \ build-essential \ pkg-config \ libbluetooth-dev \ libglib2.0-dev \ libboost-system-dev \ libboost-thread-dev
2. 下载和编译 gattlib
bash
3. CMakeLists.txt
cmake
cmake_minimum_required(VERSION 3.10)project(BLE_Scanner)set(CMAKE_CXX_STANDARD 11)set(CMAKE_CXX_STANDARD_REQUIRED ON)# 查找依赖包find_package(PkgConfig REQUIRED)pkg_check_modules(GATTLIB REQUIRED gattlib)pkg_check_modules(GLIB2 REQUIRED glib-2.0)pkg_check_modules(BLUEZ REQUIRED bluez)# 包含目录include_directories(
${CMAKE_SOURCE_DIR}/include ${GATTLIB_INCLUDE_DIRS}
${GLIB2_INCLUDE_DIRS})# 添加可执行文件add_executable(ble_scanner
src/main.cpp
src/ble_scanner.cpp)# 链接库target_link_libraries(ble_scanner ${GATTLIB_LIBRARIES}
${GLIB2_LIBRARIES}
${BLUEZ_LIBRARIES}
pthread
dl)# 安装规则install(TARGETS ble_scanner DESTINATION bin)4. 头文件 (include/ble_scanner.h)
cpp
#ifndef BLE_SCANNER_H#define BLE_SCANNER_H#include <gattlib.h>#include <string>#include <vector>#include <functional>#include <memory>// BLE 设备信息结构struct BLEDevice {
std::string address;
std::string name;
int rssi;};// 特征值通知回调类型using NotificationCallback = std::function<void(const uint8_t* data, size_t length)>;class BLEScanner {public:
BLEScanner();
~BLEScanner();
// 扫描设备
bool scanDevices(int timeout_sec, std::vector<BLEDevice>& devices);
// 连接设备
bool connect(const std::string& device_address);
// 断开连接
void disconnect();
// 发现服务
bool discoverServices();
// 读取特征值
std::vector<uint8_t> readCharacteristic(const std::string& uuid);
// 写入特征值
bool writeCharacteristic(const std::string& uuid, const std::vector<uint8_t>& data);
// 启用通知
bool enableNotification(const std::string& uuid, NotificationCallback callback);
// 禁用通知
bool disableNotification(const std::string& uuid);private:
gatt_connection_t* m_connection;
std::string m_connected_device;
NotificationCallback m_notification_callback;
// 静态回调函数
static void onNotification(const uuid_t* uuid, const uint8_t* data, size_t data_length, void* user_data);};#endif // BLE_SCANNER_H5. 实现文件 (src/ble_scanner.cpp)
cpp
#include "ble_scanner.h"#include <iostream>#include <chrono>#include <thread>#include <mutex>// 扫描回调函数static void adapter_cb(const char* addr, const char* name, void* user_data) {
auto devices = static_cast<std::vector<BLEDevice>*>(user_data);
BLEDevice device;
device.address = addr ? addr : "Unknown";
device.name = name ? name : "Unknown";
devices->push_back(device);}BLEScanner::BLEScanner() : m_connection(nullptr) {}BLEScanner::~BLEScanner() {
disconnect();}bool BLEScanner::scanDevices(int timeout_sec, std::vector<BLEDevice>& devices) {
devices.clear();
// 获取默认适配器
const char* adapter_name = nullptr; // 使用默认适配器
int ret = gattlib_adapter_scan_enable(adapter_name, adapter_cb, timeout_sec * 1000, &devices);
if (ret != 0) {
std::cerr << "Failed to start scanning: " << ret << std::endl;
return false;
}
// 等待扫描完成
std::this_thread::sleep_for(std::chrono::seconds(timeout_sec));
gattlib_adapter_scan_disable(adapter_name);
return true;}bool BLEScanner::connect(const std::string& device_address) {
if (m_connection) {
disconnect();
}
const char* adapter_name = nullptr;
const char* addr = device_address.c_str();
m_connection = gattlib_connect(adapter_name, addr, GATTLIB_CONNECTION_OPTIONS_LEGACY_DEFAULT);
if (!m_connection) {
std::cerr << "Failed to connect to device: " << device_address << std::endl;
return false;
}
m_connected_device = device_address;
std::cout << "Connected to device: " << device_address << std::endl;
return true;}void BLEScanner::disconnect() {
if (m_connection) {
gattlib_disconnect(m_connection);
m_connection = nullptr;
m_connected_device.clear();
std::cout << "Disconnected" << std::endl;
}}bool BLEScanner::discoverServices() {
if (!m_connection) {
std::cerr << "Not connected to any device" << std::endl;
return false;
}
gattlib_primary_service_t* services;
int services_count;
int ret = gattlib_discover_primary_services(m_connection, &services, &services_count);
if (ret != 0) {
std::cerr << "Failed to discover services: " << ret << std::endl;
return false;
}
std::cout << "Discovered " << services_count << " services:" << std::endl;
for (int i = 0; i < services_count; i++) {
char uuid_str[MAX_LEN_UUID_STR + 1];
gattlib_uuid_to_string(&services[i].uuid, uuid_str, sizeof(uuid_str));
std::cout << "Service " << i << ": " << uuid_str << std::endl;
}
gattlib_primary_service_discover_free(services, services_count);
return true;}std::vector<uint8_t> BLEScanner::readCharacteristic(const std::string& uuid) {
std::vector<uint8_t> data;
if (!m_connection) {
std::cerr << "Not connected to any device" << std::endl;
return data;
}
uuid_t gattlib_uuid;
if (gattlib_string_to_uuid(uuid.c_str(), uuid.length(), &gattlib_uuid) != 0) {
std::cerr << "Invalid UUID: " << uuid << std::endl;
return data;
}
uint8_t* buffer;
size_t buffer_len;
int ret = gattlib_read_char_by_uuid(m_connection, &gattlib_uuid, (void**)&buffer, &buffer_len);
if (ret != 0) {
std::cerr << "Failed to read characteristic: " << ret << std::endl;
return data;
}
data.assign(buffer, buffer + buffer_len);
free(buffer);
return data;}bool BLEScanner::writeCharacteristic(const std::string& uuid, const std::vector<uint8_t>& data) {
if (!m_connection) {
std::cerr << "Not connected to any device" << std::endl;
return false;
}
uuid_t gattlib_uuid;
if (gattlib_string_to_uuid(uuid.c_str(), uuid.length(), &gattlib_uuid) != 0) {
std::cerr << "Invalid UUID: " << uuid << std::endl;
return false;
}
int ret = gattlib_write_char_by_uuid(m_connection, &gattlib_uuid, (void*)data.data(), data.size());
if (ret != 0) {
std::cerr << "Failed to write characteristic: " << ret << std::endl;
return false;
}
return true;}void BLEScanner::onNotification(const uuid_t* uuid, const uint8_t* data, size_t data_length, void* user_data) {
BLEScanner* scanner = static_cast<BLEScanner*>(user_data);
if (scanner && scanner->m_notification_callback) {
scanner->m_notification_callback(data, data_length);
}}bool BLEScanner::enableNotification(const std::string& uuid, NotificationCallback callback) {
if (!m_connection) {
std::cerr << "Not connected to any device" << std::endl;
return false;
}
uuid_t gattlib_uuid;
if (gattlib_string_to_uuid(uuid.c_str(), uuid.length(), &gattlib_uuid) != 0) {
std::cerr << "Invalid UUID: " << uuid << std::endl;
return false;
}
m_notification_callback = callback;
int ret = gattlib_notification_start(m_connection, &gattlib_uuid, onNotification, this);
if (ret != 0) {
std::cerr << "Failed to enable notification: " << ret << std::endl;
return false;
}
return true;}bool BLEScanner::disableNotification(const std::string& uuid) {
if (!m_connection) {
std::cerr << "Not connected to any device" << std::endl;
return false;
}
uuid_t gattlib_uuid;
if (gattlib_string_to_uuid(uuid.c_str(), uuid.length(), &gattlib_uuid) != 0) {
std::cerr << "Invalid UUID: " << uuid << std::endl;
return false;
}
int ret = gattlib_notification_stop(m_connection, &gattlib_uuid);
if (ret != 0) {
std::cerr << "Failed to disable notification: " << ret << std::endl;
return false;
}
m_notification_callback = nullptr;
return true;}6. 主程序 (src/main.cpp)
cpp
#include "ble_scanner.h"#include <iostream>#include <iomanip>#include <thread>#include <atomic>std::atomic<bool> running(true);// 信号处理函数void signalHandler(int signum) {
std::cout << "Interrupt signal received. Exiting..." << std::endl;
running = false;}// 通知回调函数示例void notificationCallback(const uint8_t* data, size_t length) {
std::cout << "Notification received (" << length << " bytes): ";
for (size_t i = 0; i < length; i++) {
std::cout << std::hex << std::setw(2) << std::setfill('0')
<< static_cast<int>(data[i]) << " ";
}
std::cout << std::dec << std::endl;}int main() {
// 设置信号处理
signal(SIGINT, signalHandler);
BLEScanner scanner;
// 扫描设备
std::cout << "Scanning for BLE devices..." << std::endl;
std::vector<BLEDevice> devices;
if (!scanner.scanDevices(5, devices)) {
std::cerr << "Scan failed" << std::endl;
return 1;
}
std::cout << "Found " << devices.size() << " devices:" << std::endl;
for (size_t i = 0; i < devices.size(); i++) {
std::cout << i << ": " << devices[i].address
<< " - " << devices[i].name
<< " (RSSI: " << devices[i].rssi << ")" << std::endl;
}
if (devices.empty()) {
std::cout << "No devices found" << std::endl;
return 0;
}
// 连接第一个设备(可以根据需要修改)
std::string target_device = devices[0].address;
std::cout << "Connecting to: " << target_device << std::endl;
if (!scanner.connect(target_device)) {
std::cerr << "Connection failed" << std::endl;
return 1;
}
// 发现服务
if (!scanner.discoverServices()) {
std::cerr << "Service discovery failed" << std::endl;
scanner.disconnect();
return 1;
}
// 示例:启用通知(需要替换为实际的 UUID)
std::string notify_uuid = "00002a37-0000-1000-8000-00805f9b34fb"; // 心率测量
if (scanner.enableNotification(notify_uuid, notificationCallback)) {
std::cout << "Notification enabled for UUID: " << notify_uuid << std::endl;
// 保持运行以接收通知
while (running) {
std::this_thread::sleep_for(std::chrono::seconds(1));
}
scanner.disableNotification(notify_uuid);
} else {
std::cerr << "Failed to enable notification" << std::endl;
}
scanner.disconnect();
return 0;}7. 编译和运行
bash
# 创建构建目录mkdir buildcd build# 配置项目cmake ..# 编译make# 运行(需要 root 权限访问蓝牙)sudo ./ble_scanner
8. 注意事项
权限问题:运行程序可能需要 root 权限或蓝牙相关权限
UUID:示例中的 UUID 需要替换为实际设备的 UUID
错误处理:实际应用中需要更完善的错误处理
资源管理:确保正确释放所有资源
9. 常见问题解决
如果遇到链接错误,确保 gattlib 正确安装:
bash
# 检查 gattlib 是否安装pkg-config --exists gattlib && echo "gattlib found" || echo "gattlib not found"# 如果找不到,可以手动指定路径export PKG_CONFIG_PATH=/usr/local/lib/pkgconfig:$PKG_CONFIG_PATH
这个项目提供了完整的 BLE 扫描、连接和通知功能框架,你可以根据实际需求进行修改和扩展。
收藏的用户(0) X
正在加载信息~
推荐阅读
最新回复 (0)
站点信息
- 文章2313
- 用户1336
- 访客11743308
每日一句
Change your thoughts and you change your world.
改变你的想法,你就改变了你的世界。
改变你的想法,你就改变了你的世界。
新会员