Vue3 + UniApp 藍牙連接與數據發送(穩定版)

? ? ? 本教程適用于使用 uni-app + Vue3 (script setup) 開發的跨平臺 App(支持微信小程序、H5、Android/iOS 等)

🎯 功能目標

  • ? 獲取藍牙權限
  • ? 掃描周圍藍牙設備
  • ? 連接指定藍牙設備
  • ? 獲取服務和特征值
  • ? 向設備發送數據包(ArrayBuffer)
  • ? 頁面 UI 展示設備列表 + 操作按鈕

項目結構概覽

/pages/bluetooth/
├── index.vue         # 主頁面(本教程重點)
└── utils/Common.ts   # 公共方法(獲取系統信息等)

?其中的公共方法代碼:

export async function getSystemInfo() {return await uni.getSystemInfo();
}

第一步:申請藍牙權限并初始化藍牙適配器

onShow() 生命周期中檢查并申請藍牙權限:

import { onShow } from "@dcloudio/uni-app";
import { ref } from "vue";let btOpenStatus = ref<boolean>(false);
let devicesList = ref<UniApp.BluetoothDeviceInfo[]>([]);onShow(() => {uni.authorize({scope: 'scope.bluetooth',success() {console.log('藍牙權限已授權');initBluetooth();},fail() {showToast('請開啟藍牙權限!');}});
});

初始化藍牙模塊

