汕尾海湾 发表于 2025-1-18 21:17:42

【HarmonyOS】鸿蒙应用蓝牙功能实现 (三)

【HarmonyOS】鸿蒙应用蓝牙功能实现 (三)

   前言

   今天整理蓝牙Demo代码,查看官网时发现自己帐号没有登录,竟然也可以访问最新的API文档,真是喜大奔普。看来华为已经开始对外开放最新的API文档,不再有白名单限定了。
   https://img-blog.csdnimg.cn/img_convert/5b40523c0c5930a699b4a9451530805b.png
   HarmonyOS NEXTDeveloper Beta5
   接下来解说蓝牙配对流程。低功耗蓝牙BLE模式将在下面章节解说。
   蓝牙配对业务流程

   1.设备进入可被发现模式: 起首,设备需要进入可被发现模式,这样附近的蓝牙设备才能识别到它。一方设备(如手机)会主动搜索附近的蓝牙设备,并列出全部可用的配对选项。
   2.选择并触发配对哀求: 用户从列表中选择想要连接的设备,并触发配对哀求。此时,双方设备会交换一系列的身份验证信息,以确保相互的身份安全无误。在这个过程中,可能会要求用户输入配对码(如PIN码)或在设备上确认配对哀求。
   3.身份验证和加密: 一旦身份验证通过,设备间就会创建安全的连接通道,这一过程称为“配对乐成”。配对完成后,设备之间的连接就创建了,它们可以开始传输数据。
   4.数据传输: 设备间通过蓝牙举行数据传输,可以传输音频、文件等多种类型的数据。
   5.断开连接: 当数据传输完成后,蓝牙设备可以断开连接。断开连接的操作可以通过设备上的按钮大概软件来实现。
   蓝牙配对通常是一次性的,即一旦设备乐成配对,它们会在后续的连接中自动识别并连接,无需再次举行配对过程(除非设备被重置或用户手动取消配对)
   以下是传统的蓝牙配对流程图仅供参考:
   https://img-blog.csdnimg.cn/img_convert/0b3957294abb614cacfd82874e5bd3be.png
   通例蓝牙配对Demo

   https://img-blog.csdnimg.cn/img_convert/833132af71de43a172cb2f2e3b7cd5e9.png
   Demo包括以下内容: 1.蓝牙权限开启 2.蓝牙开启/关闭 3.蓝牙扫描开启/关闭 4.蓝牙配对 5.蓝牙code协议确认
   蓝牙UI交互类
         import { access } from '@kit.ConnectivityKit';
import { BusinessError } from '@kit.BasicServicesKit';
import { BlueToothMgr } from '../manager/BlueToothMgr';
import { abilityAccessCtrl, common } from '@kit.AbilityKit';
import { connection } from '@kit.ConnectivityKit';
import { map } from '@kit.ConnectivityKit';
import { pbap } from '@kit.ConnectivityKit';
import { HashMap } from '@kit.ArkTS';
import { DeviceInfo } from '../info/DeviceInfo';
import { promptAction } from '@kit.ArkUI';

