小程序蓝牙通信

2023-11-06

蓝牙通信能力封装

一开始是根据uniapp提供的蓝牙api写的蓝牙方法,之后发现复用性,以及一些状态的监听存在缺陷,之后整理成了类。这样复用性以及状态监听的问题就解决了。

蓝牙组件

创建蓝牙组件的类

在这里插入图片描述

单例模式是为了保证蓝牙长连接,只有一个蓝牙实例

// 单例模式
    if (Bluetooth.instance) {
      return Bluetooth.instance;
    }
    Bluetooth.instance = this;

根据需求定义变量
const serviceUUID = “”; // 主服务的UUID
const notifyUUID = “”; // 读
const writeUUID = “”; // 写
这一部分,连接蓝牙uuid是必须的,可以考虑接受参数的形式,或者在类里面写好,两种方式

根据需要传入相应的回调方法
successLinkFn:蓝牙连接成功的回调
dataChangeFn:成功接收蓝牙指令的回调
blueToothLinkStatusFn :监听蓝牙状态的回调

export class Bluetooth {
  constructor(params) {
    // 单例模式
    if (Bluetooth.instance) {
      return Bluetooth.instance;
    }
    this.bundleData = null; // 需要拼包的数据
    this.BLTdeviceId = ""; // 设备的 id
    this.deviceId = params.deviceId; // 设备号(设备序列号)
    this.serviceUUID = params.serviceUUID || serviceUUID;
    this.notifyUUID = params.notifyUUID || notifyUUID;
    this.writeUUID = params.writeUUID || writeUUID;
    this.successLinkFn = params.successLinkFn; // 蓝牙连接成功
    this.dataChangeFn = params.dataChangeFn; // 接收蓝牙指令
    this.blueToothLinkStatusFn = params.blueToothLinkStatusFn; // 蓝牙连接状态
    this.initBluetooth();
    Bluetooth.instance = this;
  }
 }

初始化蓝牙

initBluetooth() {
    let _this = this;
    uni.openBluetoothAdapter({
      success(res) {
        console.log("初始化蓝牙成功", res);
        _this.getBluetoothAdapterState();
      },
      fail(error) {
        console.log(error);
      },
    });
  }

判断手机蓝牙标识是否打开的状态

  // 判断手机蓝牙标识是否打开的状态
  getBluetoothAdapterState() {
    const _this = this;
    uni.getBluetoothAdapterState({
      success(res) {
        //如果res.avaliable==false 说明没打开蓝牙 反之则打开
        if (res.available) {
          // 蓝牙打开-----开始搜寻附近蓝牙
          _this.startBluetoothDevicesDiscovery();
        } else {
          uni.showModal({
            content:
              "当前手机蓝牙关闭,请在手机设置中开启蓝牙,并允许微信获取您的蓝牙权限",
            confirmColor: "#4AC596",
            confirmText: "确定",
            showCancel: false,
            success(res) {},
            fail() {
              // TODO 蓝牙打开失败
              // 页面返回
              uni.navigateBack({
                delta: 1, //返回层数,2则上上页
              });
            },
          });
        }
      },
    });
  }

搜寻附近的蓝牙外围设备

  // 搜寻附近的蓝牙外围设备
  // 此操作比较耗费系统资源,请在搜索并连接到设备后调用 uni.stopBluetoothDevicesDiscovery 方法停止搜索。
  startBluetoothDevicesDiscovery() {
    let _this = this;
    uni.startBluetoothDevicesDiscovery({
      success(res) {
        console.log(res);
        _this.onBluetoothDeviceFound();
      },
      fail: function(res) {
        uni.showToast({
          title: "搜寻附近的蓝牙外围设备失败",
          icon: "none",
          duration: 1000,
        });
        console.log("搜寻附近的蓝牙外围设备失败");
      },
    });
  }

发现蓝牙设备(匹配要连接的蓝牙设备)

