HarmonyOS APP开发蓝牙基础:BLE扫描与连接

举报
Jack20 发表于 2026/06/19 20:12:09 2026/06/19
【摘要】 蓝牙基础:BLE扫描与连接说到蓝牙,很多人第一反应是"配对耳机"。但在物联网时代,蓝牙的角色远不止于此——智能手环、体温计、门锁、甚至共享单车,都在用一种叫BLE(低功耗蓝牙)的技术。今天咱们就来聊聊鸿蒙系统中的BLE开发,看看如何扫描和连接这些"隐形"的智能设备。 一、背景与动机 1.1 BLE与传统蓝牙的区别先来搞清楚一个概念:BLE(Bluetooth Low Energy)和传统蓝...

蓝牙基础:BLE扫描与连接

说到蓝牙,很多人第一反应是"配对耳机"。但在物联网时代,蓝牙的角色远不止于此——智能手环、体温计、门锁、甚至共享单车,都在用一种叫BLE(低功耗蓝牙)的技术。今天咱们就来聊聊鸿蒙系统中的BLE开发,看看如何扫描和连接这些"隐形"的智能设备。

一、背景与动机

1.1 BLE与传统蓝牙的区别

先来搞清楚一个概念:BLE(Bluetooth Low Energy)和传统蓝牙(Classic Bluetooth)是两种不同的技术:

对比项 传统蓝牙 BLE蓝牙
功耗 较高(通话级) 极低(纽扣电池可用数年)
传输速率 1-3 Mbps 1 Mbps(实际更低)
连接时间 数秒 毫秒级
应用场景 音频传输、文件传输 传感器数据、控制指令
拓扑结构 点对点 星型拓扑(一对多)

BLE的典型应用

  • 健康设备:心率带、血氧仪、体温计
  • 智能家居:智能锁、灯控、温湿度传感器
  • 可穿戴设备:手环、手表、运动传感器
  • 位置服务:iBeacon、室内定位

1.2 BLE通信模型

BLE的核心概念是GATT(Generic Attribute Profile),它定义了数据的组织方式:
图片.png

角色说明

  • 外围设备(Peripheral):提供数据的设备(如传感器),作为GATT服务器
  • 中心设备(Central):消费数据的设备(如手机),作为GATT客户端

二、核心原理

2.1 BLE扫描流程

扫描BLE设备是连接的第一步,流程如下:
图片.png

2.2 扫描参数详解

BLE扫描的效率很大程度上取决于扫描参数的配置:

interface ScanParams {
  // 扫描间隔(毫秒)
  // 决定扫描的频率,越小扫描越频繁,功耗越高
  interval: number;
  
  // 扫描窗口(毫秒)
  // 每次扫描的持续时间,必须小于等于interval
  window: number;
  
  // 扫描模式
  // LOW_POWER: 低功耗模式,适合后台
  // BALANCED: 平衡模式
  // AGGRESSIVE: 激进模式,适合前台快速发现
  scanMode: ScanMode;
  
  // 过滤器
  // 用于只扫描特定设备,减少无效扫描
  filters?: ScanFilter[];
}

扫描模式对比

模式 间隔 窗口 功耗 发现速度
LOW_POWER 5000ms 500ms
BALANCED 1000ms 200ms
AGGRESSIVE 100ms 100ms

2.3 BLE连接流程

找到目标设备后,就可以发起连接:
图片.png

三、代码实战

3.1 BLE管理核心类封装

import bluetooth from '@ohos.bluetooth';
import { BusinessError } from '@ohos.base';

/**
 * BLE设备信息
 */
export interface BleDeviceInfo {
  // 设备ID
  deviceId: string;
  
  // 设备名称
  deviceName: string;
  
  // 信号强度(RSSI)
  rssi: number;
  
  // 广播数据
  advertisData: ArrayBuffer;
  
  // 服务UUID列表
  serviceUuids: Array<string>;
  
  // 是否可连接
  isConnectable: boolean;
}

/**
 * BLE管理器
 * 封装BLE扫描、连接、通信等核心功能
 */
export class BleManager {
  private static instance: BleManager;
  private gattClient: bluetooth.GattClient | null = null;
  private connectedDeviceId: string = '';
  private discoveredDevices: Map<string, BleDeviceInfo> = new Map();
  
  // 回调函数
  private onDeviceFound: ((device: BleDeviceInfo) => void) | null = null;
  private onConnectionStateChanged: ((state: bluetooth.ProfileConnectionState) => void) | null = null;
  private onCharacteristicChanged: ((data: ArrayBuffer) => void) | null = null;

