Rust蓝牙开发库objc2-core-bluetooth的使用:跨平台蓝牙设备连接与通信的核心功能实现

Rust蓝牙开发库objc2-core-bluetooth的使用:跨平台蓝牙设备连接与通信的核心功能实现

objc2-core-bluetooth是Rust对苹果CoreBluetooth框架的绑定库。

安装

在项目目录中运行以下Cargo命令:

cargo add objc2-core-bluetooth

或者在Cargo.toml中添加以下行:

objc2-core-bluetooth = "0.3.1"

使用示例

下面是一个增强版的完整蓝牙设备通信示例,包含更多细节和错误处理:

use objc2::rc::{Id, Shared};
use objc2::runtime::NSObject;
use objc2_foundation::{NSError, NSString, NSDictionary, NSNumber};
use objc2_core_bluetooth::{
    CBManager, CBPeripheral, CBPeripheralDelegate, CBService, CBCharacteristic,
    CBCentralManager, CBCentralManagerDelegate, CBUUID, CBManagerState,
    CBCharacteristicProperties, CBATTError
};

// 中央管理器状态跟踪
struct BluetoothState {
    is_scanning: bool,
    connected_devices: Vec<Id<CBPeripheral, Shared>>,
}

// 中央管理器委托实现
#[derive(Debug)]
struct CentralManagerDelegate {
    state: BluetoothState,
}

impl CentralManagerDelegate {
    fn new() -> Self {
        Self {
            state: BluetoothState {
                is_scanning: false,
                connected_devices: Vec::new(),
            }
        }
    }
}

unsafe impl CBCentralManagerDelegate for CentralManagerDelegate {
    type CentralManager = Id<CBCentralManager, Shared>;
    
    fn did_update_state(&mut self, central: Self::CentralManager) {
        match central.state() {
            CBManagerState::PoweredOn => {
                println!("蓝牙已开启,开始扫描...");
                self.state.is_scanning = true;
                central.scan_for_peripherals_with_services(None, None);
            },
            CBManagerState::PoweredOff => {
                println!("蓝牙已关闭");
                self.state.is_scanning = false;
            },
            _ => println!("蓝牙状态改变: {:?}", central.state()),
        }
    }
    
    fn did_discover_peripheral(
        &self,
        central: Self::CentralManager,
        peripheral: Id<CBPeripheral, Shared>,
        advertisement_data: Id<NSDictionary, Shared>,
        rssi: Id<NSNumber, Shared>
    ) {
        let name = peripheral.name().unwrap_or_else(|| NSString::from_str("Unknown"));
        println!("发现设备: {} (RSSI: {})", name, rssi);
        
        // 检查广告数据
        if let Some(services) = advertisement_data.object_for_key(NSString::from_str("kCBAdvDataServiceUUIDs")) {
            println!("广告服务UUID: {:?}", services);
        }
        
        // 只连接特定设备 - 这里可以添加过滤条件
        if name.to_string().contains("MyDevice") {
            println!("连接设备: {}", name);
            central.connect_peripheral(&peripheral, None);
        }
    }
    
    fn did_connect_peripheral(
        &self,
        _central: Self::CentralManager,
        peripheral: Id<CBPeripheral, Shared>
    ) {
        println!("成功连接设备: {:?}", peripheral.name());
        
        let delegate = Box::new(PeripheralDelegate::new());
        peripheral.set_delegate(Some(delegate));
        peripheral.discover_services(None);
    }
    
    fn did_fail_to_connect_peripheral(
        &self,
        _central: Self::CentralManager,
        peripheral: Id<CBPeripheral, Shared>,
        error: Option<Id<NSError, Shared>>
    ) {
        println!("连接设备失败: {:?}", peripheral.name());
        if let Some(err) = error {
            println!("错误: {:?}", err);
        }
    }
    