根据传入的设备序列号(设备号)进行匹配

  onBluetoothDeviceFound() {
    let _this = this;
    uni.onBluetoothDeviceFound(function(res) {
      res.devices.forEach(function(device) {
        let deviceName = device.localName || device.name;
        if (_this.deviceId == deviceName) { //根据传入的设备序列号(设备号)进行匹配
          console.log("发现蓝牙设备", device);
          _this.BLTdeviceId = device.deviceId;
          _this.startBluetoothDevicesDiscovery();
          _this.connectDevice();
        }
      });
    });
  }

连接蓝牙

  //连接蓝牙
  connectDevice() {
    let _this = this;
    uni.createBLEConnection({
      deviceId: _this.BLTdeviceId,
      timeout: 3000,
      success: function(res) {
        uni.showToast({
          icon: "success",
          title: "蓝牙连接成功",
          mask: true,
          duration: 3000,
        });
        console.log("蓝牙连接成功");
        _this.getBLEDeviceServices();
        // 监听蓝牙状态
        _this.blueToothLinkChange();
      },
      fail: function(res) {
        console.log("连接失败" + JSON.stringify(res));
        uni.showToast({
          icon: "none",
          title: "未成功连接设备,请重新搜索",
          mask: true,
          duration: 3000,
        });
      },
    });
  }

监听蓝牙状态

  // 监听蓝牙状态
  blueToothLinkChange() {
    const _this = this;
    uni.onBLEConnectionStateChange(function(res) {
      // 该方法回调中可以用于处理连接意外断开等异常情况
      console.log(
        `device ${res.deviceId} state has changed, connected: ${res.connected}`
      );
      _this.blueToothLinkStatusFn && _this.blueToothLinkStatusFn(res);
    });
  }

获取服务

  // 获取服务
  getBLEDeviceServices() {
    uni.showToast({
      icon: "none",
      title: "数据处理中,请稍后",
      duration: 3000,
      mask: true,
    });
    let _this = this;
    uni.getBLEDeviceServices({
      deviceId: _this.BLTdeviceId,
      success: function(res) {
        for (let i = 0; i < res.services.length; i++) {
          if (res.services[i].uuid == _this.serviceUUID) {
            console.log("获取serviceId成功", res);
            _this.getBLEDeviceCharacteristics();
            break;
          }
        }
      },
      fail: function(res) {
        console.log("获取serviceId失败", res);
        uni.showToast({
          icon: "none",
          title: "未成功连接设备,请重新搜索",
          mask: true,
          duration: 3000,
        });
      },
    });
  }

获取特征值

  // 获取特征值
  getBLEDeviceCharacteristics() {
    let _this = this;
    // 如果是自动链接的话,wx.getBLEDeviceCharacteristics方法建议使用setTimeout延迟1秒后再执行
    wx.getBLEDeviceCharacteristics({
      deviceId: _this.BLTdeviceId,
      serviceId: _this.serviceUUID,
      success(res) {
        console.log("获取特征值成功: ", res); // 可以在此判断特征值是否支持读写等操作,特征值其实也需要提前向硬件佬索取的
        _this.notify();
      },
      fail(err) {
        console.error(err);
        _this.closeBLEConnection();
        uni.showToast({
          title: "获取特征值失败",
          icon: "error",
        });
      },
    });
  }

开启消息监听(只使用一次)

  // 开启消息监听,只使用一次
  notify() {
    let _this = this;
    uni.notifyBLECharacteristicValueChange({
      state: true,
      deviceId: _this.BLTdeviceId, // 设备id
      serviceId: _this.serviceUUID, // 监听指定的服务
      characteristicId: _this.notifyUUID, // 监听对应的特征值
      type: "notification",
      success(res) {
        console.log("已开启监听: ", res);
        _this.successLinkFn && _this.successLinkFn("success");
      },
      fail(err) {
        console.error(err);
        _this.closeBLEConnection();
        uni.showToast({
          title: "监听失败",
          icon: "error",
        });
        console.log("监听失败");
      },
    });
  }

发送数据,只要有指令发送就会使用

