uniapp 实现 ble蓝牙同时连接多台蓝牙设备,支持app、苹果(ios)和安卓手机,以及ios连接蓝牙后的一些坑

首先对 uniapp BLE蓝牙API进行封装

这里我封装了一个类:bluetoothService.js

代码:

import { throttle } from 'lodash'
export default class Bluetooth {
    constructor() {
        this.device = {};
        this.connected = false;
        // 使用箭头函数绑定类实例的上下文,并在构造函数中初始化
        // 你可以在这里传入蓝牙信息进行保存
        this.throttledUpdate = throttle(async (params) => {
             // 多设备上传数据建议做下节流处理
             console.log(params)
            }
        }, 700); // 节流: 规定时间内最多更新一次

    }
    init() {
        return new Promise((resolve, reject) => {
            uni.openBluetoothAdapter({
                success: (res) => {
                    resolve(res)
                    console.log("初始化成功", res)
                },
                fail: (err) => {
                    console.log("初始化失败:", err)
                    reject(err)
                },
            })
        })
    }
    closeBluetoothAdapter() {
        return new Promise((resolve, reject) => {
            uni.closeBluetoothAdapter({
                success: (res) =>  {
                    resolve(res)
                },
                fail: (err) => {
                    reject(err)
                }
            })
        })
    }
    searchDevices() {
        return new Promise((resolve, reject) => {
            uni.startBluetoothDevicesDiscovery({
                success: () => {
                    console.log('开始搜索蓝牙设备');
                    uni.onBluetoothDeviceFound((res) => {
                        if (res.devices[0]?.name.startsWith("SW")) { // 过滤条件只获取含SW开头名称的蓝牙,大家按需修改修改
                            console.log(res)
                            // 这里是设备,这里扫描到的设备,可以保存起来
                            resolve(res.devices)
                        }

                    });
                },
                fail: (err) => {
                    console.error('搜索蓝牙设备失败:', err);
                    reject(err)
                },
            });
        })

    }
    stopSearchDevices() {
        return new Promise((resolve, reject) => {
            uni.stopBluetoothDevicesDiscovery({
                success: () => {
                    console.log('停止搜索蓝牙设备');
                    resolve('停止搜索蓝牙设备')
                },
                fail: (err) => {
                    console.error('停止搜索蓝牙设备失败:', err);
                    reject(err);
                }
            });
        })

    }
    connect(deviceId) {
        return new Promise((resolve, reject) => {
            uni.createBLEConnection({
                deviceId: deviceId,
                success: (res) => {
                    this.device.deviceId = deviceId;
                    this.connected = true;
                    resolve(res)
                },
                fail: (err) => {
                    console.error('蓝牙连接失败:', deviceId, err); // 处理连接失败
                    reject(err)
                },
                complete: () => {
                    // uni.hideLoading();
                    this.stopSearchDevices()
                }
            });
        });
    }
    // 断开连接
    disconnect() {
        return new Promise((resolve, reject) => {
            if (this.connected) {
                uni.closeBLEConnection({
                    deviceId: this.device.deviceId,
                    success: () => {
                        this.connected = false;
                        this.device = {};
                        resolve();
                    },
                    fail: (err) => {
                        reject(err);
                    }
                });
            } else {
                resolve();
            }
        });
    }
    // 监听蓝牙连接状态
    listenBLEStateChange() {
        return new Promise((resolve, reject) => {
            uni.onBLEConnectionStateChange((res) => {
                console.log(`device ${res.deviceId} state has changed, connected: ${res.connected}`);
                if (res.connected) {
                    this.connected = true;
                    resolve(res);
                } else {
                    this.connected = false;
                    reject(res)
                }
            });
        });
    }
    // 获取服务UUID
    getServices() {
        return new Promise((resolve, reject) => {
            uni.getBLEDeviceServices({
                deviceId: this.device.deviceId,
                success: (res) => {
                    console.log('获取服务成功:', res.services);
                    // 这里你们需要根据你们的设备修改
                    // this.device.serviceId = res.services[2]?.uuid;
                    this.device.serviceId = uni.getSystemInfoSync().platform === "android" ? res.services[2]?.uuid : res.services[1]?.uuid;
                    resolve(res)
                },
                fail: (err) => {
                    console.error('获取服务失败:', err);
                    reject(err)
                },
            });
        })
    }
    getCharacteristics() {
        return new Promise((resolve, reject) => {
            uni.getBLEDeviceCharacteristics({
                deviceId: this.device.deviceId,
                serviceId: this.device.serviceId,
                success: (res) => {
                    console.log('获取特征值成功:', res);
                    // 找到对应的特征值UUID
                    res.characteristics.forEach((item) => {
                        if (item.properties.notify === true) {
                            this.device.characteristicsNotify = item.uuid // 监听特征
                        }
                        if (item.properties.write === true) {
                            this.device.characteristicsWrite = item.uuid // 写入特征
                        }
                    })
                    resolve(this.device)
                },
                fail: (err) => {
                    console.error('获取特征值失败:', err);
                    reject(err)
                    // 处理获取特征值失败
                },
            });
        })

    }
    // 开启监听
    startNotify() {
        return new Promise((resolve, reject) => {
            uni.notifyBLECharacteristicValueChange({
                state: true,
                deviceId: this.device.deviceId,
                serviceId: this.device.serviceId,
                characteristicId: this.device.characteristicsNotify,
                success: () => {
                    this.listenBLEStateChange(); // 监听蓝牙状态
                    resolve(this.device);
                },
                fail: (err) => {
                    console.error('开启notify失败:', err);
                },
            });
        })
    }
    // 接收数据
    receiveData() {
        return new Promise((resolve, reject) => {
            uni.onBLECharacteristicValueChange((res) => {
                console.log("蓝牙上传的数据:", this.ab2hex(res.value))
                resolve(this.ab2hex(res.value));
            });
        });
    }
    // 发送数据
    sendData(data) {
        return new Promise((resolve, reject) => {
            if (this.connected) {
                uni.writeBLECharacteristicValue({
                    deviceId: this.device.deviceId,
                    serviceId: this.device.serviceId,
                    characteristicId: this.device.characteristicsWrite,
                    value: data,
                    success: () => {
                        resolve(this.device);
                    },
                    fail: (err) => {
                        console.log("蓝牙指令写入失败:", err)
                        reject(err);
                    }
                });
            } else {
                reject('蓝牙未连接');
            }
        });
    }
    // 监听信号
    listenRSSI(deviceId, receiveData) {
        return new Promise((resolve, reject) => {
            uni.getBLEDeviceRSSI({
                deviceId: deviceId,
                success: (res) => {
                    resolve(res.RSSI);
                },
                fail: (err) => {
                    console.error("获取 RSSI 失败:", err);
                    reject(err);
           
                }
            });
        });
    }

    ab2hex(buffer) {
        // ArrayBuffer转16进度字符串示例
        const hexArr = Array.prototype.map.call(new Uint8Array(buffer), function (bit) {
            return ("00" + bit.toString(16)).slice(-2)
        })
        return hexArr.join("").toUpperCase()
    }
    delay(ms) {
        return new Promise(resolve => setTimeout(resolve, ms));
    }

}

