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 允许您在隔离中挂载组件,这意…...

【力扣数据库知识手册笔记】索引
索引 索引的优缺点 优点1. 通过创建唯一性索引,可以保证数据库表中每一行数据的唯一性。2. 可以加快数据的检索速度(创建索引的主要原因)。3. 可以加速表和表之间的连接,实现数据的参考完整性。4. 可以在查询过程中,…...

Day131 | 灵神 | 回溯算法 | 子集型 子集
Day131 | 灵神 | 回溯算法 | 子集型 子集 78.子集 78. 子集 - 力扣(LeetCode) 思路: 笔者写过很多次这道题了,不想写题解了,大家看灵神讲解吧 回溯算法套路①子集型回溯【基础算法精讲 14】_哔哩哔哩_bilibili 完…...

聊聊 Pulsar:Producer 源码解析
一、前言 Apache Pulsar 是一个企业级的开源分布式消息传递平台,以其高性能、可扩展性和存储计算分离架构在消息队列和流处理领域独树一帜。在 Pulsar 的核心架构中,Producer(生产者) 是连接客户端应用与消息队列的第一步。生产者…...

Opencv中的addweighted函数
一.addweighted函数作用 addweighted()是OpenCV库中用于图像处理的函数,主要功能是将两个输入图像(尺寸和类型相同)按照指定的权重进行加权叠加(图像融合),并添加一个标量值&#x…...
连锁超市冷库节能解决方案:如何实现超市降本增效
在连锁超市冷库运营中,高能耗、设备损耗快、人工管理低效等问题长期困扰企业。御控冷库节能解决方案通过智能控制化霜、按需化霜、实时监控、故障诊断、自动预警、远程控制开关六大核心技术,实现年省电费15%-60%,且不改动原有装备、安装快捷、…...
【Go】3、Go语言进阶与依赖管理
前言 本系列文章参考自稀土掘金上的 【字节内部课】公开课,做自我学习总结整理。 Go语言并发编程 Go语言原生支持并发编程,它的核心机制是 Goroutine 协程、Channel 通道,并基于CSP(Communicating Sequential Processes࿰…...
Robots.txt 文件
什么是robots.txt? robots.txt 是一个位于网站根目录下的文本文件(如:https://example.com/robots.txt),它用于指导网络爬虫(如搜索引擎的蜘蛛程序)如何抓取该网站的内容。这个文件遵循 Robots…...
【决胜公务员考试】求职OMG——见面课测验1
2025最新版!!!6.8截至答题,大家注意呀! 博主码字不易点个关注吧,祝期末顺利~~ 1.单选题(2分) 下列说法错误的是:( B ) A.选调生属于公务员系统 B.公务员属于事业编 C.选调生有基层锻炼的要求 D…...

IoT/HCIP实验-3/LiteOS操作系统内核实验(任务、内存、信号量、CMSIS..)
文章目录 概述HelloWorld 工程C/C配置编译器主配置Makefile脚本烧录器主配置运行结果程序调用栈 任务管理实验实验结果osal 系统适配层osal_task_create 其他实验实验源码内存管理实验互斥锁实验信号量实验 CMISIS接口实验还是得JlINKCMSIS 简介LiteOS->CMSIS任务间消息交互…...

RSS 2025|从说明书学习复杂机器人操作任务:NUS邵林团队提出全新机器人装配技能学习框架Manual2Skill
视觉语言模型(Vision-Language Models, VLMs),为真实环境中的机器人操作任务提供了极具潜力的解决方案。 尽管 VLMs 取得了显著进展,机器人仍难以胜任复杂的长时程任务(如家具装配),主要受限于人…...