美文网首页
【最新】鸿蒙Next 蓝牙开发流程

【最新】鸿蒙Next 蓝牙开发流程

作者: 沉淀纷飞 | 来源:发表于2025-08-07 08:37 被阅读0次

1. 权限配置

在 module.json5 文件中添加蓝牙相关权限:

{
  "module": {
    "requestPermissions": [
      {
        "name": "ohos.permission.ACCESS_BLUETOOTH",
        "reason": "需要使用蓝牙功能"
      }
    ]
  }
}

2. 导入蓝牙模块

import bluetooth from '@ohos.bluetooth';

3. 蓝牙状态检查与开启

 //用户申请蓝牙权限
 async requestBlueToothPermission() {
    let grantStatus = await this.reqPermissionsFromUser();
    for (let i = 0; i < grantStatus.length; i++) {
      if (grantStatus[i] === 0) {
        //查看当前蓝牙开启状态
        this.isStartBlueTooth = BleState.getBluetoothState();
        if (!this.isStartBlueTooth) {
          showConfirmAlertDialog('蓝牙未开启,请滑出系统控制中心,打开蓝牙进行连接', () => {})
        }

      } else {
        showConfirmAlertDialog('蓝牙授权失败,请打开设置进行授权', () => {
          WantUtil.toAppSetting()
        })
        console.info(CommonConstants.log_prefix + '蓝牙授权失败!')
      }
    }
  }

  //用户申请权限
  async reqPermissionsFromUser(): Promise<number[]> {
    // 申请权限
    let context = getContext() as common.UIAbilityContext;
    // 获取AbilityAccessCtrl对象
    let atManager = abilityAccessCtrl.createAtManager();
    // 请求权限,传入上下文和需要申请的权限列表
    let grantStatus = await atManager.requestPermissionsFromUser(context, ['ohos.permission.ACCESS_BLUETOOTH']);
    // 返回授权结果列表
    return grantStatus.authResults;
 }

4. 设备扫描

  try {
    const scanOptions: ble.ScanOptions = {
        interval: 1000,
        dutyMode: ble.ScanDuty.SCAN_MODE_LOW_POWER,
        matchMode: ble.MatchMode.MATCH_MODE_AGGRESSIVE,
      };

   // 订阅 BLEDeviceFind 事件
   ble.on('BLEDeviceFind', (data: Array<ble.ScanResult>) => {
        //解析蓝牙广播数据
    });

   // 开始扫描
   ble.startBLEScan(filters, scanOptions);
      this.isScanning = true;
   } catch (err) {
      throw new Error('Failed to start BLE scan');
   }

