uni-app 藍牙通信

来源:https://www.cnblogs.com/k21vin/archive/2022/07/05/16448524.html
-Advertisement-
Play Games

本文簡介 點贊 + 關註 + 收藏 = 學會了 這是一次真實的 藍牙收發數據 的全過程講解。 本文使用 uni-app + Vue3 的方式進行開發,以手機app的方式運行(微信小程式同樣可行)。 uni-app 提供了 藍牙 和 低功耗藍牙 的 api ,和微信小程式提供的 api 是一樣的,所以 ...


本文簡介

點贊 + 關註 + 收藏 = 學會了


這是一次真實的 藍牙收發數據 的全過程講解。


本文使用 uni-app + Vue3 的方式進行開發,以手機app的方式運行(微信小程式同樣可行)。

uni-app 提供了 藍牙低功耗藍牙api ,和微信小程式提供的 api 是一樣的,所以本文的講解也適用於微信小程式


本文只實現 藍牙收發數據 功能,至於樣式,我懶得調~

藍牙相關功能我會逐步講解。如果你基礎好,又急的話,可以直接跳到 『完整代碼』的章節查看,那裡沒廢話。


file

花了幾塊錢巨款買回來的藍牙學習套裝~



環境說明

  • 開發工具:HBuilder X 3.4.7.20220422
  • uni-app + Vue3
  • 以安卓App的方式運行(iOS和小程式同理)


思路

藍牙收發數據的邏輯和我們常用的 AJAX 進行的網路請求是有一丟丟不同的。

其中較大的區別是:藍牙接收數據不是那麼的穩定,相比起網路請求,藍牙更容易出現丟包的情況。


在開發中,AJAX 發起的請求不管成功還是失敗,瀏覽器基本都會給你一個答覆。但 uni-app 提供的 api 來看,藍牙接收數據會顯得更加“非同步”



大致思路

使用藍牙進行數據傳輸的大概思路如下:

  1. 初始化:打開藍牙模塊
  2. 搜尋:檢測附近存在的設備
  3. 連接:找到目標設備進行
  4. 監聽:開啟監聽功能,接收其他設備傳過來的數據
  5. 發送指令:不管發送數據還是讀取數據,都可以理解為向外發送指令


實現

上面整理出使用藍牙傳輸數據的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代表當前藍牙適配器不可用

file

file

如果你的控制台能列印出 {"errMsg":"openBluetoothAdapter:ok"} 證明第一步已經成功了。

接下來可以開始搜索附近藍牙設備。



搜尋附近設備

這一步需要2個 api 配合完成。所以可以分解成以下2步:

  1. 開啟搜尋功能uni.startBluetoothDevicesDiscovery
  2. 監聽搜尋到新設備uni.onBluetoothDeviceFound

開發藍牙相關功能時,操作邏輯更像是推送,所以“開啟搜索”和“監聽新設備”是分開操作的。


uni.startBluetoothDevicesDiscovery 可以讓設備開始搜索附近藍牙設備,但這個方法比較耗費系統資源,建議在連接到設備之後就使用 uni.stopBluetoothDevicesDiscovery 停止繼續搜索。

uni.startBluetoothDevicesDiscovery 方法里可以傳入一個對象,該對象接收幾個參數,但初學的話我們只關註 successfail。如果你的項目中硬體佬有提供 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) 里,最後講這個列表的數據渲染到頁面上。

file



連接目標設備

連接目標設備只需要1個 api 就能完成。但根據文檔提示,我們連接後還需要關閉 “搜索附近設備” 的功能,這個很好理解,既然找到了,再繼續找就是浪費資源。


流程如下:

  1. 獲取設備ID:根據 uni.onBluetoothDeviceFound 回調,拿到設備ID
  2. 連接設備:使用設備ID進行連接 uni.createBLEConnection
  3. 停止搜索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"}

file

連接成功後,設備也亮起了綠燈。



監聽

在連接完設備後,就要先開啟監聽數據的功能。這樣才能接收到發送讀寫指令後設備給你回調的信息。

要開啟監聽,首先需要知道藍牙設備提供了那些服務,然後通過服務獲取特征值,特征值會告訴你哪個可讀,哪個可寫。最後根據特征值進行消息監聽


步驟如下:

  1. 獲取藍牙設備服務uni.getBLEDeviceServices
  2. 獲取特征值uni.getBLEDeviceCharacteristics
  3. 開啟消息監聽uni.notifyBLECharacteristicValueChange
  4. 接收消息監聽傳來的數據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

在上兩步我拿到了 設備IDB4:10:7B:C4:83:14服務ID0000FFE0-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

file

App端收到的數據轉成 16進位 後的結果:6c6569686f75

再從 16進位 轉成 ASCII碼 後的結果:leihou