同时连接多台蓝牙设备的连接操作方法:

// 封装异步操作函数(uniapp连接蓝牙获取服务需要延迟, 否则会出现无法获取服务情况)   
async connectAndConfigureDevice(deviceId) {
      try {
        const bluetooth = new Bluetooth()
        await bluetooth.connect(deviceId)
        await bluetooth.delay(1000)
        await bluetooth.getServices()
        await bluetooth.delay(1000)
        await bluetooth.getCharacteristics()
        await bluetooth.delay(1000)
        await bluetooth.startNotify()
        await bluetooth.delay(1000)
        bluetooth.receiveData()
        return bluetooth
      } catch (err) {
        console.log(err); // 连接出现错误
      }
    },

    async doConnect() {
      // deviceList 是你保存的每个蓝牙设备信息的数组
      if (this.deviceList.length > 0) {
        // 循环连接每个设备
        for (const device of this.deviceList) {
            const bluetooth = await this.connectAndConfigureDevice(device.deviceId); // 传入deviceId进行连接
            if (bluetooth) {
              console.log("连接成功"); // 这里可以把每个连接成功蓝牙实例(bluetooth)的信息保存起来,建议保存到vuex中,方便后续对某个蓝牙设备的操作
            }
          }
        }
      }
    },

ios有一个坑,需要配置后台运行能力,否则切换后台蓝牙会暂停数据上传