5. 设备连接、数据接收

  const clientDevice = ble.createGattClientDevice(deviceId)

   //连接状态
   clientDevice.on('BLEConnectionStateChange', (state: ble.BLEConnectionChangeState) => {
        const connectState = state.state;
        switch (connectState) {
          case constant.ProfileConnectionState.STATE_DISCONNECTED: // 设备已断开连接
             
            break;

          case constant.ProfileConnectionState.STATE_CONNECTING: // 设备正在连接   
            break;

          case constant.ProfileConnectionState.STATE_CONNECTED: // 设备已连接
          //发现服务
          this.getServices();
          break;
          case constant.ProfileConnectionState.STATE_DISCONNECTING: // 设备正在断开连接 
          break;
          default:
            break;
        }
      })

      //连接
      try {
        clientDevice.connect()
      } catch (err) {

     }

  //获取服务
  private async getServices() {
    if (this.gattClientDevice) {
      const result: Array<ble.GattService> = await this.gattClientDevice.getServices();
      console.info('getServices successfully:' + JSON.stringify(result));

      let gatt = this.gattClientDevice;
      LogUtils.debug('发现蓝牙服务>>>' + JSON.stringify(result))

      for (const item of result) {
        console.info('getServices item= ' + JSON.stringify(item));
        //服务UUID
        if (item.serviceUuid === ‘读写特征UUID'’) {
          this.gattServiceInfo = item;
          const writeCharacteristic = item.characteristics.find(c => c.characteristicUuid == '读写特征UUID');
          const notyCharacteristic = item.characteristics.find(c => c.characteristicUuid == '通知特征UUID');
          
          // setNotification
          if (writeCharacteristic) {
            try {
              const success = await this.setNotificationChannel(gatt, item, writeCharacteristic, true);
              if (success) {
                console.log(`Notification set successfully for characteristic(通道1) ${writeCharacteristic.characteristicUuid}`);
              } else {
                console.log(`Failed to set notification for characteristic(通道1) ${writeCharacteristic.characteristicUuid}`);
              }
            }catch (err){
            }
          }

          // setNotification
          if (notyCharacteristic) {
            try {
              const success = await this.setNotificationChannel(gatt, item, notyCharacteristic, true);
              if (success) {
                console.log(`Notification set successfully for characteristic(通道1) ${notyCharacteristic.characteristicUuid}`);
              } else {
                console.log(`Failed to set notification for characteristic(通道1) ${notyCharacteristic.characteristicUuid}`);
              }
            }catch (err){
            }
          }
        }
      }

      //接收设备数据
      this.onBleCharacteristicChange()
    }
  }


  //向下位机发送设置通知此indicate征值请求
  private async setNotificationChannel(
    gatt: ble.GattClientDevice | null ,
    gattService: ble.GattService | undefined,
    characteristic: ble.BLECharacteristic,
    enable: boolean
  ): Promise<boolean>{
    if (!gattService) {
      return false; // 返回失败
    }

    if (gatt == null){
      return false; // 返回失败
    }

    try {
      if (gatt) {
        // 向下位机发送设置通知此indicate征值请求
        await gatt?.setCharacteristicChangeNotification(characteristic, enable)

        return true; // 返回成功
      }
    } catch (err) {

      return false; // 返回失败
    }
    return false; // 如果没有gatt,返回失败
  }


  //订阅蓝牙低功耗设备的特征值变化事件 (接收消息)
  private onBleCharacteristicChange() {
    LogUtils.debug('开始监听特征值变化')

    try {
      if (this.gattClientDevice) {
        //监听
        this.gattClientDevice.on('BLECharacteristicChange', (characteristicChangeReq: ble.BLECharacteristic) => {
          let serviceUuid: string = characteristicChangeReq.serviceUuid;
          let characteristicUuid: string = characteristicChangeReq.characteristicUuid;
          const characteristicValue = characteristicChangeReq.characteristicValue

          //服务UUID
          if(serviceUuid == '读写特征UUID'){
            //回调数据

          } else {

          }
        })
      }
    } catch (err) {

    }
  }

6. 数据发送

export class TBBleCommandManager {

  private gatt: ble.GattClientDevice  | null
  private gattServiceInfo: ble.GattService | null
  private data: ArrayBuffer;//特征数据

  /**
   * 构造函数
   * @param gatt GattClientDevice实例
   * @param gattServiceInfo GattService实例
   * @param data 特征数据
   */
  constructor(
    gatt: ble.GattClientDevice | null,
    gattServiceInfo: ble.GattService | null,
    data: ArrayBuffer,
  ) {
    this.gatt = gatt
    this.gattServiceInfo = gattServiceInfo
    this.data = data
  }


  //写入特征值
  writeBrushCharacteristicValue(){
    //查找读写特征
    const characteristic = this.gattServiceInfo?.characteristics.find(c => c.characteristicUuid === BrushConst.WRITE_CHARACTERISTIC_UUID);

    if (!characteristic) {
      return
    }

    try {
      if (this.gatt) {
        // 设置特征值
        characteristic.characteristicValue = this.data

        // 写入特征值到设备中
        this.gatt.writeCharacteristicValue(characteristic, ble.GattWriteType.WRITE)
      } else {
      }
    }
    catch (err) {
      const errorCode = (err as BusinessError).code;
      const errorMessage = (err as BusinessError).message;
    }
  }

}

数据粘包的问题,我们可以采用队列的方式处理

import { BluetoothCommand } from "./BluetoothCommand";
import { BusinessError } from "@kit.BasicServicesKit";
import { TBBleCommandManager } from "../../toothbrush/model/TBBleCommandManager";


export class  BluetoothCommandQueue {
  // 队列存储指令,根据优先级排序
  private queue: BluetoothCommand[] = [];
  // 标记是否正在处理指令
  private isProcessing: boolean = false;
  // 队列最大长度限制,可根据实际情况调整
  private maxSize: number = 999;

