uni-app 蓝牙传输
https://www.cnblogs.com/ckfuture/p/16450418.html
https://www.cnblogs.com/yangxiaobai123/p/16021058.html
字符串转base64:https://www.cnblogs.com/sunny3158/p/17312158.html
将 ArrayBuffer 对象转成 Base64 字符串:基础 - uni.arrayBufferToBase64 - 《uni-app API 文档》 - 书栈网 · BookStack
ArrayBuffer:https://zhuanlan.zhihu.com/p/655456833
最近在做一个项目,要求app通过蓝牙连接设备并将报文传输至设备中,在这个过程踩过了几个坑,总结如下:
根据uni-app官网API主要涉及到“蓝牙”和“低功耗蓝牙”两个部分。
主要步骤:
步骤1:初始化蓝牙模块 openBluetoothAdapter
$openBluetoothAdapter(){uni.openBluetoothAdapter({success: (res) => {//初始化成功,搜索设备console.log('openBluetoothAdapter success', res);uni.showLoading({title: '搜索设备中'});setTimeout(()=>{this.baseList=[];//每次扫码清空设备列表,不然会导致重复this.$startBluetoothDevicesDiscovery();//扫码蓝牙设备},100);//定时关闭搜索设备setTimeout(()=>{this.$stopBluetoothDevicesDiscovery();uni.hideLoading();},10*1000);},fail: (res) => {uni.showToast({title: '请打开蓝牙',duration: 1000});if (res.errCode === 10001) {//监听蓝牙适配器状态变化事件uni.onBluetoothAdapterStateChange(function(res){console.log('onBluetoothAdapterStateChange', res);if (res.available) {//开始扫描this.$startBluetoothDevicesDiscovery()}})}}})},
步骤2:搜索附近的蓝牙外围设备 startBluetoothDevicesDiscovery
//2.搜索附近的蓝牙外围设备$startBluetoothDevicesDiscovery(){if (this._discoveryStarted) {return;}this._discoveryStarted = true;//开始搜寻附近的蓝牙外围设备uni.startBluetoothDevicesDiscovery({allowDuplicatesKey: true,success: (res) => {console.log('startBluetoothDevicesDiscovery success', res);//监听寻找到新设备的事件this.$onBluetoothDeviceFound()},fail:err=>{console.error("startBluetoothDevicesDiscoveryErr",err);uni.showToast({icon:'none',duration:2000,title:"请检查蓝牙状态",});}})},
步骤3:监听寻找到新设备 onBluetoothDeviceFound
$onBluetoothDeviceFound(){let that =this;//监听寻找到新设备的事件uni.onBluetoothDeviceFound(function(res){res.devices.forEach(device => {if (!device.name && !device.localName) {return;}//添加蓝牙设备列表let oneBluetooth={deviceId: device.deviceId,name: device.name,RSSI:device.RSSI,localName:device.localName} //判断是否存在if(that.bluetoothIndex.indexOf(device.deviceId) ==-1){that.baseList.push(oneBluetooth);that.bluetoothIndex.push(device.deviceId);}//如果名字相同连接设备//if(device.name == devicename){//$createBLEConnection(device.deviceId);//}})})},
步骤4:创建连接蓝牙事件 createBLEConnection
$createBLEConnection(deviceId){let that=this;//1)判断设备是否处于连接状态if(that.Connecting){uni.showToast({icon:'none',duration:2000,title:"设备已连接",});return} //2)创建连接uni.showLoading({title: '设备连接中',mask:true,});uni.createBLEConnection({deviceId:deviceId,success: (res) => {that._deviceId = deviceId;//不延迟造成获取不到服务!!!!,我走过的坑!!!setTimeout(function() {//获取设备的蓝牙服务that.$getBLEDeviceServices(deviceId);//关闭等待提示setTimeout(function () {uni.hideLoading();}, 100);}, 7000);},fail: (err) =>{console.log(err);}});//3)设置MTU,否则传输报文不全,默认是23bytes,但是蓝牙本身是需要3bytes,故而只能传输20bytessetTimeout(()=>{console.log('deviceId>>>',deviceId);uni.setBLEMTU({deviceId:deviceId,mtu:255,success:(res)=>{console.log('设置MTU最大值成功',res);},fail:(res)=>{console.log('设置MTU最大值失败',res);}});},9000);//4)关闭搜索this.$stopBluetoothDevicesDiscovery();},
步骤5:获取蓝牙设备的所有服务 getBLEDeviceServices
$getBLEDeviceServices(deviceId){//获取蓝牙设备所有服务(service)uni.getBLEDeviceServices({deviceId,success: (res) => {console.log('250res.services>>>',res.services);for (let i = 0; i < res.services.length; i++) {if (res.services[i].isPrimary) {this.$getBLEDeviceCharacteristics(deviceId, res.services[i].uuid);return;}}}});},
步骤6:获取蓝牙设备某个服务中所有特征值(characteristic)getBLEDeviceCharacteristics
$getBLEDeviceCharacteristics(deviceId,serviceId){let that = this;//获取蓝牙设备某个服务中所有特征值(characteristic)。uni.getBLEDeviceCharacteristics({deviceId,serviceId,success: (res) => {console.log('288getBLEDeviceCharacteristics success', res.characteristics);for (let i = 0; i < res.characteristics.length; i++) {let item = res.characteristics[i]//if (item.properties.read) {//读取低功耗蓝牙设备的特征值的二进制数据值。1uni.readBLECharacteristicValue({deviceId,serviceId,characteristicId: item.uuid,})//}if (item.properties.write) {this._deviceId = deviceId;this._serviceId = serviceId;this._characteristicId = item.uuid;//写入请求数据this.$writeBLECharacteristicValue(); }//if (item.properties.notify || item.properties.indicate) {//启用低功耗蓝牙设备特征值变化时的 notify 功能,订阅特征值。uni.notifyBLECharacteristicValueChange({deviceId,serviceId,characteristicId: item.uuid,state: true,success(res) {// console.log('notifyBLECharacteristicValueChange success:' + res.errMsg);// console.log(JSON.stringify(res));uni.onBLECharacteristicValueChange(characteristic => {console.log('监听低功耗蓝牙设备的特征值变化事件成功>>');//将字节转换成16进制字符串var data = ab2hex(characteristic.value)//将16进制转成字符串let newdataStr = Buffer.from(data,'hex');//先把数据存在buf里面//that.btvalue=newdataStr.toString("utf-8");//使用toString函数就能转换成字符串//设备返回SNthat.SN=newdataStr.toString("utf-8");console.log('设备返回SN>>>',that.SN);//根据返回的数据处理设备类型和显示的输入区域-----------------that.getEquType(that.SN);});}});//}}},fail(res) {console.error('getBLEDeviceCharacteristics', res)}})},
步骤7:向蓝牙设备发送一个16进制数据
$writeBLECharacteristicValue(){let buffer = new ArrayBuffer(1)let dataView = new DataView(buffer);dataView.setUint8(0, 0);//0x61 | 0uni.writeBLECharacteristicValue({deviceId: this._deviceId,serviceId: this._serviceId,//"0000FE61-0000-1000-8000-00805F9B34FB",characteristicId: this._characteristicId,value: buffer,success: function(res){console.log('350',res);},fail: function(res){console.log(res);}})},
步骤8:向蓝牙设备发送字符串数据 writeBLECharacteristicValueString
$writeBLECharacteristicValueString(str){// 发送方式一let buffer = new ArrayBuffer(str.length);let dataView = new DataView(buffer);for (let i in str) {dataView.setUint8(i, str[i].charCodeAt() | 0); //打印二进制字节//console.log('dataView.getUint8(i)>>',dataView.getUint8(i));}//延迟发送指令setTimeout(()=>{uni.writeBLECharacteristicValue({deviceId: this._deviceId,serviceId: this._serviceId,characteristicId: this._characteristicId,//"0000FE61-0000-1000-8000-00805F9B34FB",//,value: buffer,success: function(res){console.log("命令写入成功",res); },fail: function(res){console.error("命令写入失败",res);}});},2000);},
步骤9:关闭搜索 stopBluetoothDevicesDiscovery
$stopBluetoothDevicesDiscovery(){//关闭搜索uni.stopBluetoothDevicesDiscovery({success(res) {this._discoveryStarted=false;}})},
步骤10:断开蓝牙设备连接 closeBLEConnect
$closeBLEConnect(deviceId){uni.closeBLEConnection({deviceId,success:res=>{this.deviceId = "";console.log("断开成功",res);},fail:err=>{console.error("断开错误",err);}})},
需要注意的是:
1)和蓝牙设备通讯不能太频繁。
2)写入数据时候要注意特性值UUID。
3)写入数据超过20bytes时候需要设置MTU。否则传输的内容将被截取,造成传输信息不完整。
相关文章:
uni-app 蓝牙传输
https://www.cnblogs.com/ckfuture/p/16450418.html https://www.cnblogs.com/yangxiaobai123/p/16021058.html 字符串转base64:https://www.cnblogs.com/sunny3158/p/17312158.html 将 ArrayBuffer 对象转成 Base64 字符串:基础 - uni.arrayBufferT…...
MBR10200CT-ASEMI智能AI应用MBR10200CT
编辑:ll MBR10200CT-ASEMI智能AI应用MBR10200CT 型号:MBR10200CT 品牌:ASEMI 封装:TO-220 批号:最新 恢复时间:35ns 最大平均正向电流(IF):10A 最大循环峰值反向…...
力扣 爬楼梯
动态规划算法基础篇。 class Solution {public int climbStairs(int n) {int[] f new int[n 1];f[0] 1;f[1] 1;//当爬到n阶楼梯时,可知是由n-1阶或n-2阶楼梯而来for(int i 2; i < n; i) {f[i] f[i - 1] f[i - 2];//后面的每一阶种数由前两个状态得到}ret…...
java设计模式之:策略模式+工厂模式整合案例实战(一)
本文介绍项目中常用的策略模式工厂模式的案例,该案例是针对策略类比较少的情况;下一篇会讲解策略类比较多的案例,下面直接开始: 案例1:项目中对系统中的客户和销售进行事件通知(短信、邮件、钉钉) 首先要有通知的策略…...
国内Ubuntu安装 stable-diffusion教程,换成国内镜像
安装依赖: 首先更新系统并安装Python 3.10和pip: sudo apt update sudo apt install python3.10 python3-pip 设置Python虚拟环境(可选): 安装Python虚拟环境管理工具,并创建激活虚拟环境: su…...
JAVA final详细介绍
一、介绍 final 中文意思: 最后的,最终的. final 可以修饰类、属性、方法和局部变量, 在某些情况下,程序员可能有以下需求,就会使用到final: 1)当不希望类被继承时,可以用final修饰。 //如果我们要求A类不能被其他类继承 //可以使用fin…...
45、tomcat+课后实验
tomcat 1、tomcat tomcat和php一样,都是用来处理动态页面的。 tomcat也可以作为web应用服务器,开源的。 php .php tomcat .jsp nginx .html tomcat 是用Java代码写的程序,运行的是Java的web应用程序。 tomcat的特点和功能:…...
设计模式的七大原则
1.单一职责原则 单一职责原则(Single responsibility principle),即一个类应该只负责一项职责。如类A负责两个不同职责:职责1,职责2。当职责1需求变更而改变A时,可能造成职责2执行错误,所以需要将类A的粒度分解为A1、…...
ThreeJS-3D教学十五:ShaderMaterial(noise、random)
ThreeJS-3D教学十四:ShaderMaterial(length、fract、step) 上面这篇主要是操作 fragmentShader 片元着色器,实现对物体颜色的修改,这次咱们来看下修改 vertexShader 顶点着色器,这个其实就是位移各个顶点的位置。 接下来我们先介绍下 noise 噪声函数(Perlin Noise、Sim…...
LeetCode 2974.最小数字游戏:排序+交换奇偶位
【LetMeFly】2974.最小数字游戏:排序交换奇偶位 力扣题目链接:https://leetcode.cn/problems/minimum-number-game/ 你有一个下标从 0 开始、长度为 偶数 的整数数组 nums ,同时还有一个空数组 arr 。Alice 和 Bob 决定玩一个游戏ÿ…...
使用vllIm部署大语言模型
使用vllm部署大语言模型一般需要以下步骤: 一、准备工作 1. 系统要求 - 操作系统:常见的 Linux 发行版(如 Ubuntu、CentOS)或 Windows(通过 WSL)。 - GPU 支持:NVIDIA GPU 并安装了适当的驱动程…...
静态搜索iOS动态链接函数的调用位置
静态搜索iOS动态链接函数的调用位置 可执行文件格式mach-O,是在苹果的操作系统 macOS 和 iOS 上使用的一种二进制文件格式。 在一些iOS安全扫描中,可能存在需要获取函数具体调用位置的需求,能指导用户更精确的定位漏洞。 现在以NSLog函数为例ÿ…...
【鸿蒙学习笔记】尺寸设置・layoutWeight・对子组件进行重新布局
官方文档:尺寸设置 目录标题 layoutWeight:对子组件进行重新布局 layoutWeight:对子组件进行重新布局 设置了layoutWeight属性的子元素与兄弟元素占主轴尺寸按照权重进行分配,忽略元素本身尺寸设置。 // 引入包名 import { http…...
vue实现表单输入框数字类型校验功能
vue实现表单输入框数字类型校验功能 1. 样式代码 <el-form-item label"订单总价"><el-input size"small" v-model"form.totalPrice" placeholder"请输入订单总价 正整数或者2位数小数" input"check(form.totalPric…...
JS登录页源码 —— 可一键复制抱走
前期回顾 https://blog.csdn.net/m0_57904695/article/details/139838176?spm1001.2014.3001.5501https://blog.csdn.net/m0_57904695/article/details/139838176?spm1001.2014.3001.5501 登录页预览效果 <!DOCTYPE html> <html lang"en"><head…...
Kithara与OpenCV (一)
Kithara使用 OpenCV 库 目录 Kithara使用 OpenCV 库简介需求和支持的环境构建 OpenCV 库使用 CMake 进行配置以与 Kithara 一起工作 使用 OpenCV 库设置项目运行 OpenCV 代码图像采集和 OpenCV自动并行化限制和局限性1.系统建议2.实时限制3.不支持的功能和缺失的功能4.显示 Ope…...
什么是软件定义安全SDSec
一、软件定义安全SDSec产生的背景 软件定义安全(Software Defined Security,SDSec)的产生背景主要源于传统网络安全防护方法在面对复杂网络环境时的不适应性,以及软件定义网络(SDN)技术的发展和应用。 SD…...
【C语言】C语言可以做什么?
目录 1. 操作系统开发1.1 操作系统内核1.2 设备驱动程序1.3 系统工具和实用程序 2. 嵌入式系统2.1 微控制器编程2.2 传感器和执行器控制2.3 消费电子产品 3. 应用程序开发3.1 图形用户界面应用3.2 游戏开发3.3 多媒体处理 4. 网络编程4.1 网络协议实现4.2 服务器和客户端程序4.…...
WordPress 主题技巧:给文章页增加“谁来过”模块。
模块功能: 我个人目前在做一个电影类的网站,在开发文章页的模版时候,突然觉得给文章页增加一个“谁对本电影感兴趣”的功能模块可能会比较有趣,这个功能有点类似于‘足迹’的感觉,用户可以通过这个功能,发…...
【vue组件库搭建07】Vitest单元测试
vitest官网 vue-test-utils 我们的测试框架选择的是 Vitest 和 vue-test-utils。两者的关系为: Vitest 提供测试方法:断言、Mock 、SpyOn 等方法。vue-test-utils: 挂载和渲染组件: Vue Test Utils 允许您在隔离中挂载组件,这意…...
使用docker在3台服务器上搭建基于redis 6.x的一主两从三台均是哨兵模式
一、环境及版本说明 如果服务器已经安装了docker,则忽略此步骤,如果没有安装,则可以按照一下方式安装: 1. 在线安装(有互联网环境): 请看我这篇文章 传送阵>> 点我查看 2. 离线安装(内网环境):请看我这篇文章 传送阵>> 点我查看 说明:假设每台服务器已…...
MySQL 隔离级别:脏读、幻读及不可重复读的原理与示例
一、MySQL 隔离级别 MySQL 提供了四种隔离级别,用于控制事务之间的并发访问以及数据的可见性,不同隔离级别对脏读、幻读、不可重复读这几种并发数据问题有着不同的处理方式,具体如下: 隔离级别脏读不可重复读幻读性能特点及锁机制读未提交(READ UNCOMMITTED)允许出现允许…...
解决本地部署 SmolVLM2 大语言模型运行 flash-attn 报错
出现的问题 安装 flash-attn 会一直卡在 build 那一步或者运行报错 解决办法 是因为你安装的 flash-attn 版本没有对应上,所以报错,到 https://github.com/Dao-AILab/flash-attention/releases 下载对应版本,cu、torch、cp 的版本一定要对…...
Python如何给视频添加音频和字幕
在Python中,给视频添加音频和字幕可以使用电影文件处理库MoviePy和字幕处理库Subtitles。下面将详细介绍如何使用这些库来实现视频的音频和字幕添加,包括必要的代码示例和详细解释。 环境准备 在开始之前,需要安装以下Python库:…...
华硕a豆14 Air香氛版,美学与科技的馨香融合
在快节奏的现代生活中,我们渴望一个能激发创想、愉悦感官的工作与生活伙伴,它不仅是冰冷的科技工具,更能触动我们内心深处的细腻情感。正是在这样的期许下,华硕a豆14 Air香氛版翩然而至,它以一种前所未有的方式&#x…...
Python ROS2【机器人中间件框架】 简介
销量过万TEEIS德国护膝夏天用薄款 优惠券冠生园 百花蜂蜜428g 挤压瓶纯蜂蜜巨奇严选 鞋子除臭剂360ml 多芬身体磨砂膏280g健70%-75%酒精消毒棉片湿巾1418cm 80片/袋3袋大包清洁食品用消毒 优惠券AIMORNY52朵红玫瑰永生香皂花同城配送非鲜花七夕情人节生日礼物送女友 热卖妙洁棉…...
【从零学习JVM|第三篇】类的生命周期(高频面试题)
前言: 在Java编程中,类的生命周期是指类从被加载到内存中开始,到被卸载出内存为止的整个过程。了解类的生命周期对于理解Java程序的运行机制以及性能优化非常重要。本文会深入探寻类的生命周期,让读者对此有深刻印象。 目录 …...
Java数值运算常见陷阱与规避方法
整数除法中的舍入问题 问题现象 当开发者预期进行浮点除法却误用整数除法时,会出现小数部分被截断的情况。典型错误模式如下: void process(int value) {double half = value / 2; // 整数除法导致截断// 使用half变量 }此时...
Vite中定义@软链接
在webpack中可以直接通过符号表示src路径,但是vite中默认不可以。 如何实现: vite中提供了resolve.alias:通过别名在指向一个具体的路径 在vite.config.js中 import { join } from pathexport default defineConfig({plugins: [vue()],//…...
解析奥地利 XARION激光超声检测系统:无膜光学麦克风 + 无耦合剂的技术协同优势及多元应用
在工业制造领域,无损检测(NDT)的精度与效率直接影响产品质量与生产安全。奥地利 XARION开发的激光超声精密检测系统,以非接触式光学麦克风技术为核心,打破传统检测瓶颈,为半导体、航空航天、汽车制造等行业提供了高灵敏…...