  private constructor() {
    this.initEventListeners();
  }

  /**
   * 获取单例实例
   */
  public static getInstance(): BleManager {
    if (!BleManager.instance) {
      BleManager.instance = new BleManager();
    }
    return BleManager.instance;
  }

  /**
   * 初始化事件监听
   */
  private initEventListeners(): void {
    // 监听BLE扫描结果
    bluetooth.on('BLEDeviceFind', (result: Array<bluetooth.ScanResult>) => {
      result.forEach((scanResult) => {
        const deviceInfo: BleDeviceInfo = {
          deviceId: scanResult.deviceId,
          deviceName: scanResult.deviceName || 'Unknown',
          rssi: scanResult.rssi,
          advertisData: scanResult.advertisData,
          serviceUuids: this.parseServiceUuids(scanResult.advertisData),
          isConnectable: true
        };

        this.discoveredDevices.set(deviceInfo.deviceId, deviceInfo);
        
        if (this.onDeviceFound) {
          this.onDeviceFound(deviceInfo);
        }
      });
    });

    // 监听蓝牙状态变化
    bluetooth.on('stateChange', (state: bluetooth.BluetoothState) => {
      const stateText = this.getStateText(state);
      console.info(`[BLE] 蓝牙状态变化: ${stateText}`);
    });
  }

  /**
   * 获取状态文本
   */
  private getStateText(state: bluetooth.BluetoothState): string {
    switch (state) {
      case bluetooth.BluetoothState.STATE_OFF:
        return '已关闭';
      case bluetooth.BluetoothState.STATE_ON:
        return '已开启';
      case bluetooth.BluetoothState.STATE_TURNING_OFF:
        return '正在关闭';
      case bluetooth.BluetoothState.STATE_TURNING_ON:
        return '正在开启';
      default:
        return '未知';
    }
  }

  /**
   * 解析服务UUID
   */
  private parseServiceUuids(advertisData: ArrayBuffer): Array<string> {
    // 简化实现,实际需要解析广播数据
    return [];
  }

  /**
   * 检查蓝牙是否开启
   */
  public async isBluetoothEnabled(): Promise<boolean> {
    try {
      const state = bluetooth.getState();
      return state === bluetooth.BluetoothState.STATE_ON;
    } catch (error) {
      console.error('[BLE] 检查蓝牙状态失败:', error);
      return false;
    }
  }

  /**
   * 开启蓝牙
   */
  public async enableBluetooth(): Promise<boolean> {
    try {
      await bluetooth.enableBluetooth();
      console.info('[BLE] 蓝牙已开启');
      return true;
    } catch (error) {
      const err = error as BusinessError;
      console.error(`[BLE] 开启蓝牙失败: ${err.message}`);
      return false;
    }
  }

  /**
   * 关闭蓝牙
   */
  public async disableBluetooth(): Promise<boolean> {
    try {
      await bluetooth.disableBluetooth();
      console.info('[BLE] 蓝牙已关闭');
      return true;
    } catch (error) {
      const err = error as BusinessError;
      console.error(`[BLE] 关闭蓝牙失败: ${err.message}`);
      return false;
    }
  }

  /**
   * 开始BLE扫描
   * @param filters 扫描过滤器
   * @param scanMode 扫描模式
   */
  public async startScan(
    filters?: Array<bluetooth.ScanFilter>,
    scanMode: bluetooth.ScanMode = bluetooth.ScanMode.SCAN_MODE_LOW_POWER
  ): Promise<boolean> {
    try {
      // 检查蓝牙状态
      const isEnabled = await this.isBluetoothEnabled();
      if (!isEnabled) {
        console.error('[BLE] 蓝牙未开启');
        return false;
      }

      // 清空已发现的设备
      this.discoveredDevices.clear();

      // 配置扫描参数
      const scanParams: bluetooth.ScanParams = {
        scanMode: scanMode,
        interval: 5000,
        window: 500
      };

      // 开始扫描
      await bluetooth.startBleScan(filters, scanParams);
      console.info('[BLE] 开始扫描');
      return true;
    } catch (error) {
      const err = error as BusinessError;
      console.error(`[BLE] 启动扫描失败: ${err.message}`);
      return false;
    }
  }

