Rust蓝牙开发库bluez-async的使用:异步支持Linux蓝牙协议栈的高效通信

Rust蓝牙开发库bluez-async的使用:异步支持Linux蓝牙协议栈的高效通信

bluez-async 是一个围绕 BlueZ(Linux 蓝牙守护进程)D-Bus 接口的异步封装库。它提供了类型安全的接口来访问 BlueZ 暴露的蓝牙客户端(即中心设备,在蓝牙术语中)接口的子集,主要关注蓝牙低功耗 (BLE) 的通用属性配置文件 (GATT)。

使用示例

以下是使用 bluez-async 的基本示例代码:

// 创建新会话。这会建立与 BlueZ 通信的 D-Bus 连接。
// 这里我们忽略 join handle,因为我们不打算无限期运行。
let (_, session) = BluetoothSession::new().await?;

// 开始扫描蓝牙设备,并等待几秒钟以发现一些设备。
session.start_discovery().await?;
time::sleep(Duration::from_secs(5)).await;
session.stop_discovery().await?;

// 获取当前已知的设备列表。
let devices = session.get_devices().await?;

// 找到我们关心的设备。
let device = devices
    .into_iter()
    .find(|device| device.name.as_deref() == Some("My device"))
    .unwrap();

// 连接到该设备。
session.connect(&device.id).await?;

// 通过短 UUID 查找 GATT 服务和特征。
let service = session
    .get_service_by_uuid(&device.id, uuid_from_u16(0x1234))
    .await?;
let characteristic = session
    .get_characteristic_by_uuid(&service.id, uuid_from_u32(0x1235))
    .await?;

// 读取特征值并写入新值。
println!(
    "Value: {:?}",
    session
        .read_characteristic_value(&characteristic.id)
        .await?
);
session
    .write_characteristic_value(&characteristic.id, vec![1, 2, 3])
    .await?;

// 订阅特征的通知并打印出来。
let mut events = session
    .characteristic_event_stream(&characteristic.id)
    .await?;
session.start_notify(&characteristic.id).await?;
while let Some(event) = events.next().await {
    if let BluetoothEvent::Characteristic {
        id,
        event: CharacteristicEvent::Value { value },
    } = event
    {
        println!("Update from {}: {:?}", id, value);
    }
}

完整示例代码

以下是一个更完整的示例,展示了如何使用 bluez-async 进行蓝牙设备扫描、连接和通信:

use bluez_async::{
    BluetoothEvent, BluetoothSession, CharacteristicEvent, MacAddress, Uuid,
};
use futures::StreamExt;
use std::time::Duration;
use tokio::time;

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    // 1. 创建蓝牙会话
    let (_, session) = BluetoothSession::new().await?;

    // 2. 开始设备发现
    println!("开始扫描蓝牙设备...");
    session.start_discovery().await?;
    time::sleep(Duration::from_secs(10)).await;
    session.stop_discovery().await?;
    println!("扫描完成");

    // 3. 获取设备列表
    let devices = session.get_devices().await?;
    println!("发现 {} 个设备", devices.len());

    // 4. 筛选目标设备 (这里假设我们要连接名为"MyDevice"的设备)
    let target_device = devices
        .into_iter()
        .find(|d| d.name.as_deref() == Some("MyDevice"))
        .ok_or("未找到目标设备")?;

    println!("找到设备: {:?}", target_device);

    // 5. 连接到设备
    println!("连接到设备...");
    session.connect(&target_device.id).await?;
    println!("连接成功");

    // 6. 发现服务 (假设服务UUID为0x1234)
    let service_uuid = Uuid::from_u16(0x1234);
    println!("查找服务: {}", service_uuid);
    let service = session
        .get_service_by_uuid(&target_device.id, service_uuid)
        .await?;

    // 7. 发现特征 (假设特征UUID为0x1235)
    let char_uuid = Uuid::from_u16(0x1235);
    println!("查找特征: {}", char_uuid);
    let characteristic = session
        .get_characteristic_by_uuid(&service.id, char_uuid)
        .await?;

    // 8. 读取特征值
    let value = session.read_characteristic_value(&characteristic.id).await?;
    println!("特征值: {:?}", value);

    // 9. 写入特征值
    let new_value = vec![0x01, 0x02, 0x03];
    println!("写入新值: {:?}", new_value);
    session
        .write_characteristic_value(&characteristic.id, new_value)
        .await?;

    // 10. 订阅通知
    println!("订阅通知...");
    let mut events = session.characteristic_event_stream(&characteristic.id).await?;
    session.start_notify(&characteristic.id).await?;

    println!("等待通知 (按Ctrl+C退出)...");
    while let Some(event) = events.next().await {
        match event {
            BluetoothEvent::Characteristic {
                id,
                event: CharacteristicEvent::Value { value },
            } => {
                println!("收到通知 from {}: {:?}", id, value);
            }
            _ => {}
        }
    }

    Ok(())
}

