uniapp连接蓝牙操作(蓝牙设备地锁)

 介绍:

本文采用uni-app框架来创建一个简单的用户界面,用于搜索、连接和发送命令给蓝牙设备。

1.打开蓝牙适配器 

function openBluetooth() {
	  uni.openBluetoothAdapter({
	    success() {
	      uni.offBluetoothDeviceFound();
	      
	      // 监听新设备发现事件
	      uni.onBluetoothDeviceFound((res) => {
	        res.devices.forEach(device => {
	          if (device.name && device.name.startsWith('one-parking')) {
	            const existingDevice = devicesInfo.value.find(d => d.deviceId === device.deviceId);
	            if (!existingDevice) {
	              devicesInfo.value.push(device);
	            }
	          }
	        });
	      });
	
	      // 开始搜索蓝牙设备
	      uni.startBluetoothDevicesDiscovery({
	        success() {
	          console.log('开始搜索蓝牙设备...');
	          // 设置一个定时器,在一定时间后停止搜索
	          setTimeout(stopDiscovery, 10000);
	        },
	        fail(err) {
	          console.error('开始搜索失败', err);
	        }
	      });
	    },
	    fail() {
	      uni.showToast({
	        title: '请打开蓝牙',
	        icon: 'none'
	      });
	    }
	  });
	}
	

2.连接蓝牙

function connectBluetooth( data : any ) {
		bluetoothInfo.value = data;
		bluetoothInfo.value.deviceId = data.deviceId;
		uni.createBLEConnection({
		  deviceId : data.deviceId,
		  success() {
			// 判断连接的状态和断开重新连接
			checkSttusOrReconnect(data);
			// 获取蓝牙的服务ServiceID
		    uni.getBLEDeviceServices({
			  deviceId : data.deviceId,
			  success(servicesRes : any) {
				if(servicesRes.services.length > 0) {
					bluetoothInfo.value.serviceId = servicesRes.services[0].uuid;
					// 获取蓝牙的特征值characteristicId
					uni.getBLEDeviceCharacteristics({
					  deviceId : data.deviceId,
					  serviceId : servicesRes.services[0].uuid,
					  success(resCharacter) {
						if(resCharacter.characteristics.length > 0) {
							bluetoothInfo.value.characteristics = resCharacter.characteristics;
							// 接受通知
							notify();
							let macAddress = data.name.split('-')[1];
							let password = calculatePassword(macAddress);
							let bondCommand = `phbond${password}`;
							sendMsg(bondCommand);
						}
					  }
					})
				}
			  }
			});
		  }
		})
	}

提示:这里根据蓝牙的设备id连接后,一起获取了服务和特征id,用于方便后面蓝牙进行通信,其中的特征值是一个重点,不同的特征值id会对应不同的操作,不然就会操作不了其他的api,比如uni.notifyBLECharacteristicValueChange(OBJECT)这个就需要notify 或者 indicate 才可以成功调用

3.监听蓝牙的通知

    // 接收蓝牙端发送的通知
	function notify() {
		uni.notifyBLECharacteristicValueChange({
		  state: true,
		  // 这里的 deviceId 需要已经通过 createBLEConnection 与对应设备建立链接
		  deviceId : bluetoothInfo.value.deviceId,
		  serviceId: bluetoothInfo.value.serviceId,
		  characteristicId : bluetoothInfo.value.characteristics.find(item => item.properties.notify).uuid,
		  success() {
			monitorMsg();
		  },
		  fail(res) {
		  	console.log('不支持这个notify' + JSON.stringify(res))
		  }
		})
	}
	
	function monitorMsg() {
		uni.onBLECharacteristicValueChange((res : any) => {
			console.log("发送过来的数据---------", res);
			let resHex = ab2ascii(res.value)
			console.log("转换后的简易数据----------",resHex);
		});
	}

 提示: 刚刚在上面说到的点就是特征值id有多个,需要有对应的特征权限

 4.发送数据

    // 向蓝牙写数据
	function sendMsg( msg : string ) {
		uni.writeBLECharacteristicValue({
			 deviceId : bluetoothInfo.value.deviceId,
			 serviceId: bluetoothInfo.value.serviceId,
			 characteristicId : bluetoothInfo.value.characteristics.find(item => item.properties.write).uuid,
			 value: asciiToArrayBuffer(msg) as any,
			 writeType: 'write',
			 success(res) {
				console.log('消息发送成功', res)
			},
			fail(res) {
				console.log('消息发送失败', res)
			}
		});
	}

