使用 gattlib-0.7.2 开发蓝牙 BLE 应用

Home / C++ MrLee 3天前 13

下面是一个完整的 Ubuntu C++ 项目,使用 gattlib-0.7.2 库实现 BLE 设备的扫描、连接和通知功能,并使用 CMake 进行编译。

项目结构

text
ble_project/
├── CMakeLists.txt
├── include/
│   └── ble_scanner.h
├── src/
│   ├── ble_scanner.cpp
│   └── main.cpp
└── third_party/
    └── gattlib/
        └── (gattlib 头文件和库文件)

1. 安装依赖

首先安装必要的依赖包:

bash
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
# 下载 gattlibcd third_partygit clone https://github.com/labapart/gattlib.gitcd gattlib# 编译和安装mkdir build && cd build
cmake ..makesudo make install

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_H

5. 实现文件 (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. 注意事项

  1. 权限问题:运行程序可能需要 root 权限或蓝牙相关权限

  2. UUID:示例中的 UUID 需要替换为实际设备的 UUID

  3. 错误处理:实际应用中需要更完善的错误处理

  4. 资源管理:确保正确释放所有资源

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 扫描、连接和通知功能框架,你可以根据实际需求进行修改和扩展。


本文链接:http://it72.com/12794.htm

推荐阅读
最新回复 (0)
返回