Uni-app实现蓝牙搜索、连接、传送数据实例
·
<template>
<!-- 自定义导航栏 -->
<view class="bluetooth">
<view class="navBarBox">
<!-- 状态栏占位 -->
<view class="statusBar" :style="{ paddingTop: statusBarHeight + 'px' }"></view>
<!-- 真正的导航栏内容 -->
<view class="navBar">
<view class="titlefont" style="margin-left: 10px;">通信设备</view>
</view>
</view>
<!-- 发送数据 -->
<view class="send">
<view class="uni-textarea">
<view @tap="send_data" class="fasong">发送</view>
<view class="">
蓝牙返回的数据为:
</view>
</view>
</view>
<!-- 没有匹配的蓝牙设备 -->
<view class="no_match_bluetooth_list" v-if="no_match_list.length!=0">
<view class="h1">附近蓝牙设备</view>
<scroll-view scroll-y="100%">
<flex v-for="(item,index) in no_match_list" :key="index" >
<view class="uni-list" @tap="print(item.SN)">
<view class="uni-list-cell" hover-class="uni-list-cell-hover">
<view class="uni-list-cell-navigate uni-navigate-right uni-media-list ">
<view class="uni-media-list-body">
<image src="@/static/img/lanya.png" class="lanyaimg" mode="scaleToFill"></image>
<view class="lanySn"> {{item.name}} </view>
</view>
</view>
</view>
</view>
</flex>
</scroll-view>
</view>
<view>
<popuphelp :helpshowModal="helpshowPay" @helpclose="oncloseahelp"></popuphelp>
</view>
<view class="btn">
<view class="btn_1" @tap="open_bluetooth()">帮 助</view>
<view class="btn_3" @tap="search_bluetooth()">刷 新</view>
</view>
</view>
</template>
<script>
import popuphelp from "@/pages/main/popuphelp.vue"
import blueTool from '../../common/dataFormatUtil.js';
import uTool from '../../common/buletoothUtil.js'
var main, Context, BluetoothManager, BluetoothAdapter, BManager, BAdapter,BluetoothDevice,IntentFilter,bluetoothSocket,device,_self;
export default {
components:{
popuphelp
},
data() {
return {
bArray:[],//用于搜索蓝牙去重用的
no_match_list:[],//没有配对的蓝牙列表
send_data_onoff:false,
send_data_list:'',//要发送的数据
statusBarHeight: 0,// 状态栏高度
helpshowPay: false,
show: false,
timer: null,
BDeviceList:[],
fa: '',
shou: '',
serviceId: '', //蓝牙服务 uuid
connectionid: '', //设备的 id
characteristicid: '', //特征
sheb: "",
mList:[],//搜索到的设备列表
mDeviceId:0,//连接的设备
targetNumber:'',//要连接的指定设备
};
},
onLoad() {
//获取手机状态栏高度
this.statusBarHeight = uni.getSystemInfoSync()['statusBarHeight'];
//获取android应用Activity对象
main = plus.android.runtimeMainActivity();
Context = plus.android.importClass("android.content.Context");
BManager = main.getSystemService(Context.BLUETOOTH_SERVICE);
//蓝牙适配器
BluetoothAdapter = plus.android.importClass("android.bluetooth.BluetoothAdapter");
//蓝牙本地适配器
BAdapter = BluetoothAdapter.getDefaultAdapter();
//蓝牙设备
BluetoothDevice = plus.android.importClass('android.bluetooth.BluetoothDevice');
//过滤器
IntentFilter = plus.android.importClass('android.content.IntentFilter');
//判断蓝牙是否开启
if (!BAdapter.isEnabled()) {
BAdapter.enable();//启动蓝牙
//在页面加载时候初始化蓝牙适配器
uni.openBluetoothAdapter({
success: e => {
this.$data.isOpenBle = true;
},
fail: e => {
console.log('初始化蓝牙失败,错误码:' + (e.errCode || e.errMsg));
}
});
}
self = this;
self.search_bluetooth()
},
mounted(){
//监听本机蓝牙状态
uni.onBluetoothAdapterStateChange(function(res) {
// console.log('adapterState changed, now is', res)
})
//监听搜索设备
uni.onBluetoothDeviceFound(devices => {
let arr={
"name": devices.devices[0].name,
"SN": devices.devices[0].deviceId
}
var name=arr.name+arr.SN;
if(arr.name!=""&&arr.name!=null&&arr.name!=undefined){
if(this.bArray.indexOf(name) == -1){ //去重
this.bArray.push(name)
this.no_match_list.push(arr)
this.mList.push(devices.devices[0])
}
}
})
this.onBLEConnectionStateChange()
this.onBLECharacteristicValueChange()
},
methods:{
open_bluetooth(){
this.oncloseahelp()
this.helpshowPay = true //传给子组件动态控制显示隐藏
},
oncloseahelp(show){
this.helpshowPay = show;
},
onBLECharacteristicValueChange() {
uni.onBLECharacteristicValueChange(function(res) {
console.log(`监听低功耗蓝牙设备的特征值变化事件 ${res.characteristicId} has changed, now is ${res.value}`)
})
},
onBLEConnectionStateChange() {
uni.onBLEConnectionStateChange(res => {
// 该方法回调中可以用于处理连接意外断开等异常情况
if (res.connected == false) {
console.log(`device ${res.deviceId} state has changed, connected: ${res.connected}`)
}
})
},
//搜索蓝牙设备
search_bluetooth(){
//判断蓝牙是否开启
if (!BAdapter.isEnabled()) {
BAdapter.enable();//启动蓝牙
}
/* 1、初始化蓝牙 uni.openBluetoothAdapter(OBJECT) */
uni.openBluetoothAdapter({
success: e => {
/* 初始化蓝牙成功 */
/* 2、开始搜索蓝牙设备 uni.startBluetoothDevicesDiscovery(OBJECT */
uni.startBluetoothDevicesDiscovery({
allowDuplicatesKey:false,//是否允许同一设备多次上报,但RSSI值会有所不同
success: (res) => {
console.log("搜索成功")
},
fail: (res) => {
console.log("搜索失败")
}
})
this.$data.isOpenBle = true;
},
fail: e => {
console.log('初始化蓝牙失败,错误码:' + (e.errCode || e.errMsg));
}
});
setTimeout(() => {
uni.startBluetoothDevicesDiscovery({
// services: ['FEE7'],
// allowDuplicatesKey:true,
success: resd => {
//to do
},
fail: errd => {
//to do
}
})
}, 1000)
},
/**
* 根据蓝牙地址,连接设备
* @param {Object} address
* @return {Boolean}
*/
print(deviceId){
uni.showModal({
title: '提示',
content: '是否连接此蓝牙?',
success: function (res) {
//1.创建蓝牙连接
uni.createBLEConnection({
deviceId: deviceId,
timeout:5000,
success: res => {
uni.showLoading({
title: '蓝牙连接中...'
});
self.mDeviceId = deviceId;
self.getBLEDeviceServices(deviceId)
//2.创建蓝牙连接成功后关闭蓝牙搜索
uni.stopBluetoothDevicesDiscovery({
success(resds) {
// 打开输入发送数据
this.send_data_onoff=true;
}
})
},
fail: err => {
console.log("连接失败")
console.log(error);
}
})
}
});
},
/* 获取所有的服务(目前理解来说应该是进入服务) */
getBLEDeviceServices(deviceId){
setTimeout(()=>{
uni.getBLEDeviceServices({
// 这里的 deviceId 需要已经通过 createBLEConnection 与对应设备建立链接
deviceId,
success: (res) => {
res.services.forEach((item)=>{
let serviceId = item.uuid;
if(serviceId.includes("0000FFE0")&&item.isPrimary){
console.log('serverId:',serviceId)
/* 进入特征值 */
self.getBLEDeviceCharacteristics(deviceId,serviceId);
}
})
},
fail(res) {
console.log("获取服务失败")
console.log(error)
}
})
},1000)
},
/* 进入特征值 */
getBLEDeviceCharacteristics(deviceId,serviceId){
var flag = true;
setTimeout(()=>{
uni.getBLEDeviceCharacteristics({
// 这里的 deviceId 需要已经通过 createBLEConnection 与对应设备建立链接
deviceId,
// 这里的 serviceId 需要在 getBLEDeviceServices 接口中获取
serviceId,
success: res1 => {
res1.characteristics.forEach((item)=>{
let characteristicId = item.uuid;
if (item.properties.write&&item.properties.notify&&flag) {
self.mDeviceId = deviceId
self.serviceId = serviceId
self.characteristicId = characteristicId
self.notifyBLECharacteristicValueChange(deviceId,serviceId,characteristicId)
flag = false;
}
})
},
fail: (error) => {
console.log("获取服务失败")
console.log(error)
}
})
},1000)
},
notifyBLECharacteristicValueChange(deviceId,serviceId,characteristicId){
uni.notifyBLECharacteristicValueChange({
state: true, // 启用 notify 功能
// 这里的 deviceId 需要已经通过 createBLEConnection 与对应设备建立链接
deviceId,
// 这里的 serviceId 需要在 getBLEDeviceServices 接口中获取
serviceId,
// 这里的 characteristicId 需要在 getBLEDeviceCharacteristics 接口中获取
characteristicId,
success:(res)=> {
/* 连接成功 */
uni.hideLoading();
uni.showToast({
duration:3000,
title:"连接成功",
icon:'success'
});
// uni.navigateTo({
// url: '/pages/home/home'
// })
},
fail:(res)=> {
uni.hideLoading();
console.log('notifyBLECharacteristicValueChange success', res.errMsg)
}
})
},
/* 连接成功以后,执行设置的操作,并且打开返回值的监听开关并监听 */
send_data() {
/* 获取所有的服务 */
/* 每次设置钱都将notify打开(设备回复开关) */
// let tokenArr = '01030080000C4427';
let tokenArr = '0103011000404403';
let arrayBuffer = blueTool.string2buffer(tokenArr)
/* 测试发现如果不做分包发送的话,还是会有问题,所以这边还是需要做分包发送 */
self.writeDevice(arrayBuffer);
/* 这边对特征值2进行数据的监听 */
let resultAll="";
uni.onBLECharacteristicValueChange(function(CALLBACK){
let result = blueTool.ab2hex(CALLBACK.value);
resultAll += result;
console.log("接收报文:",resultAll)
})
},
/* 执行演示分包操作 */
writeDevice(_Buffer){
let Num = 0;
let ByteLength = _Buffer.byteLength;
let i = 1;
while (ByteLength > 0) {
i++;
let TmpBuffer;
if(ByteLength > 20){
TmpBuffer = _Buffer.slice(Num, Num + 20);
Num += 20;
ByteLength -= 20;
console.log('执行常规循环')
uTool.delayed(500).then(()=>{
uni.writeBLECharacteristicValue({
deviceId:self.mDeviceId,
serviceId:self.serviceId,
characteristicId:self.characteristicId,
value: TmpBuffer,
success: (res) => {
/* 发送成功 */
console.log('前面的循环成功', res)
},
fail: (error) => {
/* 发送失败 */
console.log('前面的循环失败',error)
}
})
})
}else{
console.log('执行最后一次')
TmpBuffer = _Buffer.slice(Num, Num + ByteLength)
Num += ByteLength
ByteLength -= ByteLength
/* 当长度不满20的时候执行最后一次发送即可 */
uni.writeBLECharacteristicValue({
deviceId:self.mDeviceId,
serviceId:self.serviceId,
characteristicId:self.characteristicId,
value: TmpBuffer,
success: (res) => {
/* 发送成功 */
console.log('最后一次写入成功', res)
},
fail: (error) => {
/* 发送失败 */
console.log('最后一次写入失败',error)
}
})
}
}
}
}
}
</script>
<style lang="scss">
.lanySn{
margin-left: 20rpx;
}
.uni-media-list-body{
margin-top: 10rpx;
margin-left: 15rpx;
display: flex;
}
.lanyaimg{
width: 50rpx;
height: 50rpx;
}
.bluetooth{
.navBarBox .navBar {
display: flex;
flex-direction: row;
justify-content: flex-start;
align-items: center;
background: #333878;
height: 92rpx;
line-height: 80rpx;
font-size: 36rpx;
color: #FFFFFF;
}
.h1{
padding: 20upx 0;color: #1482D1;border-top: 1upx solid #EFEFF4;
}
.btn{
display:flex;position:fixed;
bottom:0upx;
justify-content:space-around;
width: 100%;
color:#fff;
background-color: #fff;
view{
width: 49%;
height: 110upx;
line-height:110upx;
text-align: center;
}
.btn_1{
font-size: 36rpx;
background-color: #4768A9;
}
.btn_3{
font-size: 36rpx;
background-color: #02238E;
}
}
.uni-textarea{
textarea{
border: 1px solid #EFEFF4;border-radius: 10px;width: 100%;padding: 10upx;box-sizing: border-box;
}
.fasong{
width: 100%;padding:15upx 10upx;box-sizing: border-box;border-radius: 10px;background: #1482D1;text-align: center;color: white;margin: 10px 0;
}
}
}
</style>
蓝牙通信工具 buletoothUtil.js
export default{
/* 蓝牙通信工具 */
getError(errorCode){
if(errorCode == 0) return "OK";
if(errorCode == 10000) return "未初始化蓝牙适配器";
if(errorCode == 10001) return "当前蓝牙适配器不可用";
if(errorCode == 10002) return "没有找到指定设备";
if(errorCode == 10003) return "连接失败";
if(errorCode == 10004) return "没有找到指定服务";
if(errorCode == 10005) return "没有找到指定特征值";
if(errorCode == 10006) return "当前连接已断开";
if(errorCode == 10007) return "当前特征值不支持此操作";
if(errorCode == 10008) return "其余所有系统上报的异常";
if(errorCode == 10009) return "Android 系统特有,系统版本低于 4.3 不支持 BLE";
return "未知异常";
},
/* 异步执行,延时函数 */
delayed(ms,res){
return new Promise((resolve,reject) =>{
setTimeout(function(){
resolve(res);
},ms)
});
}
}
数据格式的转换工具类dataFormatUtil.js
/* 数据格式的转换工具类 */
export default{
/* 字符串转arraybuffer */
string2buffer: function(str) {
// 首先将字符串转为16进制
let val = str
// 将16进制转化为ArrayBuffer
return new Uint8Array(val.match(/[\da-f]{2}/gi).map(function(h) {
return parseInt(h, 16)
})).buffer
},
ab2hex(buffer) {
const hexArr = Array.prototype.map.call(
new Uint8Array(buffer),
function(bit) {
return ('00' + bit.toString(16)).slice(-2)
}
)
return hexArr.join('')
},
/* arraybuffer 转字符串 */
bufferString: function(str) {
// ArrayBuffer转16进度字符串示例
function ab2hex(buffer) {
const hexArr = Array.prototype.map.call(
new Uint8Array(buffer),
function(bit) {
return ('00' + bit.toString(16)).slice(-2)
}
)
return hexArr.join('')
}
//16进制
let systemStr = ab2hex(str)
// console.log(hexCharCodeToStr(systemStr),99)
function hexCharCodeToStr(hexCharCodeStr) {
var trimedStr = hexCharCodeStr.trim();
var rawStr = trimedStr.substr(0, 2).toLowerCase() === "0x" ?trimedStr.substr(2):trimedStr;
var len = rawStr.length;
if (len % 2 !== 0) {
alert("Illegal Format ASCII Code!");
return "";
}
var curCharCode;
var resultStr = [];
for (var i = 0; i < len; i = i + 2) {
curCharCode = parseInt(rawStr.substr(i, 2), 16); // ASCII Code Value
let str5 = String.fromCharCode(curCharCode)
if (str5.startsWith('\n') == false) {
resultStr.push(String.fromCharCode(curCharCode));
}
}
return resultStr.join("");
}
// console.log(hexCharCodeToStr(systemStr),888)
return hexCharCodeToStr(systemStr)
},
}
弹出框:蓝牙设置页面 popuphelp.vue
<template>
<view class="payment">
<u-popup v-model="show" mode="center" :show='show' :mask-close-able='false'>
<view class="popup-project">
<view class="massage">
<view class="left">
<image src="@/static/img/tixing.png" class="tixingimg"></image>
</view>
<view class="right">
</view>
</view>
<view class="buttonBox">
<button class="btn1" plain="true" @tap="okPopup">设置蓝牙</button>
<button class="btn2" plain="true" @tap="cancelPopup">关闭</button>
</view>
</view>
</u-popup>
</view>
</template>
<script>
export default {
name: "alzPopup",
data(){
return{
show: false, //弹出框显示状态
}
},
props: {
helpshowModal: {
type: Boolean,
default: ''
}
},
watch: {
helpshowModal(val) {
this.show = val;
}
},
onLoad(){},
methods:{
//取消
cancelPopup() {
this.show = false
this.$emit('helpclose', this.show)
},
//蓝牙设置
okPopup() {
var main = plus.android.runtimeMainActivity();
var Intent = plus.android.importClass("android.content.Intent");
var mIntent = new Intent('android.settings.BLUETOOTH_SETTINGS');
main.startActivity(mIntent);
},
}
}
</script>
<style lang="scss" scoped>
.popup-project {
background: #fff;
width: 600rpx;
position: relative;
box-shadow: 0 0 20rpx #5d5d5d;
.massage{
display:flex;
width: 100%;
height: 900rpx;
.left{
width: 15%;
background: #01ABF1;
text-align: center;
.tixingimg{
width: 50rpx;
height: 50rpx;
margin-top: 20rpx;
}
}
.right{
width: 85%;
.fonth4{
font-size: 42rpx;
margin-top: 28rpx;
margin-left: 20rpx;
}
.p1{
margin-top: 30rpx;
margin-left: 20rpx;
}
}
}
.buttonBox {
display:flex;
bottom:0upx;
justify-content:space-around;
width: 100%;
color:#fff;
background-color: #fff;
border-radius:0px;
.btn1 {
width: 49.7%;
color: #FFFFFF;
background: #5169a9;
height: 110upx;
line-height:110upx;
text-align: center;
border-radius:0rpx;
margin-left: 0rpx;
}
.btn2 {
width: 49.7%;
color: #FFFFFF;
background: #0b2385;
height: 110upx;
line-height:110upx;
text-align: center;
border-radius:0px;
margin-right:0rpx;
}
}
}
</style>
DAMO开发者矩阵,由阿里巴巴达摩院和中国互联网协会联合发起,致力于探讨最前沿的技术趋势与应用成果,搭建高质量的交流与分享平台,推动技术创新与产业应用链接,围绕“人工智能与新型计算”构建开放共享的开发者生态。
更多推荐

所有评论(0)