注意:其中写数据需要向设备写的是二进制,所以需要转换一下

    // 二进制转化工具
	function ab2ascii(buffer : ArrayBuffer) {
		var str = Array.prototype.map.call(
			new Uint8Array(buffer),
			function(bit : any) {
				return String.fromCharCode(bit);
			}
		)
		return str.join('');
	}
	
	
	function asciiToArrayBuffer(str : string) {
		if (!str) {
			return new ArrayBuffer(0);
		}
		var buffer = new ArrayBuffer(str.length);
		var bufView = new Uint8Array(buffer);
		for (var i = 0, strLen = str.length; i < strLen; i++) {
			bufView[i] = str.charCodeAt(i);
		}
		return buffer;
	 }

5.监听蓝牙的状态并发现断开重连

function checkSttusOrReconnect( data : any ) {
		uni.onBLEConnectionStateChange(function (state) {
			if (!state.connected) {
			  console.warn('与设备' + data.name + '的连接已断开');
			  if(breakCount.value < 2) {
				  reconnectionTimer = setTimeout(()=>{
				  	  connectBluetooth(bluetoothInfo.value);
				  },3000);
			  }
			  breakCount.value += 1;
			} else {
			  console.log('与设备 ' + data.name + ' 的连接已建立');
			  if(reconnectionTimer) { clearTimeout(reconnectionTimer) }
			  breakCount.value = 0;
			}
		});
}

6.完整的demo代码(设备是车辆地锁)

<template>
	<view class="pageBox">
		<view style="font-size: 22px;text-align: center;margin-top: 32px;">
			蓝牙测试用例
		</view>
		<view style="margin: 12px;display: flex;flex-direction: column;">
			<radio-group style="margin: 12px;gap: 26px;display: flex;flex-wrap: wrap;padding: 6px;">
				<radio value="phup" @click="cmdText = 'phup'">phup(升)</radio>
				<radio value="phdown" @click="cmdText = 'phdown'">phdown(降)</radio>
				<radio value="phstatus" @click=" cmdText = 'phstatus'">phstatus(状态)</radio>
			</radio-group>
			<button @click="sendControllCmd()" style="background-color: #564DD6;color: #fff;border-radius: 26px;margin: 12px;">发送指令</button>
		</view>
		<view style="margin: 22px;font-size: 18px;font-weight: bold;color: royalblue;">蓝牙设置信息:</view>
		<view v-for="devices in devicesInfo">
			<view style="justify-content: space-between;border-radius: 8px;display: flex;margin: 12px;margin:12px 22px;background-color: #fff;border: 2px dashed #564DD6;padding: 12px;">
				<view style="display: flex;flex-direction: column;gap: 6px;">
					<view>设备名称:{{devices.name}}</view>
					<view>ID: {{devices.deviceId}}</view>
				</view>
				<view><button style="margin: 6px;border-radius: 6px;background-color: #9A2CD7;color: #fff;" size="mini" @click="connectBluetooth(devices)">连接</button></view>
			</view>
		</view>
		
	</view>
</template>