  /**
   * 停止BLE扫描
   */
  public async stopScan(): Promise<boolean> {
    try {
      await bluetooth.stopBleScan();
      console.info('[BLE] 停止扫描');
      return true;
    } catch (error) {
      const err = error as BusinessError;
      console.error(`[BLE] 停止扫描失败: ${err.message}`);
      return false;
    }
  }

  /**
   * 获取已发现的设备列表
   */
  public getDiscoveredDevices(): Array<BleDeviceInfo> {
    return Array.from(this.discoveredDevices.values());
  }

  /**
   * 连接到BLE设备
   * @param deviceId 设备ID
   */
  public async connect(deviceId: string): Promise<boolean> {
    try {
      // 创建GATT客户端
      this.gattClient = bluetooth.createGattClient(deviceId);
      
      // 设置连接状态回调
      this.gattClient.on('connectionStateChange', (state: bluetooth.ProfileConnectionState) => {
        console.info(`[BLE] 连接状态: ${state}`);
        
        if (state === bluetooth.ProfileConnectionState.STATE_CONNECTED) {
          this.connectedDeviceId = deviceId;
        } else if (state === bluetooth.ProfileConnectionState.STATE_DISCONNECTED) {
          this.connectedDeviceId = '';
        }
        
        if (this.onConnectionStateChanged) {
          this.onConnectionStateChanged(state);
        }
      });

      // 发起连接
      await this.gattClient.connect();
      console.info(`[BLE] 正在连接到 ${deviceId}`);
      return true;
    } catch (error) {
      const err = error as BusinessError;
      console.error(`[BLE] 连接失败: ${err.message}`);
      return false;
    }
  }

  /**
   * 断开BLE连接
   */
  public async disconnect(): Promise<boolean> {
    try {
      if (this.gattClient) {
        await this.gattClient.disconnect();
        this.gattClient.close();
        this.gattClient = null;
        this.connectedDeviceId = '';
        console.info('[BLE] 已断开连接');
      }
      return true;
    } catch (error) {
      const err = error as BusinessError;
      console.error(`[BLE] 断开连接失败: ${err.message}`);
      return false;
    }
  }

  /**
   * 发现GATT服务
   */
  public async discoverServices(): Promise<Array<bluetooth.GattService> | null> {
    if (!this.gattClient) {
      console.error('[BLE] 未连接设备');
      return null;
    }

    try {
      const services = await this.gattClient.discoverServices();
      console.info(`[BLE] 发现 ${services.length} 个服务`);
      return services;
    } catch (error) {
      console.error('[BLE] 发现服务失败:', error);
      return null;
    }
  }

  /**
   * 读取特征值
   */
  public async readCharacteristic(
    serviceUuid: string,
    characteristicUuid: string
  ): Promise<ArrayBuffer | null> {
    if (!this.gattClient) {
      console.error('[BLE] 未连接设备');
      return null;
    }

    try {
      const value = await this.gattClient.readCharacteristicValue({
        serviceUuid: serviceUuid,
        characteristicUuid: characteristicUuid
      });
      console.info(`[BLE] 读取特征值成功`);
      return value;
    } catch (error) {
      console.error('[BLE] 读取特征值失败:', error);
      return null;
    }
  }

  /**
   * 写入特征值
   */
  public async writeCharacteristic(
    serviceUuid: string,
    characteristicUuid: string,
    value: ArrayBuffer,
    writeType: bluetooth.GattWriteType = bluetooth.GattWriteType.WRITE_TYPE_DEFAULT
  ): Promise<boolean> {
    if (!this.gattClient) {
      console.error('[BLE] 未连接设备');
      return false;
    }

    try {
      await this.gattClient.writeCharacteristicValue({
        serviceUuid: serviceUuid,
        characteristicUuid: characteristicUuid,
        characteristicValue: value,
        writeType: writeType
      });
      console.info('[BLE] 写入特征值成功');
      return true;
    } catch (error) {
      console.error('[BLE] 写入特征值失败:', error);
      return false;
    }
  }

  /**
   * 启用特征值通知
   */
  public async enableNotification(
    serviceUuid: string,
    characteristicUuid: string,
    enable: boolean = true
  ): Promise<boolean> {
    if (!this.gattClient) {
      console.error('[BLE] 未连接设备');
      return false;
    }

    try {
      await this.gattClient.setCharacteristicChangeNotification({
        serviceUuid: serviceUuid,
        characteristicUuid: characteristicUuid,
        enable: enable
      });

      // 设置通知回调
      if (enable) {
        this.gattClient.on('characteristicValueChange', (data: ArrayBuffer) => {
          if (this.onCharacteristicChanged) {
            this.onCharacteristicChanged(data);
          }
        });
      }

      console.info(`[BLE] 通知已${enable ? '启用' : '禁用'}`);
      return true;
    } catch (error) {
      console.error('[BLE] 设置通知失败:', error);
      return false;
    }
  }