@Entry
@Component
struct Index {

private TAG: string = "BlueToothTest";

// 扫描状态定时器
private mNumInterval: number = -1;
// 当前设备蓝牙名
@State mCurrentDeviceName: string = "";
// 蓝牙状态
@State @Watch('onChangeBlueTooth') isStartBlueTooth: boolean = false;
// 蓝牙扫描状态
@State @Watch('onChangeBlueTooth') isStartScan: boolean = false;
// 当前蓝牙权限
@State userGrant: boolean = false;
// 扫描到设备名
@State mMapDevice: HashMap<string, DeviceInfo> = new HashMap();
// ui展现的设备列表
@State mListDeviceInfo: Array<DeviceInfo> = new Array();

async aboutToAppear() {
    await this.requestBlueToothPermission();

    let state = access.getState();
    console.log(this.TAG, "getState state: " + state);
    if(state == 2){
      this.isStartBlueTooth = true;
      console.log(this.TAG, "getState isStartBlueTooth: " + this.isStartBlueTooth);
    }else{
      this.isStartBlueTooth = false;
      console.log(this.TAG, "getState isStartBlueTooth: " + this.isStartBlueTooth);
    }
}

private onChangeBlueTooth(){
    if(!this.isStartBlueTooth){
      this.mMapDevice = new HashMap();
      return;
    }

    this.mCurrentDeviceName = BlueToothMgr.Ins().getCurrentDeviceName();
}

// 用户申请权限
async reqPermissionsFromUser(): Promise<number[]> {
    let context = getContext() as common.UIAbilityContext;
    let atManager = abilityAccessCtrl.createAtManager();
    let grantStatus = await atManager.requestPermissionsFromUser(context, ['ohos.permission.ACCESS_BLUETOOTH']);
    return grantStatus.authResults;
}

// 用户申请蓝牙权限
async requestBlueToothPermission() {
    let grantStatus = await this.reqPermissionsFromUser();
    for (let i = 0; i < grantStatus.length; i++) {
      if (grantStatus === 0) {
      // 用户授权,可以继续访问目标操作
      this.userGrant = true;
      promptAction.showToast({ message: "蓝牙授权成功!"});
      }else{
      promptAction.showToast({ message: "蓝牙授权失败!"});
      }
    }
}

setBlueToothScan = ()=>{
    if(!this.isStartScan){
      promptAction.showToast({ message: "开启扫描!"});
      BlueToothMgr.Ins().startScanDevice((data: Array<string>)=>{
      let deviceId: string = data;
      if(this.mMapDevice.hasKey(deviceId)){
          // 重复设备,丢弃不处理
      }else{
          // 添加到表中
          let deviceInfo: DeviceInfo = new DeviceInfo();
          deviceInfo.deviceId = deviceId;
          deviceInfo.deviceName = BlueToothMgr.Ins().getDeviceName(deviceId);
          deviceInfo.deviceClass = BlueToothMgr.Ins().getDeviceClass(deviceId);
          this.mMapDevice.set(deviceId, deviceInfo);
          this.mListDeviceInfo = this.mListDeviceInfo.concat(deviceInfo);
      }
      });
      this.mMapDevice.clear();
      this.mListDeviceInfo = [];
      // 开启定时器
      this.mNumInterval = setInterval(()=>{
      let discovering = BlueToothMgr.Ins().isCurrentDiscovering();
      if(!discovering){
          this.closeScanDevice();
      }
      }, 1000);
      this.isStartScan = true;
    }else{
      promptAction.showToast({ message: "关闭扫描!"});
      BlueToothMgr.Ins().stopScanDevice();
      this.closeScanDevice();
    }
}

private closeScanDevice(){
    clearInterval(this.mNumInterval);
    this.isStartScan = false;
}

setBlueToothState = ()=>{
    try {
      if(!this.isStartBlueTooth){
      // 开启蓝牙
      BlueToothMgr.Ins().setBlueToothAccess(true, (state: access.BluetoothState) => {
          console.log(this.TAG, "getState setBlueToothAccessTrue: " + state);
          if(state == access.BluetoothState.STATE_ON){
            this.isStartBlueTooth = true;
            promptAction.showToast({ message: "开启蓝牙!"});
            console.log(this.TAG, "getState isStartBlueTooth: " + this.isStartBlueTooth);
          }
      });
      }else{
      BlueToothMgr.Ins().setBlueToothAccess(false, (state: access.BluetoothState) => {
          console.log(this.TAG, "getState setBlueToothAccessFalse: " + state);
          if(state == access.BluetoothState.STATE_OFF){
            this.isStartBlueTooth = false;
            promptAction.showToast({ message: "关闭蓝牙!"});
            console.log(this.TAG, "getState isStartBlueTooth: " + this.isStartBlueTooth);
          }
      });
      }
    } catch (err) {
      console.error(this.TAG,'errCode: ' + (err as BusinessError).code + ', errMessage: ' + (err as BusinessError).message);
    }
}

private isLog(){
    console.log(this.TAG, "isLog isStartBlueTooth: " + this.isStartBlueTooth);
    return true;
}

build() {
    Column() {
      if(this.userGrant){
      if(this.isLog()){
          Text("当前蓝牙设备信息:\n " + this.mCurrentDeviceName)
            .fontSize(px2fp(80))
            .margin({ top: px2vp(100) })
            .fontWeight(FontWeight.Bold)

          Text(this.isStartBlueTooth ? "蓝牙状态: 开启" : "蓝牙状态: 关闭")
            .fontSize(px2fp(80))
            .margin({ top: px2vp(100) })
            .fontWeight(FontWeight.Bold)
            .onClick(this.setBlueToothState)

          Text(this.isStartScan ? "蓝牙扫描: 开启ing" : "蓝牙扫描: 关闭")
            .margin({ top: px2vp(100) })
            .fontSize(px2fp(80))
            .fontWeight(FontWeight.Bold)
            .onClick(this.setBlueToothScan)

          this.ListView()
      }
      }
    }
    .justifyContent(FlexAlign.Center)
    .height('100%')
    .width('100%')
}

@Builder ListView(){
    List() {
      ForEach(this.mListDeviceInfo, (item: DeviceInfo, index: number) => {
      ListItem() {
          Column(){
            Row() {
            Text("设备ID: " + item.deviceId).fontSize(px2fp(42)).fontColor(Color.Black)
            Blank()
            Text("设备名: " + item.deviceName).fontSize(px2fp(42)).fontColor(Color.Black)
            }
            .width('100%')
            Text(item.deviceClass).fontSize(px2fp(42)).fontColor(Color.Black)
          }
          .width('100%')
          .height(px2vp(200))
          .justifyContent(FlexAlign.Start)
          .onClick(()=>{
            // 点击选择处理配对
            AlertDialog.show({
            title:"选择配对",
            message:"是否选择该设备进行蓝牙配对?",
            autoCancel: true,
            primaryButton: {
                value:"确定",
                action:()=>{
                  promptAction.showToast({ message: item.deviceName + "配对ing!"});
                  BlueToothMgr.Ins().pairDevice(item.deviceId);
                }
            },
            secondaryButton: {
                value:"取消",
                action:()=>{
                  promptAction.showToast({ message: "取消!"});
                }
            },
            cancel:()=>{
                promptAction.showToast({ message: "取消!"});
            }
            })
          })
      }
      }, (item: string, index: number) => JSON.stringify(item) + index)
    }
    .width('100%')
}
}          蓝牙管理类
         import { access, ble } from '@kit.ConnectivityKit';
