這裡給大家分享我在網上總結出來的一些知識,希望對大家有所幫助 本文簡介 這是一次真實的 藍牙收發數據 的全過程講解。 本文使用 uni-app + Vue3 的方式進行開發,以手機app的方式運行(微信小程式同樣可行)。 uni-app 提供了 藍牙 和 低功耗藍牙 的 api ,和微信小程式提供的 ...
這裡給大家分享我在網上總結出來的一些知識,希望對大家有所幫助
本文簡介
這是一次真實的 藍牙收發數據 的全過程講解。
本文使用 uni-app + Vue3 的方式進行開發,以手機app的方式運行(微信小程式同樣可行)。
uni-app 提供了 藍牙 和 低功耗藍牙 的 api ,和微信小程式提供的 api 是一樣的,所以本文的講解也適用於微信小程式。
本文只實現 藍牙收發數據 功能,至於樣式,我懶得調~
藍牙相關功能我會逐步講解。如果你基礎好,又急的話,可以直接跳到 『完整代碼』的章節查看,那裡沒廢話。
環境說明
開發工具:HBuilder X 3.4.7.20220422
uni-app + Vue3
以安卓App的方式運行(iOS和小程式同理)
思路
藍牙收發數據的邏輯和我們常用的 AJAX 進行的網路請求是有一丟丟不同的。
其中較大的區別是:藍牙接收數據不是那麼的穩定,相比起網路請求,藍牙更容易出現丟包的情況。
在開發中,AJAX 發起的請求不管成功還是失敗,瀏覽器基本都會給你一個答覆。但 uni-app 提供的 api 來看,藍牙接收數據會顯得更加 “非同步” 。
大致思路
使用藍牙進行數據傳輸的大概思路如下:
初始化:打開藍牙模塊
搜尋:檢測附近存在的設備
連接:找到目標設備進行
監聽:開啟監聽功能,接收其他設備傳過來的數據
發送指令:不管發送數據還是讀取數據,都可以理解為向外發送指令
實現
上面整理出使用藍牙傳輸數據的5大動作,但每個動作其實都是由 uni-app 提供的一個或者多個 api 組合而成。
初始化階段
使用藍牙之前,需要初始化藍牙模塊,這是最最最開始就要做的!
使用 uni.openBluetoothAdapter 這個 api 就可以初始化藍牙模塊。其他藍牙相關 API 必須在 uni.openBluetoothAdapter 調用之後使用。否則 API 會返回錯誤( errCode=10000 )。
代碼示例
<template> <view> <button @click="initBlue">初始化藍牙</button> </view> </template> <script setup> // 【1】初始化藍牙 function initBlue() { uni.openBluetoothAdapter({ success(res) { console.log('初始化藍牙成功') console.log(res) }, fail(err) { console.log('初始化藍牙失敗') console.error(err) } }) } </script>
如果你手機開啟了藍牙,點擊頁面上的按鈕後,控制台就會輸出如下內容
初始化藍牙成功 {"errMsg":"openBluetoothAdapter:ok"}
如果手機沒開啟藍牙,就會返回如下內容
初始化藍牙失敗 {"errMsg":"openBluetoothAdapter:fail not available","code":10001}
根據文檔提示,10001代表當前藍牙適配器不可用。
如果你的控制台能列印出 {"errMsg":"openBluetoothAdapter:ok"} 證明第一步已經成功了。
接下來可以開始搜索附近藍牙設備。
搜尋附近設備
這一步需要2個 api 配合完成。所以可以分解成以下2步:
開啟搜尋功能:uni.startBluetoothDevicesDiscovery
監聽搜尋到新設備:uni.onBluetoothDeviceFound
開發藍牙相關功能時,操作邏輯更像是推送,所以“開啟搜索”和“監聽新設備”是分開操作的。
uni.startBluetoothDevicesDiscovery 可以讓設備開始搜索附近藍牙設備,但這個方法比較耗費系統資源,建議在連接到設備之後就使用 uni.stopBluetoothDevicesDiscovery 停止繼續搜索。
uni.startBluetoothDevicesDiscovery 方法里可以傳入一個對象,該對象接收幾個參數,但初學的話我們只關註 success 和 fail。如果你的項目中硬體佬有提供 service 的 uuid 給你的話,你也可以在 services 里傳入。其他參數可以查看官方文檔的介紹。
在使用 uni.startBluetoothDevicesDiscovery (開始搜索)後,可以使用 uni.onBluetoothDeviceFound 進行監聽,這個方法裡面接收一個回調函數。
代碼示例
<template> <view> <scroll-view scroll-y class="box" > <view class="item" v-for="item in blueDeviceList"> <view> <text>id: {{ item.deviceId }}</text> </view> <view> <text>name: {{ item.name }}</text> </view> </view> </scroll-view> <button @click="initBlue">初始化藍牙</button> <button @click="discovery">搜索附近藍牙設備</button> </view> </template> <script setup> import { ref } from 'vue' // 搜索到的藍牙設備列表 const blueDeviceList = ref([]) // 【1】初始化藍牙 function initBlue() { uni.openBluetoothAdapter({ success(res) { console.log('初始化藍牙成功') console.log(res) }, fail(err) { console.log('初始化藍牙失敗') console.error(err) } }) } // 【2】開始搜尋附近設備 function discovery() { uni.startBluetoothDevicesDiscovery({ success(res) { console.log('開始搜索') // 開啟監聽回調 uni.onBluetoothDeviceFound(found) }, fail(err) { console.log('搜索失敗') console.error(err) } }) } // 【3】找到新設備就觸發該方法 function found(res) { console.log(res) blueDeviceList.value.push(res.devices[0]) } </script> <style> .box { width: 100%; height: 400rpx; box-sizing: border-box; margin-bottom: 20rpx; border: 2px solid dodgerblue; } .item { box-sizing: border-box; padding: 10rpx; border-bottom: 1px solid #ccc; } button { margin-bottom: 20rpx; } </style>
上面代碼的邏輯是,如果開啟 “尋找附近設備” 功能成功,接著就開啟 “監聽尋找到新設備的事件” 。
搜索到的設備會返回以下數據:
{ "devices": [{ "deviceId": "B4:10:7B:C4:83:14", "name": "藍牙設備名", "RSSI": -58, "localName": "", "advertisServiceUUIDs": ["0000FFF0-0000-1000-8000-00805F9B34FB"], "advertisData": {} }] }
每監聽到一個新的設備,我都會將其添加到 藍牙設備列表(blueDeviceList) 里,最後講這個列表的數據渲染到頁面上。
[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-cePpaik0-1652253685142)(https://p1-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/f3a21f372d3b40e996e3a0db9f4aa66b~tplv-k3u1fbpfcp-zoom-in-crop-mark:1956:0:0:0.image?)]
連接目標設備
連接目標設備只需要1個 api 就能完成。但根據文檔提示,我們連接後還需要關閉 “搜索附近設備” 的功能,這個很好理解,既然找到了,再繼續找就是浪費資源。
流程如下:
獲取設備ID:根據 uni.onBluetoothDeviceFound 回調,拿到設備ID
連接設備:使用設備ID進行連接 uni.createBLEConnection
停止搜索:uni.stopBluetoothDevicesDiscovery
我給每條搜索到的藍牙結果添加一個 click 事件,會向目標設備發送連接請求。
我的設備名稱是 leihou ,所以我點擊了這條。
代碼示例
<template> <view> <scroll-view scroll-y class="box" > <view class="item" v-for="item in blueDeviceList" @click="connect(item)"> <view> <text>id: {{ item.deviceId }}</text> </view> <view> <text>name: {{ item.name }}</text> </view> </view> </scroll-view> <button @click="initBlue">初始化藍牙</button> <button @click="discovery">搜索附近藍牙設備</button> </view> </template> <script setup> import { ref } from 'vue' // 搜索到的藍牙設備列表 const blueDeviceList = ref([]) // 【1】初始化藍牙 function initBlue() { uni.openBluetoothAdapter({ success(res) { console.log('初始化藍牙成功') console.log(res) }, fail(err) { console.log('初始化藍牙失敗') console.error(err) } }) } // 【2】開始搜尋附近設備 function discovery() { uni.startBluetoothDevicesDiscovery({ success(res) { console.log('開始搜索') // 開啟監聽回調 uni.onBluetoothDeviceFound(found) }, fail(err) { console.log('搜索失敗') console.error(err) } }) } // 【3】找到新設備就觸發該方法 function found(res) { console.log(res) blueDeviceList.value.push(res.devices[0]) } // 藍牙設備的id const deviceId = ref('') // 【4】連接設備 function connect(data) { console.log(data) deviceId.value = data.deviceId uni.createBLEConnection({ deviceId: deviceId.value, success(res) { console.log('連接成功') console.log(res) // 停止搜索 stopDiscovery() }, fail(err) { console.log('連接失敗') console.error(err) } }) } // 【5】停止搜索 function stopDiscovery() { uni.stopBluetoothDevicesDiscovery({ success(res) { console.log('停止成功') console.log(res) }, fail(err) { console.log('停止失敗') console.error(err) } }) } </script> <style> .box { width: 100%; height: 400rpx; box-sizing: border-box; margin-bottom: 20rpx; border: 2px solid dodgerblue; } .item { box-sizing: border-box; padding: 10rpx; border-bottom: 1px solid #ccc; } button { margin-bottom: 20rpx; } </style>
連接成功後在控制台會輸出
連接成功 {"errMsg":"createBLEConnection:ok"}
在連接成功後就立刻調用 uni.stopBluetoothDevicesDiscovery
方法停止繼續搜索附近其他設備,停止成功後會輸出
停止成功 {"errMsg":"stopBluetoothDevicesDiscovery:ok"}
監聽
在連接完設備後,就要先開啟監聽數據的功能。這樣才能接收到發送讀寫指令後設備給你回調的信息。
要開啟監聽,首先需要知道藍牙設備提供了那些服務,然後通過服務獲取特征值,特征值會告訴你哪個可讀,哪個可寫。最後根據特征值進行消息監聽。
步驟如下:
獲取藍牙設備服務:uni.getBLEDeviceServices
獲取特征值:uni.getBLEDeviceCharacteristics
開啟消息監聽:uni.notifyBLECharacteristicValueChange
接收消息監聽傳來的數據:uni.onBLECharacteristicValueChange
正常情況下,硬體佬會提前把藍牙設備的指定服務還有特征值告訴你。
比如我這個設備的藍牙服務是:0000FFE0-0000-1000-8000-00805F9B34FB
特征值是:0000FFE1-0000-1000-8000-00805F9B34FB
第一步,獲取藍牙服務
<template> <view> <!-- 省略上一步的代碼 --> <button @click="getServices">獲取藍牙服務</button> </view> </template> <script setup> import { ref } from 'vue' // 省略上一步的代碼…… // 【6】獲取服務 function getServices() { uni.getBLEDeviceServices({ deviceId: deviceId.value, // 設備ID,在上一步【4】里獲取 success(res) { console.log(res) }, fail(err) { console.error(err) } }) } </script>
此時點擊按鈕,將會獲取到已連接設備的所有服務。
我的設備有以下幾個服務。你在工作中拿到的 服務uuid
和我的是不一樣的,數量也不一定相同。
可以發現,我拿到的結果里有 0000FFE0-0000-1000-8000-00805F9B34FB
這條服務。
{ "services": [{ "uuid": "00001800-0000-1000-8000-00805F9B34FB", "isPrimary": true }, { "uuid": "00001801-0000-1000-8000-00805F9B34FB", "isPrimary": true }, { "uuid": "0000180A-0000-1000-8000-00805F9B34FB", "isPrimary": true }, { "uuid": "0000FFF0-0000-1000-8000-00805F9B34FB", "isPrimary": true }, { "uuid": "0000FFE0-0000-1000-8000-00805F9B34FB", "isPrimary": true }], "errMsg": "getBLEDeviceServices:ok" }
第二步,獲取指定服務的特征值
獲取特征值,需要傳 設備ID 和 服務ID。
在上兩步我拿到了 設備ID 為 B4:10:7B:C4:83:14
,服務ID 為 0000FFE0-0000-1000-8000-00805F9B34FB
。
<template> <view> <!-- 省略前面幾步代碼 --> <button @click="getCharacteristics">獲取特征值</button> </view> </template> <script setup> import { ref } from 'vue' // 省略前面幾步代碼 // 【7】獲取特征值 function getCharacteristics() { uni.getBLEDeviceCharacteristics({ deviceId: deviceId.value, // 設備ID,在【4】里獲取到 serviceId: '0000FFE0-0000-1000-8000-00805F9B34FB', // 服務UUID,在【6】里能獲取到 success(res) { console.log(res) }, fail(err) { console.error(err) } }) } </script>
最後成功輸出
{ "characteristics": [{ "uuid": "0000FFE1-0000-1000-8000-00805F9B34FB", "properties": { "read": true, "write": true, "notify": true, "indicate": false } }], "errMsg": "getBLEDeviceCharacteristics:ok" }
characteristics 欄位里保存了該服務的所有特征值,我的設備這個服務只有1個特征值,並且讀、寫、消息推送都為 true。
你的設備可能不止一條特征值,需要監聽那條特征值這需要你和硬體佬協商的(通常也是硬體佬直接和你說要監聽哪條)。
第三、四步,開啟消息監聽 並 接收消息監聽傳來的數據
根據已經拿到的 設備ID、服務ID、特征值,就可以開啟對應的監聽功能。
使用 uni.notifyBLECharacteristicValueChange 開啟消息監聽;
併在 uni.onBLECharacteristicValueChange 方法觸發監聽到的消息。
<template> <view> <!-- 省略前面幾步代碼 --> <button @click="notify">開啟消息監聽</button> </view> </template> <script setup> import { ref } from 'vue' // 省略前面幾步代碼 // 【8】開啟消息監聽 function notify() { uni.notifyBLECharacteristicValueChange({ deviceId: deviceId.value, // 設備ID,在【4】里獲取到 serviceId: '0000FFE0-0000-1000-8000-00805F9B34FB', // 服務UUID,在【6】里能獲取到 characteristicId: '0000FFE1-0000-1000-8000-00805F9B34FB', // 特征值,在【7】里能獲取到 success(res) { console.log(res) // 接受消息的方法 listenValueChange() }, fail(err) { console.error(err) } }) } // ArrayBuffer轉16進度字元串示例 function ab2hex(buffer) { const hexArr = Array.prototype.map.call( new Uint8Array(buffer), function (bit) { return ('00' + bit.toString(16)).slice(-2) } ) return hexArr.join('') } // 將16進位的內容轉成我們看得懂的字元串內容 function hexCharCodeToStr(hexCharCodeStr) { var trimedStr = hexCharCodeStr.trim(); var rawStr = trimedStr.substr(0, 2).toLowerCase() === "0x" ? trimedStr.substr(2) : trimedStr; var len = rawStr.length; if (len % 2 !== 0) { alert("存在非法字元!"); return ""; } var curCharCode; var resultStr = []; for (var i = 0; i < len; i = i + 2) { curCharCode = parseInt(rawStr.substr(i, 2), 16); resultStr.push(String.fromCharCode(curCharCode)); } return resultStr.join(""); } // 【9】監聽消息變化 function listenValueChange() { uni.onBLECharacteristicValueChange(res => { // 結果 console.log(res) // 結果里有個value值,該值為 ArrayBuffer 類型,所以在控制台無法用肉眼觀察到,必須將該值轉換為16進位 let resHex = ab2hex(res.value) console.log(resHex) // 最後將16進位轉換為ascii碼,就能看到對應的結果 let result = hexCharCodeToStr(resHex) console.log(result) }) } </script>
listenValueChange
方法是用來接收設備傳過來的消息。
上面的例子中,res
的結果是
{ "deviceId": "B4:10:7B:C4:83:14", "serviceId": "0000FFE0-0000-1000-8000-00805F9B34FB", "characteristicId": "0000FFE1-0000-1000-8000-00805F9B34FB", "value": {} }
設備傳過來的內容就放在 value 欄位里,但因為該欄位的類型是 ArrayBuffer,所以無法在控制台用肉眼直接觀察。於是就通過 ab2hex 方法將該值轉成 16進位 ,最後再用 hexCharCodeToStr 方法將 16進位 轉成 ASCII碼。
我從設備里發送一段字元串過來:leihou
App端收到的數據轉成 16進位 後的結果:6c6569686f75
再從 16進位 轉成 ASCII碼 後的結果:leihou
發送指令
終於到最後一步了。
從 uni-app 和 微信小程式 提供的藍牙api 來看,發送指令只要有2個方法:
uni.writeBLECharacteristicValue:向低功耗藍牙設備特征值中寫入二進位數據。
uni.readBLECharacteristicValue:讀取低功耗藍牙設備的特征值的二進位數據值。
這裡需要理清一個概念,本節的內容為 “發送指令” ,也就是說,從你的app或小程式向其他藍牙設備發送指令,而這個指令分2種情況,一種是你要發送一些數據給藍牙設備,另一種情況是你叫藍牙設備給你發點信息。
uni.writeBLECharacteristicValue
這兩種情況我們需要分開討論,先講講 uni.writeBLECharacteristicValue 。
uni.writeBLECharacteristicValue 從文檔可以看出,這個 api 是可以發送一些數據給藍牙設備,但發送的值要轉成 ArrayBuffer 。
代碼示例
<template> <view> <!-- 省略前面幾步代碼 --> <button @click="send">發送數據</button> </view> </template> <script setup> import { ref } from 'vue' // 省略前面幾步代碼 // 【10】發送數據 function send() { // 向藍牙設備發送一個0x00的16進位數據 let msg = 'hello' const buffer = new ArrayBuffer(msg.length) const dataView = new DataView(buffer) // dataView.setUint8(0, 0) for (var i = 0; i < msg.length; i++) { dataView.setUint8(i, msg.charAt(i).charCodeAt()) } uni.writeBLECharacteristicValue({ deviceId: deviceId.value, // 設備ID,在【4】里獲取到 serviceId: '0000FFE0-0000-1000-8000-00805F9B34FB', // 服務UUID,在【6】里能獲取到 characteristicId: '0000FFE1-0000-1000-8000-00805F9B34FB', // 特征值,在【7】里能獲取到 value: buffer, success(res) { console.log(res) }, fail(err) { console.error(err) } }) } </script>
此時,如果 uni.writeBLECharacteristicValue 走 success ,證明你已經把數據向外成功發送了,但不代表設備一定就收到了。
通常設備收到你發送過去的信息,會返回一條消息給你,而這個回調消息會在 uni.onBLECharacteristicValueChange 觸發,也就是 第【9】步 那裡。但這是藍牙設備那邊控制的,你作為前端佬,人家“已讀不回”你也拿人家沒辦法。
uni.readBLECharacteristicValue
在 “監聽” 部分,我們使用了 uni.getBLEDeviceCharacteristics 獲取設備的特征值,我的設備提供的特征值支持 read ,所以可以使用 uni.readBLECharacteristicValue 向藍牙設備發送一條 “讀取” 指令。然後在 uni.onBLECharacteristicValueChange 里可以接收設備發送過來的數據。
代碼示例
<template> <view> <!-- 省略前面幾步代碼 --> <button @click="read">讀取數據</button> </view> </template> <script setup> import { ref } from 'vue' // 省略前面幾步代碼 // 【11】讀取數據 function read() { uni.readBLECharacteristicValue({ deviceId: deviceId.value, serviceId: serviceId.value, characteristicId: characteristicId.value, success(res) { console.log('讀取指令發送成功') console.log(res) }, fail(err) { console.log('讀取指令發送失敗') console.error(err) } }) } </script>
使用 “讀取” 的方式向設備發送指令,是不需要另外傳值的。
此時我的設備返回 00
這個數據是硬體那邊設置的。
在日常工作中,uni.readBLECharacteristicValue 的作用主要是讀取數據,但使用場景不算很多。
我在工作中遇到的場景是:藍牙設備提供了幾個介面,而且傳過來的數據比較大,比如傳圖片給app這邊。我就會先用 uni.writeBLECharacteristicValue 告訴設備我現在需要取什麼介面的數據,然後用 uni.readBLECharacteristicValue 發送讀取數據的請求,如果數據量比較大,就要重覆使用 uni.readBLECharacteristicValue 進行讀取。比如上面的例子,我讀第一次的時候返回 00 ,讀第二次就返回 01 ……
最後再提醒一下,uni.readBLECharacteristicValue 只負責發送讀取的請求,並且裡面的 success 和 fail 只是返回你本次發送請求的動作是否成功,至於對面的藍牙設備有沒有收到這個指令你是不清楚的。
最後需要通過 uni.getBLEDeviceCharacteristics 監聽設備傳過來的數據。
完整代碼
<template> <view> <scroll-view scroll-y class="box" > <view class="item" v-for="item in blueDeviceList" @click="connect(item)"> <view> <text>id: {{ item.deviceId }}</text> </view> <view> <text>name: {{ item.name }}</text> </view> </view> </scroll-view> <button @click="initBlue">1 初始化藍牙</button> <button @click="discovery">2 搜索附近藍牙設備</button> <button @click="getServices">3 獲取藍牙服務</button> <button @click="getCharacteristics">4 獲取特征值</button> <button @click="notify">5 開啟消息監聽</button> <button @click="send">6 發送數據</button> <button @click="read">7 讀取數據</button> <view class="msg_x"> <view class="msg_txt"> 監聽到的內容:{{ message }} </view> <view class="msg_hex"> 監聽到的內容(十六進位):{{ messageHex }} </view> </view> </view> </template> <script setup> import { ref } from 'vue' // 搜索到的藍牙設備列表 const blueDeviceList = ref([]) // 【1】初始化藍牙 function initBlue() { uni.openBluetoothAdapter({ success(res) { console.log('初始化藍牙成功') console.log(res) }, fail(err) { console.log('初始化藍牙失敗') console.error(err) } }) } // 【2】開始搜尋附近設備 function discovery() { uni.startBluetoothDevicesDiscovery({ success(res) { console.log('開始搜索') // 開啟監聽回調 uni.onBluetoothDeviceFound(found) }, fail(err) { console.log('搜索失敗') console.error(err) } }) } // 【3】找到新設備就觸發該方法 function found(res) { console.log(res) blueDeviceList.value.push(res.devices[0]) } // 藍牙設備的id const deviceId = ref('') // 【4】連接設備 function connect(data) { console.log(data) deviceId.value = data.deviceId // 將獲取到的設備ID存起來 uni.createBLEConnection({ deviceId: deviceId.value, success(res) { console.log('連接成功') console.log(res) // 停止搜索 stopDiscovery() uni.showToast({ title: '連接成功' }) }, fail(err) { console.log('連接失敗') console.error(err) uni.showToast({ title: '連接成功', icon: 'error' }) } }) } // 【5】停止搜索 function stopDiscovery() { uni.stopBluetoothDevicesDiscovery({ success(res) { console.log('停止成功') console.log(res) }, fail(err) { console.log('停止失敗') console.error(err) } }) } // 【6】獲取服務 function getServices() { // 如果是自動鏈接的話,uni.getBLEDeviceServices方法建議使用setTimeout延遲1秒後再執行 uni.getBLEDeviceServices({ deviceId: deviceId.value, success(res) { console.log(res) // 可以在res里判斷有沒有硬體佬給你的服務 uni.showToast({ title: '獲取服務成功' }) }, fail(err) { console.error(err) uni.showToast({ title: '獲取服務失敗', icon: 'error' }) } }) } // 硬體提供的服務id,開發中需要問硬體佬獲取該id const serviceId = ref('0000FFE0-0000-1000-8000-00805F9B34FB') // 【7】獲取特征值 function getCharacteristics() { // 如果是自動鏈接的話,uni.getBLEDeviceCharacteristics方法建議使用setTimeout延遲1秒後再執行 uni.getBLEDeviceCharacteristics({ deviceId: deviceId.value, serviceId: serviceId.value, success(res) { console.log(res) // 可以在此判斷特征值是否支持讀寫等操作,特征值其實也需要提前向硬體佬索取的 uni.showToast({ title: '獲取特征值成功' }) }, fail(err) { console.error(err) uni.showToast({ title: '獲取特征值失敗', icon: 'error' }) } }) } const characteristicId = ref('0000FFE1-0000-1000-8000-00805F9B34FB') // 【8】開啟消息監聽 function notify() { uni.notifyBLECharacteristicValueChange({ deviceId: deviceId.value, // 設備id serviceId: serviceId.value, // 監聽指定的服務 characteristicId: characteristicId.value, // 監聽對應的特征值 success(res) { console.log(res) listenValueChange() uni.showToast({ title: '已開啟監聽' }) }, fail(err) { console.error(err) uni.showToast({ title: '監聽失敗', icon: 'error' }) } }) } // ArrayBuffer轉16進度字元串示例 function ab2hex(buffer) { const hexArr = Array.prototype.map.call( new Uint8Array(buffer), function (bit) { return ('00' + bit.toString(16)).slice(-2) } ) return hexArr.join('') } // 將16進位的內容轉成我們看得懂的字元串內容 function hexCharCodeToStr(hexCharCodeStr) { var trimedStr = hexCharCodeStr.trim(); var rawStr = trimedStr.substr(0, 2).toLowerCase() === "0x" ? trimedStr.substr(2) : trimedStr; var len = rawStr.length; if (len % 2 !== 0) { alert("存在非法字元!"); return ""; } var curCharCode; var resultStr = []; for (var i = 0; i < len; i = i + 2) { curCharCode = parseInt(rawStr.substr(i, 2), 16); resultStr.push(String.fromCharCode(curCharCode)); } return resultStr.join(""); } // 監聽到的內容 const message = ref('') const messageHex = ref('') // 十六進位 // 【9】監聽消息變化 function listenValueChange() { uni.onBLECharacteristicValueChange(res => { console.log(res) let resHex = ab2hex(res.value) console.log(resHex) messageHex.value = resHex let result = hexCharCodeToStr(resHex) console.log(String(result)) message.value = String(result) }) } // 【10】發送數據 function send() { // 向藍牙設備發送一個0x00的16進位數據 let msg = 'hello' const buffer = new ArrayBuffer(msg.length) const dataView = new DataView(buffer) // dataView.setUint8(0, 0) for (var i = 0; i < msg.length; i++) { dataView.setUint8(i, msg.charAt(i).charCodeAt()) } uni.writeBLECharacteristicValue({ deviceId: deviceId.value, serviceId: serviceId.value, characteristicId: characteristicId.value, value: buffer, success(res) { console.log('writeBLECharacteristicValue success', res.errMsg) uni.showToast({ title: 'write指令發送成功' }) }, fail(err) { console.error(err) uni.showToast({ title: 'write指令發送失敗', icon: 'error' }) } }) } // 【11】讀取數據 function read() { uni.readBLECharacteristicValue({ deviceId: deviceId.value, serviceId: serviceId.value, characteristicId: characteristicId.value, success(res) { console.log(res) uni.showToast({ title: 'read指令發送成功' }) }, fail(err) { console.error(err) uni.showToast({ title: 'read指令發送失敗', icon: 'error' }) } }) } </script> <style> .box { width: 98%; height: 400rpx; box-sizing: border-box; margin: 0 auto 20rpx; border: 2px solid dodgerblue; } .item { box-sizing: border-box; padding: 10rpx; border-bottom: 1px solid #ccc; } button { margin-bottom: 20rpx; } .msg_x { border: 2px solid seagreen; width: 98%; margin: 10rpx auto; box-sizing: border-box; padding: 20rpx; } .msg_x .msg_txt { margin-bottom: 20rpx; } </style>