function initBluetooth() {uni.onBluetoothAdapterStateChange(function (res) {btOpenStatus.value = res.available;if (res.available) startBluetoothScan(); // 藍牙打開后開始掃描});uni.openBluetoothAdapter({success: () => {startBluetoothScan();},fail: (err) => {if (err.errCode == 10001) {btOpenStatus.value = false;showToast('藍牙未打開!');}}});
}

🔍 第二步:掃描藍牙設備

function startBluetoothScan() {uni.startBluetoothDevicesDiscovery({success: (res) => {console.log("開始掃描藍牙設備...", res);},fail: (err) => {console.error("啟動掃描失敗", err);showToast("啟動藍牙掃描失敗");}});uni.onBluetoothDeviceFound((res) => {res.devices.forEach((device) => {const exists = devicesList.value.some(d => d.deviceId === device.deviceId);if (!exists) devicesList.value.push(device);});});
}

🔗 第三步:連接藍牙設備

const connectedDevice = ref({serviceOrFeature: [] as Array<{ service: any, characteristics?: any }>,devicesInfo: {} as UniApp.BluetoothDeviceInfo
});async function createBLEConnection(device: UniApp.BluetoothDeviceInfo) {uni.showToast({duration: 30000,icon: "loading",title: '藍牙正在連接中!'});uni.createBLEConnection({deviceId: device.deviceId,success(connectionRes) {if (connectionRes.errCode === 0) {showToast('藍牙連接成功');connectedDevice.value.devicesInfo = device;getBLEDeviceServices(device.deviceId).then(res => {if (res.code === 200) console.log('藍牙服務初始化完成');});}},fail(connectionRes) {if (connectionRes.errCode === 10000) {showToast('請檢查藍牙是否開啟!');} else if (connectionRes.errCode === 10010 || connectionRes.errCode === -1) {console.log('已經連接');}},complete() {uni.hideToast();}});
}

?? 第四步:獲取服務與特征值

function getBLEDeviceServices(deviceId: string): Promise<{ code: number }> {return new Promise(ok => {uni.getBLEDeviceServices({deviceId,success: (res) => {res.services.forEach(async (item) => {let characteristicsRes = await getBLEDeviceCharacteristics(deviceId, item.uuid);if (characteristicsRes.code === 200) {connectedDevice.value.serviceOrFeature.push({service: item,characteristics: characteristicsRes.data});ok({ code: 200 });}});},fail: (err) => {ok({ code: 201 });}});});
}function getBLEDeviceCharacteristics(deviceId: string, serviceId: string): Promise<{ code: number, data?: any }> {return new Promise(ok => {uni.getBLEDeviceCharacteristics({deviceId,serviceId,success: (res) => {ok({ code: 200, data: res.characteristics });},fail: () => {ok({ code: 201 });}});});
}

💬 第五步:向藍牙設備發送數據

function getBluetoothServiceFeature(propertyName: string): { serviceUUID: string, feature: any } {let result = { serviceUUID: '', feature: {} };connectedDevice.value.serviceOrFeature.forEach(item => {let found = item.characteristics.find(f => f.properties[propertyName]);if (found) {result.serviceUUID = item.service.uuid;result.feature = found;}});return result;
}function sendMsg(msg: any, isBuffer?: boolean) {let writeFeature = getBluetoothServiceFeature('write');if (!writeFeature) {console.log('藍牙沒有對應的寫服務權限!');return;}uni.writeBLECharacteristicValue({deviceId: connectedDevice.value.devicesInfo.deviceId,serviceId: writeFeature.serviceUUID,characteristicId: writeFeature.feature.uuid,value: isBuffer ? msg : stringToArrayBuffer(msg),success(res) {console.log('消息發送成功', res);},fail(res) {console.log('消息發送失敗', res);}});
}function stringToArrayBuffer(str: string): ArrayBuffer {const buffer = new ArrayBuffer(str.length);const view = new Uint8Array(buffer);for (let i = 0; i < str.length; i++) {view[i] = str.charCodeAt(i);}return buffer;
}

完整代碼

<template><template><scroll-view scroll-y style="height: 100vh;background: #f9f9f9;" class="device-list"><!-- 設備列表 --><view v-for="device in devicesList" :key="device.deviceId" class="device-card"><!-- 設備信息 --><view class="device-info"><text class="name">{{ device.name || '未知設備' }}</text><text class="id">ID: {{ device.deviceId }}</text></view><!-- 操作按鈕 --><view class="actions"><text class="btn connect" @click.stop="createBLEConnection(device)">連接</text><text class="btn send" @click.stop="sendMsg('測試發送信息')">發送信息</text></view></view><!-- 空狀態提示 --><view v-if="devicesList.length === 0" class="empty-state">正在搜索附近的藍牙設備...</view></scroll-view></template>
</template><script setup lang="ts">import { onShow } from "@dcloudio/uni-app";import { ref , watch } from "vue";import { getSystemInfo } from "@/utils/Common";let systemInfo = ref();	let btOpenStatus = ref<boolean>();let devicesList = ref<UniApp.BluetoothDeviceInfo[]>([]); // 用于存儲搜索到的設備onShow( async () => {systemInfo.value = await getSystemInfo();uni.authorize({scope: 'scope.bluetooth',success() {console.log('藍牙權限已授權');initBluetooth();},fail() {showToast('請開啟藍牙權限!');}});});function initBluetooth() {uni.onBluetoothAdapterStateChange(function (res) {console.log(`藍牙狀態變化,用戶${res.available ? '打開' : '關閉'}藍牙!`);btOpenStatus.value = res.available;if(res.available) {startBluetoothScan();}});uni.openBluetoothAdapter({success: () => {console.log("藍牙適配器已打開!");startBluetoothScan(); // 開始掃描設備},fail: (err) => {if (err.errCode == 10001) {btOpenStatus.value = false;showToast('藍牙未打開!');}}});}function startBluetoothScan() {uni.startBluetoothDevicesDiscovery({success: (res) => {console.log("開始掃描藍牙設備...",res);},fail: (err) => {console.error("啟動掃描失敗", err);showToast("啟動藍牙掃描失敗");}});// 監聽新發現的設備uni.onBluetoothDeviceFound((res) => {// 遍歷發現的設備res.devices.forEach((device) => {// 去重:根據 deviceId 判斷是否已存在const exists = devicesList.value.some(d => d.deviceId === device.deviceId);if (!exists) {devicesList.value.push(device);}});});}const connectedDevice = ref({serviceOrFeature: [] as Array<{ service: any, characteristics ? : any }>,devicesInfo: {} as UniApp.BluetoothDeviceInfo});/*** 連接藍牙設備*/async function createBLEConnection(device: UniApp.BluetoothDeviceInfo) {await uni.getLocation({});if(devicesList.value.length <= 0) {showToast('正在搜索附近的藍牙設備');return;}uni.showToast({duration: 30000,icon: "loading",title: '藍牙正在連接中!'});console.log('選擇的藍牙設備:',device);if(device) {connectedDevice.value.devicesInfo = device;uni.createBLEConnection({deviceId: device.deviceId,async success(connectionRes) {if(connectionRes.errCode == 0) {console.log('連接成功!');showToast('藍牙連接成功');let servicesRes = await getBLEDeviceServices(device.deviceId);if(servicesRes.code == 200) {console.log('藍牙初始化服務完成');}}},fail(connectionRes) {if(connectionRes.errCode == 10000) {showToast('請檢查藍牙是否開啟!');}else if(connectionRes.errCode == 10000) {showToast('藍牙連接失敗,可以重試!');}else if(connectionRes.errCode == 10010 || connectionRes.errCode == -1) {console.log('已經連接');}},complete() {uni.hideToast();}});}}/*** 獲取藍牙設備的服務(service)*/function getBLEDeviceServices(deviceId: string) : Promise<{code : number}> {return new Promise( ok => {uni.getBLEDeviceServices({deviceId,success: (res) => {res.services.forEach(async (item) => {let characteristicsRes = await getBLEDeviceCharacteristics(deviceId,item.uuid);if(characteristicsRes.code == 200) {connectedDevice.value.serviceOrFeature.push({service: item,characteristics: characteristicsRes.data});ok({ code : 200 });}});},fail: (err) => {console.log("獲取服務失敗", err);ok({ code : 201 });}});});}/*** 獲取藍牙設備的特征值(characteristic)*/async function getBLEDeviceCharacteristics(deviceId: string, serviceId: string) : Promise<{ code : number , data ? : any }> {return new Promise( ok => {uni.getBLEDeviceCharacteristics({deviceId,serviceId,success: (res) => {ok({code: 200,data: res.characteristics});},fail: () => {ok({code : 201})}});});}/*** 獲取連接設備的寫特征值(wirteCharacteristic)*/function getBluetoothServiceFeature(propertyName: string): { serviceUUID: string, feature: any } {let serviceFeatureInfo: { serviceUUID: string, feature: any } = { serviceUUID: '', feature: {} };connectedDevice.value.serviceOrFeature.forEach(item => {let foundFeature = item.characteristics.find((feature: any) => feature.properties[propertyName]);if (foundFeature) {serviceFeatureInfo.serviceUUID = item.service.uuid;serviceFeatureInfo.feature = foundFeature;return;}});return serviceFeatureInfo;}// 向藍牙寫數據function sendMsg(msg: any, isBuffer ? : boolean ) {console.log('發送的信息:',msg);	let writeServiceFeature = getBluetoothServiceFeature('write');if (!writeServiceFeature) {console.log('藍牙沒有對應的寫服務權限!');return;}uni.writeBLECharacteristicValue({deviceId: connectedDevice.value.devicesInfo.deviceId,serviceId: writeServiceFeature.serviceUUID,characteristicId: writeServiceFeature.feature.uuid, value: isBuffer ? msg : stringToArrayBuffer(msg) as any,writeType: systemInfo.value.osName == 'ios' ? 'write' : 'writeNoResponse',success(res) {console.log('消息發送成功', res);},fail(res) {console.log('消息發送失敗', res);}});}function stringToArrayBuffer(str: string): ArrayBuffer {const buffer = new ArrayBuffer(str.length);const view = new Uint8Array(buffer);for (let i = 0; i < str.length; i++) {view[i] = str.charCodeAt(i);}return buffer;}function showToast(title: string) {uni.showToast({icon: 'none',title});}</script><style lang="scss" scoped>.device-card {background-color: #fff;border-radius: 8px;padding: 16px;margin-bottom: 12px;box-shadow: 0 2px 4px rgba(0, 0, 0, 0.05);display: flex;flex-direction: column;gap: 10px;}.device-info {.name {font-weight: bold;font-size: 16px;color: #333;}.id {font-size: 14px;color: #888;display: block;margin-top: 4px;}}.actions {display: flex;gap: 10px;.btn {flex: 1;text-align: center;padding: 8px 0;border-radius: 4px;font-size: 14px;}.connect {color: #fff;background-color: #6659E5;}.send {color: #fff;background-color: #FC5531;}}.empty-state {text-align: center;padding: 20px;color: #999;}
</style>

🛠? 補充建議

功能實現方式
顯示 RSSI 信號強度在設備項中顯示?{{ device.RSSI }} dBm
自動刷新設備列表使用定時器每隔幾秒重新掃描
防止重復點擊連接添加?connectingDeviceId?狀態控制
發送自定義數據包使用?buildBluetoothPacket()?構造特定格式數據

📦 最終效果預覽

前端UI部分


📌 總結

? 本教程實現了從藍牙權限申請 → 設備掃描 → 連接設備 → 獲取服務 → 特征值讀寫 → 數據發送的一整套流程。

🎯 適用于智能門鎖、手環、打印機、IoT 等需要藍牙通信的場景。

💡 如果你需要對接具體藍牙協議(如 BLE 服務 UUID、數據格式),歡迎繼續提問,我可以幫你定制!

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/bicheng/83910.shtml
繁體地址,請注明出處:http://hk.pswp.cn/bicheng/83910.shtml
英文地址,請注明出處:http://en.pswp.cn/bicheng/83910.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

Docker + Nginx + Logrotate 日志管理與輪換實踐

概述與背景 Docker 容器化環境中 Nginx 日志管理的挑戰Logrotate 的作用與必要性結合場景的實際需求&#xff08;如日志切割、壓縮、歸檔&#xff09; Docker 環境下的 Nginx 日志配置 Nginx 日志路徑與 Docker 數據卷映射 volumes:- ./nginx/logs:/var/log/nginxLogrotate …

涂膠協作機器人解決方案 | Kinova Link 6 Cobot在涂膠工業的方案應用與價值

涂膠工業現狀背景&#xff1a; 涂膠工藝在汽車制造、電子組裝、航空航天等工業領域極為關鍵&#xff0c;關乎產品密封、防水、絕緣性能及外觀質量。 然而&#xff0c;傳統涂膠作業問題頻發。人工操作重復性強易疲勞&#xff0c;涂膠質量波動大&#xff1b;大型涂膠器使用增加工…

釋放模型潛力:淺談目標檢測微調技術(Fine-tuning)

引言 在計算機視覺領域&#xff0c;目標檢測是一項至關重要的任務&#xff0c;它不僅要識別出圖像中存在哪些物體&#xff0c;還要精確地定位它們的位置。從自動駕駛汽車識別行人與車輛&#xff0c;到醫療影像輔助診斷病灶&#xff0c;再到智能安防監控異常事件&#xff0c;目標…

Unreal從入門到精通之 UE4 vs UE5 VR性能優化實戰

文章目錄 前言:準備工作UE4 vs UE5 性能對比引擎核心技術方案對比UE5 優化總結項目設置可伸縮性組設置VolumetricCloud最后前言: 最近在使用UE5制作VR項目 制作完后發現,我們的場景一直很卡頓,場景優化也做到了極致,但是幀率最高也才30+ 但是我們看到一個競品,他的幀率竟…

爆炸仿真的學習日志

今天學習了一下【Workbench LS-DYNA中炸藥在空氣中爆炸的案例-嗶哩嗶哩】 https://b23.tv/kmXlN29 一開始 如果你的 ANSYS Workbench 工具箱&#xff08;Toolbox&#xff09;里 只有 SPEOS&#xff0c;即使嘗試了 右鍵刷新、重置視圖、顯示全部 等方法仍然沒有其他分析系統&a…

Redis部署架構詳解:原理、場景與最佳實踐

文章目錄 Redis部署架構詳解&#xff1a;原理、場景與最佳實踐單點部署架構原理適用場景優勢劣勢最佳實踐 主從復制架構原理消息同步機制1. 全量同步&#xff08;Full Resynchronization&#xff09;2. 部分重同步&#xff08;Partial Resynchronization&#xff09;3. 心跳檢測…

AI預測3D新模型百十個定位預測+膽碼預測+去和尾2025年6月6日第100彈

從今天開始&#xff0c;咱們還是暫時基于舊的模型進行預測&#xff0c;好了&#xff0c;廢話不多說&#xff0c;按照老辦法&#xff0c;重點8-9碼定位&#xff0c;配合三膽下1或下2&#xff0c;殺1-2個和尾&#xff0c;再殺4-5個和值&#xff0c;可以做到100-300注左右。 (1)定…

驗證電機理論與性能:電機試驗平板提升測試效率

電機試驗平板提升測試效率是驗證電機理論與性能的重要環節之一。通過在平板上進行電機試驗&#xff0c;可以對電機的性能參數進行準確測量和分析&#xff0c;從而驗證電機的理論設計是否符合實際表現。同時&#xff0c;提升測試效率可以加快試驗過程&#xff0c;節約時間和成本…

C語言 — 編譯和鏈接

目錄 1.程序從源文件到結果輸出的執行過程2.預處理3.編譯3.1 詞法分析3.2 語法分析3.3 語義分析3.4 生成test.s文件 4.匯編5.鏈接6.運行 1.程序從源文件到結果輸出的執行過程 2.預處理 預處理階段的執行操作&#xff1a; 預處理階段會將#define定義的常量或宏進行替換&#x…

傳統業務對接AI-AI編程框架-Rasa的業務應用實戰(5)--Rasa成型可用 rasa服務化部署及識別意圖后的決策及行為

此篇接續上一篇 傳統業務對接AI-AI編程框架-Rasa的業務應用實戰&#xff08;4&#xff09;--Rasa成型可用 針對業務配置rasa并訓練和部署 上一篇我們已經讓Rasa準確識別了我們自然語言指令的開票和查詢發票的意圖和實體。 # 開具發票場景 用戶輸入&#xff1a;開具一張1000元…

MajicTryOn(基于wanvideo的虛擬試穿項目)

網絡結構 Attention模塊詳解 左邊服裝通過qwen2.5-VL-7B來生成詳細的服裝描述&#xff1b;線條提取器產生相應的線條map&#xff1b;garment和line map通過vae轉換為潛在空間特征&#xff0c;然后分別經過patchfier,最后通過zero proj得到Garment Tokens和Line Tokens;右邊是di…

JAVA-什么是JDK?

1.JDK 的定義 JDK&#xff08;Java Development Kit&#xff09;是 Java 開發工具包&#xff0c;是 Oracle 官方提供的用于開發、編譯和運行 Java 應用程序的核心工具集。它包含了編寫 Java 程序所需的編譯器、調試工具、庫文件以及運行時環境&#xff08;JRE&#xff09;。 2…

Palo Alto Networks Expedition存在命令注入漏洞(CVE-2025-0107)

免責聲明 本文檔所述漏洞詳情及復現方法僅限用于合法授權的安全研究和學術教育用途。任何個人或組織不得利用本文內容從事未經許可的滲透測試、網絡攻擊或其他違法行為。使用者應確保其行為符合相關法律法規,并取得目標系統的明確授權。 對于因不當使用本文信息而造成的任何直…

分布式光纖傳感(DAS)技術應用解析:從原理到落地場景

近年來&#xff0c;分布式光纖傳感&#xff08;Distributed Acoustic Sensing&#xff0c;DAS&#xff09;技術正悄然改變著眾多傳統行業的感知方式。它將普通的通信光纜轉化為一個長距離、連續分布的“聽覺傳感器”&#xff0c;對振動、聲音等信號實現高精度、高靈敏度的監測。…

獨家首發!低照度環境下YOLOv8的增強方案——從理論到TensorRT部署

文章目錄 引言一、低照度圖像增強技術現狀1.1 傳統低照度增強方法局限性1.2 深度學習-based方法進展 二、Retinexformer網絡原理2.1 Retinex理論回顧2.2 Retinexformer創新架構2.2.1 光照感知Transformer2.2.2 多尺度Retinex分解2.2.3 自適應特征融合 三、YOLOv8-Retinexformer…

96. 2017年藍橋杯省賽 - Excel地址(困難)- 進制轉換

96. Excel地址&#xff08;進制轉換&#xff09; 1. 2017年藍橋杯省賽 - Excel地址&#xff08;困難&#xff09; 標簽&#xff1a;2017 省賽 1.1 題目描述 Excel 單元格的地址表示很有趣&#xff0c;它使用字母來表示列號。 比如&#xff0c; A 表示第 1 列&#xff0c;…

EtherNet/IP轉DeviceNet協議網關詳解

一&#xff0c;設備主要功能 疆鴻智能JH-DVN-EIP本產品是自主研發的一款EtherNet/IP從站功能的通訊網關。該產品主要功能是連接DeviceNet總線和EtherNet/IP網絡&#xff0c;本網關連接到EtherNet/IP總線中做為從站使用&#xff0c;連接到DeviceNet總線中做為從站使用。 在自動…

Druid連接池實現自定義數據庫密碼加解密功能詳解

Druid連接池實現自定義數據庫密碼加解密功能詳解 在企業級應用開發中&#xff0c;數據庫密碼的明文存儲是一個顯著的安全隱患。Druid作為阿里巴巴開源的高性能數據庫連接池組件&#xff0c;提供了靈活的密碼加密與解密功能&#xff0c;允許開發者通過自定義邏輯實現數據庫密碼…

生成 Git SSH 證書

&#x1f511; 1. ??生成 SSH 密鑰對?? 在終端&#xff08;Windows 使用 Git Bash&#xff0c;Mac/Linux 使用 Terminal&#xff09;執行命令&#xff1a; ssh-keygen -t rsa -b 4096 -C "your_emailexample.com" ??參數說明??&#xff1a; -t rsa&#x…

Java并發編程實戰 Day 12:阻塞隊列與線程協作

【Java并發編程實戰 Day 12】阻塞隊列與線程協作 開篇 歡迎來到“Java并發編程實戰”系列的第12天&#xff01;今天我們將深入探討阻塞隊列&#xff08;BlockingQueue&#xff09;及其在線程協作中的應用。阻塞隊列是Java并發編程中一個非常重要的工具&#xff0c;它不僅簡化…