这是由于ios系统限制导致的,需要配置后台运行能力,在Hbuilderx中配置即可,如下图

 

 

c29a698f2064bd9586f5826c55b96287.png

 

uniapp官方说明:uni-app官网

 

"audio"表示后台播放音乐能力,"location"表示后台定位能力,'bluetooth-central'表示后台蓝牙功能。

更多后台能力配置参考苹果官网UIBackgroundModes文档

 

 

 

995762ae6a5d72dc93f4ae672aa68d83.png

 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/916719.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

波段多空强弱指标案例,源码分享

俗话说,涨有涨势,跌有跌势,最怕涨跌不成型。对于波段来说,不论上涨还是下跌,都是可以进行操作或者回避的。但是波动的走势,往往只有走完才能完全确认。那么能不能量化波段里面涨跌的强弱变化呢?…

第21课-C++[set和map学习和使用]

🌼引言 C 标准模板库(STL)中的 set 和 map 是两种非常实用的关联式容器。它们具备快速查找、有序存储的特点,因而在很多需要高效数据管理的场景中被广泛应用。本文将深入讲解 set 和 map 的用法,并通过实际例子分析如何…

视频流媒体播放器EasyPlayer.js RTSP播放器视频颜色变灰色/渲染发绿的原因分析

EasyPlayer.js RTSP播放器属于一款高效、精炼、稳定且免费的流媒体播放器,可支持多种流媒体协议播放,无须安装任何插件,起播快、延迟低、兼容性强,使用非常便捷。 EasyPlayer.js播放器不仅支持H.264与H.265视频编码格式&#xff0…

(一)- DRM架构

一,DRM简介 linux内核中包含两类图形显示设备驱动框架: FB设备:Framebuffer图形显示框架; DRM:直接渲染管理器(Direct Rendering Manager),是linux目前主流的图形显示框架; 1&am…

远程控制步骤

当远在千里之外的朋友想求助你帮他找到他电脑上的文件、或者是给他安装软件时。但是你给他说了他又找不到,那么这时你就可以通过控制对方的电脑去做一系列的操作。 如何远程控制对方的电脑非常关键。 方法一(Windows自带远程桌面功能)&#…

InternVL 多模态模型部署微调实践 | 书生大模型

文章目录 多模态大模型简介基本介绍例子常见设计模式BLIP 2Q-Former 模块细节应用案例:MiniGPT - 4Q-Former 的缺点 LLaVALLaVA - 1.5 - HDLLaVA - Next InternVL2 介绍架构设计Intern VitPixel ShuffleDynamic High - ResolutionMultitask output 训练方法 环境配置…

javaScript交互补充(元素的三大系列)

1、元素的三大系列 1.1、offset系列 1.1.1、offset初相识 使用offset系列相关属性可以动态的得到该元素的位置(偏移)、大小等 获得元素距离带有定位祖先元素的位置获得元素自身的大小(宽度高度)注意:返回的数值都不…

AI大模型(二):AI编程实践

一、软件安装 1. 安装 Visual Studio Code VSCode官方下载:Visual Studio Code - Code Editing. Redefined 根据自己的电脑系统选择相应的版本下载 安装完成! 2. 安装Tongyi Lingma 打开VSCode,点击左侧菜单栏【extensions】,…

linux c 语言回调函数学习

动机 最近在看 IO多路复用,包括 select() poll () epoll() 的原理以及libevent, 对里面提及的回调机制 比较头大,特写此文用例记录学习笔记。 什么是回调函数 网上看到的最多的一句话便是:回调函数 就是 函数指针的一种用法&am…