  /**
   * 设置回调函数
   */
  public setOnDeviceFound(callback: (device: BleDeviceInfo) => void): void {
    this.onDeviceFound = callback;
  }

  public setOnConnectionStateChanged(callback: (state: bluetooth.ProfileConnectionState) => void): void {
    this.onConnectionStateChanged = callback;
  }

  public setOnCharacteristicChanged(callback: (data: ArrayBuffer) => void): void {
    this.onCharacteristicChanged = callback;
  }

  /**
   * 移除所有监听
   */
  public removeAllListeners(): void {
    bluetooth.off('BLEDeviceFind');
    bluetooth.off('stateChange');
    
    if (this.gattClient) {
      this.gattClient.off('connectionStateChange');
      this.gattClient.off('characteristicValueChange');
    }
    
    console.info('[BLE] 已移除所有监听');
  }

  /**
   * 获取当前连接的设备ID
   */
  public getConnectedDeviceId(): string {
    return this.connectedDeviceId;
  }

  /**
   * 判断是否已连接
   */
  public isConnected(): boolean {
    return this.connectedDeviceId !== '';
  }
}

3.2 BLE设备扫描与连接UI

import { BleManager, BleDeviceInfo } from './BleManager';
import bluetooth from '@ohos.bluetooth';

@Entry
@Component
struct BleScanPage {
  private bleManager: BleManager = BleManager.getInstance();

  @State isBluetoothEnabled: boolean = false;
  @State isScanning: boolean = false;
  @State discoveredDevices: Array<BleDeviceInfo> = [];
  @State isConnected: boolean = false;
  @State connectedDeviceName: string = '';
  @State services: Array<bluetooth.GattService> = [];

  aboutToAppear(): void {
    this.checkBluetoothState();
    this.registerCallbacks();
  }

  aboutToDisappear(): void {
    this.bleManager.removeAllListeners();
  }

  /**
   * 检查蓝牙状态
   */
  async checkBluetoothState(): Promise<void> {
    this.isBluetoothEnabled = await this.bleManager.isBluetoothEnabled();
  }

  /**
   * 注册回调
   */
  registerCallbacks(): void {
    // 设备发现回调
    this.bleManager.setOnDeviceFound((device: BleDeviceInfo) => {
      // 更新设备列表
      const index = this.discoveredDevices.findIndex(d => d.deviceId === device.deviceId);
      if (index >= 0) {
        this.discoveredDevices[index] = device;
      } else {
        this.discoveredDevices.push(device);
      }
    });

    // 连接状态回调
    this.bleManager.setOnConnectionStateChanged((state: bluetooth.ProfileConnectionState) => {
      if (state === bluetooth.ProfileConnectionState.STATE_CONNECTED) {
        this.isConnected = true;
        this.discoverServices();
      } else if (state === bluetooth.ProfileConnectionState.STATE_DISCONNECTED) {
        this.isConnected = false;
        this.connectedDeviceName = '';
        this.services = [];
      }
    });
  }

  /**
   * 发现服务
   */
  async discoverServices(): Promise<void> {
    const services = await this.bleManager.discoverServices();
    if (services) {
      this.services = services;
    }
  }

  /**
   * 开启蓝牙
   */
  async enableBluetooth(): Promise<void> {
    const success = await this.bleManager.enableBluetooth();
    if (success) {
      this.isBluetoothEnabled = true;
    }
  }

  /**
   * 开始扫描
   */
  async startScan(): Promise<void> {
    this.isScanning = true;
    this.discoveredDevices = [];
    
    const success = await this.bleManager.startScan();
    if (!success) {
      this.isScanning = false;
    }

    // 设置扫描超时
    setTimeout(() => {
      if (this.isScanning) {
        this.stopScan();
      }
    }, 30000);
  }

  /**
   * 停止扫描
   */
  async stopScan(): Promise<void> {
    await this.bleManager.stopScan();
    this.isScanning = false;
  }

  /**
   * 连接设备
   */
  async connectDevice(device: BleDeviceInfo): Promise<void> {
    await this.stopScan();
    const success = await this.bleManager.connect(device.deviceId);
    if (success) {
      this.connectedDeviceName = device.deviceName;
    }
  }