    fn did_disconnect_peripheral(
        &self,
        central: Self::CentralManager,
        peripheral: Id<CBPeripheral, Shared>,
        error: Option<Id<NSError, Shared>>
    ) {
        println!("设备断开连接: {:?}", peripheral.name());
        if let Some(err) = error {
            println!("错误: {:?}", err);
        }
        
        // 自动重连
        central.connect_peripheral(&peripheral, None);
    }
}

// 外围设备委托实现
#[derive(Debug)]
struct PeripheralDelegate;

impl PeripheralDelegate {
    fn new() -> Self {
        Self
    }
}

unsafe impl CBPeripheralDelegate for PeripheralDelegate {
    type Peripheral = Id<CBPeripheral, Shared>;
    
    fn did_discover_services(
        &self,
        peripheral: Self::Peripheral,
        error: Option<Id<NSError, Shared>>
    ) {
        if let Some(err) = error {
            println!("发现服务错误: {:?}", err);
            return;
        }
        
        println!("发现的服务:");
        if let Some(services) = peripheral.services() {
            for service in services.iter() {
                println!("- UUID: {:?}", service.uuid());
                
                // 发现每个服务的特征
                peripheral.discover_characteristics(None, &service);
            }
        }
    }
    
    fn did_discover_characteristics_for_service(
        &self,
        peripheral: Self::Peripheral,
        service: Id<CBService, Shared>,
        error: Option<Id<NSError, Shared>>
    ) {
        if let Some(err) = error {
            println!("发现特征错误: {:?}", err);
            return;
        }
        
        println!("服务 {} 的特征:", service.uuid());
        if let Some(characteristics) = service.characteristics() {
            for characteristic in characteristics.iter() {
                println!("- UUID: {:?}", characteristic.uuid());
                println!("  属性: {:?}", characteristic.properties());
                
                // 读取可读特征
                if characteristic.properties().contains(CBCharacteristicProperties::Read) {
                    peripheral.read_value_for_characteristic(&characteristic);
                }
                
                // 订阅通知
                if characteristic.properties().contains(CBCharacteristicProperties::Notify) {
                    peripheral.set_notify_value(true, &characteristic);
                }
            }
        }
    }
    
    fn did_update_value_for_characteristic(
        &self,
        _peripheral: Self::Peripheral,
        characteristic: Id<CBCharacteristic, Shared>,
        error: Option<Id<NSError, Shared>>
    ) {
        if let Some(err) = error {
            println!("更新特征值错误: {:?}", err);
            return;
        }
        
        if let Some(value) = characteristic.value() {
            println!("特征 {} 更新值: {:?}", characteristic.uuid(), value);
        }
    }
    
    fn did_write_value_for_characteristic(
        &self,
        _peripheral: Self::Peripheral,
        characteristic: Id<CBCharacteristic, Shared>,
        error: Option<Id<NSError, Shared>>
    ) {
        if let Some(err) = error {
            println!("写入特征值错误: {:?}", err);
        } else {
            println!("成功写入特征: {:?}", characteristic.uuid());
        }
    }
    
    fn did_update_notification_state_for_characteristic(
        &self,
        _peripheral: Self::Peripheral,
        characteristic: Id<CBCharacteristic, Shared>,
        error: Option<Id<NSError, Shared>>
    ) {
        if let Some(err) = error {
            println!("更新通知状态错误: {:?}", err);
        } else {
            println!("特征 {} 通知状态更新", characteristic.uuid());
        }
    }
}

fn main() {
    // 创建中央管理器
    let delegate = Box::new(CentralManagerDelegate::new());
    let _manager = CBCentralManager::new(delegate, None);
    
    println!("蓝牙管理器已创建,等待事件...");
    
    // 运行事件循环
    unsafe {
        let run_loop = objc2_foundation::CFRunLoopGetCurrent();
        objc2_foundation::CFRunLoopRun();
    }
}