  // 添加指令到队列
  enqueue(command: BluetoothCommand): boolean {
    if (this.queue.length >= this.maxSize) {
      return false;
    }

    // 根据优先级插入队列
    let inserted = false;
    for (let i = 0; i < this.queue.length; i++) {
      if (this.queue[i].priority < command.priority) {
        this.queue.splice(i, 0, command);
        inserted = true;
        break;
      }
    }

    if (!inserted) {
      this.queue.push(command);
    }
    this.processQueue();
    return true;
  }

  // 从队列取出指令
  dequeue(): BluetoothCommand | undefined {
    return this.queue.shift();
  }

  // 处理队列中的指令
  private async processQueue(): Promise<void> {
    if (this.isProcessing || this.queue.length === 0) {
      return;
    }
    this.isProcessing = true;
    while (this.queue.length > 0) {
      const command:BluetoothCommand = this.dequeue() as BluetoothCommand;
      if (command) {
        try {
          // 这里调用实际的蓝牙发送接口
          await this.sendBluetoothCommand(command);
          command.callback?.(null);
        } catch (error) {
          command.callback?.(error as BusinessError);
        }
      }
    }
    // 处理完成后,标记为非处理状态
    this.isProcessing = false;
  }


  // 发送蓝牙指令的方法
  private sendBluetoothCommand(command: BluetoothCommand): Promise<void> {
    return new Promise((resolve, reject) => {
      // 发送蓝牙指令
      new TBBleCommandManager(command.gatt, command.gattServiceInfo, command.command).writeBrushCharacteristicValue()
      setTimeout(() => {
        console.info('Bluetooth command sent successfully');
        // 成功回调
        resolve();
      }, 100);
    });
  }


  // 清空队列
  clear(): void {
    this.queue = [];
    this.isProcessing = false;
  }

  // 获取队列大小
  size(): number {
    return this.queue.length;
  }


}

import { BusinessError } from "@kit.BasicServicesKit";
import { ble } from "@kit.ConnectivityKit";


export class BluetoothCommand {

  command: ArrayBuffer;
  priority: number;
  timestamp: number;
  callback?: (result: BusinessError | null) => void;
  gatt: ble.GattClientDevice | null;
  gattServiceInfo: ble.GattService | null;

  /**
   * 构造函数
   * @param command 命令
   * @param priority 优先级
   * @param gatt GattClientDevice实例
   * @param gattServiceInfo GattService实例
   * @param callback 回调函数
   */
  constructor(command: ArrayBuffer, priority: number = 0, gatt: ble.GattClientDevice , gattServiceInfo: ble.GattService , callback?: (result: BusinessError | null) => void) {
    this.command = command;
    this.gatt = gatt;
    this.gattServiceInfo = gattServiceInfo;
    this.priority = priority;
    this.timestamp = Date.now();
    this.callback = callback;
  }

}

队列的调用方法如下

  private commandQueue: BluetoothCommandQueue = new BluetoothCommandQueue();//指令发送队列


 // 写入BLE特征值
 // 创建蓝牙指令对象
 const command = new BluetoothCommand( buff, 0, this.gattClientDevice!, this.gattServiceInfo!,
          (error: BusinessError | null) => {
            if (error) {
              //  `发送失败: ${error.message}`;
            } else {
              // '发送成功';
            }
          }
        );
this.commandQueue.enqueue(command)


注意事项

  • 权限申请:必须在配置文件中声明蓝牙权限,并在运行时检查权限状态
  • 异步操作:蓝牙操作多为异步,需正确处理回调
  • 状态监听:建议监听蓝牙状态变化,及时响应开关变化
  • 资源释放:使用完毕后及时断开连接,释放蓝牙资源
  • 异常处理:做好异常捕获和错误处理机制
  • 兼容性:注意不同设备型号和系统版本的兼容性问题
  • 安全性:配对和数据传输时注意安全加密
  • 功耗控制:合理控制扫描频率,避免过度耗电

相关文章

网友评论

      本文标题:【最新】鸿蒙Next 蓝牙开发流程

      本文链接:https://www.haomeiwen.com/subject/mwjsojtx.html