  /**
   * 断开连接
   */
  async disconnect(): Promise<void> {
    await this.bleManager.disconnect();
  }

  /**
   * 获取信号强度图标
   */
  getRssiIcon(rssi: number): string {
    if (rssi >= -50) return '📶';
    if (rssi >= -70) return '📶';
    return '📶';
  }

  build() {
    Column() {
      // 标题栏
      Row() {
        Text('BLE设备')
          .fontSize(24)
          .fontWeight(FontWeight.Bold)
          .fontColor('#FFFFFF')

        Blank()

        Toggle({ type: ToggleType.Switch, isOn: this.isBluetoothEnabled })
          .onChange((isOn: boolean) => {
            if (isOn) {
              this.enableBluetooth();
            } else {
              this.bleManager.disableBluetooth();
              this.isBluetoothEnabled = false;
            }
          })
      }
      .width('100%')
      .padding(20)
      .backgroundColor('#1A1A2E')

      // 连接状态
      if (this.isConnected) {
        Column() {
          Row() {
            Text('已连接')
              .fontSize(16)
              .fontColor('#4CAF50')

            Text(this.connectedDeviceName)
              .fontSize(14)
              .fontColor('#FFFFFF')
              .margin({ left: 10 })
          }
          .width('100%')

          if (this.services.length > 0) {
            Text(`发现 ${this.services.length} 个服务`)
              .fontSize(12)
              .fontColor('#AAAAAA')
              .margin({ top: 10 })
          }

          Button('断开连接')
            .width('100%')
            .height(40)
            .backgroundColor('#FF5722')
            .margin({ top: 15 })
            .onClick(() => {
              this.disconnect();
            })
        }
        .width('90%')
        .padding(20)
        .backgroundColor('#16213E')
        .borderRadius(12)
        .margin({ top: 20 })
      }

      // 扫描按钮
      if (this.isBluetoothEnabled && !this.isConnected) {
        Button(this.isScanning ? '扫描中...' : '扫描设备')
          .width('90%')
          .height(50)
          .backgroundColor(this.isScanning ? '#666666' : '#4A90E2')
          .enabled(!this.isScanning)
          .onClick(() => {
            this.startScan();
          })
          .margin({ top: 20 })

        if (this.isScanning) {
          Progress({ value: 0, total: 100, type: ProgressType.Linear })
            .width('90%')
            .color('#4A90E2')
            .margin({ top: 10 })
        }
      }

      // 设备列表
      if (this.discoveredDevices.length > 0) {
        Column() {
          Text(`发现 ${this.discoveredDevices.length} 台设备`)
            .fontSize(16)
            .fontColor('#FFFFFF')
            .width('100%')
            .margin({ bottom: 15 })

          List() {
            ForEach(this.discoveredDevices, (device: BleDeviceInfo) => {
              ListItem() {
                Row() {
                  // 信号图标
                  Text(this.getRssiIcon(device.rssi))
                    .fontSize(28)

                  // 设备信息
                  Column() {
                    Text(device.deviceName)
                      .fontSize(16)
                      .fontColor('#FFFFFF')

                    Row() {
                      Text(`${device.rssi} dBm`)
                        .fontSize(12)
                        .fontColor('#AAAAAA')

                      Text(device.deviceId.substring(0, 8) + '...')
                        .fontSize(12)
                        .fontColor('#666666')
                        .margin({ left: 10 })
                    }
                    .margin({ top: 5 })
                  }
                  .alignItems(HorizontalAlign.Start)
                  .margin({ left: 15 })
                  .layoutWeight(1)

                  // 连接按钮
                  Button('连接')
                    .width(70)
                    .height(35)
                    .backgroundColor('#4A90E2')
                    .onClick(() => {
                      this.connectDevice(device);
                    })
                }
                .width('100%')
                .padding(15)
                .backgroundColor('#1A1A2E')
                .borderRadius(8)
                .margin({ bottom: 10 })
              }
            }, (device: BleDeviceInfo) => device.deviceId)
          }
          .width('100%')
          .layoutWeight(1)
        }
        .width('90%')
        .padding(20)
        .backgroundColor('#16213E')
        .borderRadius(12)
        .margin({ top: 20 })
        .layoutWeight(1)
      }

      // 提示信息
      if (!this.isBluetoothEnabled) {
        Column() {
          Text('蓝牙未开启')
            .fontSize(18)
            .fontColor('#AAAAAA')

          Text('请开启蓝牙以扫描BLE设备')
            .fontSize(14)
            .fontColor('#666666')
            .margin({ top: 10 })
        }
        .margin({ top: 100 })
      }
    }
    .width('100%')
    .height('100%')
    .backgroundColor('#0F0F1A')
  }
}

