实现了开启蓝牙——搜索附近蓝牙——获取对应读写特征值服务——断开蓝牙全过程。
<template>
<view class='content_box'>
<view class="pay_box">
<button class="btn" type="primary" @click="initBlue">开启蓝牙</button>
<!-- <button class="btn" type="primary" @click="stopBluetooth">停止搜索外围</button> -->
<button class="btn" type="primary" @click="findBlue">搜寻附近蓝牙</button>
</view>
<view>
<scroll-view scroll-y class="box">
<view class="itemContent" v-for="(item,index) in bleDevs" :key='index' @click="connetBlue(item)">
<view>
<text>设备名称: {{ item.name }}</text>
</view>
<view>
<text>设备id: {{ item.deviceId }}</text>
</view>
</view>
</scroll-view>
</view>
</view>
</template>
<script>
export default {
data() {
return {
bleDevs: [],//蓝牙列表
}
},
onload() {
// 初始化蓝牙设备
// this.initBlue();
},
methods: {
// 初始化蓝牙设备
initBlue() {
let vm = this;
uni.openBluetoothAdapter({ // 初始化蓝牙、判断是否开启蓝牙
success: function(res) {
vm.getState();
},
fail: res => {
uni.showModal({
title: '提示',
content: '蓝牙初始化失败,请到设置打开蓝牙',
showCancel: false
})
}
})
},
//获取本机蓝牙适配器状态
getState() {
uni.getBluetoothAdapterState({ //获取本机蓝牙适配器状态
success(res) {
uni.showModal({
title: '提示',
content: '蓝牙初始化成功,获取本机蓝牙适配器状态成功',
showCancel: false
})
},
fail(err) {
uni.showModal({
title: '提示',
content: '查看手机蓝牙是否打开',
showCancel: false
})
}
})
},
// 停止搜索外围蓝牙设备
// stopBluetooth() {
// uni.stopBluetoothDevicesDiscovery({
// success: e => {
// uni.showModal({
// title: '提示',
// content: e.errMsg,
// showCancel: false
// })
// },
// fail: err => {
// console.log("停止搜索失败",err);
// }
// });
// },
//获取蓝牙设备信息
findBlue() {
var vm = this;
uni.showLoading({
title: '正在加载',
icon: 'loading',
});
uni.startBluetoothDevicesDiscovery({
interval: 0, //上报设备的间隔
allowDuplicatesKey: false,//是否允许重复上报
success: (res) => {
//获取所有的蓝牙设备列表
vm.getBlue();
},
fail: err => {
uni.showToast({
title: '搜索蓝牙设备失败',
icon: 'none',
duration: 1000
})
}
})
},
// 获取设备列表
getBlue() {
var vm = this;
setTimeout(() => {
uni.hideLoading();
uni.getBluetoothDevices({
success: function(res) {
// 过滤掉name为未知设备的设备
var bluetoothArr = [];
res.devices.forEach((obj) => {
if(obj.name !== "未知设备") {
bluetoothArr.push(obj);
}
})
vm.bleDevs = bluetoothArr;
},
fail: function() {
uni.showToast({
title: '搜索蓝牙设备失败',
icon: 'none',
duration: 1000
})
},
});
},2000)
},
// 连接蓝牙设备
connetBlue(item) {
// uni.setStorageSync("name", item);
// // 跳转到成功页面
// uni.navigateTo({
// url:'/pages/blueSuccess/blueSuccess',
// })
let vm = this;
uni.showLoading({
title: "连接中,请稍等",
mask: true,
});
uni.createBLEConnection({
deviceId: item.deviceId, //设备的 id
success(res) {
console.log("连接蓝牙成功",res);
uni.setStorageSync("name", item);
uni.hideLoading();
uni.showToast({
title: item.name + "蓝牙连接成功",
icon: "none",
duration: 1000
});
// 跳转到成功页面
uni.navigateTo({
url:'/pages/blueSuccess/blueSuccess',
})
},
fail(res) {
uni.hideLoading();
uni.showToast({
title: item.name + "蓝牙连接失败",
icon: "none",
});
}
});
},
}
}
</script>
<style lang="scss" scoped>
.blueList {
width: 100%;
height: 100px;
background-color: #ffd5de;
}
.itemContent{
width: 80%;
height: 60px;
border-radius: 10px;
background-color: #e0ffff;
text-align: center;
margin: 15px auto;
line-height: 30px;
}
.content_box {
width: 100%;
.pay_box {
display: flex;
flex-wrap: wrap;
justify-content: space-evenly;
margin-top: 30rpx;
}
.btn {
background-color: #007aff;
padding: 20rpx;
box-sizing: border-box;
font-size: 26rpx;
border-radius: 60rpx;
width: 28%;
margin-top: 10px;
}
}
</style>
<template>
<view>
<view class="pay_box">
<!-- <button class="btn" type="primary" @click="connetBlue">连接蓝牙</button> -->
<button class="btn" type="primary" @click="getServiceId">获取服务</button>
<button class="btn" type="primary" @click="closeBlue">断开蓝牙</button>
</view>
<bushOver v-if='showBtn' @childEvent="getWrite" ></bushOver>
<view class='displayFrame'>
<view>蓝牙表号:{{ servicesDataName }}</view>
<scroll-view scroll-y="true" class="scroll-Y" >
<view class="dataWidth">{{ ServerSuccess }} </view>
<view class="dataWidth">{{ stateSuccess }} </view>
<view class="dataWidth">{{ notifyStateSuccess }}</view>
<view class="dataWidth">{{ writeStateSuccess }}</view>
<view class="dataWidth">{{ cardInfo }}</view>
<view class="dataWidth">{{ agoData }}</view>
<view class="dataWidth">{{ closeBlueData }}</view>
</scroll-view>
</view>
</view>
</template>
<script>
import bushOver from '../component/bushOver.vue'
export default {
components: {
bushOver,
},
data() {
return {
ServicesData: {}, //传过来的deviceId
servicesDataName:'',
getCharateristicId: '', //特征值的charateristicId(uuid)
getServiceIdData:'', // 服务的serviceId(uuid)
hexStringData: '',
showBtn:false, //抄读和充值是否显示
stateSuccess:'', //特征值数据
notifyStateSuccess:'', //notify数据
writeStateSuccess:'',//写入数据
cardInfo:'', //解析后的结果
ServerSuccess:'', //获取服务成功数据
agoData:'',//接收的原始数据
closeBlueData:'',//断开蓝牙
closeNotify:true,//关闭监听
errCode: {
10000: '未初始化蓝牙适配器',
10001: '当前蓝牙适配器不可用',
10002: '没有找到指定设备',
10003: '连接失败',
10004: '没有找到指定服务',
10005: '没有找到指定特征值',
10006: '当前连接已断开',
10007: '当前特征值不支持此操作',
10008: '其余所有系统上报的异常',
10009: 'Android 系统特有,系统版本低于 4.3 不支持 BLE',
10010: '已连接',
10011: '配对设备需要配对码',
10012: '连接超时',
10013: '连接 deviceId 为空或者是格式不正确',
}
}
},
onLoad(options) {
// 接收跳转传递的参数
let vm = this;
this.ServicesData = uni.getStorageSync('name');
const name = uni.getStorageSync('name').name;
this.servicesDataName = name.replace(/[a-zA-Z]/g, '');
// 充值成功后调用写入函数
if(options.source === 'pay') {
uni.getStorage({
key:'payData',
success: (res) => {
let data = res.data;
vm.getWrite(data);
uni.removeStorage('payData');
}
});
}
},
onUnload() {
this.closeNotify = false;
// 关闭监听状态
this.getNotify();
// 断开蓝牙
this.closeBlue();
},
methods: {
// 连接蓝牙设备
// connetBlue() {
// let vm = this;
// let item = uni.getStorageSync("name");
// uni.showLoading({
// title: "连接中,请稍等",
// mask: true,
// });
// uni.createBLEConnection({
// deviceId: item.deviceId, //设备的 id
// success(res) {
// console.log("连接蓝牙成功",res);
// uni.hideLoading();
// uni.showToast({
// title: item.name + "蓝牙连接成功",
// icon: "none",
// duration: 1000
// });
// },
// fail(res) {
// uni.hideLoading();
// uni.showToast({
// title: item.name + "蓝牙连接失败",
// icon: "none",
// });
// }
// });
// },
// // 获取蓝牙的所有服务
getServiceId() {
let vm = this;
let item = vm.ServicesData;
setTimeout(() => {//获取数据可能有延迟
uni.getBLEDeviceServices({
deviceId: item.deviceId, //蓝牙设备 id
success(res) {
const useData = 'D973F2E0';
let chooseData = res.services;
// 选择合适的uuid
chooseData.forEach( item => {
let data = item.uuid.split('-');
if(data[0] === useData) {
vm.getServiceIdData = item.uuid;
uni.setStorageSync('serId',vm.getServiceIdData);
}
})
console.log("获取服务成功", res);
vm.ServerSuccess = '获取服务成功,数据为' + vm.getServiceIdData;
// 获取蓝牙特征
vm.getCharacteId(vm.getServiceIdData);
},
fail(err) {
console.log("获取服务失败", err);
vm.ServerSuccess = '获取服务失败' + err.errCode;
}
});
},1000);
},
//获取蓝牙特征
getCharacteId(data) {
let vm = this;
let serID = vm.ServicesData;
uni.getBLEDeviceCharacteristics({
deviceId: serID.deviceId,
serviceId: vm.getServiceIdData,
success: (res) => {
console.log("获取特征值成功",res);
vm.showBtn = true;
var characteristicsData = res.characteristics;
// 判断服务都支持什么操作
characteristicsData.forEach(item => {
vm.stateSuccess = '获取特征值成功,数据为' + item.uuid;
uni.setStorageSync('gChargeId',item.uuid);
// 读操作
// if(item.properties.read) {
// uni.readBLECharacteristicValue({
// deviceId:serID.deviceId,
// serviceId:vm.getServiceIdData,
// characteristicId:item.uuid,
// success(res) {
// console.log("读成功",res);
// }
// })
// }
// 写操作
if(item.properties.write) {
vm.getCharateristicId = item.uuid;
}
// notify操作
if (item.properties.notify || item.properties.indicate) {
vm.getNotify();
}
});
},
fail(err) {
vm.stateSuccess = '获取蓝牙特征值失败' + err.errCode;
console.log("获取蓝牙特征值失败",err.errCode);
},
});
},
// notify变化
getNotify() {
let vm = this;
let serID = vm.ServicesData;
vm.getServiceIdData = uni.getStorageSync('serId');
vm.getCharateristicId = uni.getStorageSync('gChargeId');
uni.notifyBLECharacteristicValueChange({
deviceId:serID.deviceId,
serviceId:vm.getServiceIdData,
characteristicId: vm.getCharateristicId,
state:vm.closeNotify,
success(res) {
console.log("开启订阅数据监听成功",res);
vm.notifyStateSuccess = '开启订阅数据监听成功';
vm.hexStringData = '';
uni.onBLECharacteristicValueChange(characteristic => {
console.log('接收原始数据:', characteristic);
// 获取到的所以数据转化拼接
var array = new Uint8Array(characteristic.value);
var hexString = Array.from(array, byte => byte.toString(16).padStart(2, '0')).join('');
vm.hexStringData += hexString;
// 判断最后两位是否为16
if (vm.hexStringData.substr(vm.hexStringData.length - 2, 2) == "16"){
// 将数据转化为大写
let data = vm.hexStringData.toUpperCase();
vm.methodFn(data);
}
});
},
fail(err) {
console.log("开启订阅数据监听失败",err);
vm.notifyStateSuccess = '开启订阅数据监听失败' + err.errCode
}
});
},
// 监听回调
methodFn(val) {
console.log("val",val);
let vm = this;
let params = {
"data":val,
"psw":44,
"sign":"36576fb294"
};
let hash = vm.$md5(JSON.stringify(params) + '1350fee134a4ad0cae4da9a8');
// #ifdef APP-PLUS
uni.request({
method: 'POST',
url:'http://test.yourmeter.cn/umeter/openApi/cmd/readCmd',
data: params,
header: {
'appId':2,
'sign':hash,
},
success(res) {
console.log('获取卡信息成功',res.data.msg);
vm.agoData = '接收的原始数据为:' + val;
vm.cardInfo = '数据解析结果为:' + res.data.msg;
},
fail(err) {
vm.agoData = '接收的原始数据为:' + val;
vm.cardInfo = '数据解析结果为:' + err;
}
});
// #endif
// #ifdef MP-WEIXIN
uni.request({
method: 'POST',
url:'http://test.yourmeter.cn/umeter/openApi/cmd/readCmd',
data: params,
header: {
'appId':2,
'sign':hash,
},
success(res) {
console.log('获取卡信息成功',res.data.msg);
vm.agoData = '接收的原始数据为:' + val;
vm.cardInfo = '数据解析结果为:' + res.data.msg;
},
fail(err) {
vm.agoData = '接收的原始数据为:' + val;
vm.cardInfo = '数据解析结果为:' + err;
}
});
// #endif
// #ifdef H5
uni.request({
method: 'POST',
url:'/api/openApi/cmd/readCmd',
data: params,
header: {
'appId':2,
'sign':hash,
},
success(res) {
if(res.data.msg === 'OK') {
console.log('获取卡信息成功',res.data.msg);
vm.agoData = '接收的原始数据为:' + val;
vm.cardInfo = '数据接收成功:' + res.data.msg;
}
},
fail(err) {
vm.agoData = '接收的原始数据为:' + val;
vm.cardInfo = '数据解析结果为:' + err;
}
})
// #endif
},
//字符串函数
ab2hex: function (buffer) {
var hexArr = Array.prototype.map.call(
new Uint8Array(buffer),
function (bit) {
return ('00' + bit.toString(16)).slice(-2)
}
)
return hexArr.join('');
},
// 写入
getWrite(data) {
console.log("写入data",data);
let vm = this;
vm.hexStringData = '';
let serID = vm.ServicesData;
var hex = 'FEFEFE'+ data;
var buffer = new Uint8Array(hex.match(/[\da-f]{2}/gi).map(function (h) {
return parseInt(h, 16)
}))
buffer = buffer.buffer;
let pos = 0;
let bytes = buffer.byteLength;
while (bytes > 0) {
var tmpBuffer;
if (bytes > 20) {
tmpBuffer = buffer.slice(pos, pos + 20);
pos += 20;
bytes -= 20;
} else {
tmpBuffer = buffer.slice(pos, pos + bytes);
pos += bytes;
bytes -= bytes;
}
vm.getServiceIdData = uni.getStorageSync('serId');
vm.getCharateristicId = uni.getStorageSync('gChargeId');
uni.writeBLECharacteristicValue({
// 这里的 deviceId 需要在 getBluetoothDevices 或 onBluetoothDeviceFound 接口中获取
deviceId:serID.deviceId,
// 这里的 serviceId 需要在 getBLEDeviceServices 接口中获取
serviceId:vm.getServiceIdData,
// 这里的 characteristicId 需要在 getBLEDeviceCharacteristics 接口中获取
characteristicId:vm.getCharateristicId,
// 这里的value是ArrayBuffer类型
value: tmpBuffer,
success(res) {
console.log("蓝牙写入成功",res);
vm.writeStateSuccess = '命令发送成功,数据:' + hex;
},
fail(err) {
vm.writeStateSuccess = '命令发送失败,原因:' + err.errCode;
console.log("蓝牙写入失败",err.errCode);
}
})
};
},
// 断开蓝牙
closeBlue() {
let vm = this;
uni.closeBLEConnection({
deviceId: vm.ServicesData.deviceId, //设备的 id
success(res) {
uni.showToast({
title:"蓝牙断开成功",
icon: "none",
duration: 2000
});
vm.closeBlueData = "蓝牙断开成功";
console.log("蓝牙断开成功",res);
}
})
},
}
}
</script>
<style lang="scss">
.pay_box {
width: 100%;
display: flex;
flex-wrap: wrap;
justify-content: space-evenly;
margin-top: 30rpx;
}
.btn {
background-color: #007aff;
padding: 20rpx;
box-sizing: border-box;
font-size: 26rpx;
border-radius: 60rpx;
width: 30%;
height: 40px;
margin-top: 10px;
}
.displayFrame{
width: 90%;
height: 400px;
margin: 80px auto 0;
border: 3px solid #000000;
.dataWidth{
width: 90%;
display:inline-block;
white-space: pre-wrap;
word-wrap: break-word;
height: auto;
}
}
</style>