前言
- 前段時(shí)間接手了一個(gè)微信小程序的開(kāi)發(fā)弱匪,主要使用了小程序在今年 3 月開(kāi)放的藍(lán)牙 API 畔裕,此過(guò)程踩坑無(wú)數(shù),特此記錄一下跳坑過(guò)程酿联。順便開(kāi)了另一個(gè)相關(guān)的小項(xiàng)目终息,歡迎 start 和
fork: BLE_MiniProgram
API簡(jiǎn)介
- 微信小程序目前有藍(lán)牙 API 共 18 個(gè)
- 操作藍(lán)牙適配器的共有 4 個(gè),分別是
wx.openBluetoothAdapter 初始化藍(lán)牙適配器
wx.closeBluetoothAdapter 關(guān)閉藍(lán)牙模塊
wx.getBluetoothAdapterState 獲取本機(jī)藍(lán)牙適配器狀態(tài)
wx.onBluetoothAdapterStateChange 監(jiān)聽(tīng)藍(lán)牙適配器狀態(tài)變化事件 - 連接前使用的共有 4 個(gè)贞让,分別是
wx.startBluetoothDevicesDiscovery 開(kāi)始搜尋附近的藍(lán)牙外圍設(shè)備
wx.stopBluetoothDevicesDiscovery 停止搜尋附近的藍(lán)牙外圍設(shè)備
wx.getBluetoothDevices 獲取所有已發(fā)現(xiàn)的藍(lán)牙設(shè)備
wx.onBluetoothDeviceFound 監(jiān)聽(tīng)尋找到新設(shè)備的事件 - 連接和斷開(kāi)時(shí)使用的共有 2 個(gè)周崭,分別是
wx.createBLEConnection 連接低功耗藍(lán)牙設(shè)備
wx.closeBLEConnection 斷開(kāi)與低功耗藍(lán)牙設(shè)備的連接 - 連接成功后使用的共有 8 個(gè),分別是
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è)備特征值變化時(shí)的 notify 功能
wx.onBLECharacteristicValueChange 監(jiān)聽(tīng)低功耗藍(lán)牙設(shè)備的特征值變化
wx.onBLEConnectionStateChange 監(jiān)聽(tīng)低功耗藍(lán)牙連接的錯(cuò)誤事件
基本操作流程
- 最基本的操作流程是:初始化藍(lán)牙適配器→開(kāi)始搜尋附近的藍(lán)牙外圍設(shè)備→監(jiān)聽(tīng)尋找到新設(shè)備的事件→連接低功耗藍(lán)牙設(shè)備→獲取藍(lán)牙設(shè)備所有 service 和 characteristic →讀取或?qū)懭氲凸乃{(lán)牙設(shè)備的特征值的二進(jìn)制數(shù)據(jù)值喳张。
踩過(guò)的幾個(gè)坑
支持藍(lán)牙 API 的版本
Android從微信 6.5.7 開(kāi)始支持续镇,iOS從微信 6.5.6 開(kāi)始支持,因此小程序中需要做好版本檢測(cè)销部,在 app.js 文件中加入以下代碼磨取,其中 wx.getSystemInfoSync 是一個(gè)獲取系統(tǒng)信息的API。
onLaunch: function() {
this.globalData.sysinfo = wx.getSystemInfoSync()
},
getModel: function () { //獲取手機(jī)型號(hào)
return this.globalData.sysinfo["model"]
},
getVersion: function () { //獲取微信版本號(hào)
return this.globalData.sysinfo["version"]
},
getSystem: function () { //獲取操作系統(tǒng)版本
return this.globalData.sysinfo["system"]
},
getPlatform: function () { //獲取客戶端平臺(tái)
return this.globalData.sysinfo["platform"]
},
getSDKVersion: function () { //獲取客戶端基礎(chǔ)庫(kù)版本
return this.globalData.sysinfo["SDKVersion"]
}
在初始頁(yè)面(一般是 index.wxml)對(duì)應(yīng)的 js 文件中使用 app.getPlatform() 和 app.getVersion() 即可獲取到客戶端平臺(tái)(安卓或 iOS)和微信版本號(hào)柴墩。在onLoad中獲取這兩個(gè)信息后進(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)前微信版本過(guò)低江咳,請(qǐng)更新至最新版本',
showCancel: false
})
}
else if (app.getPlatform() == 'ios' && this.versionCompare('6.5.6', app.getVersion())) {
wx.showModal({
title: '提示',
content: '當(dāng)前微信版本過(guò)低,請(qǐng)更新至最新版本',
showCancel: false
})
}
安卓 6.0 及以上設(shè)備需打開(kāi)定位服務(wù)
在測(cè)試中發(fā)現(xiàn)安卓 6.0 以上的手機(jī)未打開(kāi)系統(tǒng)定位服務(wù)時(shí)哥放,搜索不到藍(lán)牙設(shè)備歼指,因此最好在頁(yè)面中提示用戶打開(kāi)定位服務(wù)。
wx.onBluetoothDeviceFound 不兼容
安卓及iOS設(shè)備使用 wx.onBluetoothDeviceFound 時(shí)會(huì)出現(xiàn)不同的返回值甥雕,且有概率出現(xiàn)重復(fù)設(shè)備踩身,所以使用以下代碼可以清除重復(fù)的設(shè)備和解決 API 不兼容問(wèn)題。
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])
}
})
讀取廣播數(shù)據(jù)和特征值
小程序中讀取 BLE 廣播數(shù)據(jù)使用 wx.onBluetoothDeviceFound 接口中的 advertisData社露,對(duì)應(yīng)上面兼容問(wèn)題的 devices 格式挟阻,如 devices.advertisData,這個(gè)數(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('');
}
發(fā)送大于 20 字節(jié)的數(shù)據(jù)包
眾所周知,BLE 4.0 中發(fā)送一個(gè)數(shù)據(jù)包只能包含 20 字節(jié)的數(shù)據(jù)情臭,大于 20 字節(jié)只能分包發(fā)送省撑。微信小程序提供的 API 中似乎沒(méi)有自動(dòng)分包的功能赌蔑,這就只能自己手動(dòng)分包了。調(diào)試中發(fā)現(xiàn)竟秫,在 iOS 系統(tǒng)中調(diào)用 wx.writeBLECharacteristicValue 發(fā)送數(shù)據(jù)包娃惯,回調(diào) success 后緊接著發(fā)送下一個(gè)數(shù)據(jù)包,很少出現(xiàn)問(wèn)題鸿摇,可以很快全部發(fā)送完畢石景。而安卓系統(tǒng)中,發(fā)送一個(gè)數(shù)據(jù)包成功后緊接著發(fā)送下一個(gè)拙吉,很大概率會(huì)出現(xiàn)發(fā)送失敗的情況潮孽,在中間稍做延時(shí)再發(fā)送下一個(gè)就可以解決這個(gè)問(wèn)題(不同安卓手機(jī)的時(shí)間長(zhǎng)短也不一致),照顧下一些比較奇葩的手機(jī)筷黔,大概需要延時(shí) 250 ms 往史。不太好的但是比較科學(xué)的辦法是,只要成功發(fā)送一個(gè)數(shù)據(jù)包則發(fā)送下一個(gè)佛舱,否則不斷重發(fā)椎例,具體就是
wx.writeBLECharacteristicValue 回調(diào) fail 則重新發(fā)送,直至發(fā)送完畢请祖。
補(bǔ)充說(shuō)明
此處補(bǔ)充說(shuō)明一下订歪,華為榮耀部分機(jī)型、還有藍(lán)綠廠的部分機(jī)型肆捕,在藍(lán)牙 API 有深坑刷晋,謹(jǐn)慎調(diào)試。另:發(fā)現(xiàn)挺多同學(xué)沒(méi)有注意到官方文檔最下方的錯(cuò)誤碼列表慎陵,順便在此處貼出來(lái)眼虱。
藍(lán)牙錯(cuò)誤碼 (errCode) 列表
錯(cuò)誤碼 | 說(shuō)明 | 備注 |
---|---|---|
0 | ok | 正常 |
10000 | not init | 未初始化藍(lán)牙適配器 |
10001 | not available | 當(dāng)前藍(lán)牙適配器不可用 |
10002 | no device | 沒(méi)有找到指定設(shè)備 |
10003 | connection fail | 連接失敗 |
10004 | no service | 沒(méi)有找到指定服務(wù) |
10005 | no characteristic | 沒(méi)有找到指定特征值 |
10006 | no connection | 當(dāng)前連接已斷開(kāi) |
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 |