本文簡介 點贊 + 關註 + 收藏 = 學會了 這是一次真實的 藍牙收發數據 的全過程講解。 本文使用 uni-app + Vue3 的方式進行開發,以手機app的方式運行(微信小程式同樣可行)。 uni-app 提供了 藍牙 和 低功耗藍牙 的 api ,和微信小程式提供的 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) 里,最後講這個列表的數據渲染到頁面上。
連接目標設備
連接目標設備只需要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>
以上就是本文的完整代碼。
相關文檔
推薦閱讀