最近在做一個藍牙售貨機的項目,放在酒店房間內,哈哈,里邊是什么自行腦補。
因為需要適配微信小程序和支付寶小程序,所以最后選用uni-app來開發,省時省力更省錢。
第一次對接藍牙,做一次記錄,話不多說,開始上代碼。
*uni-app藍牙連接的整個流程 *1、初始化藍牙 uni.openBluetoothAdapter(OBJECT) *2、開始搜索藍牙設備 uni.startBluetoothDevicesDiscovery(OBJECT) *3、發現外圍設備 uni.onBluetoothDeviceFound(CALLBACK) *4、停止搜尋附近的藍牙外圍設備 uni.stopBluetoothDevicesDiscovery(OBJECT) *5、連接低功耗藍牙設備 uni.createBLEConnection(OBJECT) *6、獲取藍牙設備所有服務 uni.getBLEDeviceServices(OBJECT) *7、獲取藍牙特征 uni.getBLEDeviceCharacteristics(OBJECT) *8、啟用藍牙設備特征值變化時的 notify 功能 uni.notifyBLECharacteristicValueChange(OBJECT) *9、監聽低功耗藍牙設備的特征值變化 uni.onBLECharacteristicValueChange(CALLBACK) *10、對需要操作的特征值進行讀、寫操作 import store from '@/store/index.js' let _self; export default class deviceClass { constructor() { _self = this; _self.deviceId = ""; _self.isConnect = false; //是否已連接 _self.isStop = false; //是否停止搜所 _self.UUID_SERVICE = '0000FEE0-0000-1000-8000-00805F9B34FB'; //服務UUID,設備方提供 _self.UUID_CHAR = '0000FEE1-0000-1000-8000-00805F9B34FB'; //特征值UUID,設備方提供 } /* 初始化並搜索藍牙操作 */ initEventDevice() { uni.showLoading({ title: "藍牙連接中" }) /* 1、初始化藍牙 uni.openBluetoothAdapter(OBJECT) */ uni.openBluetoothAdapter({ complete: () => { /* 監聽藍牙是否開啟*/ uni.onBluetoothAdapterStateChange(function(res) { //res.available 藍牙適配器是否可用 //res.discovering 藍牙適配器是否處於搜索狀態 console.log('適配器狀態變化', res) if(!res.available && !res.discovering){ _self.isStop = false; uni.showToast({ duration: 2500, title: "請打開手機藍牙", icon: 'error', }) store.commit('updateStatus', false); } else if(_self.isStop==false && res.available && !res.discovering){ console.log("藍牙已開啟,可以執行搜索操作") _self.initEventDevice(); } else if(res.available && res.discovering){ console.log("藍牙已開啟,並且正在搜索中") } }) }, success: (res) => { /* 初始化藍牙成功 */ /* 2、開始搜索藍牙設備 uni.startBluetoothDevicesDiscovery(OBJECT) */ /* 監聽藍牙適配器狀態變化事件 可以檢測當前設備的藍牙的斷開或者連接的情況*/ _self.startBluetoothDevicesDiscovery(); _self.connectTimeout(); }, fail: (res) => { console.log("初始化藍牙失敗:" + JSON.stringify(res)) uni.hideLoading(); uni.showToast({ duration: 2500, title: "請打開手機藍牙", icon: 'error', }) } }) } /*搜索藍牙*/ startBluetoothDevicesDiscovery() { uni.startBluetoothDevicesDiscovery({ allowDuplicatesKey: false, //是否允許同一設備多次上報,但RSSI值會有所不同 success: (res) => { console.log("搜索成功:" + JSON.stringify(res)) _self.onBluetoothDeviceFound(); }, fail: (res) => { uni.showModal({ title: '藍牙連接失敗了哦', content: ` 1、請查看周圍是否有連接此設備的藍牙; 2、請先關閉手機藍牙在打開一次; 3、請檢查位置信息是否打開; 4、退出小程序重新掃碼進入。`, showCancel: false, confirmText: "我知道了", success: () => {} }) console.log("搜索失敗:" + JSON.stringify(res)) } }) } /* 搜索藍牙設備回調 */ onBluetoothDeviceFound() { uni.onBluetoothDeviceFound(function(CALLBACK) { console.log('搜索藍牙設備回調===' + JSON.stringify(CALLBACK)) /* 每次遍歷指定設備,一旦搜索到指定設備就結束搜索*/ let bluename = JSON.parse(uni.getStorageSync('deviceInfo')).bluename; //需要搜索的設備名稱 /* 搜索過程中如果搜索到需要的設備,就停止搜索 */ CALLBACK.devices.forEach(device => { /* 篩選需要的deviceId */ if (device.deviceId === bluename) { //找到需要的設備之后,停止搜索 console.log("搜索到指定設備=="+device.deviceId) _self.isConnect = true; /*停止搜索*/ _self.stopBluetoothDevicesDiscovery(); /* 執行連接 */ _self.connectDevice(device.deviceId); } }) }) } /*設置藍牙搜索時間30秒, 如果超時沒有搜索到就停止搜索*/ connectTimeout() { setTimeout(() => { if(!_self.isConnect) { uni.hideLoading(); _self.stopBluetoothDevicesDiscovery(); uni.showModal({ title: '藍牙連接失敗了哦', content: ` 1、請查看周圍是否有連接此設備的藍牙; 2、請先關閉手機藍牙在打開一次; 3、請檢查位置信息是否打開; 4、退出小程序重新掃碼進入。`, showCancel: false, confirmText: "我知道了", success: () => {} }) } }, 30000) } /* 停止搜索 */ stopBluetoothDevicesDiscovery() { uni.stopBluetoothDevicesDiscovery({ complete: () => { _self.isStop = true; }, success: (res) => { console.log("停止搜索成功") }, fail: (res) => { console.log("停止失敗") uni.hideLoading(); } }) } /* 連接低功耗藍牙 */ connectDevice(deviceId) { console.log("執行到這里了") uni.createBLEConnection({ deviceId, //點擊的DeviceId timeout: 5000, success: (res) => { console.log("連接成功") _self.deviceId = deviceId; /* 這邊獲取全部的服務,並篩選出當前需要的*/ _self.getBLEDeviceServices(deviceId) }, fail: (error) => { /* 連接失敗 */ uni.hideLoading(); console.log("連接失敗") console.log(error); uni.showModal({ title: '藍牙連接失敗了哦', content: ` 1、請查看周圍是否有連接此設備的藍牙; 2、請先關閉手機藍牙在打開一次; 3、請檢查位置信息是否打開; 4、退出小程序重新掃碼進入。`, showCancel: false, confirmText: "我知道了", success: () => {} }) } }) /* 監聽藍牙設備的連接狀態 */ uni.onBLEConnectionStateChange(function(res) { console.log(res.deviceId + "連接狀態:" + res.connected) }) } /* 獲取所有的服務*/ getBLEDeviceServices(deviceId) { console.log("開始獲取服務") //連接成功之后需要延時,繼續操作才不會出問題,延時時間短一點都沒關系,我這邊用了1秒 setTimeout(() => { uni.getBLEDeviceServices({ // 這里的 deviceId 需要已經通過 createBLEConnection 與對應設備建立鏈接 deviceId, success: (res) => { console.log('device services:', res) res.services.forEach((item) => { //這里微信小程序獲取的服務ID是大寫,支付寶是小寫,所以統一轉換為大寫 let serviceId = item.uuid.toUpperCase(); if (serviceId == _self.UUID_SERVICE) { console.log('serverId:', serviceId) /* 進入特征值 */ _self.getBLEDeviceCharacteristics(deviceId, serviceId); } }) }, fail: (error) => { console.log("獲取服務失敗") console.log(error) uni.hideLoading(); } }) }, 1000) } /* 獲取所有特征值 */ getBLEDeviceCharacteristics(deviceId, serviceId) { console.log("開始獲取特征") setTimeout(() => { uni.getBLEDeviceCharacteristics({ deviceId, serviceId, success: (res) => { console.log(res) res.characteristics.forEach((item) => { console.log(item) //這里跟服務ID一樣,支付寶需要轉換為大寫 //#ifdef MP-ALIPAY let characterId = item.characteristicId.toUpperCase(); //#endif //#ifdef MP-WEIXIN let characterId = item.uuid; //#endif /* 只要用到的喚醒即可 */ if (characterId == _self.UUID_CHAR) { console.log('characteristicId:', characterId) _self.notifyBLECharacteristicValueChange(deviceId, serviceId, characterId) } }) }, fail: (res) => { uni.hideLoading(); console.log(res) } }) }, 1000) } /* 啟用藍牙設備特征值變化時的 notify 功能 */ notifyBLECharacteristicValueChange(deviceId, serviceId, characteristicId) { console.log("開始喚醒") uni.notifyBLECharacteristicValueChange({ state: true, // 啟用 notify 功能 // 這里的 deviceId 需要已經通過 createBLEConnection 與對應設備建立鏈接 deviceId, // 這里的 serviceId 需要在 getBLEDeviceServices 接口中獲取 serviceId, // 這里的 characteristicId 需要在 getBLEDeviceCharacteristics 接口中獲取 characteristicId, success: (res) => { uni.hideLoading(); /* 連接成功 */ uni.showToast({ title: "連接成功", icon: 'success' }); //儲存藍牙連接狀態 store.commit('updateStatus', true); console.log('notifyBLECharacteristicValueChange success', res.errMsg) /* 連接成功以后,執行設置的操作,並且打開返回值的監聽開關並監聽 */ //獲取設備信息 let DeviceCommand = "01,02,03,04," + func("01020304") + ",1E"; let DeviceArrayBuffer = string2buffer(DeviceCommand); _self.writeDevice(DeviceArrayBuffer); //出貨后關門延時設置 let delayedCommand = "01,02,03,04,0A," + func("010203040A") + ",1E"; let delayedArrayBuffer = string2buffer(delayedCommand); _self.writeDevice(delayedArrayBuffer); /* 監聽低功耗藍牙設備的特征值變化 */ uni.onBLECharacteristicValueChange(function(CALLBACK) { let result = blueTool.ab2hex(CALLBACK.value); console.log("收到的數據值==", result); // 這里做一些具體的業務邏輯 //我這里是監聽低功耗藍牙設備的特征值變化,然后調后台接口獲取設備的電量,展示在頁面 if ("73080182" === result.substring(0, 8)) { var o = result.substring(8, 10); o = parseInt(o, 16); let dl = ""; if (o >= 41) { dl = 100; } else if (o <= 37) { dl = 0; } else { dl = (o - 37) * 100 / (41 - 37); } //獲取設備電量 uni.request({ url: baseUrl+"/api/*****", method: "POST", data: { key: value }, header: { "Content-Type": "application/x-www-form-urlencoded" }, success: function(t) { store.commit('setDeviceBattery', t.data.data); console.log("設備上傳回調" + JSON.stringify(t.data)); }, fail: function(t) {} }); } }) }, fail: (res) => { uni.hideLoading(); console.log('notifyBLECharacteristicValueChange success', res.errMsg) } }) } /* 執行寫入命令操作 */ writeDevice(_Buffer) { uni.writeBLECharacteristicValue({ deviceId: _self.deviceId, serviceId: _self.UUID_SERVICE, characteristicId: _self.UUID_CHAR, value: _Buffer, success: (res) => { /* 發送成功 */ console.log('最后一次寫入成功', res) }, fail: (error) => { /* 發送失敗 */ console.log('最后一次寫入失敗', error) } }) } /* 獲取本機藍牙適配器狀態 */ getBluetoothState() { uni.getBluetoothAdapterState({ success: (res) => { console.log("狀態" + JSON.stringify(res)) } }) } /* 關閉藍牙適配器*/ closeBlueAdapter() { uni.closeBluetoothAdapter({ success() { console.log("關閉藍牙適配器成功") } }) } /* 斷開低功耗藍牙的連接 */ disconnectBle() { uni.closeBLEConnection({ deviceId: _self.deviceId, success: (res) => { console.log("斷開成功" + res) store.commit('updateStatus', false); store.commit('setDeviceBattery', 0); } }) }, /* 具體的命令BCC校驗*/ func(t) { for (var e = t, a = [], i = 0; i < e.length - 1; i += 2) a.push(e.substring(i, i + 2)); for (var o = a[0], s = 0; s < a.length - 1; s++) o ^= a[s + 1]; return o; }, /* 字符串轉arraybuffer */ string2buffer: function(str) { // 首先將字符串轉為16進制 let val = str console.log(val) // 將16進制轉化為ArrayBuffer return new Uint8Array(val.match(/[\da-f]{2}/gi).map(function(h) { return parseInt(h, 16) })).buffer }, }