前言
- 前段時間接手了一個微信小程序的開發,主要使用了小程序在今年 3 月開放的藍牙 API ,此過程踩坑無數,特此記錄一下跳坑過程。順便開了另一個相關的小項目,歡迎 start 和
fork: BLE_MiniProgram
API簡介
- 微信小程序目前有藍牙 API 共 18 個
- 操作藍牙適配器的共有 4 個,分別是
wx.openBluetoothAdapter 初始化藍牙適配器
wx.closeBluetoothAdapter 關閉藍牙模塊
wx.getBluetoothAdapterState 獲取本機藍牙適配器狀態
wx.onBluetoothAdapterStateChange 監聽藍牙適配器狀態變化事件 - 連接前使用的共有 4 個,分別是
wx.startBluetoothDevicesDiscovery 開始搜尋附近的藍牙外圍設備
wx.stopBluetoothDevicesDiscovery 停止搜尋附近的藍牙外圍設備
wx.getBluetoothDevices 獲取所有已發現的藍牙設備
wx.onBluetoothDeviceFound 監聽尋找到新設備的事件 - 連接和斷開時使用的共有 2 個,分別是
wx.createBLEConnection 連接低功耗藍牙設備
wx.closeBLEConnection 斷開與低功耗藍牙設備的連接 - 連接成功后使用的共有 8 個,分別是
wx.getConnectedBluetoothDevices 根據 uuid 獲取處于已連接狀態的設備
wx.getBLEDeviceServices 獲取藍牙設備所有 service(服務)
wx.getBLEDeviceCharacteristics 獲取藍牙設備所有 characteristic(特征值)
wx.readBLECharacteristicValue 讀取低功耗藍牙設備的特征值的二進制數據值
wx.writeBLECharacteristicValue 向低功耗藍牙設備特征值中寫入二進制數據
wx.notifyBLECharacteristicValueChange 啟用低功耗藍牙設備特征值變化時的 notify 功能
wx.onBLECharacteristicValueChange 監聽低功耗藍牙設備的特征值變化
wx.onBLEConnectionStateChange 監聽低功耗藍牙連接的錯誤事件
基本操作流程
- 最基本的操作流程是:初始化藍牙適配器→開始搜尋附近的藍牙外圍設備→監聽尋找到新設備的事件→連接低功耗藍牙設備→獲取藍牙設備所有 service 和 characteristic →讀取或寫入低功耗藍牙設備的特征值的二進制數據值。
踩過的幾個坑
支持藍牙 API 的版本
Android從微信 6.5.7 開始支持,iOS從微信 6.5.6 開始支持,因此小程序中需要做好版本檢測,在 app.js 文件中加入以下代碼,其中 wx.getSystemInfoSync 是一個獲取系統信息的API。
onLaunch: function() {
this.globalData.sysinfo = wx.getSystemInfoSync()
},
getModel: function () { //獲取手機型號
return this.globalData.sysinfo["model"]
},
getVersion: function () { //獲取微信版本號
return this.globalData.sysinfo["version"]
},
getSystem: function () { //獲取操作系統版本
return this.globalData.sysinfo["system"]
},
getPlatform: function () { //獲取客戶端平臺
return this.globalData.sysinfo["platform"]
},
getSDKVersion: function () { //獲取客戶端基礎庫版本
return this.globalData.sysinfo["SDKVersion"]
}
在初始頁面(一般是 index.wxml)對應的 js 文件中使用 app.getPlatform() 和 app.getVersion() 即可獲取到客戶端平臺(安卓或 iOS)和微信版本號。在onLoad中獲取這兩個信息后進行比較即可,使用了下面的版本比較方法。
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: '當前微信版本過低,請更新至最新版本',
showCancel: false
})
}
else if (app.getPlatform() == 'ios' && this.versionCompare('6.5.6', app.getVersion())) {
wx.showModal({
title: '提示',
content: '當前微信版本過低,請更新至最新版本',
showCancel: false
})
}
安卓 6.0 及以上設備需打開定位服務
在測試中發現安卓 6.0 以上的手機未打開系統定位服務時,搜索不到藍牙設備,因此最好在頁面中提示用戶打開定位服務。
wx.onBluetoothDeviceFound 不兼容
安卓及iOS設備使用 wx.onBluetoothDeviceFound 時會出現不同的返回值,且有概率出現重復設備,所以使用以下代碼可以清除重復的設備和解決 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 廣播數據使用 wx.onBluetoothDeviceFound 接口中的 advertisData,對應上面兼容問題的 devices 格式,如 devices.advertisData,這個數據是 ArrayBuffer,需要轉換,可以使用以下兩種轉換方法。另外 wx.getBLEDeviceCharacteristics 讀取的特征值 characteristic.value 也是 ArrayBuffer,用同樣的方法轉換。
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('');
}
發送大于 20 字節的數據包
眾所周知,BLE 4.0 中發送一個數據包只能包含 20 字節的數據,大于 20 字節只能分包發送。微信小程序提供的 API 中似乎沒有自動分包的功能,這就只能自己手動分包了。調試中發現,在 iOS 系統中調用 wx.writeBLECharacteristicValue 發送數據包,回調 success 后緊接著發送下一個數據包,很少出現問題,可以很快全部發送完畢。而安卓系統中,發送一個數據包成功后緊接著發送下一個,很大概率會出現發送失敗的情況,在中間稍做延時再發送下一個就可以解決這個問題(不同安卓手機的時間長短也不一致),照顧下一些比較奇葩的手機,大概需要延時 250 ms 。不太好的但是比較科學的辦法是,只要成功發送一個數據包則發送下一個,否則不斷重發,具體就是
wx.writeBLECharacteristicValue 回調 fail 則重新發送,直至發送完畢。
補充說明
此處補充說明一下,華為榮耀部分機型、還有藍綠廠的部分機型,在藍牙 API 有深坑,謹慎調試。另:發現挺多同學沒有注意到官方文檔最下方的錯誤碼列表,順便在此處貼出來。
藍牙錯誤碼 (errCode) 列表
錯誤碼 | 說明 | 備注 |
---|---|---|
0 | ok | 正常 |
10000 | not init | 未初始化藍牙適配器 |
10001 | not available | 當前藍牙適配器不可用 |
10002 | no device | 沒有找到指定設備 |
10003 | connection fail | 連接失敗 |
10004 | no service | 沒有找到指定服務 |
10005 | no characteristic | 沒有找到指定特征值 |
10006 | no connection | 當前連接已斷開 |
10007 | property not support | 當前特征值不支持此操作 |
10008 | system error | 其余所有系統上報的異常 |
10009 | system not support | Android 系統特有,系統版本低于 4.3 不支持BLE |