前段時間接手了一個微信小程序的開發(fā),主要使用了小程序在今年 3 月開放的藍牙 API ,此過程踩坑無數(shù),特此記錄一下跳坑過程。順便開了另一個相關(guān)的小項目,歡迎 start 和 fork: BLE_MiniProgram
微信小程序目前有藍牙 API 共 18 個,其中操作藍牙適配器的共有 4 個,分別是
wx.openBluetoothAdapter 初始化藍牙適配器wx.closeBluetoothAdapter 關(guān)閉藍牙模塊wx.getBluetoothAdapterState 獲取本機藍牙適配器狀態(tài)wx.onBluetoothAdapterStateChange 監(jiān)聽藍牙適配器狀態(tài)變化事件
連接前使用的共有 4 個,分別是
wx.startBluetoothDevicesDiscovery 開始搜尋附近的藍牙外圍設(shè)備wx.stopBluetoothDevicesDiscovery 停止搜尋附近的藍牙外圍設(shè)備wx.getBluetoothDevices 獲取所有已發(fā)現(xiàn)的藍牙設(shè)備wx.onBluetoothDeviceFound 監(jiān)聽尋找到新設(shè)備的事件
連接和斷開時使用的共有 2 個,分別是
wx.createBLEConnection 連接低功耗藍牙設(shè)備wx.closeBLEConnection 斷開與低功耗藍牙設(shè)備的連接
連接成功后使用的共有 8 個,分別是
wx.getConnectedBluetoothDevices 根據(jù) uuid 獲取處于已連接狀態(tài)的設(shè)備wx.getBLEDeviceServices 獲取藍牙設(shè)備所有 service(服務(wù))wx.getBLEDeviceCharacteristics 獲取藍牙設(shè)備所有 characteristic(特征值)wx.readBLECharacteristicValue 讀取低功耗藍牙設(shè)備的特征值的二進制數(shù)據(jù)值wx.writeBLECharacteristicValue 向低功耗藍牙設(shè)備特征值中寫入二進制數(shù)據(jù)wx.notifyBLECharacteristicValueChange 啟用低功耗藍牙設(shè)備特征值變化時的 notify 功能wx.onBLECharacteristicValueChange 監(jiān)聽低功耗藍牙設(shè)備的特征值變化wx.onBLEConnectionStateChange 監(jiān)聽低功耗藍牙連接的錯誤事件
最基本的操作流程是:初始化藍牙適配器→開始搜尋附近的藍牙外圍設(shè)備→監(jiān)聽尋找到新設(shè)備的事件→連接低功耗藍牙設(shè)備→獲取藍牙設(shè)備所有 service 和 characteristic →讀取或?qū)懭氲凸乃{牙設(shè)備的特征值的二進制數(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 () { //獲取手機型號
    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中獲取這兩個信息后進行比較即可,使用了下面的版本比較方法。
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 以上的手機未打開系統(tǒng)定位服務(wù)時,搜索不到藍牙設(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ā)送下一個就可以解決這個問題(不同安卓手機的時間長短也不一致),照顧下一些比較奇葩的手機,大概需要延時 250 ms 。不太好的但是比較科學(xué)的辦法是,只要成功發(fā)送一個數(shù)據(jù)包則發(fā)送下一個,否則不斷重發(fā),具體就是
wx.writeBLECharacteristicValue 回調(diào) fail 則重新發(fā)送,直至發(fā)送完畢。
此處補充說明一下,華為榮耀部分機型、還有藍綠廠的部分機型,在藍牙 API 有深坑,謹慎調(diào)試。另:發(fā)現(xiàn)挺多同學(xué)沒有注意到官方文檔最下方的錯誤碼列表,順便在此處貼出來。
| 錯誤碼 | 說明 | 備注 | 
|---|---|---|
| 0 | ok | 正常 | 
| 10000 | not init | 未初始化藍牙適配器 | 
| 10001 | not available | 當(dāng)前藍牙適配器不可用 | 
| 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)上報的異常 | 
| 10009 | system not support | Android 系統(tǒng)特有,系統(tǒng)版本低于 4.3 不支持BLE | 
聯(lián)系人:黃金
手 機:153 0755 0221
郵 箱:jane@dzc.hk
公 司:深圳市山星盛電子科技有限公司-稱重產(chǎn)品官方展示網(wǎng)站
地 址:廣東省深圳市寶安鳳塘大道25號(山星盛電子秤)