3.3 BLE数据通信示例

连接成功后,通过GATT进行数据交互:

import { BleManager } from './BleManager';

/**
 * BLE数据通信示例
 * 以心率传感器为例
 */
export class HeartRateMonitor {
  private bleManager: BleManager = BleManager.getInstance();
  
  // 心率服务的UUID(标准UUID)
  private readonly HEART_RATE_SERVICE_UUID = '0000180D-0000-1000-8000-00805F9B34FB';
  
  // 心率测量特征值的UUID
  private readonly HEART_RATE_MEASUREMENT_UUID = '00002A37-0000-1000-8000-00805F9B34FB';
  
  // 心率数据回调
  private onHeartRateReceived: ((heartRate: number) => void) | null = null;

  /**
   * 开始监听心率数据
   */
  public async startMonitoring(): Promise<boolean> {
    if (!this.bleManager.isConnected()) {
      console.error('[心率] 设备未连接');
      return false;
    }

    try {
      // 启用心率通知
      const success = await this.bleManager.enableNotification(
        this.HEART_RATE_SERVICE_UUID,
        this.HEART_RATE_MEASUREMENT_UUID,
        true
      );

      if (success) {
        // 设置数据回调
        this.bleManager.setOnCharacteristicChanged((data: ArrayBuffer) => {
          const heartRate = this.parseHeartRate(data);
          if (this.onHeartRateReceived) {
            this.onHeartRateReceived(heartRate);
          }
        });
      }

      return success;
    } catch (error) {
      console.error('[心率] 启动监听失败:', error);
      return false;
    }
  }

  /**
   * 停止监听心率数据
   */
  public async stopMonitoring(): Promise<boolean> {
    return await this.bleManager.enableNotification(
      this.HEART_RATE_SERVICE_UUID,
      this.HEART_RATE_MEASUREMENT_UUID,
      false
    );
  }

  /**
   * 解析心率数据
   * 根据蓝牙心率服务规范解析
   */
  private parseHeartRate(data: ArrayBuffer): number {
    const view = new DataView(data);
    
    // 第一个字节是标志位
    const flags = view.getUint8(0);
    
    // 检查心率值格式(8位或16位)
    const is16Bit = (flags & 0x01) === 1;
    
    if (is16Bit) {
      // 16位心率值
      return view.getUint16(1, true);
    } else {
      // 8位心率值
      return view.getUint8(1);
    }
  }

  /**
   * 设置心率回调
   */
  public setOnHeartRateReceived(callback: (heartRate: number) => void): void {
    this.onHeartRateReceived = callback;
  }
}

/**
 * 智能灯控制示例
 */
export class SmartLightController {
  private bleManager: BleManager = BleManager.getInstance();
  
  // 灯控服务UUID(自定义)
  private readonly LIGHT_SERVICE_UUID = '0000FFF0-0000-1000-8000-00805F9B34FB';
  
  // 开关控制特征值UUID
  private readonly SWITCH_UUID = '0000FFF1-0000-1000-8000-00805F9B34FB';
  
  // 亮度控制特征值UUID
  private readonly BRIGHTNESS_UUID = '0000FFF2-0000-1000-8000-00805F9B34FB';
  
  // 颜色控制特征值UUID
  private readonly COLOR_UUID = '0000FFF3-0000-1000-8000-00805F9B34FB';

  /**
   * 开灯
   */
  public async turnOn(): Promise<boolean> {
    const data = new ArrayBuffer(1);
    new Uint8Array(data)[0] = 0x01; // 1表示开
    
    return await this.bleManager.writeCharacteristic(
      this.LIGHT_SERVICE_UUID,
      this.SWITCH_UUID,
      data
    );
  }

  /**
   * 关灯
   */
  public async turnOff(): Promise<boolean> {
    const data = new ArrayBuffer(1);
    new Uint8Array(data)[0] = 0x00; // 0表示关
    
    return await this.bleManager.writeCharacteristic(
      this.LIGHT_SERVICE_UUID,
      this.SWITCH_UUID,
      data
    );
  }

