1:新建一个vue页面 index.vue
<template> <view> <view class="list" v-if="isHaveDevice"> </view> <view class="" v-else> <!-- 搜索中 --> <view class="ks" v-if="state==1"> <view class="img"> <image src="https://beoka-file.oss-cn-beijing.aliyuncs.com/beoka_oxy/img/1000001900587011.gif"> </image> </view> <view class="searchText"> 蓝牙搜索中... </view> </view> <!-- 搜索失败 --> <view class="fail" v-if="state==3"> <view class="img"> <image src="@/static/none.png"></image> </view> <view class="text"> 搜索失败 </view> </view> <view class="zy"> <view class="title"> 注意: </view> <view class="text"> <span> 1、 </span> <p> 请保持设备为开机状态; </p> </view> </view> </view> <view class="btns" v-show="state==3" > <!-- <view class="clear" @click="clickClear"> 取消 </view> --> <view class="comfirm" @click='clickRetry'> 再次搜索 </view> </view> </view> </template> <script> import bluetooth from '@/uilts/boxYj.js'; import { deviceInfoApi } from "@/api/device.js" export default { data() { return { state: 1, deviceNo: '', orderNo: "", stateText: '未连接', bluetoothStatus: false, // 蓝牙连接状态 bluetoothName: 'Hi-S6-A-', Hi-S6-A-//蓝牙名字 buffer: '', //下发数据 devicesList: [], //蓝牙列表 isHaveDevice: false, //是否搜到设备 time: 0, timer: null } }, onLoad() { // 搜索蓝牙 this.initBluetooth(); }, onShow() { this.devicesList=[] }, onUnload() { console.log('页面卸载,执行一些清理工作'); bluetooth.closeBlec().then(res => { console.log('断开蓝牙', res); }) }, methods: { // 立即重试 clickRetry() { let _this = this _this.devicesList=[] _this.state = 1 _this.isHaveDevice = false clearInterval(_this.timer) _this.timer = null _this.time = 0 if (!_this.timer) { this.initBluetooth() } }, // 绑卡 clickBild(item) { uni.showLoading({ title: '获取盒子信息中...', icon: 'none' }) this.bluetoothConnect(item, () => { let _this = this //1 10进制的需求 2 组装下发 16进制 // let num = this.$config.IntToBytesLittleEndian(43000, 2) let v = ["BB", "66", "06", "01", '01']; //命令数据表示16进制 //转16进制 let list = [...this.$config.parseInt(v)] console.log(v, list, '---'); // 10进制转换 let buffer = this.$config.arr2ab(list) if (this.bluetoothStatus) { _this.writeBlueData(buffer, '获取数据成功', 1) } }) }, // 开锁并向设备发送命令开锁 open(item) { uni.showLoading({ title: '开锁中...', icon: 'none' }) this.bluetoothConnect(item, () => { let _this = this //1 10进制的需求 2 组装下发 16进制 // let num = this.$config.IntToBytesLittleEndian(43000, 2) let v = ["BB", "66", "01", "01", '01']; //命令数据表示16进制 //转16进制 let list = [...this.$config.parseInt(v)] console.log(v, list, '---'); // 10进制转换 let buffer = this.$config.arr2ab(list) if (this.bluetoothStatus) { _this.writeBlueData(buffer, '开锁成功', 0) } }) }, // 向设备发送数据 writeBlueData(buffer, title, type) { let _this = this _this.$store.state.type = type || 0; bluetooth.writeData(buffer).then(res => { uni.hideLoading() _this.bluetoothStatus = false // 蓝牙连接状态 uni.showToast({ title: title || '操作成功', icon: 'none' }) }).catch(err => { _this.state = 3 }) }, // 点击连接 bluetoothConnect(item, callBack) { var _this = this; clearInterval(_this.timer) _this.timer = null _this.time = 0 bluetooth.closeBlec().finally(res => { console.log('断开蓝牙', res); setTimeout(() => { bluetooth.connectBluetooth(item).then(( bluetoothConnect) => { console.log('连接成功'); setTimeout(() => { _this.stateText = '连接成功' _this.bluetoothStatus = true; callBack() }, 1000) // 监听蓝牙与设备连接状态 bluetooth.BLECStateChange().then(res => { _this.bluetoothStatus = res.connected _this.stateText = '未连接' console.log(res, '监听设备连接状态'); }) }).catch((err) => { uni.showToast({ title: '开锁失败', icon: 'none' }) _this.bluetoothStatus = false; _this.deviceState = false _this.state = 3 // #ifdef MP-ALIPAY uni.hideLoading(); // #endif }) },2000) }) }, // 获取蓝牙和设备 initBluetooth() { let _this = this // 初始化蓝牙 bluetooth.getBluetoothState().then(() => { // 搜索外围蓝牙设备 bluetooth.discoveryBluetooth().then(() => { _this.discoveryLoading = true; // 获取蓝牙设备 _this.timer = setInterval(() => { if (_this.time <= 30) { bluetooth.getBluetoothDevices(this.bluetoothName).then((res) => { console.log(res, '回调', this.bluetoothName); ++this.time if (res.isHaveDevice) { _this.isHaveDevice = res.devices.length ? true : false _this.devicesList = res.devices _this.state = res.devices.length ? 3 : 1 } else { // 未搜到设备 this.isHaveDevice = false _this.bluetoothStatus = false; _this.deviceState = false _this.state = 3 _this.devicesList = res.devices clearInterval(_this.timer) _this.timer = null _this.time = 0 } }).catch((err) => { this.isHaveDevice = false _this.devicesList = err.devices // 蓝牙搜索失败 _this.bluetoothStatus = false; _this.deviceState = false _this.state = 3 clearInterval(_this.timer) _this.timer = null _this.time = 0 }); } else { this.isHaveDevice = _this.devicesList.length ? true : false _this.state = 3 clearInterval(_this.timer) _this.timer = null this.time = 0 } }, 1000) }); }).catch((err) => { // 未开启蓝牙 _this.bluetoothStatus = false; _this.deviceState = false _this.state = 3 }); }, // 返回 leftClick() { var _this = this; clearInterval(_this.timer) _this.timer = null _this.time = 0 bluetooth.closeBlec() uni.navigateBack() }, // 取消 clickClear() { var _this = this; clearInterval(_this.timer) _this.timer = null _this.time = 0 bluetooth.closeBlec() uni.showToast({ title: '取消成功', icon: 'none' }) }, } } </script> <style lang="less"> page { background-color: #f4f4f8; } .list { padding: 24rpx; box-sizing: border-box; .item { width: 100%; height: 230rpx; padding: 32rpx; box-sizing: border-box; background: #ffffff; border-radius: 24rpx; display: flex; margin-bottom: 32rpx; .img { width: 190rpx; height: 166rpx; border-radius: 16rpx; overflow: hidden; image { width: 100%; height: 100%; } } .info { flex: 1; margin-left: 32rpx; display: flex; flex-direction: column; justify-content: space-between; .name { font-size: 32rpx; font-family: PingFang HK, PingFang HK-500; font-weight: 500; text-align: LEFT; color: #333333; line-height: 38rpx; } .code { font-size: 28rpx; font-family: PingFang HK, PingFang HK-400; font-weight: 400; text-align: LEFT; color: #969696; line-height: 32rpx; } .kg { display: flex; font-size: 24rpx; font-family: PingFang HK, PingFang HK-400; font-weight: 400; text-align: CENTER; color: #ffffff; line-height: 60rpx; .open { width: 150rpx; height: 60rpx; background: #f98518; border: 2rpx solid #f98518; border-radius: 70rpx; } .car { width: 150rpx; height: 60rpx; border: 2rpx solid #bdbdbd; border-radius: 70rpx; color: #333; margin-left: 32rpx; } .sx { width: 150rpx; height: 60rpx; background: #d2d2d2; border-radius: 70rpx; margin-left: 32rpx; border: 2rpx solid #d2d2d2; } } } } } .ks { margin: 35rpx auto 0; display: flex; flex-direction: column; justify-content: center; align-items: center; .img { width: 460rpx; height: 460rpx; image { width: 100%; height: 100%; } } .searchText { text-align: center; font-size: 24rpx; font-family: PingFang HK, PingFang HK-400; font-weight: 400; color: rgba(0, 0, 0, 0.20); line-height: 28rpx; } } .fail { margin: 166rpx auto 0; display: flex; flex-direction: column; justify-content: center; align-items: center; .img { width: 208rpx; height: 186rpx; image { width: 100%; height: 100%; } } .text { font-size: 24rpx; font-family: PingFang HK, PingFang HK-400; font-weight: 400; text-align: LEFT; color: rgba(0, 0, 0, 0.20); line-height: 28rpx; margin: 46rpx 0 64rpx; } .btn { width: 512rpx; height: 96rpx; line-height: 96rpx; background: #192006; border-radius: 100rpx; text-align: center; font-size: 28rpx; font-family: PingFang HK, PingFang HK-400; font-weight: 400; text-align: CENTER; color: #FFFFFF; //letter-spacing: 0.2rpx; } } .zy { margin: 25% 0 0 112rpx; .title { font-size: 28rpx; font-family: PingFang HK, PingFang HK-400; font-weight: 400; text-align: LEFT; color: #333333; line-height: 32rpx; margin-bottom: 16rpx; } .text { font-size: 26rpx; font-family: PingFang HK, PingFang HK-400; font-weight: 400; text-align: LEFT; color: #333333; line-height: 48rpx; display: flex; margin-bottom: 10rpx; p { width: 80%; } } } .btns { background-color: #fff; position: fixed; bottom: 0; left: 0; width: 100%; height: 160rpx; align-items: center; display: flex; align-items: center; justify-content: center; border-radius: 40rpx 40rpx 0rpx 0rpx; box-shadow: 0px -8rpx 112rpx 0rpx rgba(125, 130, 139, 0.08); .clear, .comfirm { width: 94%; height: 96rpx; line-height: 96rpx; border-radius: 128rpx; font-size: 28rpx; font-family: PingFang HK, PingFang HK-400; font-weight: 400; text-align: CENTER; color: #333333; //letter-spacing: 2rpx; box-sizing: border-box; } .clear { border: 2rpx solid #DCE4F0; color: #9AA1B8; } .comfirm { background-color: #F98518; color: #fff; } } </style>
2: 创建boxYj.js
// import { TextDecoder } from 'text-encoding-utf-8';
import store from '../store';
let bluetoothOpen = false; // 手机蓝牙是否打开
let bluetoothConnect = false; // 设备和蓝牙是否连接
let isHaveDevice = false; // 是否查找到设备
let deviceId = null; // 设备id
let serviceId = null; // 服务id
let notify = null; // 监听uuid
let writeId = null; // 写入uuid
let dataList = []
let platform = ''
/**
* 获取手机蓝牙是否打开
*/
const getBluetoothState = () => {
// 主机模式
return new Promise((resolve, reject) => {
// mode: 'central',
uni.openBluetoothAdapter({
success: (r) => {
console.log("蓝牙初始化成功");
// 获取蓝牙的匹配状态
uni.getBluetoothAdapterState({
success: function(row) {
console.log('蓝牙状态:', row.available);
if (row.available) {
bluetoothOpen = true;
getLocation().then(res => {
console.log('4444');
resolve();
}).catch(err => {
bluetoothOpen = false;
bluetoothConnect = false;
reject();
})
} else {
// 请开启蓝牙
uni.showToast({
title: '请打开蓝牙',
icon: 'none'
})
bluetoothOpen = false;
bluetoothConnect = false;
reject();
}
},
fail: function(err) {
// 请开启蓝牙
uni.showToast({
title: '请打开蓝牙',
icon: 'none'
})
bluetoothOpen = false;
bluetoothConnect = false;
reject();
}
})
},
fail: (err) => {
// 请开启蓝牙
console.log('蓝牙初始化失败', err);
if (err.errno == 103) {
uni.showModal({
title: '温馨提示',
content: '您已拒绝授权,是否去设置打开?',
confirmText: "确认",
cancelText: "取消",
success: function(res) {
// console.log(res);
if (res.confirm) {
uni.openSetting({
success: (res) => {
console.log(res, '设置');
// res.authSetting[scope] = true
// resolve()
}
});
}
},
});
} else if (err.errCode == 10001) {
uni.showToast({
title: '请前往手机设置,打开此APP蓝牙权限',
icon: 'none'
})
} else {
uni.showToast({
title: '请打开手机蓝牙',
icon: 'none'
})
}
bluetoothOpen = false;
bluetoothConnect = false;
reject();
}
});
});
};
/**
* 开始搜索蓝牙设备
*/
const discoveryBluetooth = () => {
return new Promise((resolve) => {
uni.startBluetoothDevicesDiscovery({
success(res) {
console.log('搜索蓝牙外围设备完成', res)
setTimeout(() => {
resolve();
}, 2000);
}
});
})
};
// 关闭蓝牙搜索
const stopDiscoveryBluetooth = () => {
uni.stopBluetoothDevicesDiscovery({
success(r) {
console.log("停止搜索蓝牙设备", r);
}
});
};
/**
* 获取搜索到的设备信息
*/
const getBluetoothDevices = (deviceName) => {
return new Promise((resolve, reject) => {
uni.getBluetoothDevices({
success(res) {
bluetoothConnect = false;
// 过滤掉name为空或者未知设备的设备
let devices = res.devices.filter(function(obj) {
return obj.name !== "" && obj.name !== "未知设备" && obj.name.substring(0,
8) == deviceName
});
// console.log('有名称蓝牙列表1', devices);
console.log(devices, '蓝牙1', deviceName);
isHaveDevice = true;
resolve({
isHaveDevice,
devices
});
},
fail: function() {
console.log('搜索蓝牙设备失败');
bluetoothConnect = false;
isHaveDevice = false;
reject({
isHaveDevice,
devices: []
});
}
});
});
};
/**
* 连接蓝牙
* deviceId 蓝牙设备id
*/
const connectBluetooth = (item) => {
deviceId = item.deviceId
return new Promise((resolve, reject) => {
uni.createBLEConnection({
deviceId: deviceId, // 设备id
success() {
bluetoothConnect = true;
if (platform == 'android') {
// 修改蓝牙MTU值
uni.setBLEMTU({
deviceId: deviceId,
mtu: 200,
success: (res) => {
console.log(res, 'mtu设置成功传输最大值');
},
fail: (res) => {
console.log(res, 'mtu设置失败');
},
});
}
console.log('连接蓝牙成功', deviceId);
// 蓝牙连接成功后关闭蓝牙搜索
stopDiscoveryBluetooth();
// 获取服务id
getServiceId();
setTimeout(() => {
resolve();
})
},
fail(err) {
bluetoothConnect = false;
console.log("蓝牙连接失败", err);
reject();
}
});
});
};
// 获取服务id
const getServiceId = () => {
uni.getBLEDeviceServices({
deviceId: deviceId,
success(res) {
console.log(res.services, '服务id成功');
// 方案一
res.services.forEach(item => {
let firstFive = ''
firstFive = item.uuid.substring(0, 8);
var FFF0 = /fff0/i;
console.log(firstFive, 'firstFive');
if (FFF0.test(firstFive)) {
serviceId = item.uuid;
// 调用蓝牙监听和写入功能
getCharacteId();
}
});
},
fail(err) {
console.log('获取服务失败', err);
}
})
};
// 获取蓝牙低功耗设备某个服务中所有特征
const getCharacteId = () => {
uni.getBLEDeviceCharacteristics({
deviceId: deviceId, // 蓝牙设备id
serviceId: serviceId, // 蓝牙服务UUID
success(res) {
console.log('数据监听', res);
res.characteristics.forEach(item => {
let firstFive = ''
// #ifdef MP-WEIXIN
firstFive = item.uuid.substring(0, 8);
// #endif
// #ifdef MP-ALIPAY
firstFive = item.characteristicId.substring(0, 8);
// #endif
console.log(firstFive, 'firstFive');
var FFF1 = /fff1/i;
var FFF2 = /fff2/i;
// 001
if (item.properties.notify === true && FFF1.test(firstFive)) {
// 监听
// 微信
// #ifdef MP-WEIXIN
notify = item.uuid;
// #endif
//支付宝
// #ifdef MP-ALIPAY
notify = item.characteristicId;
// #endif
startNotice();
}
// 002
if (item.properties.write === true && FFF2.test(firstFive)) {
// 写入
// 微信
// #ifdef MP-WEIXIN
let writeId = item.uuid;
uni.setStorageSync("writeId", item.uuid);
// #endif
//支付宝
// #ifdef MP-ALIPAY
let writeId = item.serviceId;
uni.setStorageSync("writeId", item.characteristicId);
// #endif
}
});
},
fail(err) {
console.log("数据监听失败", err)
}
})
};
// 启用低功耗蓝牙设备特征值变化时的notify功能
const startNotice = () => {
uni.notifyBLECharacteristicValueChange({
characteristicId: notify,
deviceId: deviceId,
serviceId: serviceId,
state: true,
success(res) {
// 监听低功耗蓝牙设备的特征值变化
uni.onBLECharacteristicValueChange(result => {
console.log("监听低功耗蓝牙设备的特征值变化", result);
if (result.value) {
// let decoder = new TextDecoder('utf-8');
// let data = decoder.decode(result.value);
// let data = result.value;
dataList = ab2hex(result.value)
let info = {
deviceId,
dataList
}
let type = store.state.type
console.log(dataList, '帽子返回数据', type)
if (type == 1) {
uni.navigateTo({
url: '/pagesB/bindCar/bindCar?info=' + JSON.stringify(info)
})
} else if (type == 2) {
console.log('我在绑卡', type);
store.state.cardCodeLsit = dataList
} else if (type == 3) {
store.state.bindCardState = true
}
}
})
}
});
};
// 蓝牙发送数据
const writeData = (buffer) => {
return new Promise((resolve, reject) => {
console.log(uni.getStorageSync("writeId"), '下发命令1writeId');
console.log(deviceId, '下发命令2deviceId');
console.log(serviceId, '下发命令3serviceId');
console.log(buffer, '下发命令4buffer');
uni.writeBLECharacteristicValue({
characteristicId: uni.getStorageSync("writeId"),
deviceId: deviceId,
serviceId: serviceId,
value: buffer,
success(res) {
console.log("writeBLECharacteristicValue success", res);
resolve();
},
fail(err) {
console.log("报错了", err);
reject();
}
});
});
};
// 读取数据并解析
const ab2hex = (buffer) => {
let hexArr = Array.prototype.map.call(new Uint8Array(buffer), function(
bit
) {
return ("00" + bit.toString(16)).slice(-2);
});
return hexArr;
// const hexArr = Array.prototype.map.call(
// new Uint8Array(buffer),
// function(bit) {
// return ('00' + bit.toString(16)).slice(-2)
// }
// )
// return hexArr.join('')
}
// 断开蓝牙
const closeBlec = () => {
return new Promise((resolve, reject) => {
uni.closeBLEConnection({
deviceId: deviceId,
success(res) {
resolve();
console.log(res, '断开成功')
},
fail(err) {
reject();
console.log("断开失败", err);
}
})
})
}
// 监听蓝牙连接状态
const BLECStateChange = () => {
return new Promise((resolve, reject) => {
uni.onBLEConnectionStateChange(function(res) {
store.state.blueStatu = res.connected
// 该方法回调中可以用于处理连接意外断开等异常情况
console.log(
`device ${res.deviceId} state has changed, connected: ${res.connected}`)
return resolve(res)
})
})
}
// 根据 ios 安卓处理不同逻辑
const getLocation = () => {
return new Promise((resolve, reject) => {
uni.getSystemInfo({
success: function(info) {
console.log('手机', info);
if (info.platform == 'android') {
platform = 'android'
// 安卓手机
console.log('1111');
if (info.locationEnabled) {
if (info.locationAuthorized) {
resolve()
} else {
// #ifdef MP-WEIXIN
uni.showToast({
title: '请打开微信定位权限',
icon: 'none'
})
// #endif
// #ifdef MP-ALIPAY
uni.showToast({
title: '请打开支付宝定位权限',
icon: 'none'
})
// #endif
reject()
}
} else {
console.log('333');
uni.showToast({
title: '请打开手机定位权限',
icon: 'none'
})
reject()
}
} else {
// 非安卓手机
platform = 'ios'
console.log('非安卓手机');
resolve()
}
}
});
})
}
// 抛出方法
export default {
getBluetoothState,
discoveryBluetooth,
stopDiscoveryBluetooth,
getBluetoothDevices,
connectBluetooth,
getServiceId,
getCharacteId,
startNotice,
writeData,
closeBlec,
BLECStateChange
};