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

news2024/11/13 15:13:55

【HarmonyOS】鸿蒙应用蓝牙功能实现 (二)在这里插入图片描述

前言

蓝牙一般分为传统蓝牙(BR/EDR),低功耗蓝牙(BLE)两种。

鸿蒙将蓝牙的功能模块分的非常细。

基本上我们会用到access进行蓝牙状态的开启和关闭,以及状态查询。

在使用connection进行传统蓝牙模式的扫描和配对。

或者再使用ble低功耗蓝牙模式进行广播,发起广播,传输数据,以及消息订阅。

Demo示例:

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';



struct Index {

  private TAG: string = "BlueToothTest";

  // 蓝牙状态
   ('onChangeBlueTooth') isStartBlueTooth: boolean = false;
   userGrant: boolean = false;

   mListDevice: Array<string> = [];

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

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

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

    // 访问信息相关功能
    try {
      let mapMseProfile = map.createMapMseProfile();
      console.info(this.TAG, 'MapMse success:' + JSON.stringify(mapMseProfile));
    } catch (err) {
      console.error(this.TAG, 'errCode: ' + (err as BusinessError).code + ', errMessage: ' + (err as BusinessError).message);
    }

    // 访问电话簿相关功能
    try {
      let pbapServerProfile = pbap.createPbapServerProfile();
      console.info(this.TAG, 'pbapServer success:' + JSON.stringify(pbapServerProfile));
    } catch (err) {
      console.error(this.TAG, 'errCode: ' + (err as BusinessError).code + ', errMessage: ' + (err as BusinessError).message);
    }

    try {
      connection.on('bluetoothDeviceFind', (data: Array<string>)=> {
        console.info(this.TAG, 'data length' + JSON.stringify(data));
        // 获取扫描可配对的设备列表
        this.mListDevice = data;
      });
      connection.startBluetoothDiscovery();
    } catch (err) {
      console.error(this.TAG, 'errCode: ' + (err as BusinessError).code + ', errMessage: ' + (err as BusinessError).message);
    }
  }

  // 用户申请权限
  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[i] === 0) {
        // 用户授权,可以继续访问目标操作
        this.userGrant = true;
      }
    }
  }

  setBlueToothState =()=>{
    try {
      if(!this.isStartBlueTooth){
        BlueToothMgr.Ins().setBlueToothAccess(true);
      }else{
        BlueToothMgr.Ins().setBlueToothAccess(false);
      }
      let state = access.getState();
      if(state == 2){
        this.isStartBlueTooth = true;
      }else{
        this.isStartBlueTooth = false;
      }
      console.log(this.TAG, "getState state: " + state);
    } catch (err) {
      console.error(this.TAG,'errCode: ' + (err as BusinessError).code + ', errMessage: ' + (err as BusinessError).message);
    }
  }

  build() {
    RelativeContainer() {
      if(this.userGrant){
        Text("蓝牙状态:" + this.isStartBlueTooth ? "开启" : "关闭")
          .id('HelloWorld')
          .fontSize(50)
          .fontWeight(FontWeight.Bold)
          .alignRules({
            center: { anchor: '__container__', align: VerticalAlign.Center },
            middle: { anchor: '__container__', align: HorizontalAlign.Center }
          })
          .onClick(this.setBlueToothState)

        Text("蓝牙状态:" + this.isStartBlueTooth ? "开启" : "关闭")
          .id('HelloWorld')
          .fontSize(50)
          .fontWeight(FontWeight.Bold)
          .alignRules({
            center: { anchor: '__container__', align: VerticalAlign.Center },
            middle: { anchor: '__container__', align: HorizontalAlign.Center }
          })
          .onClick(this.setBlueToothState)


      }
    }
    .height('100%')
    .width('100%')
  }

   ListView(){
    List() {
      ForEach(this.mListDevice, (item: string, index: number) => {
        ListItem() {
          Row() {
            Text(item).fontSize(px2fp(22)).fontColor(Color.Black)
          }
          .width('100%')
          .height(px2vp(100))
          .justifyContent(FlexAlign.Start)
        }
      }, (item: string, index: number) => JSON.stringify(item) + index)
    }
    .width('100%')
    .height('100%')
  }
}

import { access, ble } from '@kit.ConnectivityKit';
import { BusinessError } from '@kit.BasicServicesKit';

export class BlueToothMgr {

  private TAG: string = "BlueToothTest";

  private static mBlueToothMgr: BlueToothMgr | undefined = undefined;

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

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

  // 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模式。