<script setup lang="ts">
	
	
	import { onReady } from "@dcloudio/uni-app";
	import { ref } from "vue";
	
	onReady(()=>{
		openBluetooth();
	});
	
	
	
	let devicesInfo = ref([]);
	let reconnectionTimer : any;
	let breakCount = ref<number>(0);
	
	
	function openBluetooth() {
	  uni.openBluetoothAdapter({
	    success() {
	      uni.offBluetoothDeviceFound();
	      
	      // 监听新设备发现事件
	      uni.onBluetoothDeviceFound((res) => {
	        res.devices.forEach(device => {
	          if (device.name && device.name.startsWith('one-parking')) {
	            const existingDevice = devicesInfo.value.find(d => d.deviceId === device.deviceId);
	            if (!existingDevice) {
	              devicesInfo.value.push(device);
	            }
	          }
	        });
	      });
	
	      // 开始搜索蓝牙设备
	      uni.startBluetoothDevicesDiscovery({
	        success() {
	          console.log('开始搜索蓝牙设备...');
	          // 设置一个定时器,在一定时间后停止搜索
	          setTimeout(stopDiscovery, 10000);
	        },
	        fail(err) {
	          console.error('开始搜索失败', err);
	        }
	      });
	    },
	    fail() {
	      uni.showToast({
	        title: '请打开蓝牙',
	        icon: 'none'
	      });
	    }
	  });
	}
	
	function stopDiscovery() {
	  uni.stopBluetoothDevicesDiscovery({
	    success() {
	      console.log('停止蓝牙嗅探成功');
	    },
	    fail(err) {
	      console.error('停止蓝牙嗅探失败', err);
	    }
	  });
	}

    
	
	
	// 地锁密码获取: 提取MAC地址的最后6位转换为十进制 , 加上520168 , 取结果的最后六位
	function calculatePassword(mac : string) {
	  let lastSixHex = mac.slice(-6);
	  let hexToInt = parseInt(lastSixHex, 16);
	  let sum = hexToInt + 520168;
	  let finalPassword = ('000000' + sum).slice(-6);
	  return finalPassword;
	}
	
	
	let bluetoothInfo = ref({
		deviceId: '',
		serviceId: '',
		characteristics: [],
		devicesInfo: {} // 连接的蓝牙设备
	});
	function connectBluetooth( data : any ) {
		bluetoothInfo.value = data;
		bluetoothInfo.value.deviceId = data.deviceId;
		uni.createBLEConnection({
		  deviceId : data.deviceId,
		  success() {
			// 判断连接的状态和断开重新连接
			checkSttusOrReconnect(data);
			// 获取蓝牙的服务ServiceID
		    uni.getBLEDeviceServices({
			  deviceId : data.deviceId,
			  success(servicesRes : any) {
				if(servicesRes.services.length > 0) {
					bluetoothInfo.value.serviceId = servicesRes.services[0].uuid;
					// 获取蓝牙的特征值characteristicId
					uni.getBLEDeviceCharacteristics({
					  deviceId : data.deviceId,
					  serviceId : servicesRes.services[0].uuid,
					  success(resCharacter) {
						if(resCharacter.characteristics.length > 0) {
							bluetoothInfo.value.characteristics = resCharacter.characteristics;
							// 接受通知
							notify();
							let macAddress = data.name.split('-')[1];
							let password = calculatePassword(macAddress);
							let bondCommand = `phbond${password}`;
							sendMsg(bondCommand);
						}
					  }
					})
				}
			  }
			});
		  }
		})
	}
	
	
	// 检查设备并重连
	function checkSttusOrReconnect( data : any ) {
		uni.onBLEConnectionStateChange(function (state) {
			if (!state.connected) {
			  console.warn('与设备' + data.name + '的连接已断开');
			  if(breakCount.value < 2) {
				  reconnectionTimer = setTimeout(()=>{
				  	  connectBluetooth(bluetoothInfo.value);
				  },3000);
			  }
			  breakCount.value += 1;
			} else {
			  console.log('与设备 ' + data.name + ' 的连接已建立');
			  if(reconnectionTimer) { clearTimeout(reconnectionTimer) }
			  breakCount.value = 0;
			}
		});
	}
	
	
	// 接收蓝牙端发送的通知
	function notify() {
		uni.notifyBLECharacteristicValueChange({
		  state: true,
		  // 这里的 deviceId 需要已经通过 createBLEConnection 与对应设备建立链接
		  deviceId : bluetoothInfo.value.deviceId,
		  serviceId: bluetoothInfo.value.serviceId,
		  characteristicId : bluetoothInfo.value.characteristics.find(item => item.properties.notify).uuid,
		  success() {
			monitorMsg();
		  },
		  fail(res) {
		  	console.log('不支持这个notify' + JSON.stringify(res))
		  }
		})
	}
	
	function monitorMsg() {
		uni.onBLECharacteristicValueChange((res : any) => {
			console.log("发送过来的数据---------", res);
			let resHex = ab2ascii(res.value)
			console.log("转换后的简易数据----------",resHex);
		});
	}
	
	
	let cmdText = ref('');
	function sendControllCmd() {
		console.log('发送的指令:' + cmdText.value);
		sendMsg(cmdText.value);
	}
	
	// 向蓝牙写数据
	function sendMsg( msg : string ) {
		uni.writeBLECharacteristicValue({
			 deviceId : bluetoothInfo.value.deviceId,
			 serviceId: bluetoothInfo.value.serviceId,
			 characteristicId : bluetoothInfo.value.characteristics.find(item => item.properties.write).uuid,
			 value: asciiToArrayBuffer(msg) as any,
			 writeType: 'write',
			 success(res) {
				console.log('消息发送成功', res)
			},
			fail(res) {
				console.log('消息发送失败', res)
			}
		});
	}
	
	
	// 二进制转化工具
	function ab2ascii(buffer : ArrayBuffer) {
		var str = Array.prototype.map.call(
			new Uint8Array(buffer),
			function(bit : any) {
				return String.fromCharCode(bit);
			}
		)
		return str.join('');
	}
	
	
	function asciiToArrayBuffer(str : string) {
		if (!str) {
			return new ArrayBuffer(0);
		}
		var buffer = new ArrayBuffer(str.length);
		var bufView = new Uint8Array(buffer);
		for (var i = 0, strLen = str.length; i < strLen; i++) {
			bufView[i] = str.charCodeAt(i);
		}
		return buffer;
	 }
	
