uniapp小程序连接蓝牙设备

news/2024/9/13 0:36:00/ 标签: uni-app, 小程序

uniapp小程序连接蓝牙设备

      • 一、初始化蓝牙模块
      • 二、开始搜索
      • 三、连接蓝牙
      • 四、监听特征值变化
      • 五、调用示例
      • utils.js文件

一、初始化蓝牙模块

这一步是必须的,在开发项目过程中,初始化蓝牙模块之后,紧接着就要开启一些监听的api,供后续设备读写数据时监听变化。

initBLE(callback) {//提前定义变量,直接在uni的api中使用this是不可以的var self = this;// `第一步:初始化蓝牙模块`uni.openBluetoothAdapter({success: function (e) {utils.toast("初始化蓝牙适配器成功");},fail: function (e) {utils.toast("初始化蓝牙适配器失败 : " + JSON.stringify(e));},});// `检查蓝牙适配器状态变化`// `这一步主要是为了在连接成功蓝牙之后关闭蓝牙搜索,蓝牙搜索非常耗费性能`uni.onBluetoothAdapterStateChange(function (e) {self.bluetooth.available = e.available;if (!e.available) {utils.toast("蓝牙适配器不可用");if (self.bluetooth.startDiscovery) {self.bluetooth.startDiscovery = false;self.stopBluetoothDevicesDiscovery();}}});// `监听搜索到设备`uni.onBluetoothDeviceFound(function (e) {// `当搜索到设备后,可以在e.devices中获取到设备信息列表`if (!e.devices) return;for (var i = 0; i < e.devices.length; i++) {var device = e.devices[i];for (var j = 0; j < self.discoveryList.length; j++) {var item = self.discoveryList[j];// 去重if (item.deviceId === device.deviceId) {return;}}self.discoveryList.push(device);}});// `监听蓝牙设备连接变化`uni.onBLEConnectionStateChange(function (e) {// 该方法回调中可以用于处理连接意外断开等异常情况self.currentDeviceStatus = e.connected ? 1 : 2;// 在连续盘点过程中是不允许断开的,万一因为其他什么原因断开了,则尝试停止连续盘点if (!e.connected && self.inventoryLabelForm.looping) {self.sendStopLoopInventoryCmd(); //停止盘点}});// `读数据`uni.onBLECharacteristicValueChange(function (e) {if (e.deviceId != self.currentDevice.deviceId) return;var value = self.buffer2Hex(e.value);console.log(e); //查看是否有R20固定返回值utils.toast("读数据178:" + JSON.stringify(e));self.lastRcvData += value;self.doRcvData();});self.historyList = utils.getHistoryList();// 如果有cb回调函数,则需要停止下拉刷新if (callback) {// setTimeout这里等待一下上面的异步初始化setTimeout(() => {callback();}, 1000);}},

二、开始搜索

以上只是蓝牙的初始化操作,要想真正实现蓝牙连接从第二步开始

// 开启蓝牙搜索服务startBluetoothDevicesDiscovery() {var self = this;// 小程序环境需要等待一下,不然会报错setTimeout(() => {// 开始搜寻附近的蓝牙外围设备uni.startBluetoothDevicesDiscovery({allowDuplicatesKey: false,success: function (e) {self.bluetooth.discoverying = true;//开启搜索成功,此时初始化中定义的onBluetoothDeviceFound会自动执行},fail: function (e) {utils.toast("开始搜索蓝牙设备失败 : " + JSON.stringify(e));},});}, 1000);},

三、连接蓝牙

目前为止我们已经搜索到蓝牙设备了,下面要做的就是在蓝牙列表中选择对应的蓝牙设备进行连接

// 连接设备onConnectDevice(device) {// `device是选择的设备对象,在这可以取到设备的deviceId供后续使用`var self = this;this.onCloseDiscoveryDialog();this.onCloseHistoryDialog();this.currentDevice = device;this.currentDeviceStatus = 3;this.lastRcvData = "";// 创建一个BLE连接uni.createBLEConnection({deviceId: device.deviceId,//这里使用设备id来创建连接success: function (e) {// 创建完成后获取服务(这在ios中是必须的,否则会导致后面读取数据失败)uni.getBLEDeviceServices({// 这里的 deviceId 需要已经通过 createBLEConnection 与对应设备建立链接deviceId: device.deviceId,success(res) {// 获取特征值(这在ios中是必须的,否则会导致后面读取数据失败)uni.getBLEDeviceCharacteristics({// 这里的 deviceId 需要已经通过 createBLEConnection 与对应设备建立链接deviceId: device.deviceId,// 这里的 serviceId 需要在 getBLEDeviceServices 接口中获取serviceId: RX_SERVICE_UUID, //因为这里使用的是固定服务所以事先定义过常量了,这里直接使用success(res) {// 创建连接-获取服务-获取特征值之后就可以开启nofity功能了self.enableBleNotify(device);},fail() {},});},});// 开启nofity功能self.enableBleNotify(device);},fail: function (e) {utils.toast("连接蓝牙设备失败 : " + JSON.stringify(e));self.currentDeviceStatus = 2;},});// 保存设备到历史记录里var flag = true;for (var i = 0; i < this.historyList.length; i++) {var item = this.historyList[i];if (item.deviceId === device.deviceId) {this.historyList[i] = device;flag = false;}}if (flag) {this.historyList.push(device);}utils.saveHistoryList(this.historyList);},

四、监听特征值变化

 enableBleNotify(device) {// 这里监听时,ios测试需要有点延迟,安卓的话,只在测试机上测试过,不需要延迟// 这里延迟1秒兼容,反正差距不大setTimeout(function () {uni.notifyBLECharacteristicValueChange({state: true,deviceId: device.deviceId,serviceId: RX_SERVICE_UUID,characteristicId: R_UUID,success: function (e) {//到了此时蓝牙才算真正的连接成功},fail: function (e) {},});}, 1000);},

五、调用示例

这里以单次盘点为例

 // 单步盘点标签async  onOneStepInventory() {var self = this;if (!this.checkDeviceConnect()) return;this.inventoryLabelForm.startTime = utils.currentTimeMillis(); //获取盘点时间的// 单步盘点关闭enableRssithis.inventoryLabelForm.enableRssi = false;// 发送固定值await this.sendR20Hex(); //这里是定制需要没有可以省去var cmd = this.generateUHFProtocl(0x80, utils.number2Hex(5000, 2));self.sendData(cmd);},//发送固定值async sendR20Hex() {await this.sendData("A55A0029F011D75238EE46C3ECFFCE3B9AFC093ACC13F711A6ADF3FF76ACE59A8DF1BA704E22EC0D0A");},//写入操作(发送数据,连续存盘指令自行实现了,不调用该方法)async sendData(hexStr, serviceId, characteristicId) {if (utils.isBlank(hexStr)) return;serviceId = serviceId || RX_SERVICE_UUID;characteristicId = characteristicId || W_UUID;var self = this;var sendData;// ble发送数据时,需要分包,最多20字节if (hexStr.length > 40) {sendData = hexStr.substr(0, 40);hexStr = hexStr.substr(40, hexStr.length - 40);} else {sendData = hexStr;hexStr = null;}this.logSend(sendData);var buffer = new ArrayBuffer(sendData.length / 2);var bufView = new Uint8Array(buffer);for (var i = 0; i < sendData.length; i += 2) {bufView[i / 2] = parseInt(sendData.substr(i, 2), 16);}//这里写了一个promise是为了在执行正常操作之前,先执行固定值操作return new Promise((resolve, reject) => {uni.writeBLECharacteristicValue({deviceId: self.currentDevice.deviceId,serviceId: serviceId,characteristicId: characteristicId,value: buffer,success: async function (e) {if (hexStr) {//超过20字节 递归循环写入await self.sendData(hexStr);}console.log("写入数据成功391" + sendData);// 由于写字节限制,如果还有未发送完的数据,接着继续发送resolve();},fail: function (e) {},});});},

utils.js文件

这里主要存放一些转换方法,上面用到的utils.xx方法可以在此处查看

export default {alert: function (content, title = "提示") {uni.showModal({title,content,showCancel: false,success: function (res) {},});},confirm: function (content, title = "提示") {uni.showModal({title,content,success: function (res) {if (res.confirm) {console.log("用户点击确定");} else if (res.cancel) {console.log("用户点击取消");}},});},toast: function (title, duration = 1000) {uni.showToast({title,icon: "none",duration,});},showWaiting: function (title = "加载中") {uni.showLoading({title,});},closeWaiting: function () {uni.hideLoading();},actionSheet: function (itemList) {uni.showActionSheet({itemList,success: function (res) {console.log("选中了第" + (res.tapIndex + 1) + "个按钮");},fail: function (res) {console.log(res.errMsg);},});},saveHistoryList: function (list) {try {uni.setStorageSync("history_list", JSON.stringify(list));} catch (e) {// error}},getHistoryList: function () {try {const value = uni.getStorageSync("history_list");if (value) {return JSON.parse(value);} else {return [];}} catch (e) {// error}},clearHistoryList: function () {try {uni.removeStorageSync("history_list");} catch (e) {// error}},leftPad: function (value, length, fill) {while (length - value.length > 0) {value = fill + value;}return value;},number2Hex: function (value, byteLength) {value = parseInt(value);var hex = value.toString(16).toUpperCase();byteLength = byteLength || hex.length / 2 + (hex.length % 2);return this.leftPad(hex, byteLength * 2, "0");},bin2Hex: function (value, byteLength) {byteLength = byteLength || 1;byteLength = Math.max(value.length / 8 + (value.length % 8 > 0 ? 1 : 0),byteLength);value = this.leftPad(value, byteLength * 8, "0");var hex = "";for (var i = 0; i < value.length; i += 8) {hex += this.number2Hex(parseInt(value.substr(i, 8), 2), 1);}return hex;},isNull: function (value) {if (value == null) return true;if ("undefined" == typeof value) return true;return false;},isString: function (value) {return "[object String]" === Object.prototype.toString.call(value);},isBlank: function (value) {if (this.isNull(value)) return true;if (!this.isString(value)) return true;if (value.trim().length == 0) return true;return false;},str2Hex: function (str) {var hex = "";for (var i = 0; i < str.length; i++) {hex += this.number2Hex(str.charCodeAt(i), 1);}return hex;},currentTimeMillis: function () {return new Date().getTime();},hex2ByteArray: function (hex) {var result = [];for (var i = 0; i < hex.length; i += 2) {var value = parseInt(hex.substr(i, 2), 16);if (value > 127) {value -= 0x100;}result.push(value);}return result;},/*** hex转字符串* @param {Object} hex* @param {Object} charset	编码格式,默认为utf-8*/hex2Str: function (hex, charset = "utf-8") {var bytesArray = this.hex2ByteArray(hex);try {var str;// 创建一个TextDecoder对象,指定所需的编码格式const decoder = new TextDecoder(charset);// 将ByteArray转换为字符串str = decoder.decode(bytesArray);return str;} catch (e) {this.toast("错误的编码格式");}},
};

http://www.ppmy.cn/news/1475345.html

相关文章

Python高级(四)_内存管理

Python高级-内存管理 第四章 内存管理 1、对象池 小整数池 系统默认创建好的,等着你使用 概述:整数在程序中的使用非常广泛,Python为了优化速度,使用了小整数对象池,避免为整数频繁申请和销毁内存空间。Python 对小整数的定义是 [-5, 256] ,这些整数对象是提前建立好的…

AI推介-大语言模型LLMs论文速览(arXiv方向):2024.07.01-2024.07.05

文章目录&#xff5e; 1.LLM Internal States Reveal Hallucination Risk Faced With a Query2.Fine-Tuning with Divergent Chains of Thought Boosts Reasoning Through Self-Correction in Language Models3.Investigating Decoder-only Large Language Models for Speech-t…

TCP网络传输控制协议

目录 什么是TCP TCP的特点 TCP通信步骤 三次握手&#xff08;建立连接&#xff09; 数据传输 四次挥手&#xff08;连接释放&#xff09; 为什么要进行三次握手&#xff1f;两次握手行不行&#xff1f;一次握手行不行&#xff1f; 为什么是四次挥手&#xff1f;三次、两…

小红书后端

牛客网 - 找工作神器|笔试题库|面试经验|实习招聘内推&#xff0c;求职就业一站解决_牛客网 (nowcoder.com) 小红书推荐系统 题目&#xff1a; 思路&#xff1a; 尝试&#xff08;标题4&#xff09; import java.util.*;// 注意类名必须为 Main, 不要有任何 package xxx 信息…

社交论坛圈子系统APP开发社交圈子小程序系统源码开源,带语音派对聊天室/圈子社交论坛及时聊天

功能// 首页左右滑动切换分类 使用资讯类app常见的滑动切换分类&#xff0c;让用户使用更方便。 2信息卡片流展示 每条信息都是一个卡片&#xff0c;头像展示会员标签&#xff0c;单图自动宽度&#xff0c;多图九宫格展示&#xff0c;底部展示信息发布地址&#xff0c;阅读量、…

react VS vue

什么是react 用于构建用户界面的js库 什么是vue 用于构建用户界面的渐进式js库 react是基于mvvm设计还是mvc设计 1、react是基于mvc设计的&#xff0c;但也不是完整的mvc模式 2、react把自己定义为view 3、组件逻辑定义为Controller层 4、虽然react不是完整的mvc模式&a…

iPhone删除所有照片的高效三部曲

苹果手机用久了&#xff0c;系统缓存包括自己使用手机留下的内存肯定会越来越多。其中&#xff0c;相册中的照片数量可能会急剧增加&#xff0c;占据大量的存储空间。当用户们想要对相册进行彻底清理&#xff0c;实现iPhone删除所有照片时&#xff0c;不妨跟随以下详细的三部曲…

音频合并最简单的方法。将音频合并成一个的几种简单方法

音频合并最简单的方法。音乐是一种无声的语言&#xff0c;能够诉说情感、唤起记忆&#xff0c;为我们的生活增添了丰富的色彩。有时候&#xff0c;我们渴望将几段动人的音乐融为一体&#xff0c;创造出一个更为宏大、更为连贯的作品&#xff0c;以满足独特的需求或展示我们的创…

代码覆盖率的守护者:Gradle中配置覆盖率工具全指南

代码覆盖率的守护者&#xff1a;Gradle中配置覆盖率工具全指南 在软件开发的精细打磨中&#xff0c;代码覆盖率工具是确保质量的得力助手。Gradle&#xff0c;作为一款强大的构建工具&#xff0c;提供了与多种代码覆盖率工具的集成能力。本文将详细指导如何在Gradle中配置代码…

Python学习2---装饰器

装饰器是Python中的一种高级功能&#xff0c;允许您修改或增强函数或类的行为&#xff0c;而无需直接修改其源代码。 基本概念&#xff1a; 装饰器本质上是一个函数&#xff0c;它接受一个函数作为参数&#xff0c;并返回一个新的函数。 基本语法&#xff1a; decorator_fun…

中介者模式(大话设计模式)C/C++版本

中介者模式 C // 中介者模式(Mediator),用一个中介对象来封装一系列的对象交互。中介者使各对象不需要显示得相互引用&#xff0c;从而使得其耦合松散&#xff0c;而且可以独立地改变他们之间得交互#include <iostream> #include <string>using namespace std;#d…

需求实现:字数限制500字

摘要&#xff1a; 在浩瀚的技术实现海洋中&#xff0c;每一个需求都如同一块磁石&#xff0c;吸引着多样化的解决策略与技术手段的汇聚。然而&#xff0c;无论选择多么丰富&#xff0c;核心始终在于对业务逻辑的深刻理解&#xff0c;以及为当前特定场景量身定制的最佳实践。本…

使用YOLO5进行模型训练机器学习【教程篇】

准备工作 YOLOv5 是目前非常流行的目标检测模型,广泛应用于各种计算机视觉任务,可以检测到图片中的具体目标。 我们借助开源的模型训练框架,省去了自己写算法的步骤,有技术的伙伴可以深入了解并自己写算法训练。 电脑要求: GPU ,内存 >12G python > =3.8 windows o…

抗量子密码算法:保障未来信息安全的新盾牌

随着量子计算的迅猛发展&#xff0c;传统加密算法正面临着前所未有的挑战。量子计算机利用量子比特的特殊性质&#xff0c;能在极短时间内破解目前广泛使用的公钥加密体系&#xff0c;如RSA、ECC等。这使得我国及全球的信息安全体系遭受严重威胁。为了应对这一挑战&#xff0c;…

STM32智能门禁系统教程

目录 引言环境准备智能门禁系统基础代码实现&#xff1a;实现智能门禁系统 4.1 数据采集模块 4.2 数据处理与控制模块 4.3 通信与网络系统实现 4.4 用户界面与数据可视化应用场景&#xff1a;门禁管理与优化问题解决方案与优化收尾与总结 1. 引言 智能门禁系统通过STM32嵌入…

5G与未来通信技术

随着科技的迅猛发展&#xff0c;通信技术也在不断演进。5G技术作为第五代移动通信技术&#xff0c;已成为现代通信技术的一个重要里程碑。本文将详细介绍5G及其对未来通信技术的影响&#xff0c;重点探讨超高速互联网和边缘网络的应用。 一、超高速互联网 1. 低延迟 5G技术最显…

富格林:正确击破暗箱稳健出金

富格林悉知&#xff0c;现在喜欢投资的人都在不断增加&#xff0c;在享受贵金属投资较高出金回报的同时&#xff0c;也该做好遭遇暗箱亏损情况的预防措施。对于新手投资者群体来说&#xff0c;如何客观地认识造成亏损的原因和暗箱&#xff0c;也是贵金属投资道路上必须克服的一…

GPT-4o在Excel的应用

引言 随着人工智能技术的不断进步&#xff0c;GPT-4o已成为许多领域中的重要工具。特别是在数据处理和分析方面&#xff0c;GPT-4o的强大功能可以大大提升工作效率&#xff0c;解放用户从繁琐的公式和手动操作中。本文将详细介绍如何利用GPT-4o在Excel中完成常见但复杂的任务&…

性能监控的革命:Eureka引领分布式服务监控新纪元

性能监控的革命&#xff1a;Eureka引领分布式服务监控新纪元 引言 在微服务架构中&#xff0c;服务的分布式性能监控对于维护系统健康和优化用户体验至关重要。Eureka作为Netflix开源的服务发现框架&#xff0c;为服务的注册与发现提供了强大支持&#xff0c;而结合其他工具&…

【RNN练习】天气预测

&#x1f368; 本文为&#x1f517;365天深度学习训练营 中的学习记录博客&#x1f356; 原作者&#xff1a;K同学啊 一、环境及数据准备 1. 我的环境 语言环境&#xff1a;Python3.11.9编译器&#xff1a;Jupyter notebook深度学习框架&#xff1a;TensorFlow 2.15.0 2. 导…