功能说明

  1. 蓝牙状态管理:跟踪蓝牙适配器的状态变化
  2. 设备扫描与过滤:扫描周围设备并可选择性地连接特定设备
  3. 服务与特征发现:自动发现连接设备的所有服务和特征
  4. 数据通信
    • 自动读取可读特征值
    • 自动订阅支持通知的特征
    • 处理特征值更新事件
  5. 错误处理:全面处理各种蓝牙操作可能产生的错误
  6. 连接管理:处理设备断开连接并自动重连

许可证

该库采用以下许可证之一:

  • Zlib
  • Apache-2.0
  • MIT

项目信息

  • 最新版本:0.3.1
  • 分类:API绑定、FFI、外部FFI绑定、macOS API

这个库是objc2项目的一部分,该项目提供了多个与Objective-C运行时交互的Rust库。


1 回复

Rust蓝牙开发库objc2-core-bluetooth使用指南

介绍

objc2-core-bluetooth是一个Rust库,它提供了跨平台的蓝牙设备连接与通信功能。这个库封装了不同操作系统(主要是iOS/macOS)的底层蓝牙API,为Rust开发者提供了统一的接口来操作蓝牙设备。

主要功能

  • 扫描发现附近的蓝牙设备
  • 连接到指定的蓝牙设备
  • 发现设备的服务和特征
  • 读取和写入特征值
  • 订阅特征通知
  • 设备连接状态监控

使用方法

添加依赖

首先在Cargo.toml中添加依赖:

[dependencies]
objc2-core-bluetooth = "0.1"
objc2 = "0.3"
block = "0.1"

基本使用示例

use objc2::rc::autoreleasepool;
use objc2_core_bluetooth::{CBManager, CBPeripheral, CBUUID};

fn main() {
    autoreleasepool(|pool| {
        // 创建蓝牙中心管理器
        let manager = CBManager::new(pool);
        
        // 开始扫描设备
        manager.scan_for_peripherals_with_services(None);
        
        // 设置发现设备回调
        manager.set_did_discover_peripheral_callback(Box::new(|peripheral, _| {
            println!("发现设备: {:?}", peripheral.name());
            
            // 停止扫描
            manager.stop_scan();
            
            // 连接到设备
            manager.connect_peripheral(peripheral);
        }));
        
        // 设置连接成功回调
        manager.set_did_connect_peripheral_callback(Box::new(|peripheral| {
            println!("连接成功: {:?}", peripheral.name());
            
            // 发现服务
            peripheral.discover_services(None);
        }));
        
        // 设置发现服务回调
        manager.set_did_discover_services_callback(Box::new(|peripheral, _| {
            if let Some(services) = peripheral.services() {
                for service in services {
                    println!("发现服务: {:?}", service.uuid());
                    
                    // 发现特征
                    peripheral.discover_characteristics(None, service);
                }
            }
        }));
        
        // 设置发现特征回调
        manager.set_did_discover_characteristics_callback(Box::new(|peripheral, service, _| {
            if let Some(characteristics) = service.characteristics() {
                for characteristic in characteristics {
                    println!("发现特征: {:?}", characteristic.uuid());
                    
                    // 订阅特征通知
                    if characteristic.properties().contains(CBCharacteristicProperties::Notify) {
                        peripheral.set_notify_value(true, characteristic);
                    }
                }
            }
        }));
        
        // 设置特征值更新回调
        manager.set_did_update_value_for_characteristic_callback(Box::new(|peripheral, characteristic, error| {
            if let Some(value) = characteristic.value() {
                println!("收到数据: {:?}", value);
            }
        }));
        
        // 运行事件循环
        manager.run_loop();
    });
}

高级用法

写入特征值

// 假设已经获取到特征对象 characteristic
let data = vec![0x01, 0x02, 0x03]; // 要写入的数据
peripheral.write_value(data, characteristic, CBCharacteristicWriteType::WithResponse);

读取RSSI值

peripheral.read_rssi();
manager.set_did_read_rssi_callback(Box::new(|peripheral, rssi, _| {
    println!("设备 {} 的RSSI: {}", peripheral.name().unwrap_or("未知"), rssi);
}));

完整示例代码