</script> 

<style scoped lang="scss">
	.pageBox {
		height: 100%;
		width: 100%;
		position: fixed;
	}
</style>
ui界面
UI操作界面

操作台连接和蓝牙通知日志

最后官方文档地址:
Uniapp蓝牙连接文档icon-default.png?t=O83Ahttps://uniapp.dcloud.net.cn/api/system/bluetooth.html

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

《拉依达的嵌入式\驱动面试宝典》—前言目录篇

《拉依达的嵌入式\驱动面试宝典》—前言&目录篇 你好&#xff0c;我是拉依达。 感谢所有阅读关注我的同学支持&#xff0c;目前博客累计阅读 27w&#xff0c;关注1.5w人。其中博客《最全Linux驱动开发全流程详细解析&#xff08;持续更新&#xff09;-CSDN博客》已经是 Lin…

【博弈模型】古诺模型、stackelberg博弈模型、伯特兰德模型、价格领导模型

博弈模型 1、古诺模型&#xff08;cournot&#xff09;&#xff08;1&#xff09;假设&#xff08;2&#xff09;行为分析&#xff08;3&#xff09;经济后果&#xff08;4&#xff09;例题 2、stackelberg博弈模型&#xff08;产量领导模型&#xff09;&#xff08;1&#xff…

如何利用Python爬虫获得1688商品详情

在这个信息爆炸的时代&#xff0c;数据就像是一块块美味的奶酪&#xff0c;而爬虫就是我们手中的瑞士军刀。今天&#xff0c;我要带你一起潜入1688这个巨大的奶酪洞穴&#xff0c;用Python爬虫捞起那些香气四溢的商品详情。别担心&#xff0c;我们的工具箱里有各种各样的工具&a…

blender 制作莫比乌斯带

创建 Curve -> Cycle 在 Edit 模式下&#xff0c;选择&#xff1a; 选中两个点&#xff0c;按 delete 删除 Segment 如下选中&#xff1a; 选中最上面的点&#xff0c;然后按 E 将它拖到右边的点上。 按 R 旋转 90 度。 依次调整参数&#xff1a; 回到 Object 模式下&#x…

《云原生安全攻防》-- K8s安全框架:认证、鉴权与准入控制

从本节课程开始&#xff0c;我们将来介绍K8s安全框架&#xff0c;这是保障K8s集群安全比较关键的安全机制。接下来&#xff0c;让我们一起来探索K8s安全框架的运行机制。 在这个课程中&#xff0c;我们将学习以下内容&#xff1a; K8s安全框架&#xff1a;由认证、鉴权和准入控…

研华运动控制卡 (如PCI1245)单轴编辑路

问题描述: 单轴如何编辑路径&#xff1f; n 问题分析及处理办法– 步骤 在utility软件中&#xff0c;编辑路径和运行路径只能在多轴运动这个界面&#xff0c;而且&#xff0c;使用函数来加载路径Acm_GpLoadPath&#xff0c;也是需要多个轴 ​ 如果只运行一个轴&#xff0c;需…

LM芯片学习

1、LM7805稳压器 https://zhuanlan.zhihu.com/p/626577102?utm_campaignshareopn&utm_mediumsocial&utm_psn1852815231102873600&utm_sourcewechat_sessionhttps://zhuanlan.zhihu.com/p/626577102?utm_campaignshareopn&utm_mediumsocial&utm_psn18528…

ChromeOS 131 版本更新

ChromeOS 131 版本更新 1. ChromeOS Flex 自动注册 在 ChromeOS 131 中&#xff0c;ChromeOS Flex 的自动注册功能现已允许大规模部署 ChromeOS Flex 设备。与 ChromeOS 零接触注册类似&#xff0c;自动注册将通过组织管理员创建的注册令牌嵌入到 ChromeOS Flex 镜像中。这将…

electron打包linux环境