功能说明

  1. 设备发现:通过 start_discovery()stop_discovery() 方法扫描周围的蓝牙设备。
  2. 设备连接:使用 connect() 方法连接到特定设备。
  3. 服务发现:通过 UUID 查找设备提供的服务。
  4. 特征操作
    • 读取特征值 (read_characteristic_value)
    • 写入特征值 (write_characteristic_value)
    • 订阅通知 (start_notify + characteristic_event_stream)

依赖项

要使用 bluez-async,需要在 Cargo.toml 中添加以下依赖:

[dependencies]
bluez-async = "0.8.2"
tokio = { version = "1.0", features = ["full"] }
futures = "0.3"

许可证

bluez-async 采用双重许可:

  • Apache License, Version 2.0
  • MIT license

您可以根据需要选择其中一种许可证。


1 回复

Rust蓝牙开发库bluez-async使用指南

简介

bluez-async是一个Rust库,提供了对Linux蓝牙协议栈(BlueZ)的异步支持,使开发者能够高效地进行蓝牙通信开发。它基于Tokio异步运行时,提供了简洁的API来与BlueZ的DBus接口交互。

主要特性

  • 完全异步支持(Tokio)
  • 类型安全的DBus接口
  • 自动处理DBus对象路径和信号
  • 支持蓝牙设备发现、连接、服务发现等操作

安装

在Cargo.toml中添加依赖:

[dependencies]
bluez-async = "0.3"
tokio = { version = "1.0", features = ["full"] }

基本使用方法

初始化蓝牙适配器

use bluez_async::BluetoothSession;

#[tokio::main]
async fn main() -> Result<(), eyre::Report> {
    // 创建蓝牙会话
    let (_, session) = BluetoothSession::new().await?;
    
    // 获取默认适配器
    let adapter = session.get_default_adapter().await?;
    println!("使用适配器: {}", adapter.id);
    
    Ok(())
}

扫描蓝牙设备

use bluez_async::{BluetoothEvent, BluetoothSession};
use futures::stream::StreamExt;

#[tokio::main]
async fn main() -> Result<(), eyre::Report> {
    let (sender, session) = BluetoothSession::new().await?;
    let adapter = session.get_default_adapter().await?;
    
    // 开始扫描
    session.start_discovery(&adapter.id).await?;
    
    // 监听设备发现事件
    let mut events = sender.incoming();
    while let Some(event) = events.next().await {
        match event {
            BluetoothEvent::Device { id, address, rssi, .. } => {
                println!("发现设备: {} (地址: {}), 信号强度: {:?}", id, address, rssi);
            }
            _ => {}
        }
    }
    
    Ok(())
}

连接设备并发现服务

use bluez_async::{BluetoothSession, MacAddress};

#[tokio::main]
async fn main() -> Result<(), eyre::Report> {
    let (_, session) = BluetoothSession::new().await?;
    
    // 设备MAC地址
    let device_addr = MacAddress::from([0xAA, 0xBB, 0xCC, 0xDD, 0xEE, 0xFF]);
    
    // 连接设备
    let device = session.get_device_by_address(device_addr).await?;
    session.connect(&device.id).await?;
    
    // 发现服务
    let services = session.get_services(&device.id).await?;
    for service in services {
        println!("服务 UUID: {}", service.uuid);
    }
    
    Ok(())
}