發送指令

終於到最後一步了。

uni-app微信小程式 提供的藍牙api 來看,發送指令只要有2個方法:


這裡需要理清一個概念,本節的內容為 “發送指令”,也就是說,從你的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.writeBLECharacteristicValuesuccess ,證明你已經把數據向外成功發送了,但不代表設備一定就收到了。

通常設備收到你發送過去的信息,會返回一條消息給你,而這個回調消息會在 uni.onBLECharacteristicValueChange 觸發,也就是 第【9】步 那裡。但這是藍牙設備那邊控制的,你作為前端佬,人家“已讀不回”你也拿人家沒辦法。

file



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

file

這個數據是硬體那邊設置的。


在日常工作中,uni.readBLECharacteristicValue 的作用主要是讀取數據,但使用場景不算很多。

我在工作中遇到的場景是:藍牙設備提供了幾個介面,而且傳過來的數據比較大,比如傳圖片給app這邊。我就會先用 uni.writeBLECharacteristicValue 告訴設備我現在需要取什麼介面的數據,然後用 uni.readBLECharacteristicValue 發送讀取數據的請求,如果數據量比較大,就要重覆使用 uni.readBLECharacteristicValue 進行讀取。比如上面的例子,我讀第一次的時候返回 00 ,讀第二次就返回 01 ……


最後再提醒一下,uni.readBLECharacteristicValue 只負責發送讀取的請求,並且裡面的 successfail 只是返回你本次發送請求的動作是否成功,至於對面的藍牙設備有沒有收到這個指令你是不清楚的。

最後需要通過 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>

file

file


以上就是本文的完整代碼。



相關文檔

uni-app 藍牙文檔

uni-app 低功耗藍牙文檔


微信小程式 藍牙文檔

微信小程式 低功耗藍牙文檔



DataView 使用方法

推薦閱讀


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

-Advertisement-
Play Games
更多相關文章
  • mysql拆分字元串作為查詢條件 有個群友問一個問題 這表的ancestors列存放的是所有的祖先節點,以,分隔 例如我查詢dept_id為103的所有祖先節點,現在我只有一個dept_id該怎麼查 然後我去網上找到這樣一個神奇的sql,改改表名就成了下麵的這樣 SELECT substring_i ...
  • 在如今的業務場景下,高可用性要求越來越高,核心業務跨可用區已然成為標配。騰訊雲資料庫高級工程師劉家文結合騰訊雲資料庫的內核實戰經驗,給大家分享Redis是如何實現多可用區,內容包含Redis主從版、集群版原生架構,騰訊雲Redis集群模式主從版、多AZ架構實現以及多AZ關鍵技術點,具體可分為以下四個 ...
  • 本文主要介紹無損壓縮圖片的概要流程和原理,以及lepton無損壓縮在前期調研中遇到的問題。 ...
  • 安卓往系統中添加日程提醒,吭比較多。 首先有個需求(仿製 ios 日曆),為什麼仿製ios呢?這個得問產品了,我只是一個搬磚的程式員 (*´艸`) 捂嘴 大致有日期,時間,重覆設置,自定義重覆設置,位置提醒設置 跟系統日曆的設置類似,畢竟需要同步到系統,所以設置上面保持規範,都是設置好日期時間,然後 ...
  • 實現APP首頁tabbar滾動吸頂功能 首頁代碼: WillPopScope( child: Scaffold( backgroundColor: Colors.white, appBar: PreferredSize( preferredSize: Size(double.infinity, 0. ...
  • 如何搭建android源代碼repo倉庫 . 版本: v0.3 作者:河東西望 日期:2022-7-5 . 如果你的開發是基於AOSP源碼來建倉,那麼搭建repo伺服器和部署自己的repo倉庫就是非常必要的工作了。 現實中很多公司都是直接把AOSP源代碼放在一個git倉庫中來管理,這樣做沒什麼毛病。 ...
  • 在互聯網世界,驗證用戶身份是一個常見又重要的場景,應用最廣泛的方式當屬帳號密碼驗證。隨著開發者對身份驗證安全性要求不斷提升,加之用戶更加註重過程中的隱私與便捷,身份驗證的方式逐漸多樣化,有動態令牌、簡訊驗證碼、生物特征認證等方式。本文主要從安全性的角度,探討幾種常見身份驗證方式存在的安全漏洞,為開發 ...
  • 本文簡介 點贊 + 關註 + 收藏 = 學會了 ES6 推出的 const 可以定義常量。在 JS 中,常量是不可改變的。這個 “不可改變” 指的是常量存放的記憶體地址不變。 眾所周知,使用 const 定義的常量,如果是基礎類型的數據,值不能變。但如果是引用類型的數據(比如對象、數組等),是可以修改 ...
一周排行
    -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.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...