根据蓝牙协议处理需要发送的数据,处理数据属于业务代码,处理好了直接调用send方法即可。

  // 发送数据,只要有指令发送就会使用
  send(data) {
    // 向蓝牙设备发送
    const _this = this;
    let buffer = Util.string2buffer(data);
    const BLTdeviceId = wx.getStorageSync("BLTdeviceId");
    wx.writeBLECharacteristicValue({
      deviceId: _this.BLTdeviceId,
      serviceId: _this.serviceUUID,
      characteristicId: _this.writeUUID,
      value: buffer,
      writeType: "writeNoResponse",
      success(res) {
        console.log("指令发送成功:", res);
        setTimeout(() => {
          _this.listenValueChange();
        }, 500);
      },
      fail(err) {
        console.error(err);
        _this.closeBLEConnection();
      },
    });
  }

监听消息变化

  // 监听消息变化
  listenValueChange() {
    const _this = this;
    uni.onBLECharacteristicValueChange((res) => {
      const data = Util.ab2hex(res.value);
      console.log("监听消息变化:", data);
      // 返回的数据有拆包的情况,所以需要判断data是否有帧头与帧尾,如无则需要拼接
      // 作为业务代码在回调方法中处理数据
      _this.dataChangeFn && _this.dataChangeFn(data, res);
    });
  }

蓝牙设备停止搜索

stopBluetoothDevicesDiscovery() {
    wx.stopBluetoothDevicesDiscovery({
      success(res) {
        console.log("蓝牙设备停止搜索:", res);
      },
    });
  }

断开蓝牙

// 断开蓝牙连接
  closeBLEConnection() {
    uni.closeBLEConnection({
      deviceId: _this.deviceId,
    });
    uni.closeBluetoothAdapter();
  }

使用蓝牙组件

在需要连接蓝牙的页面,调用Bluetooth的类,根据需要传入参数以及回调方式即可,可以在回调函数里面处理蓝牙数据,监听蓝牙连接状态

// 蓝牙连接
			handleBlueToothLink() {
				let _this = this
				let params = {
					deviceId: _this.deviceId,
					serviceUUID: "", // 主服务的UUID
					notifyUUID: "", // 读
					writeUUID: "", // 写
					successLinkFn: function(res) {
						_this.successLinkFn(res);
					},
					dataChangeFn: function(data, res) {
						_this.dataChangeFn(data, res);
					},
					blueToothLinkStatusFn: function(res) {
						_this.blueToothStatusChange(res);
					},
				};
				_this.blueTooth = new Bluetooth(params);
			},

发送蓝牙指令

在页面上点击按钮,发送对应的蓝牙指令,需要将指令处理为蓝牙协议需要的数据格式。

handlerClick(instruct, dataLen, data) {
	this.blueTooth.send(Util.instructData(instruct, dataLen, data));
},

由于处理蓝牙协议使用的方法很多 ,单独写在了util.js方法中进行调用,包含进制转换,帧头和帧尾的处理,补零,数据处理等方法。(本文中提供ArrayBuffer转换的方法,有需要的话,再提供)。

蓝牙传输需要ArrayBuffer格式的数据

// 将字符串转换成ArrayBufer
string2buffer(str) {
  let val = ""
  if(!str) return;
  let length = str.length;
  let index = 0;
  let array = []
  while(index < length){
    array.push(str.substring(index,index+2));
     index = index + 2;
  }
  val = array.join(",");
  // 将16进制转化为ArrayBuffer
  return new Uint8Array(val.match(/[\da-f]{2}/gi).map(function (h) {
    return parseInt(h, 16)
  })).buffer
},

// 将ArrayBuffer转换成字符串
ab2hex(buffer) {
  var hexArr = Array.prototype.map.call(
    new Uint8Array(buffer),
    function (bit) {
      return ('00' + bit.toString(16)).slice(-2)
    }
  )
  return hexArr.join('');
},

  // ArrayBuffer转字符串
  arrayBufferToString(buffer) {
    return String.fromCharCode.apply(null, new Uint8Array(buffer));
  },

总结

蓝牙方法都是调用的uniapp提供的蓝牙api,按照文档来编写的。
类的封装是为了代码的复用性和组件化。以后再遇到蓝牙需求,可以直接使用BlueTooth的类。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