  /**
   * 设置亮度
   * @param brightness 亮度值(0-100)
   */
  public async setBrightness(brightness: number): Promise<boolean> {
    // 限制范围
    const value = Math.max(0, Math.min(100, brightness));
    
    const data = new ArrayBuffer(1);
    new Uint8Array(data)[0] = value;
    
    return await this.bleManager.writeCharacteristic(
      this.LIGHT_SERVICE_UUID,
      this.BRIGHTNESS_UUID,
      data
    );
  }

  /**
   * 设置颜色
   * @param r 红色分量(0-255)
   * @param g 绿色分量(0-255)
   * @param b 蓝色分量(0-255)
   */
  public async setColor(r: number, g: number, b: number): Promise<boolean> {
    const data = new ArrayBuffer(3);
    const view = new Uint8Array(data);
    view[0] = Math.max(0, Math.min(255, r));
    view[1] = Math.max(0, Math.min(255, g));
    view[2] = Math.max(0, Math.min(255, b));
    
    return await this.bleManager.writeCharacteristic(
      this.LIGHT_SERVICE_UUID,
      this.COLOR_UUID,
      data
    );
  }
}

四、踩坑与注意事项

4.1 权限配置

// module.json5
{
  "module": {
    "requestPermissions": [
      {
        "name": "ohos.permission.USE_BLUETOOTH",
        "reason": "使用蓝牙功能"
      },
      {
        "name": "ohos.permission.DISCOVER_BLUETOOTH",
        "reason": "发现蓝牙设备"
      },
      {
        "name": "ohos.permission.MANAGE_BLUETOOTH",
        "reason": "管理蓝牙设置"
      },
      {
        "name": "ohos.permission.LOCATION",
        "reason": "BLE扫描需要位置权限"
      },
      {
        "name": "ohos.permission.APPROXIMATELY_LOCATION",
        "reason": "粗略位置权限"
      }
    ]
  }
}

4.2 扫描过滤器优化

坑点:不过滤会扫描到所有BLE设备,浪费资源。

/**
 * 使用过滤器优化扫描
 */
async function scanWithFilter(targetServiceUuid: string): Promise<boolean> {
  const filter: bluetooth.ScanFilter = {
    serviceUuid: targetServiceUuid  // 只扫描包含指定服务的设备
  };

  return await bleManager.startScan([filter]);
}

// 示例:只扫描心率设备
const HEART_RATE_SERVICE = '0000180D-0000-1000-8000-00805F9B34FB';
await scanWithFilter(HEART_RATE_SERVICE);

4.3 连接超时处理

坑点:BLE连接可能超时,需要处理。

/**
 * 带超时的BLE连接
 */
async function connectWithTimeout(
  deviceId: string,
  timeout: number = 15000
): Promise<boolean> {
  return new Promise(async (resolve) => {
    let resolved = false;
    
    // 设置超时
    const timer = setTimeout(() => {
      if (!resolved) {
        resolved = true;
        resolve(false);
        console.warn('[BLE] 连接超时');
      }
    }, timeout);

    // 设置连接回调
    bleManager.setOnConnectionStateChanged((state) => {
      if (!resolved) {
        if (state === bluetooth.ProfileConnectionState.STATE_CONNECTED) {
          resolved = true;
          clearTimeout(timer);
          resolve(true);
        } else if (state === bluetooth.ProfileConnectionState.STATE_DISCONNECTED) {
          resolved = true;
          clearTimeout(timer);
          resolve(false);
        }
      }
    });

    // 发起连接
    await bleManager.connect(deviceId);
  });
}

4.4 MTU大小协商

坑点:BLE默认MTU较小(20字节),大数据传输需要协商更大的MTU。

/**
 * 协商MTU大小
 */
async function negotiateMtu(mtu: number = 512): Promise<boolean> {
  if (!gattClient) return false;
  
  try {
    const actualMtu = await gattClient.requestMtuSize(mtu);
    console.info(`[BLE] MTU协商成功: ${actualMtu}`);
    return true;
  } catch (error) {
    console.error('[BLE] MTU协商失败:', error);
    return false;
  }
}

4.5 数据分包处理

坑点:超过MTU的数据需要分包发送。

/**
 * 分包发送大数据
 */
