前段時間接手了一個微信小程序的開發(fā),主要使用了小程序在今年 3 月開放的藍(lán)牙 API ,此過程踩坑無數(shù),特此記錄一下跳坑過程。順便開了另一個相關(guān)的小項(xiàng)目,歡迎 start 和 fork: BLE_MiniProgram
微信小程序目前有藍(lán)牙 API 共 18 個,其中操作藍(lán)牙適配器的共有 4 個,分別是
wx.openBluetoothAdapter 初始化藍(lán)牙適配器wx.closeBluetoothAdapter 關(guān)閉藍(lán)牙模塊wx.getBluetoothAdapterState 獲取本機(jī)藍(lán)牙適配器狀態(tài)wx.onBluetoothAdapterStateChange 監(jiān)聽藍(lán)牙適配器狀態(tài)變化事件
連接前使用的共有 4 個,分別是
wx.startBluetoothDevicesDiscovery 開始搜尋附近的藍(lán)牙外圍設(shè)備wx.stopBluetoothDevicesDiscovery 停止搜尋附近的藍(lán)牙外圍設(shè)備wx.getBluetoothDevices 獲取所有已發(fā)現(xiàn)的藍(lán)牙設(shè)備wx.onBluetoothDeviceFound 監(jiān)聽尋找到新設(shè)備的事件
連接和斷開時使用的共有 2 個,分別是
wx.createBLEConnection 連接低功耗藍(lán)牙設(shè)備wx.closeBLEConnection 斷開與低功耗藍(lán)牙設(shè)備的連接
連接成功后使用的共有 8 個,分別是
wx.getConnectedBluetoothDevices 根據(jù) uuid 獲取處于已連接狀態(tài)的設(shè)備wx.getBLEDeviceServices 獲取藍(lán)牙設(shè)備所有 service(服務(wù))wx.getBLEDeviceCharacteristics 獲取藍(lán)牙設(shè)備所有 characteristic(特征值)wx.readBLECharacteristicValue 讀取低功耗藍(lán)牙設(shè)備的特征值的二進(jìn)制數(shù)據(jù)值wx.writeBLECharacteristicValue 向低功耗藍(lán)牙設(shè)備特征值中寫入二進(jìn)制數(shù)據(jù)wx.notifyBLECharacteristicValueChange 啟用低功耗藍(lán)牙設(shè)備特征值變化時的 notify 功能wx.onBLECharacteristicValueChange 監(jiān)聽低功耗藍(lán)牙設(shè)備的特征值變化wx.onBLEConnectionStateChange 監(jiān)聽低功耗藍(lán)牙連接的錯誤事件
最基本的操作流程是:初始化藍(lán)牙適配器→開始搜尋附近的藍(lán)牙外圍設(shè)備→監(jiān)聽尋找到新設(shè)備的事件→連接低功耗藍(lán)牙設(shè)備→獲取藍(lán)牙設(shè)備所有 service 和 characteristic →讀取或?qū)懭氲凸乃{(lán)牙設(shè)備的特征值的二進(jìn)制數(shù)據(jù)值。
Android 從微信 6.5.7 開始支持,iOS 從微信 6.5.6 開始支持,因此小程序中需要做好版本檢測,在 app.js 文件中加入以下代碼,其中 wx.getSystemInfoSync 是一個獲取系統(tǒng)信息的API。
onLaunch: function() { this.globalData.sysinfo = wx.getSystemInfoSync() },getModel: function () { //獲取手機(jī)型號 return this.globalData.sysinfo["model"] },getVersion: function () { //獲取微信版本號 return this.globalData.sysinfo["version"] },getSystem: function () { //獲取操作系統(tǒng)版本 return this.globalData.sysinfo["system"] },getPlatform: function () { //獲取客戶端平臺 return this.globalData.sysinfo["platform"] },getSDKVersion: function () { //獲取客戶端基礎(chǔ)庫版本 return this.globalData.sysinfo["SDKVersion"] }
在初始頁面(一般是 index.wxml)對應(yīng)的 js 文件中使用 app.getPlatform() 和 app.getVersion() 即可獲取到客戶端平臺(安卓或 iOS)和微信版本號。在onLoad中獲取這兩個信息后進(jìn)行比較即可,使用了下面的版本比較方法。
versionCompare: function (ver1, ver2) { //版本比較 var version1pre = parseFloat(ver1) var version2pre = parseFloat(ver2) var version1next = parseInt(ver1.replace(version1pre + ".", "")) var version2next = parseInt(ver2.replace(version2pre + ".", "")) if (version1pre > version2pre) return true else if (version1pre < version2pre) return false else { if (version1next > version2next) return true else return false } }
if (app.getPlatform() == 'android' && this.versionCompare('6.5.7', app.getVersion())) { wx.showModal({ title: '提示', content: '當(dāng)前微信版本過低,請更新至最新版本', showCancel: false }) } else if (app.getPlatform() == 'ios' && this.versionCompare('6.5.6', app.getVersion())) { wx.showModal({ title: '提示', content: '當(dāng)前微信版本過低,請更新至最新版本', showCancel: false }) }
在測試中發(fā)現(xiàn)安卓 6.0 以上的手機(jī)未打開系統(tǒng)定位服務(wù)時,搜索不到藍(lán)牙設(shè)備,因此最好在頁面中提示用戶打開定位服務(wù)。
安卓及iOS設(shè)備使用 wx.onBluetoothDeviceFound 時會出現(xiàn)不同的返回值,且有概率出現(xiàn)重復(fù)設(shè)備,所以使用以下代碼可以清除重復(fù)的設(shè)備和解決 API 不兼容問題。
wx.onBluetoothDeviceFound(function (devices) { var isnotExist = true if (devices.deviceId) { for (var i = 0; i < foundDevice.length; i ++) { if (devices.deviceId == foundDevice[i].deviceId) { isnotExist = false } } if (isnotexist) foundDevice.push(devices) } else if (devices.devices) { for (var i = 0; i < foundDevice.length; i++) { if (devices.devices[0].deviceId == foundDevice[i].deviceId) { isnotExist = false } } if (isnotexist) foundDevice.push(devices.devices[0]) } else if (devices[0]) { for (var i = 0; i < foundDevice.length; i++) { if (devices[0].deviceId == foundDevice[i].deviceId) { isnotExist = false } } if (isnotexist) foundDevice.push(devices[0]) } })
小程序中讀取 BLE 廣播數(shù)據(jù)使用 wx.onBluetoothDeviceFound 接口中的 advertisData,對應(yīng)上面兼容問題的 devices 格式,如 devices.advertisData,這個數(shù)據(jù)是 ArrayBuffer,需要轉(zhuǎn)換,可以使用以下兩種轉(zhuǎn)換方法。另外 wx.getBLEDeviceCharacteristics 讀取的特征值 characteristic.value 也是 ArrayBuffer,用同樣的方法轉(zhuǎn)換。
buf2string: function (buffer) { var arr = Array.prototype.map.call(new Uint8Array(buffer), x => x) var str = '' for (var i = 0; i < arr.length; i++) { str += String.fromCharCode(arr[i]) } return str }
buf2hex: function (buffer) { return Array.prototype.map.call(new Uint8Array(buffer), x => ('00' + x.toString(16)).slice(-2)).join(''); }
眾所周知,BLE 4.0 中發(fā)送一個數(shù)據(jù)包只能包含 20 字節(jié)的數(shù)據(jù),大于 20 字節(jié)只能分包發(fā)送。微信小程序提供的 API 中似乎沒有自動分包的功能,這就只能自己手動分包了。調(diào)試中發(fā)現(xiàn),在 iOS 系統(tǒng)中調(diào)用 wx.writeBLECharacteristicValue 發(fā)送數(shù)據(jù)包,回調(diào) success 后緊接著發(fā)送下一個數(shù)據(jù)包,很少出現(xiàn)問題,可以很快全部發(fā)送完畢。而安卓系統(tǒng)中,發(fā)送一個數(shù)據(jù)包成功后緊接著發(fā)送下一個,很大概率會出現(xiàn)發(fā)送失敗的情況,在中間稍做延時再發(fā)送下一個就可以解決這個問題(不同安卓手機(jī)的時間長短也不一致),照顧下一些比較奇葩的手機(jī),大概需要延時 250 ms 。不太好的但是比較科學(xué)的辦法是,只要成功發(fā)送一個數(shù)據(jù)包則發(fā)送下一個,否則不斷重發(fā),具體就是
wx.writeBLECharacteristicValue 回調(diào) fail 則重新發(fā)送,直至發(fā)送完畢。
此處補(bǔ)充說明一下,華為榮耀部分機(jī)型、還有藍(lán)綠廠的部分機(jī)型,在藍(lán)牙 API 有深坑,謹(jǐn)慎調(diào)試。另:發(fā)現(xiàn)挺多同學(xué)沒有注意到官方文檔最下方的錯誤碼列表,順便在此處貼出來。
錯誤碼 | 說明 | 備注 |
---|---|---|
0 | ok | 正常 |
10000 | not init | 未初始化藍(lán)牙適配器 |
10001 | not available | 當(dāng)前藍(lán)牙適配器不可用 |
10002 | no device | 沒有找到指定設(shè)備 |
10003 | connection fail | 連接失敗 |
10004 | no service | 沒有找到指定服務(wù) |
10005 | no characteristic | 沒有找到指定特征值 |
10006 | no connection | 當(dāng)前連接已斷開 |
10007 | property not support | 當(dāng)前特征值不支持此操作 |
10008 | system error | 其余所有系統(tǒng)上報(bào)的異常 |
10009 | system not support | Android 系統(tǒng)特有,系統(tǒng)版本低于 4.3 不支持BLE |
聯(lián)系人:黃金
手 機(jī):153 0755 0221
郵 箱:jane@dzc.hk
公 司:深圳市山星盛電子科技有限公司-稱重產(chǎn)品官方展示網(wǎng)站
地 址:廣東省深圳市寶安鳳塘大道25號(山星盛電子秤)