小程序蓝牙通信 的相关文章

  • Angular UI 模式的范围问题

    我无法理解 使用角度 UI 模式的范围 虽然这里不是很明显 但我已经正确设置了模块和所有内容 据我所知 但这些代码示例尤其是我发现错误的地方 index html 其中重要部分 div class btn group div
  • 将 Javascript 对象的属性从 string 更改为 int

    我有一个对象数组 每个对象具有三个属性 年份 总计 人均 例子 0 Object per capita 125 8 total 1007 2 year 2009 这些属性是字符串 我想创建一个循环来遍历数组并将它们转换为 int 我尝试了以
  • FullCalendar-vue + Typescript:属性“getApi”不存在

    我尝试将 FullCalendar vue 与 Typescript 结合使用 但在访问其 API 时遇到错误 我的日历设置如下
  • 如何删除除任何特定 id 之外的元素

    假设有一个父 id 其中包含许多元素 我想删除除一个元素之外的所有元素 ex parent id children not id n remove
  • 在 Internet Explorer 中使用什么来监视 jscript 内存使用情况

    我们正在调试 GWT 应用程序 在 Firefox 中运行正常 在 IE6 0 中开始运行正常 但一段时间后 它就会崩溃并开始爬行 经过一些测试后 我们怀疑存在一些内存问题 使用了太多内存 内存泄漏等 除了使用taskmanager和pro
  • Vue 3 Composition API 提供/注入在单文件组件中不起作用

    我正在使用 Composition API 在 VueJS 3 中创建一个库 我实现了提供 注入 如中所述docs https v3 vuejs org guide composition api provide inject html i
  • 使用 Angular 指令禁用文本选择

    我正在学习 JavaScript 和 AngularJS 我想使用 Angular Directive 禁用文本选择 我有该函数的 JavaScript 代码 function clearSelection if document sele
  • 如何使用有角度的材料创建卡片网格?

    我正在尝试使用 ng repeat 创建每行三张卡片的网格 我有一个普通的 javascript 对象数组附加到范围 下面的代码将为每张卡创建一个新行 div div
  • 在渲染组件之前从 api 获取数据

    我在渲染页面之前发送 2 个 api 请求 const Profile template profile attributes null photos data function return attributes Profile attr
  • 本地推送通知到在应用程序内运行 JS 代码的 Win8 Live Tile

    我正在尝试将更新发送到我的应用程序的磁贴 当应用程序运行时 这可以正常工作 例如 当用户单击按钮时 我可以轻松地将磁贴更新通知发送到磁贴 我无法解决的是当应用程序无法运行时如何更新磁贴 我找到的唯一选择是使用以下命令从远程 Web 服务器拉
  • ReactTransitionGroup 不适用于 React-redux 连接组件

    我正在开发一个更大的项目 但我创建了这个简短的示例来说明问题 如果我使用Box组件 它的工作原理 它在控制台中输出componentWillEnter and componentWillLeave当我们点击按钮时 如果我使用BoxConta
  • JavaScript 继承;调用和原型

    要在Javascript中实现继承 通常需要执行以下两个步骤 假设我有一个基类 Animal var Animal function name this name name 我现在想从中派生一个子类 Dog 所以我想说 var Dog fu
  • React Router v4 不渲染组件

    React Router v4 渲染组件存在问题 在应用程序初始加载时 它将呈现与 URL 相对应的正确组件 但是 任何后续的组件Link单击不会呈现所需的组件 图书馆 反应路由器 4 2 2 https reacttraining com
  • 如何使用 Javascript 设置查询字符串

    有没有办法使用 javascript 设置查询字符串的值 我的页面有一个过滤器列表 单击该列表时 它将更改右侧的页内结果窗格 我正在尝试更新 url 的查询字符串值 因此如果用户离开页面 然后单击 后退 按钮 他们将返回到最后一个过滤器选择
  • 使用 JavaScript 移动页面上的按钮

    我的按钮可以移动 但奇怪的是 我无法弄清楚偏移是否有问题 我希望我的按钮随着鼠标光标移动 但现在它的移动方式不是我想要的 有时它会消失 另外 创建的新按钮是重叠的 我不知道如何解决这个问题并拥有更好的外观 var coorA var coo
  • 如何始终将焦点保持在文本框中

    我创建了一个包含两个 div 的 HTML 页面 左侧的 div 页面的 90 是 ajax 结果的目标 右侧的 div 页面的 10 包含一个文本框 该页面的想法是在文本框中输入零件编号 通过条形码扫描仪 并显示与该零件编号匹配的绘图 显
  • 如何计算特定字符在字符串中出现的次数

    我正在尝试创建一个函数来查看数组中的任何字符是否在字符串中 如果是 有多少个 我尝试计算每一种模式 但是太多了 我尝试使用 Python 中的 in 运算符的替代方案 但效果不佳 function calc fit element var
  • $resource.query 返回分割字符串(字符数组)而不是字符串

    我正在使用像下面这样的 Angular resource angular module app factory data function resource var Con resource api data update method P
  • 滚动顶部不符合预期

    Note 由于上次忘记奖励而重新开放赏金 A Woff 大师已经给出答案 我想在用户展开某一行时到达该行 这样当最后一个可见行展开时 用户不必向下滚动即可查看内容 I used example tbody on click td green
  • 如何从 json 文件创建模型? (ExtJS)

    这是我想使用 json 文件创建的模型 Ext define Users extend Ext data Model fields name user id type int name user name type string 为了根据服

