記錄--『uni-app、小程式』藍牙連接、讀寫數據全過程

来源:https://www.cnblogs.com/smileZAZ/archive/2023/02/15/17124076.html
-Advertisement-
Play Games

這裡給大家分享我在網上總結出來的一些知識,希望對大家有所幫助 本文簡介 這是一次真實的 藍牙收發數據 的全過程講解。 本文使用 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> 

本文轉載於:

https://blog.csdn.net/pfourfire/article/details/124711068

如果對您有所幫助,歡迎您點個關註,我會定時更新技術文檔,大家一起討論學習,一起進步。

 


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 閱識風雲是華為雲信息大咖,擅長將複雜信息多元化呈現,其出品的一張圖(雲圖說)、深入淺出的博文(雲小課)或短視頻(雲視廳)總有一款能讓您快速上手華為雲。更多精彩內容請單擊此處。 摘要:當HDFS集群出現DataNode節點間磁碟利用率不平衡時,會導致MapReduce應用程式無法很好地利用本地計算的優 ...
  • pdf下載:密碼7281 專欄目錄首頁:【專欄必讀】(考研覆試)資料庫系統概論第五版(王珊)專欄學習筆記目錄導航及課後習題答案詳解 SQL數據更新主要有三種形式 插入數據(INSERT) 修改數據(UPDATE) 刪除數據(DELETE) 一:插入數據(INSERT) (1)插入元組 語法:格式如下 ...
  • 1.創建物化視圖 alter session set container=pdb; grant create materialized view to scott; create materialized view 物化視圖名 -- 1. 創建物化視圖build [immediate | defer ...
  • 摘要:本篇文檔為使用GDS導入示例的具體簡單步驟和示例。 本文分享自華為雲社區《帶你快速入門GDS導入導出,玩轉PB級數倉GaussDB(DWS)》,作者: yd_220527686。 1、創建導入目標表 CREATE TABLE tpcds_reasons ( r_reason_sk intege ...
  • GreatSQL社區原創內容未經授權不得隨意使用,轉載請聯繫小編並註明來源。 GreatSQL是MySQL的國產分支版本,使用上與MySQL一致。 作者: 花家舍 文章來源:GreatSQL社區原創 前文回顧 實現一個簡單的Database系列 譯註:cstack在github維護了一個簡單的、類似 ...
  • 本文翻譯自: Composition in Flutter & Dart 在 Flutter & Dart 中使用組合創建模塊化應用程式。 什麼是組合? 在dictionary.com 中 composition 的定義為:將部分或者元素組合成一個整體的行為。簡單說,組合就像堆樂高積木,我們可以將積 ...
  • 相關文檔鏈接 https://developer.mozilla.org/zh-CN/docs/Web/API/fetch https://developer.mozilla.org/zh-CN/docs/Web/API/Fetch_API/Using_Fetch https://developer ...
  • html篇之《表格》 1. 結構 <table> <!-- 表格標簽 --> <caption>標題,自動居中對齊</caption> <thead> <!-- 表格結構化-表頭,內容始終保持在頁面最上方,不受tbody和tfoot擺放順序影響 --> <tr> <!-- 行標簽 --> <th>表 ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...