高级用法

读取特征值

use bluez_async::{BluetoothSession, MacAddress, Uuid};

#[tokio::main]
async fn main() -> Result<(), eyre::Report> {
    let (_, session) = BluetoothSession::new().await?;
    let device_addr = MacAddress::from([0xAA, 0xBB, 0xCC, 0xDD, 0xEE, 0xFF]);
    let device = session.get_device_by_address(device_addr).await?;
    
    // 假设我们已经知道特征UUID
    let char_uuid = Uuid::parse_str("00002a00-0000-1000-8000-00805f9b34fb")?;
    
    // 获取特征
    let characteristic = session.get_characteristic_by_uuid(&device.id, char_uuid).await?;
    
    // 读取特征值
    let value = session.read_value(&characteristic.id).await?;
    println!("特征值: {:?}", value);
    
    Ok(())
}

写入特征值

use bluez_async::{BluetoothSession, MacAddress, Uuid};

#[tokio::main]
async fn main() -> Result<(), eyre::Report> {
    let (_, session) = BluetoothSession::new().await?;
    let device_addr = MacAddress::from([0xAA, 0xBB, 0xCC, 0xDD, 0xEE, 0xFF]);
    let device = session.get_device_by_address(device_addr).await?;
    
    let char_uuid = Uuid::parse_str("00002a01-0000-1000-8000-00805f9b34fb")?;
    let characteristic = session.get_characteristic_by_uuid(&device.id, char_uuid).await?;
    
    // 写入特征值
    let data = vec![0x01, 0x02, 0x03];
    session.write_value(&characteristic.id, data).await?;
    
    Ok(())
}

完整示例

下面是一个完整的蓝牙设备扫描和连接示例:

use bluez_async::{BluetoothEvent, BluetoothSession, MacAddress};
use futures::stream::StreamExt;
use std::time::Duration;
use tokio::time::sleep;

#[tokio::main]
async fn main() -> Result<(), eyre::Report> {
    // 1. 初始化蓝牙会话
    let (sender, session) = BluetoothSession::new().await?;
    let adapter = session.get_default_adapter().await?;
    println!("默认蓝牙适配器: {}", adapter.id);

    // 2. 开始扫描设备
    session.start_discovery(&adapter.id).await?;
    println!("开始扫描蓝牙设备...");

    // 3. 监听设备发现事件
    let mut events = sender.incoming();
    let mut target_device = None;
    
    // 扫描10秒
    tokio::select! {
        _ = async {
            sleep(Duration::from_secs(10)).await;
            println!("扫描结束");
        } => {}
        
        device = async {
            while let Some(event) = events.next().await {
                if let BluetoothEvent::Device { id, address, name, rssi, .. } = event {
                    println!("发现设备: {} (地址: {}), 名称: {:?}, 信号强度: {:?}", 
                           id, address, name, rssi);
                    
                    // 假设我们要连接名为"MyDevice"的设备
                    if name.as_deref() == Some("MyDevice") {
                        target_device = Some((id, address));
                        break;
                    }
                }
            }
        } => {}
    }

    // 4. 停止扫描
    session.stop_discovery(&adapter.id).await?;

    // 5. 连接目标设备
    if let Some((device_id, address)) = target_device {
        println!("尝试连接设备: {}", address);
        
        // 连接设备
        session.connect(&device_id).await?;
        println!("设备已连接");
        
        // 发现服务
        let services = session.get_services(&device_id).await?;
        println!("发现{}个服务:", services.len());
        
        for service in services {
            println!("服务 UUID: {}", service.uuid);
        }
    } else {
        println!("未找到目标设备");
    }

    Ok(())
}

注意事项

  1. 需要Linux系统和BlueZ蓝牙协议栈
  2. 需要DBus系统服务正常运行
  3. 程序运行时需要适当的权限(通常需要root或加入bluetooth组)
  4. 某些操作可能需要先停止扫描才能执行

bluez-async库为Rust开发者提供了高效、类型安全的方式来与Linux蓝牙设备交互,特别适合需要高性能蓝牙通信的应用场景。

回到顶部