随机推荐

  • 大型工厂MES管理系统源码

    生产制造业MES管理系统源码 开发环境 ASP NET C VS2010以上 SQL2008R2 C S架构 主要功能 一 用户管理 1 用户登录 2 用户管理 3 操作员录入 4 用户注销 二 系统设置 1 数据库设置 2 机型设置 3
  • vscode中检查代码插件:flake8

    1 命令行安装 pip install flake8 2 在vscode中的用户设置 settings json 中添加以下两行即可 也可以搜索flake8 python linting flake8Enabled true on form
  • BitLocker恢复获取密钥官方操作指南

    下载驱动安装禁用签名 进入Bios一堆骚操作出现下图问题 BitLocker恢复 下面的识别密钥是不能直接用 真正的恢复密钥是一串四十多位的数字序列 第一步 用另一台电脑浏览器进入aka ms myrecoverykey这个网址 就是图里那
  • unity游戏重新开始,退出,停止,继续按钮及打包发布

    一 游戏退出按钮 脚本Out 分为编辑状态下退出和打包编译后退出 这两种都要写上 using System Collections using System Collections Generic using UnityEngine usi
  • Flutter封装自定义按钮(过渡色、水波纹)

    Flutter封装自定义按钮 过渡色 水波纹 全局公用的按钮组件 用的时候不用写一大坨了 直接往里面传值就好了 注意 这里面的ScreenAdapter是我封装的适配方法 用的时候替换为自己的就好了 import package flutt
  • ES的安装使用(windows版)

    前言 Elasticsearch VS Java jdk 版本兼容性 Oracle OpenJDK AdoptOpenJDK 1 8 0 Oracle OpenJDK 9 Oracle OpenJDK 10 Oracle OpenJDK 1
  • HCIP 笔记

    1 交换基础 1 交换机启用过程 RAM 随机存储 当前配置文件 ROM 只读存储 自检程序 NVRAM 非易失性存储 保存配置 Flash 闪存 操作系统 2 交换机 二层基于MAC地址转发设备 依靠MAC地址表进行数据的转发 3 MAC
  • 线程管理之获取和设置线程信息

    获取和设置线程信息 Thread类的对象中保存了一些属性信息能够帮助我们来辨别每一个线程 知道它的状态 调整控制其优先级 这些属性是 ID 每个线程的独特标识 Name 线程的名称 Priority 线程对象的优先级 优先级别在1 10之间
  • Qt实现登录效果(超详细!超简单!)

    title Qt实现登录效果 超详细 超简单 date 2022 8 13 tags 学习 C Qt categories Qt tip 从一个界面按登录跳转到另一个界面 效果图 首先在登录界面输入正确的用户名和密码后点击登录 即可跳到第二
  • Electron项目打包

    文章目录 前置条件 配置 1 在vue config js 文件中增加配置项 2 根目录增加 scripts 文件夹 新建 installer nsi 文件 文件中具体脚本内容 可参考 3 执行打包指令 4 打包成功后会生成exe文件 5
  • 分享35个非常漂亮的单页网站设计案例

    单页网站是指只有一个页面的网站 这种形式的网站曾经非常流行 现在依然有很多人喜欢 不过 并不是每个网站都适合做成单页 一般都是内容比较少而且将来内容也不怎么增加的情况才适合这样做 另外 单页网站有个很大的特点就是导航都很新颖 大多是使用了锚
  • Kendo UI开发教程(3): 初始化Data 属性

    前面在介绍准备Kendo UI开发环境时我们使用jQuery的方法将一个HTML元素转换成一个Kendo UI控件 datepicker kendoDatePicker 除了使用jQuery插件的方法来初始化方法外 每个Kendo 控件还可
  • 序列两两比对算法_学会正确选择多序列比对(coding-sequences)软件

    前几天 实验室的师弟师妹通过本地blast获取一些没有基因组注释物种的蛋白编码序列 原本以为可以快速地进行下一步的选择压力分析 没想到却在多序列比对这一环节出现了棘手的问题 以前 我都是经过PRANK软件进行多序列比对 然后再使用Gbloc
  • TP调试记录

    1 USB tp触摸不准 项目中用到一款触摸屏显示是正常的就是TP不准 触摸的方向也是正确的就是触摸位置不准 屏幕中央触摸位置相对还好 越往边沿越不准 下面是当插上USB tp时抓到了logcat日志 03 06 03 23 18 449
  • 常用DOS命令

    文章目录 一 DOS介绍 二 常用命令 2 1 切换目录 2 2 查看目录 2 3 基本操作 2 4 其他命令 一 DOS介绍 DOS Disk Operating System 磁盘操作系统 二 常用命令 2 1 切换目录 命令 cd 目
  • ai模仿声音软件_浙大学霸本科生顶会研究!只需20分钟音频,AI就能逼真模仿你的声音...

    铜灵 发自 凹非寺量子位 出品 公众号 QbitAI AI精准模仿你的声音 需要多大规模的训练 浙江大学和微软的新研究证明 从0开始学习你的声音到准确逼真 AI只需要200个音频片段和相应标注 20分钟的素材就足够了 并且 这是一种近乎无监
  • 【uni-app教程】四、UniAPP 路由配置及页面跳转

    四 UniAPP 路由配置及页面跳转 1 路由配置 uni app页面路由为框架统一管理 开发者需要在pages json里配置每个路由页面的路径及页面样式 类似小程序在 app json 中配置页面路由一样 所以 uni app 的路由用
  • Qt实现在表格中添加气泡显示功能

    经常在项目中会使用到QTableWidget QTableView QTreeWidget等这类的表格 其中的Item中显示的内容较多 如果设置Item过宽的话 就会导致出现横向滚动条很难看 所以为了解决上述问题 可以采用气泡的形式 当鼠标
  • c语言基础(6)-----逗号运算及数据类型转换

    逗号运算符 表达式1 表达式2 逗号右边的表达式作为运算结果 4 数据类型的转换 4 1 自动类型转换 当用一种数据类型的数据赋值给另一数据类型的变量时 int a 1 1 int i a char 97 char a float f 0
  • 小程序蓝牙通信

    蓝牙通信能力封装 一开始是根据uniapp提供的蓝牙api写的蓝牙方法 之后发现复用性 以及一些状态的监听存在缺陷 之后整理成了类 这样复用性以及状态监听的问题就解决了 蓝牙组件 创建蓝牙组件的类 单例模式是为了保证蓝牙长连接 只有一个蓝牙