  /**
   * 设置蓝牙访问(开关状态)
   * @param isAccess true: 打开蓝牙
   */
  setBlueToothAccess(isAccess: boolean){
    try {
      if(isAccess){
        console.info(this.TAG, 'bluetooth enableBluetooth 1');
        access.enableBluetooth();
        console.info(this.TAG, 'bluetooth enableBluetooth ');
        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);
        })
      }else{
        console.info(this.TAG, 'bluetooth disableBluetooth 1');
        access.disableBluetooth();
        console.info(this.TAG, 'bluetooth disableBluetooth ');
        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);
        })
      }
    } 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[0] = 5;
    serviceValueBuffer[1] = 6;
    serviceValueBuffer[2] = 7;
    serviceValueBuffer[3] = 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: [manufactureDataUnit],
      serviceData: [serviceDataUnit],
      includeDeviceName: false // 表示是否携带设备名,可选参数。注意带上设备名时广播包长度不能超出31个字节。
    };

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

    // 首次启动广播设置的参数。
    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);
    }
  }

}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2054774.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

BUUCTF PWN wp--warmup_csaw_2016

第一步 先checksec一下&#xff08;没有启用NX保护、PIE、完整的RELRO和栈保护&#xff0c;还有具有RWX权限的内存段。&#xff09; 分析一下这个文件的保护机制&#xff1a; Arch: amd64-64-little 这表示该可执行文件是为64位的AMD64架构编译的&#xff0c;并且使用的是小…

科大讯飞飞凡计划面经(2024年秋招新出炉)

7月笔完一个多月约的面试 —————————————- 一面8.16 不问项目&#xff0c;上来就是八股轰炸 c11新特性 shared_ptr是线程安全的吗&#xff0c;不安全的话怎么实现线程安全的 stl容器中频繁查找用什么&#xff0c;频繁增删用什么 vector中间插入元素会发生什么&…

NVIDIA Isaac Lab 入门教程(一)

系列文章目录 前言 Isaac Lab 是一个用于机器人学习的统一模块化框架&#xff0c;旨在简化机器人研究中的常见工作流程&#xff08;如 RL、从演示中学习和运动规划&#xff09;。它建立在英伟达 Isaac Sim 的基础上&#xff0c;利用最新的仿真功能实现逼真的场景和快速高效的仿…

74、docker容器编译安装lnmp

一、docker部署lnmp l linux n nginx 1.22 172.111.0.10 docker–nginx m mysql 8.0.30 172.111.0.20 docker–mysql p php 8.1.27 172.111.0.30 docker–php docker&#xff1a;单节点部署&#xff0c;在一台机器上部署&#xff0c;跨了机器容器无法通信&#xff0c;做高…

git 如何生成sshkey公钥

打开git客户端 输入 ssh-keygen -t rsa -b 4096 -C "xxxxxxexample.com" 然后根据提示按enter 或者y 直到出现下图所示 打开 c盘的路径下的文件&#xff0c;/c/Users/18159/.ssh/id_rsa.pub 将id_rsa.pub中的公钥贴到git 网站上的SSH keys即可

网络编程(TCP/UDP)

网络编程 一.网络编程 1.1 概述 ​ Java是 Internet 上的语言&#xff0c;它从语言级上提供了对网络应用程序的支持&#xff0c;程序员能够很容易开发常见的网络应用程序。 ​ Java提供的网络类库&#xff0c;可以实现无痛的网络连接&#xff0c;联网的底层细节被隐藏在Java…

【Hot100】LeetCode—2. 两数相加

目录 1- 思路思路 2- 实现⭐2. 两数相加——题解思路 3- ACM 实现 原题连接&#xff1a;2. 两数相加 1- 思路 思路 分为几个步骤 ①数据结构&#xff1a;遍历指针,进位符、②遍历两个链表、③处理最后的进位符 1- 数据结构 定义 curA 和 curB 用来遍历两个链表定义 carry 记…

ubuntu22.04新机器安装五笔wb、onlyoffice、pyenv、pipenv和虚拟环境virtualenv

wb安装方法 重新开机后右上角点击输入法&#xff0c;在下面打开的界面可以点击配置 左下角shift可快速切换五笔和英文 输入 johnjohn-hp:~$ sudo apt-get install fcitx-table-wbpy johnjohn-hp:~$ sudo apt-get install fcitx-googlepinyin johnjohn-hp:~$ sudo apt-get ins…

二叉树OJ刷题与讲解