use objc2::rc::autoreleasepool;
use objc2_core_bluetooth::{
    CBCharacteristicProperties, CBCharacteristicWriteType, CBManager, CBPeripheral, CBUUID
};

fn main() {
    autoreleasepool(|pool| {
        // 1. 初始化蓝牙管理器
        let manager = CBManager::new(pool);
        
        println!("蓝牙管理器初始化完成,开始扫描设备...");
        
        // 2. 开始扫描蓝牙设备
        manager.scan_for_peripherals_with_services(None);
        
        // 3. 设置设备发现回调
        manager.set_did_discover_peripheral_callback(Box::new(|peripheral, _| {
            println!("发现蓝牙设备: {:?}", peripheral.name());
            
            // 停止扫描
            manager.stop_scan();
            
            // 连接到设备
            println!("尝试连接设备...");
            manager.connect_peripheral(peripheral);
        }));
        
        // 4. 设置连接成功回调
        manager.set_did_connect_peripheral_callback(Box::new(|peripheral| {
            println!("设备连接成功: {:?}", peripheral.name());
            
            // 开始发现服务
            println!("开始发现服务...");
            peripheral.discover_services(None);
        }));
        
        // 5. 设置服务发现回调
        manager.set_did_discover_services_callback(Box::new(|peripheral, _| {
            if let Some(services) = peripheral.services() {
                println!("发现{}个服务", services.len());
                
                for service in services {
                    println!("服务UUID: {:?}", service.uuid());
                    
                    // 发现服务特征
                    peripheral.discover_characteristics(None, service);
                }
            }
        }));
        
        // 6. 设置特征发现回调
        manager.set_did_discover_characteristics_callback(Box::new(|peripheral, service, _| {
            if let Some(characteristics) = service.characteristics() {
                println!("服务 {} 发现{}个特征", service.uuid(), characteristics.len());
                
                for characteristic in characteristics {
                    println!("特征UUID: {:?}", characteristic.uuid());
                    println!("特征属性: {:?}", characteristic.properties());
                    
                    // 如果特征支持通知,则订阅通知
                    if characteristic.properties().contains(CBCharacteristicProperties::Notify) {
                        println!("订阅特征通知...");
                        peripheral.set_notify_value(true, characteristic);
                    }
                    
                    // 如果特征可读,则读取特征值
                    if characteristic.properties().contains(CBCharacteristicProperties::Read) {
                        println!("读取特征值...");
                        peripheral.read_value(characteristic);
                    }
                }
            }
        }));
        
        // 7. 设置特征值更新回调
        manager.set_did_update_value_for_characteristic_callback(Box::new(|peripheral, characteristic, error| {
            if let Some(value) = characteristic.value() {
                println!("特征 {} 收到数据: {:?}", characteristic.uuid(), value);
            }
        }));
        
        // 8. 设置特征写入完成回调
        manager.set_did_write_value_for_characteristic_callback(Box::new(|peripheral, characteristic, error| {
            println!("特征 {} 写入完成", characteristic.uuid());
        }));
        
        // 9. 运行事件循环
        println!("运行事件循环...");
        manager.run_loop();
    });
}

注意事项

  1. 在macOS/iOS上使用需要确保应用有蓝牙权限
  2. 某些操作是异步的,需要通过回调处理结果
  3. 连接和通信前确保设备已配对(如果需要)
  4. 使用完毕后应断开连接释放资源

跨平台兼容性

虽然objc2-core-bluetooth主要面向Apple平台,但可以结合其他Rust蓝牙库(如btleplug)实现真正的跨平台支持。可以通过条件编译在不同平台使用不同的实现:

#[cfg(target_os = "macos")]
use objc2_core_bluetooth as bluetooth;

#[cfg(not(target_os = "macos"))]
use btleplug as bluetooth;

这个库为Rust开发者提供了在Apple平台上操作蓝牙设备的便捷方式,特别适合需要在macOS或iOS上实现蓝牙功能的Rust应用。

回到顶部