import { BusinessError } from '@kit.BasicServicesKit';
import { connection } from '@kit.ConnectivityKit';

export class BlueToothMgr {

private TAG: string = "BlueToothTest";

private static mBlueToothMgr: BlueToothMgr | undefined = undefined;

private advHandle: number = 0xFF; // default invalid value

private mDeviceDiscoverArr: Array<string> = new Array<string>();

public static Ins(){
    if(!BlueToothMgr.mBlueToothMgr){
      BlueToothMgr.mBlueToothMgr = new BlueToothMgr();
      BlueToothMgr.init();
    }
    return BlueToothMgr.mBlueToothMgr;
}

private static init(){
    try {
      connection.on('pinRequired', (data: connection.PinRequiredParam) =>{
      // data为配对请求参数
      console.info("BlueToothTest",'pinRequired pin required = '+ JSON.stringify(data));
      });
    } catch (err) {
      console.error("BlueToothTest", 'pinRequired errCode: ' + (err as BusinessError).code + ', errMessage: ' + (err as BusinessError).message);
    }
}

/**
   * 当前设备蓝牙设备名称
   */
public getCurrentDeviceName(){
    let localName: string = "";
    try {
      localName = connection.getLocalName();
      console.info(this.TAG, 'getCurrentDeviceName localName: ' + localName);
    } catch (err) {
      console.error(this.TAG, 'getCurrentDeviceName errCode: ' + (err as BusinessError).code + ', errMessage: ' + (err as BusinessError).message);
    }
    return localName;
}

/**
   * 当前设备蓝牙可发现状态
   */
public isCurrentDiscovering(){
    let res: boolean = false;
    try {
      res = connection.isBluetoothDiscovering();
      console.info(this.TAG, 'isCurrentDiscovering isBluetoothDiscovering: ' + res);
    } catch (err) {
      console.error(this.TAG, 'isCurrentDiscovering errCode: ' + (err as BusinessError).code + ', errMessage: ' + (err as BusinessError).message);
    }
    return res;
}

// STATE_OFF        0        表示蓝牙已关闭。
// STATE_TURNING_ON        1        表示蓝牙正在打开。
// STATE_ON        2        表示蓝牙已打开。
// STATE_TURNING_OFF        3        表示蓝牙正在关闭。
// STATE_BLE_TURNING_ON        4        表示蓝牙正在打开LE-only模式。
// STATE_BLE_ON        5        表示蓝牙正处于LE-only模式。
// STATE_BLE_TURNING_OFF        6        表示蓝牙正在关闭LE-only模式。

public getBlueToothState(): access.BluetoothState {
    let state = access.getState();
    return state;
}

/**
   * 设置蓝牙访问(开关状态)
   * @param isAccess true: 打开蓝牙
   */
setBlueToothAccess(isAccess: boolean, callbackBluetoothState: Callback<access.BluetoothState>){
    try {
      if(isAccess){
      console.info(this.TAG, 'bluetooth enableBluetooth 1');
      access.enableBluetooth();
      console.info(this.TAG, 'bluetooth enableBluetooth 2');
      access.on('stateChange', (data: access.BluetoothState) => {
          let btStateMessage = this.switchState(data);
          if (btStateMessage == 'STATE_ON') {
            access.off('stateChange');
          }
          console.info(this.TAG, 'bluetooth statues: ' + btStateMessage);
          callbackBluetoothState(data);
      })
      }else{
      console.info(this.TAG, 'bluetooth disableBluetooth 1');
      access.disableBluetooth();
      console.info(this.TAG, 'bluetooth disableBluetooth 2');
      access.on('stateChange', (data: access.BluetoothState) => {
          let btStateMessage = this.switchState(data);
          if (btStateMessage == 'STATE_OFF') {
            access.off('stateChange');
          }
          console.info(this.TAG, "bluetooth statues: " + btStateMessage);
          callbackBluetoothState(data);
      })
      }
    } catch (err) {
      console.error(this.TAG, 'errCode: ' + (err as BusinessError).code + ', errMessage: ' + (err as BusinessError).message);
    }
}

private switchState(data: access.BluetoothState){
    let btStateMessage = '';
    switch (data) {
      case 0:
      btStateMessage += 'STATE_OFF';
      break;
      case 1:
      btStateMessage += 'STATE_TURNING_ON';
      break;
      case 2:
      btStateMessage += 'STATE_ON';
      break;
      case 3:
      btStateMessage += 'STATE_TURNING_OFF';
      break;
      case 4:
      btStateMessage += 'STATE_BLE_TURNING_ON';
      break;
      case 5:
      btStateMessage += 'STATE_BLE_ON';
      break;
      case 6:
      btStateMessage += 'STATE_BLE_TURNING_OFF';
      break;
      default:
      btStateMessage += 'unknown status';
      break;
    }
    return btStateMessage;
}

/**
   * 主播蓝牙广播
   */
public registerBroadcast(){
    try {
      ble.on('advertisingStateChange', (data: ble.AdvertisingStateChangeInfo) => {
      console.info(this.TAG, 'bluetooth advertising state = ' + JSON.stringify(data));
      AppStorage.setOrCreate('advertiserState', data.state);
      });
    } catch (err) {
      console.error(this.TAG, 'errCode: ' + (err as BusinessError).code + ', errMessage: ' + (err as BusinessError).message);
    }
}

/**
   * 开启蓝牙广播
   */
public async startBroadcast(valueBuffer: Uint8Array){

    // 表示发送广播的相关参数。
    let setting: ble.AdvertiseSetting = {
      // 表示广播间隔,最小值设置160个slot表示100ms,最大值设置16384个slot,默认值设置为1600个slot表示1s。
      interval: 160,
      // 表示发送功率,最小值设置-127,最大值设置1,默认值设置-7,单位dbm。推荐值:高档(1),中档(-7),低档(-15)。
      txPower: 0,
      // 表示是否是可连接广播,默认值设置为true,表示可连接,false表示不可连接。
      connectable: true
    };

    // BLE广播数据包的内容。
    let manufactureDataUnit: ble.ManufactureData = {
      // 表示制造商的ID,由蓝牙SIG分配。
      manufactureId: 4567,
      manufactureValue: valueBuffer.buffer
    };

    let serviceValueBuffer = new Uint8Array(4);
    serviceValueBuffer = 5;
    serviceValueBuffer = 6;
    serviceValueBuffer = 7;
    serviceValueBuffer = 8;

    // 广播包中服务数据内容。
    let serviceDataUnit: ble.ServiceData = {
      serviceUuid: "00001888-0000-1000-8000-00805f9b34fb",
      serviceValue: serviceValueBuffer.buffer
    };

    // 表示广播的数据包内容。
    let advData: ble.AdvertiseData = {
      serviceUuids: ["00001888-0000-1000-8000-00805f9b34fb"],
      manufactureData: ,
      serviceData: ,
      includeDeviceName: false // 表示是否携带设备名,可选参数。注意带上设备名时广播包长度不能超出31个字节。
    };

    // 表示回复扫描请求的响应内容。
    let advResponse: ble.AdvertiseData = {
      serviceUuids: ["00001888-0000-1000-8000-00805f9b34fb"],
      manufactureData: ,
      serviceData:
    };

    // 首次启动广播设置的参数。
    let advertisingParams: ble.AdvertisingParams = {
      advertisingSettings: setting,
      advertisingData: advData,
      advertisingResponse: advResponse,
      //         表示发送广播持续的时间。单位为10ms,有效范围为1(10ms)到65535(655350ms),如果未指定此参数或者将其设置为0,则会连续发送广播。
      duration: 0 // 可选参数,若大于0,则广播发送一段时间后,则会临时停止,可重新启动发送
    }

    // 首次启动广播,且获取所启动广播的标识ID
    try {
      this.registerBroadcast();
      this.advHandle = await ble.startAdvertising(advertisingParams);
    } catch (err) {
      console.error(this.TAG, 'errCode: ' + (err as BusinessError).code + ', errMessage: ' + (err as BusinessError).message);
    }
}

/**
   * 开始蓝牙扫描
   */
public startScanDevice(callback: Callback<Array<string>>){
    try {
      connection.on('bluetoothDeviceFind', (data: Array<string>)=>{
      // 随机MAC地址
      console.info(this.TAG, 'bluetooth device bluetoothDeviceFind = '+ JSON.stringify(data));
      callback(data);
      });
      connection.startBluetoothDiscovery();
    } catch (err) {
      console.error(this.TAG, 'errCode: ' + (err as BusinessError).code + ', errMessage: ' + (err as BusinessError).message);
    }
}

/**
   * 停止蓝牙扫描
   */
public stopScanDevice(){
    try {
      connection.off('bluetoothDeviceFind');
      connection.stopBluetoothDiscovery();
    } catch (err) {
      console.error(this.TAG, 'errCode: ' + (err as BusinessError).code + ', errMessage: ' + (err as BusinessError).message);
    }
}

public getDeviceName(deviceID: string){
    let remoteDeviceName: string = "";
    try {
      remoteDeviceName = connection.getRemoteDeviceName(deviceID);
      console.info(this.TAG, 'getDeviceName device = '+ JSON.stringify(remoteDeviceName));
    } catch (err) {
      console.error(this.TAG, 'getDeviceName errCode: ' + (err as BusinessError).code + ', errMessage: ' + (err as BusinessError).message);
    }
    return remoteDeviceName;
}

public getDeviceClass(deviceID: string){
    let remoteDeviceClass: string = "";
    try {
      let classObj = connection.getRemoteDeviceClass(deviceID);
      remoteDeviceClass = JSON.stringify(classObj);
    } catch (err) {
      console.error(this.TAG, 'getDeviceName errCode: ' + (err as BusinessError).code + ', errMessage: ' + (err as BusinessError).message);
    }
    return remoteDeviceClass;
}

// BondState
// BOND_STATE_INVALID        0        无效的配对。
// BOND_STATE_BONDING        1        正在配对。
// BOND_STATE_BONDED        2        已配对。

/**
   * 发起配对蓝牙
   */
public pairDevice(deviceID: string){
    try {
      connection.on('bondStateChange', (data: connection.BondStateParam) =>{
      console.info(this.TAG, 'pairDevice pair state = '+ JSON.stringify(data));
      // 当蓝牙配对类型PinType为PIN_TYPE_ENTER_PIN_CODE或PIN_TYPE_PIN_16_DIGITS时调用此接口,请求用户输入PIN码。
      });
      connection.pairDevice(deviceID, (err: BusinessError) => {
      console.info(this.TAG, 'pairDevice device name err:' + JSON.stringify(err));
      });
    } catch (err) {
      console.error(this.TAG, 'pairDevice errCode: ' + (err as BusinessError).code + ', errMessage: ' + (err as BusinessError).message);
    }
}

}          ![在这里插入图片描述](https://i-blog.csdnimg.cn/direct/fda00352f394401cbc3a6b884469f6ce.png![在这里插入图片描述](https://i-blog.csdnimg.cn/direct/2208c37af700411ea0140f7e4141d59c.png)

免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。
页: [1]
查看完整版本: 【HarmonyOS】鸿蒙应用蓝牙功能实现 (三)