1.单值二叉树 题目链接&#xff1a;单值二叉树 题目描述&#xff1a; 这道题要求判断是否是单值二叉树也就是说每个节点的值都要相等才是单值二叉树&#xff0c;如果相等就返回true否则返回false&#xff0c;我们可以使用递归来解这道题&#xff0c;我们把它分为根左子树右子…

Bean对象生命周期流程图

Bean生命周期流程图&#xff1a;https://www.processon.com/view/link/5f8588c87d9c0806f27358c1 Spring扫描底层流程&#xff1a;https://www.processon.com/view/link/61370ee60e3e7412ecd95d43

EmguCV学习笔记 VB.Net 4.5 像素距离和连通区域

版权声明&#xff1a;本文为博主原创文章&#xff0c;转载请在显著位置标明本文出处以及作者网名&#xff0c;未经作者允许不得用于商业目的。 教程VB.net版本请访问&#xff1a;EmguCV学习笔记 VB.Net 目录-CSDN博客 教程C#版本请访问&#xff1a;EmguCV学习笔记 C# 目录-CSD…

【数据结构与算法 | 图篇】最小生成树之Kruskal(克鲁斯卡尔)算法

1. 前言 克鲁斯卡尔算法&#xff08;Kruskals algorithm&#xff09;是一种用于寻找加权图的最小生成树&#xff08;Minimum Spanning Tree, MST&#xff09;的经典算法。这种算法是由约瑟夫克鲁斯卡尔&#xff08;Joseph Kruskal&#xff09;提出的&#xff0c;并且适用于所有…

七夕情人节动态花瓣表白网页源码

软件介绍 七夕情人节动态花瓣表白网页源码是一款功能强大的表白网页程序&#xff0c;拥有超级好看的花瓣表白效果和多种不同的动态切换效果&#xff0c;非常适合七夕表白活动。 拥有独特的设计风格和花瓣表白效果&#xff0c;可以让用户在表白过程中更加浪漫和温馨。该程序还…

Seaborn:基于 Matplotlib 的高级绘图库,提供了更高级的接口来绘制吸引人的统计图形。

引言 Seaborn 是基于 Matplotlib 的高级数据可视化库&#xff0c;专为绘制统计图形而设计。相比于 Matplotlib&#xff0c;Seaborn 提供了更高级的接口&#xff0c;简化了绘图过程&#xff0c;同时默认的美观配色和样式使得图形更加吸引人。Seaborn 特别适合用于探索性数据分析…

Cesium 实战 - 自定义纹理材质系列之 - 半球雷达效果(力场闪烁)

Cesium 实战 - 自定义纹理材质系列之 - 半球雷达效果(力场闪烁) 核心代码完整代码在线示例Cesium 给实体对象(Entity)提供了很多实用的样式,基本满足普通项目需求; 但是作为 WebGL 引擎,肯定不够丰富,尤其是动态效果样式。 对于实体对象(Entity),可以通过自定义材…

springsecurity 登录认证一(ajax)

一、准备工作 1.1 导入依赖 因springboot 3.0 以上版本只能支持java17 顾使用2.5.0 版本 <parent><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-parent</artifactId><version>2.5.0</version><…

CSDN AI-WEB-1.0 攻略

找到一个目标靶场的IP &#xff0c; 这里以172.16.1.98 为例 1、使用命令 /robots.txt 来确定目录 2、分别测试两个文件 均无法访问&#xff0c;可返回其根目录查询 3、到根目录&#xff0c;出现搜索框 4、输入ID为1 5、使用虚拟机kali的终端 搜索命令 dirsearch -u http:…

Python青少年简明教程:基础知识

Python青少年简明教程&#xff1a;基础知识 Python是大小写敏感的语言。编程语言中的“大小写敏感”意味着该语言在识别标识符、关键字或者其他语法元素时&#xff0c;会区分字母的大小写。在这种语言中&#xff0c;大写字母和小写字母被视为不同的字符&#xff0c;因此&#x…

[论文翻译]使用 BERT 检测安卓恶意软件

Android Malware Detection Using BERT Souani B, Khanfir A, Bartel A, et al. Android malware detection using bert[C]//International Conference on Applied Cryptography and Network Security. Cham: Springer International Publishing, 2022: 575-591. 摘要 在本文…

cad文件被写保护怎么解除保护?

CAD文件被写保护怎么解除保护?四五设计网小编给大家整理出的方法希望能够帮助到大家&#xff01; 解除CAD文件写保护的最简单方法是创建一个文件的副本&#xff0c;并在副本上进行编辑。 原文件可能因为文件属性或权限设置而被设置为写保护。创建副本不会改变原始文件的保护…