async function sendDataInChunks(
  serviceUuid: string,
  characteristicUuid: string,
  data: ArrayBuffer,
  mtu: number = 20
): Promise<boolean> {
  const totalLength = data.byteLength;
  const chunkCount = Math.ceil(totalLength / mtu);
  
  for (let i = 0; i < chunkCount; i++) {
    const start = i * mtu;
    const end = Math.min(start + mtu, totalLength);
    const chunk = data.slice(start, end);
    
    const success = await bleManager.writeCharacteristic(
      serviceUuid,
      characteristicUuid,
      chunk,
      bluetooth.GattWriteType.WRITE_TYPE_NO_RESPONSE
    );
    
    if (!success) {
      console.error(`[BLE] 发送第 ${i + 1} 包失败`);
      return false;
    }
    
    // 等待一小段时间,避免发送过快
    await delay(10);
  }
  
  console.info(`[BLE] 数据发送完成,共 ${chunkCount}`);
  return true;
}

function delay(ms: number): Promise<void> {
  return new Promise(resolve => setTimeout(resolve, ms));
}

五、HarmonyOS 6适配

5.1 API变更

API HarmonyOS 5 HarmonyOS 6 说明
startBleScan() 返回void 返回Promise<void> 改为异步
createGattClient() 同步创建 异步创建 统一异步模式
新增 - getBleScanResults() 获取扫描结果

适配代码

/**
 * HarmonyOS 6 BLE适配
 */
async function startScanAdaptive(filters?: Array<bluetooth.ScanFilter>): Promise<boolean> {
  const apiVersion = getApiVersion();

  try {
    if (apiVersion >= 6) {
      // HarmonyOS 6
      await bluetooth.startBleScan(filters);
      const results = await bluetooth.getBleScanResults();
      return results.length >= 0;
    } else {
      // HarmonyOS 5
      await bluetooth.startBleScan(filters);
      return true;
    }
  } catch (error) {
    console.error('[BLE] 扫描失败:', error);
    return false;
  }
}

5.2 新增功能

HarmonyOS 6增强了BLE能力:

// 1. 扫描结果缓存
const cachedResults = await bluetooth.getBleScanResults();
console.info(`缓存结果: ${cachedResults.length} 个设备`);

// 2. 扫描设置增强
const scanSettings: bluetooth.ScanSettings = {
  scanMode: bluetooth.ScanMode.SCAN_MODE_BALANCED,
  legacyMode: false,  // 使用扩展广播
  phy: 1,  // 1M PHY
  callbackType: bluetooth.CallbackType.CALLBACK_TYPE_ALL_MATCHES
};
await bluetooth.startBleScan(filters, scanSettings);

// 3. 连接参数优化
const connectionParams: bluetooth.ConnectionParams = {
  minInterval: 6,    // 最小间隔(7.5ms)
  maxInterval: 12,   // 最大间隔(15ms)
  latency: 0,        // 延迟
  timeout: 500       // 超时(5s)
};
await gattClient.connect(connectionParams);

5.3 隐私增强

HarmonyOS 6对蓝牙隐私有更严格的保护:

// 1. 设备地址随机化
const privacyConfig: bluetooth.PrivacyConfig = {
  privacyMode: bluetooth.PrivacyMode.DEVICE_PRIVACY_MODE,
  irk: new ArrayBuffer(16)  // 身份解析密钥
};
await bluetooth.setPrivacy(privacyConfig);

// 2. 广播数据过滤
const advertisFilter: bluetooth.AdvertisFilter = {
  includeDeviceName: false,  // 不包含设备名
  includeTxPowerLevel: false
};

六、总结

BLE蓝牙是物联网应用的重要通信方式,掌握它对于开发智能硬件应用至关重要。本文全面讲解了鸿蒙系统中BLE开发的核心要点:

核心要点回顾

  1. 权限管理:BLE扫描需要位置权限,不可遗漏
  2. 扫描优化:使用过滤器和合适的扫描模式
  3. 连接管理:处理连接超时、断开重连等场景
  4. GATT操作:理解服务、特征值的概念和操作方法
  5. 数据传输:注意MTU限制和数据分包

最佳实践建议

  • 封装统一的BLE管理类,简化使用
  • 使用扫描过滤器减少无效扫描
  • 实现完善的连接重试机制
  • 处理好MTU协商和数据分包

下一步学习

  • 蓝牙GATT服务与特征值操作(下一篇文章)
  • 蓝牙音频传输
  • 多设备同时连接
  • BLE安全机制

BLE开发看似复杂,但掌握了GATT模型和核心API后,实现起来其实有章可循。希望本文能帮助你建立起完整的知识体系,在物联网开发中游刃有余!

【声明】本内容来自华为云开发者社区博主,不代表华为云及华为云开发者社区的观点和立场。转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息,否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。