注意:新版的electron已经不支持在win上直接打包Linux的环境了,服务会卡住,会一直生成文件占用磁盘(我发现的时候占了我100G&#xff0c;而且文件夹很深&#xff0c;找了java代码while循环&#xff0c;好不容易删除的o(╥﹏╥)o) electron有一个专门打包的docker镜像&#xff0c…

【SAP FICO】物料分类账详述

系列文章目录 文章目录 系列文章目录前言一、必备基础1、标准价和移动平均价2、概念3、意义4、功能 二、工作原理三、差异的种类与来源1、采用S价可能产生的差异2、单层价格差异和多层价格差异 四、后台配置总结 前言 业务背景&#xff1a;中国会计准则规定&#xff0c;对存货…

电脑文档损坏:原因剖析和修复方法

在使用电脑的过程中&#xff0c;许多用户可能会遇到文档突然提示损坏、无法打开的情况。这种情况的发生往往让人感到困惑&#xff0c;特别是当并未进行任何明显错误操作时。以下是一些常见的原因以及应对方法。 一、文档损坏的常见原因 1、非人为的异常操作&#xff1a; 在编…

使用国内镜像网站在线下载安装Qt(解决官网慢的问题)——Qt

国内镜像网站 中国科学技术大学&#xff1a;http://mirrors.ustc.edu.cn/qtproject/清华大学&#xff1a;https://mirrors.tuna.tsinghua.edu.cn/qt/北京理工大学&#xff1a;http://mirror.bit.edu.cn/qtproject/ 南京大学&#xff1a;https://mirror.nju.edu.cn/qt腾讯镜像&…

活动预告|云原生创新论坛:知乎携手 AutoMQ、OceanBase、快猫星云的实践分享

近年来&#xff0c;云原生技术迅猛发展&#xff0c;成为企业数字化转型的关键动力&#xff0c;云原生不仅极大地提升了系统的灵活性和可扩展性&#xff0c;还为企业带来了前所未有的创新机遇。 12 月 28 日 知乎携手 AutoMQ、OceanBase 和快猫星云推出“云原生创新论坛”主题的…

02-2.python入门语法一变量与数据类型2

四、Python 整数数据类型 &#xff08;一&#xff09;整数的表示方式 1. 十进制表示 十进制是我们在日常生活中最常用的数字表示形式&#xff0c;由 0 到 9 这十个数字排列组合而成。 2. 二进制表示 二进制数由 0 和 1 这两个数字组成&#xff0c;在 Python 中&#xff0c;…

如果在 Swift 数组中寻找最大相邻差值的线性时间算法

文章目录 摘要问题描述解决方案Swift 代码实现代码解析测试用例及结果时间复杂度空间复杂度总结 摘要 本文探讨如何在未排序的数组中&#xff0c;通过线性时间算法找到排序后相邻元素之间的最大差值。我们采用桶排序的思想&#xff0c;给出一个高效的 Swift 实现&#xff0c;并…

EasyExcel 动态设置表格的背景颜色和排列

项目中使用EasyExcel把数据以excel格式导出&#xff0c;其中设置某一行、某一列单元格的背景颜色、排列方式十分常用&#xff0c;记录下来方便以后查阅。 1. 导入maven依赖&#xff1a; <dependency><groupId>com.alibaba</groupId><artifactId>easy…

如何在谷歌浏览器中设置标签页分组

在日常浏览网页时&#xff0c;我们常常会打开多个标签页。随着标签页数量的增加&#xff0c;管理它们变得越来越困难。幸运的是&#xff0c;谷歌浏览器提供了一些实用的功能&#xff0c;可以帮助我们更好地组织和分组标签页。本教程将向您展示如何设置标签页分组&#xff0c;并…

多协议视频监控汇聚/视频安防系统Liveweb搭建智慧园区视频管理平台

智慧园区作为现代化城市发展的重要组成部分&#xff0c;不仅承载着产业升级的使命&#xff0c;更是智慧城市建设的重要体现。随着产业园区竞争的逐渐白热化&#xff0c;将项目打造成完善的智慧园区是越来越多用户关注的内容。 然而我们往往在规划前期就开始面临众多难题&#…

vscode中同时运行两个python文件(不用安装插件)

如何在vscode中同时运行两个python文件呢&#xff1f;今天在工作中遇到了这个问题。 查了网上的方法是安装coder runner插件&#xff0c;后来发现自身就有这个功能。所以记录一下,方便后续查找: 这是我的第一个文件&#xff0c;点击右上角的运行旁边的小箭头&#xff0c;有一…

java全栈day17--Web后端实战(java操作数据库)

前言&#xff1a;本章应该是针对数据库基础讲解&#xff0c;数据的增删改查但是本人忘记对知识进行归纳总结就直接跳过&#xff0c;基本的内容都很简单&#xff0c;都是套式子使用。现在开始学习本章&#xff0c;很重要需要好好掌握。 一、使用的工具 二、JDBC 2.1概述 JDBC …