uniapp蓝牙连接封装
bluetooth.js
// import { TextDecoder } from 'text-encoding-utf-8'; 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 /** * 获取手机蓝牙是否打开 */ 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; resolve(); } 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('蓝牙初始化失败'+ JSON.stringify(err)); 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) { console.log('获取搜索到的设备信息', res.devices); bluetoothConnect = false; // 过滤掉name为空或者未知设备的设备 let devices = res.devices.filter(function(obj) { return obj.name !== "" && obj.name !== "未知设备" }); console.log('有名称蓝牙列表', devices, deviceName); // devices && devices.forEach(item => { // if (item.name && item.name === deviceName) { // deviceId = item.deviceId; // isHaveDevice = true; resolve(devices); // console.log('设备ID', deviceId, item); // } // }); }, fail: function() { console.log('搜索蓝牙设备失败'); bluetoothConnect = false; isHaveDevice = false; reject(isHaveDevice); }, complete: function() { console.log("蓝牙搜索完成"); // // 是否具有当前设备 // if (deviceId) { // isHaveDevice = true; // } else { // isHaveDevice = false; // } // resolve(isHaveDevice); } }); }); } /** * 连接蓝牙 * deviceId 蓝牙设备id */ const connectBluetooth = (deviceIdd) => { return new Promise((resolve, reject) => { // console.log(deviceId); deviceId=deviceIdd uni.createBLEConnection({ deviceId: deviceId, // 设备id success() { bluetoothConnect = true; console.log('连接蓝牙成功', deviceId); // 蓝牙连接成功后关闭蓝牙搜索 stopDiscoveryBluetooth(); // 获取服务id getServiceId(); resolve(); }, fail(err) { bluetoothConnect = false; console.log("蓝牙连接失败",err); reject(); } }); }); }; // 获取服务id const getServiceId = () => { uni.getBLEDeviceServices({ deviceId: deviceId, success(res) { console.log("deviceId",deviceId); console.log(res); let model = res.services[0]; serviceId = model.uuid; console.log("获取服务Id",serviceId, res) // 调用蓝牙监听和写入功能 getCharacteId(); } }) }; // 获取蓝牙低功耗设备某个服务中所有特征 const getCharacteId = () => { uni.getBLEDeviceCharacteristics({ deviceId: deviceId, // 蓝牙设备id serviceId: serviceId, // 蓝牙服务UUID success(res) { console.log('数据监听', res); res.characteristics.forEach(item => { // 003 if (item.properties.notify === true) { // 监听 // 微信 // #ifdef MP-WEIXIN notify = item.uuid; // #endif //支付宝 // #ifdef MP-ALIPAY notify = item.characteristicId; // #endif startNotice(); } // 002 if (item.properties.write === true) { // 写入 // 微信 // #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; console.log('帽子返回数据', result) } }) } }); }; // 蓝牙发送数据 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 createBlec = () => { uni.createBLEConnection({ deviceId:deviceId, success(res) { console.log(res, '断开成功') }, fail(err) { console.log("断开失败", err); } }) } const BLECStateChange=()=>{ return new Promise((resolve, reject) => { uni.onBLEConnectionStateChange(function (res) { // 该方法回调中可以用于处理连接意外断开等异常情况 console.log(`device ${res.deviceId} state has changed, connected: ${res.connected}`) return resolve(res) }) }) } // closeBLEConnection deviceId 断开蓝牙 export default { getBluetoothState, discoveryBluetooth, stopDiscoveryBluetooth, getBluetoothDevices, connectBluetooth, getServiceId, getCharacteId, startNotice, writeData, createBlec, BLECStateChange };
使用
<template> <view class="device_container"> <!-- <image src="@/static/bg.png" class="bg"></image> --> <view class="textimg"> <!-- <image src="@/static/text.png"></image> --> <!-- <button type="default" @click="login">微信登录</button> <button open-type="getAuthorize" scope='userInfo' @getAuthorize="loginAli" onError="onAuthError">支付宝登录</button> --> </view> <view class="orderText" @click="clickOrder"> 订单列表 </view> <view class="card"> <view class="tsip" @click="clicljydTisp"> </view> <!-- <image class="mb" src="@/static/mb.png" style="width: 100%;height: 100%;"></image> --> <view class="info"> <view class="beoka"> <!-- <image src="@/static/beoka.png"></image> --> </view> <view class="infos"> <view> 产品型号: {{deviceName?'Beoka12e432':'XXXXXXXXXX'}} </view> <view> 产品编号:{{deviceName?deviceName:'XXXX XXXX XXXX'}} <!-- 产品编号:{{deviceName?'866 9288 98376':' XXXX XXXX XXXX'}} --> </view> </view> <view class="priceText"> <view> 计费规则 </view> <view> 五分钟内归还免费,20元/小时,300元/24小时封顶1000元 </view> </view> </view> </view> <view v-if="state"> <view class="text" v-if="deviceName&&bluetoothStatus" @click="clickUse"> 立即使用 </view> <view class="sm" v-else @click="snacode">扫码连接设备 {{stateText}}</view> </view> <view class="" v-if="!state"> <view class="ljgh" @click="clickReturn" v-if="deviceInfo.state==0"> 立即归还 </view> <view class="ljgh" @click="clickPay" v-if="deviceInfo.state==1"> 去支付 </view> </view> <!-- 连接引导弹窗 --> <uni-popup ref="ljydPopup" type="center"> <view class="ljyd"> <view class="title"> 连接引导 <!-- <image src="@/static/close.png" @click="clickClose('ljydPopup')"></image> --> </view> <view class="item"> <view class="itemText"> 1.打开设备电源 </view> <view class="itemImg"> <!-- <image src="@/static/img1.png"></image> --> </view> </view> <view class="item"> <view class="itemText"> 1.打开手机蓝牙 </view> <view class="itemImg"> <!-- <image src="@/static/img2.png"></image> --> </view> </view> <view class="item"> <view class="itemText"> 1.连接设备 </view> <view class="itemImg"> <!-- <image src="@/static/img3.png"></image> --> </view> </view> </view> </uni-popup> <!-- 归还提示弹窗 --> <uni-popup ref="tispPopup" type="center" :is-mask-click='false'> <view class="tisp"> <view class="tispTitle"> 提示 </view> <view class="tispText"> 是否归还该设备 </view> <view class="tispBtn"> <view class="close" @click="clickClose('tispPopup')"> 取消 </view> <view class="confirm" @click="clickConfirm"> 确认 </view> </view> </view> </uni-popup> <!-- 归还成功 --> <uni-popup ref="successPopup" type="center" :is-mask-click='false'> <view class="success"> <view class="img"> <!-- <image src="@/static/success.png"></image> --> </view> <view class="successtext"> 归还成功 </view> <view class="successConfirm" @click="clickConfirmSuccess"> 确认 </view> </view> </uni-popup> </view> </template> <script> // Hi-BEOKA-POC003AEE import bluetooth from '@/utils/ly.js'; // import { // oderList, // deviceReturn, // wxPay, // createOrder // } from "@/api/index.js" export default { data() { return { state: true, deviceState: null, //判断是扫码进入的小程序还是 点击小程序进入的 stateText: '未连接', bluetoothStatus: false, // 蓝牙连接状态 deviceName: '', //蓝牙名字 deviceInfo: {}, //设备信息 deviceConnectState: true, //监听状态 } }, onLoad(e) { let str = e.q console.log('indexOnLoad'); let _this = this if (str) { this.deviceState = true let bluetooth = str.substring(str.length - 18) this.deviceName = bluetooth } else { this.deviceState = true } }, onShow() { // #ifdef MP-ALIPAY console.log('---'); my.tradePay({ orderStr:'201711152100110410533667792', }) // #endif let _this = this this.getOderList(() => { if (this.state && this.deviceName && !this.bluetoothStatus && this.deviceConnectState) { _this.initBluetooth(() => { console.log('连接成功'); }, _this.deviceName) } }) }, onUnload() { }, methods: { // 支付宝登录 loginAli() { my.getUserInfo({ success: (res) => { console.log('支付宝用户基础信息', res); uni.showToast({ title: res.userName, icon: 'none' }) }, complete: (err) => { console.log(err, '00000'); } }); }, // 微信登录 login() { uni.login({ provider: 'weixin', success: function(loginRes) { console.log(loginRes); // 获取用户信息 uni.getUserInfo({ provider: 'weixin', success: function(infoRes) { console.log('用户昵称为:' + JSON.stringify(infoRes.userInfo)); } }); } }); }, // 查询订单 getOderList(callBack) { console.log('订单信息请求'); // oderList({ // state: '', // deviceId: '' // }).then(res => { // console.log(res.list, '订单信息'); // let _this = this // let v = res.list[0] // if (v) { // if (v.state == 0 || v.state == 1) { // this.state = false // this.deviceInfo = v // this.deviceName = v.deviceId // } else { // this.state = true // if (callBack) { // callBack() // } // } // } else { // this.state = true // // 获取蓝牙和设备连接状态 // } // }) }, // 获取蓝牙和设备是否已连接 initBluetooth(callback, deviceName) { let _this = this; _this.stateText = '搜索蓝牙' uni.showLoading({ title: _this.stateText }) // 初始化蓝牙 bluetooth.getBluetoothState().then(() => { // 搜索外围蓝牙设备 bluetooth.discoveryBluetooth().then(() => { _this.discoveryLoading = true; // 获取蓝牙设备 bluetooth.getBluetoothDevices(deviceName).then((isHaveDevice) => { if (isHaveDevice) { _this.stateText = '连接中...' // 搜索到指定设备,连接蓝牙 bluetooth.connectBluetooth().then((bluetoothConnect) => { console.log('连接成功'); callback() _this.deviceName = deviceName _this.stateText = '连接成功' uni.hideLoading(); _this.bluetoothStatus = true; // 监听蓝牙与设备连接状态 bluetooth.BLECStateChange().then(res => { _this.bluetoothStatus = res.connected _this.deviceConnectState = res.connected _this.stateText = '未连接' console.log(res, '监听设备连接状态'); }) }).catch((err) => { _this.stateText = '连接失败' _this.bluetoothStatus = false; _this.deviceState = false uni.hideLoading(); uni.showToast({ title: '蓝牙连接失败', icon: 'none' }) }) } else { // 未搜到设备 _this.bluetoothStatus = false; _this.deviceState = false uni.hideLoading(); uni.showToast({ title: '请打开设备', icon: 'none' }) } }, () => { // 蓝牙搜索失败 _this.bluetoothStatus = false; _this.deviceState = false }); }); }, () => { // 未开启蓝牙 _this.bluetoothStatus = false; _this.deviceState = false }); }, // 向设备发送数据 writeBlueData(buffer, type) { let _this = this bluetooth.writeData(buffer).then(res => { uni.showLoading({ title: type == 1 ? "开启中..." : "关闭中..." }) if (type == 1) { console.log(_this.deviceName, type); // createOrder({ // deviceId: _this.deviceName // }).then(res => { // console.log('订单创建成功', res); // uni.showToast({ // title: '开启成功', // icon: 'none' // }) // _this.getOderList() // }).catch(err => { // uni.showToast({ // title: err.msg, // icon: 'none' // }) // }) } else { // deviceReturn({ // deviceId: _this.deviceName // }).then(res => { // _this.state = true // _this.$refs.tispPopup.close() // this.$refs.successPopup.open() // }).catch(err => { // uni.showToast({ // title: '归还失败', // icon: 'none' // }) // }) } uni.hideLoading() }) }, // 立即使用 并向设备发送命令 clickUse() { let _this = this let buffer = _this.arr2ab([170, 85, 1, 1, 1]) console.log(buffer, 'buffer', _this.deviceName); if (this.bluetoothStatus) { _this.writeBlueData(buffer, 1) } else { _this.initBluetooth(() => { _this.writeBlueData(buffer, 1) }, _this.deviceName); } }, // 扫码 snacode() { let _this = this; uni.scanCode({ success: function(res) { let str = res.result let bluetooth = str.substring(str.length - 18) console.log('1111', bluetooth); _this.initBluetooth(() => { _this.deviceName = bluetooth }, bluetooth); _this.deviceState = true }, fail: (err => { _this.deviceState = false }) }); }, // 点击归还 clickReturn() { let _this = this if (this.bluetoothStatus) { _this.$refs.tispPopup.open() } else { _this.initBluetooth(() => { _this.$refs.tispPopup.open() }, _this.deviceName); } }, // 连接引导弹窗 clicljydTisp() { console.log('999'); this.$refs.ljydPopup.open() }, // 归还成功确认弹窗 clickConfirmSuccess() { this.$refs.successPopup.close() this.getOderList() }, clickPay() { uni.navigateTo({ url: '/pages/index/orderlist' }) }, // 确定归还弹窗 确认 clickConfirm() { let _this = this let buffer = _this.arr2ab([170, 85, 1, 1, 0]) _this.writeBlueData(buffer, 2) // if (this.bluetoothStatus) { // _this.writeBlueData(buffer, 2) // } else { // _this.initBluetooth(() => { // _this.writeBlueData(buffer, 2) // }, _this.deviceName); // } }, // 10进制转换 arr2ab(arr) { const buffer = new ArrayBuffer(arr.length); const dataView = new DataView(buffer); for (var i = 0; i < arr.length; i++) { dataView.setUint8(i, arr[i]); } return buffer; }, // 点击关闭 clickClose(name) { this.$refs[name].close() }, // 点击订单列表 clickOrder() { uni.navigateTo({ url: '/pages/index/orderlist' }) }, }, } </script> <style lang="scss"> page { height: 100%; } .bg { width: 100vw; height: 100vh; } .ljgh, .text, .sm { border-radius: 100px; background: #3A87EF; width: calc(100% - 96rpx); height: 112rpx; box-shadow: 0px 20px 60px 0px rgba(58, 96, 178, 0.30); text-align: center; line-height: 112rpx; color: #FFF; font-size: 32rpx; font-style: normal; font-weight: 500; position: fixed; bottom: 120rpx; left: 50%; transform: translateX(-50%); letter-spacing: 2rpx; } .textimg { width: 530rpx; height: 204rpx; image { width: 100%; height: 100%; } position: fixed; bottom: 120rpx; left: 16rpx; top: 10%; } .orderText { border-radius: 30rpx 0px 0px 30rpx; background: rgba(1, 31, 76, 0.45); width: 160rpx; height: 60rpx; text-align: center; font-family: PingFang HK; font-size: 28rpx; font-weight: 400; line-height: 60rpx; position: absolute; right: 0; top: 12.7%; color: #FFF; } .card { width: calc(100% - 64rpx); height: 520rpx; position: fixed; top: 43%; left: 50%; transform: translateX(-50%); .md { width: 100%; height: 100%; } .tsip { width: 32rpx; height: 32rpx; border-radius: 100%; position: absolute; right: 44rpx; top: 32rpx; // background-color: red; z-index: 1; } .info { position: absolute; top: 0; padding: 60rpx 40rpx 22rpx; box-sizing: border-box; .beoka { width: 65px; height: 20px; image { width: 65px; height: 20px; } } } .infos { font-size: 32rpx; font-style: normal; font-weight: 700; line-height: normal; margin-top: 44rpx; border-bottom: 2rpx dashed #E9ECF3; padding-bottom: 32rpx; view { margin-top: 32rpx; } } .priceText { box-sizing: border-box; height: 146rpx; border-radius: 8rpx; box-sizing: border-box; border: 2rpx solid rgba(0, 130, 180, 0.06); background: #E7F0F7; padding: 16rpx 22rpx; margin: 32rpx 0 20rpx; view { color: #3D4C5F; font-family: PingFang HK; font-size: 24rpx; font-weight: 400; line-height: 38rpx; } } } .ljyd { width: 644rpx; background-color: #fff; border-radius: 28rpx; padding-bottom: 16rpx; // height: 517px; .title { color: #333; text-align: center; font-size: 32rpx; font-style: normal; font-weight: 600; line-height: 44rpx; /* 137.5% */ width: 100%; padding: 32rpx 0 48rpx; position: relative; image { width: 20rpx; height: 20rpx; position: absolute; right: 44rpx; top: 45rpx; } } .item { margin: 0 32rpx 32rpx; .itemText { color: var(--unnamed, #333); text-align: left; font-family: PingFang HK; font-size: 16px; font-style: normal; font-weight: 400; line-height: normal; letter-spacing: -0.408px; margin-bottom: 12rpx; } .itemImg { width: 340rpx; height: 210rpx; margin: auto; image { width: 100%; height: 100%; } } } } .tisp { width: 566rpx; height: 304rpx; border-radius: 28rpx; background: #FFF; padding: 32rpx; box-sizing: border-box; display: flex; flex-direction: column; justify-content: space-between; .tispTitle { color: #333; text-align: center; font-size: 32rpx; font-style: normal; font-weight: 600; line-height: 44rpx; /* 137.5% */ width: 100%; } .tispText { text-align: center; color: #5C5C5C; font-size: 28rpx; font-style: normal; font-weight: 400; line-height: 44rpx; /* 157.143% */ } .tispBtn { display: flex; justify-content: space-between; .close, .confirm { text-align: center; width: 234rpx; height: 72rpx; line-height: 72rpx; border-radius: 32rpx; font-size: 28rpx; font-weight: 400; letter-spacing: 2rpx; } .close { color: #3A87EF; background-color: #EBF3FF; } .confirm { color: #fff; background-color: #3A87EF; } } } .success { width: 566rpx; height: 432rpx; border-radius: 28rpx; background: #FFF; padding: 32rpx; box-sizing: border-box; .img { width: 344rpx; height: 206rpx; margin: 0 auto; image { width: 100%; height: 100%; } } .successtext { margin-top: 16rpx; color: #333; font-size: 32rpx; font-weight: 700; text-align: center; } .successConfirm { text-align: center; width: 234rpx; height: 72rpx; line-height: 72rpx; border-radius: 32rpx; font-size: 28rpx; font-weight: 400; letter-spacing: 2rpx; color: #fff; background-color: #3A87EF; margin: 30rpx auto 0; } } </style>