Python 正则表达式的一些介绍和使用方法说明(数字、字母和数字、电子邮件地址、网址、电话号码(简单)、IPv4 )

## 正则表达式的概念和用途 正则表达式(Regular Expression,简称Regex)是对字符串操作的一种逻辑公式,由一些事先定义好的特定字符以及这些特定字符的组合所构成。这些特定字符及其组合被用来描述在搜索文本时要匹配的一个或多个…

DreamClear:字节跳动开源了高性能图像修复技术,中科院加持,商业免费使用

哇,字节跳动开源了DreamClear项目,采用的是Apache-2.0开源协议,可以商用,并且用户可以自由地使用、复制、修改和分发该软件,甚至可以用于私有项目中。这对于开发者和企业来说是个好消息,因为它们可以利用这…

Flutter:android studio无法运行到模拟机的问题

提示如下错误信息: Entrypoint is not a Dart filenot applicable for the "main.dart" configurat点击运行按钮提示让填写以下信息 或者出现无法选择模拟机的情况 发下下列问题: 无法运行的项目默认根目录地址: 可以正常运行…

FromData格式提交接口时入参被转成JSON格式问题

本地上传文件后通过事件提交文件,一般先通过前端组件生成文本流,在通过接口提交文本流,提交文本流一般使用FormData的入参形式传入,接口请求头也默认"Content-Type": “multipart/form-data”,但是某些场景统…

<AI 学习> 下载 Stable Diffusions via Windows OS

注意: 不能使用 网络路径 不再支持 HTTPS 登录,需要 Token 1. 获得合法的授权 Stability AI License — Stability AI 上面的链接打开,去申请 许可 2. 拥有 HuggingFace 账号 注册:https://huggingface.co/ 3. 配置 Tok…

MySQL缓存使用率超过80%的解决方法

MySQL缓存使用率超过80%的解决方法 一、识别缓存使用率过高的问题1.1 使用SHOW GLOBAL STATUS命令监控1.2 监控其他相关指标二、分析缓存使用率过高的原因2.1 数据量增长2.2 查询模式变化2.3 配置不当三、解决缓存使用率过高的方法3.1 调整Buffer Pool大小3.1.1 计算合理的Buff…

39.安卓逆向-壳-smali语法3(方法)

免责声明:内容仅供学习参考,请合法利用知识,禁止进行违法犯罪活动! 内容参考于:图灵Python学院 本人写的内容纯属胡编乱造,全都是合成造假,仅仅只是为了娱乐,请不要盲目相信。 工…

《FreeRTOS任务基础知识以及任务创建相关函数》

目录 1.FreeRTOS多任务系统与传统单片机单任务系统的区别 2.FreeRTOS中的任务(Task)介绍 2.1 任务特性 2.2 FreeRTOS中的任务状态 2.3 FreeRTOS中的任务优先级 2.4 在任务函数中退出 2.5 任务控制块和任务堆栈 2.5.1 任务控制块 2.5.2 任务堆栈…

RHCE的学习(18)

第二章 变量和引用 深入认识变量 在程序设计语言中,变量是一个非常重要的概念。也是初学者在进行Shell程序设计之前必须掌握的一个非常基础的概念。只有理解变量的使用方法,才能设计出良好的程序。本节将介绍Shell中变量的相关知识。 什么是变量 顾名思义…

AG32 FPGA部分简单开发

环境 Quartus 13.0(Quartus 不能使用Lite 版本,需要使用Full 版本)AGM SDKSupra(快捷方式在SDK目录下,具体路径为AgRV_pio\packages\tool-agrv_logic\bin) FPGA编程 在AG32芯片中,拥有异构双…

__VUE_PROD_HYDRATION_MISMATCH_DETAILS__ is not explicitly defined

VUE_PROD_HYDRATION_MISMATCH_DETAILS 未明确定义。您正在运行 Vue 的 esm-bundler 构建,它期望这些编译时功能标志通过捆绑器配置全局注入,以便在生产捆绑包中获得更好的tree-shaking优化。 Vue.js应用程序正在使用ESM(ECMAScript模块&#…