微信小程序之藍牙 BLE 踩坑記錄

前言

  • 前段時間接手了一個微信小程序的開發,主要使用了小程序在今年 3 月開放的藍牙 API ,此過程踩坑無數,特此記錄一下跳坑過程。順便開了另一個相關的小項目,歡迎 start 和
    fork: BLE_MiniProgram

API簡介

  • 微信小程序目前有藍牙 API 共 18 個
  1. 操作藍牙適配器的共有 4 個,分別是
    wx.openBluetoothAdapter 初始化藍牙適配器
    wx.closeBluetoothAdapter 關閉藍牙模塊
    wx.getBluetoothAdapterState 獲取本機藍牙適配器狀態
    wx.onBluetoothAdapterStateChange 監聽藍牙適配器狀態變化事件
  2. 連接前使用的共有 4 個,分別是
    wx.startBluetoothDevicesDiscovery 開始搜尋附近的藍牙外圍設備
    wx.stopBluetoothDevicesDiscovery 停止搜尋附近的藍牙外圍設備
    wx.getBluetoothDevices 獲取所有已發現的藍牙設備
    wx.onBluetoothDeviceFound 監聽尋找到新設備的事件
  3. 連接和斷開時使用的共有 2 個,分別是
    wx.createBLEConnection 連接低功耗藍牙設備
    wx.closeBLEConnection 斷開與低功耗藍牙設備的連接
  4. 連接成功后使用的共有 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
最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 227,818評論 6 531
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 98,185評論 3 414
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 175,656評論 0 373
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 62,647評論 1 309
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 71,446評論 6 405
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 54,951評論 1 321
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,041評論 3 440
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,189評論 0 287
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 48,718評論 1 333
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 40,602評論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 42,800評論 1 369
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,316評論 5 358
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,045評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,419評論 0 26
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,671評論 1 281
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,420評論 3 390
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 47,755評論 2 371

推薦閱讀更多精彩內容

  • Guide to BluetoothSecurity原文 本出版物可免費從以下網址獲得:https://doi.o...
    公子小水閱讀 8,101評論 0 6
  • 給提問的開發者的建議:提問之前先查詢 文檔、通過社區右上角搜索搜索已經存在的問題。 寫一個簡明扼要的標題,并且...
    極樂叔閱讀 13,510評論 0 3
  • 藍牙 藍牙的波段為2400-2483.5MHz(包括防護頻帶)。這是全球范圍內無需取得執照(但定不是無管制的)的工...
    蘇永茂閱讀 6,251評論 0 11
  • 背景 藍牙歷史說到藍牙,就不得不說下藍牙技術聯盟(Bluetooth SIG),它負責藍牙規范制定和推廣的國際組織...
    徐正峰閱讀 12,445評論 6 33
  • 第一章 我們每個人都有自己的系統,在使用這個系統的過程中不斷的去完善它優化它。 沒有必要一次性把一本書看完,人記住...
    kinfoNg_1998閱讀 277評論 0 0