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

news2024/11/17 6:13:00

首先对 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

 

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

51单片机应用开发(进阶)---模块化编程

实现目标 1、掌握.h 文件的格式、extern 的用法; 2、握模块化编程方法步骤; 3、具体实现:(1)提供一个C文件,将其按照功能模块进行模块化。 一、为什么要进行模块化编程? 传统的编程方式&…

arkUI:水果选择与管理:基于 ArkUI 的长按编辑功能实现

水果选择与管理:基于 ArkUI 的长按编辑功能实现 1 主要内容说明2 相关内容2.1 相关内容2.1.1 源码1内容的相关说明2.1.1.1 数据结构与状态管理2.1.1.2 添加水果功能2.1.1.3 水果列表展示2.1.1.4 长按进入编辑模式2.1.1.5 复选框的多选功能2.1.1.6 删除水果功能2.1.1…

小程序20-样式:自适应尺寸单位 rpx

手机设备的宽度逐渐多元化,也就需要开发者开发过程中,去适配不同屏幕宽度的手机,为了解决屏幕适配问题,微信小程序推出了 rpx 单位 rpx:小程序新增的自适应单位,可以根据不同设备的屏幕宽度进行自适应缩放 …

unity3d————Resources异步加载

知识点一:Resources异步加载是什么? 在Unity中,资源加载可以分为同步加载和异步加载两种方式。同步加载会在主线程中直接进行,如果加载的资源过大,可能会导致程序卡顿,因为从硬盘读取数据到内存并进行处理…

C#/WinForm拖拽文件上传

一、首先创建一个上传文件的类,继承Control类,如下: public class UploadControl : Control{private Image _image;public UploadControl(){this.SetStyle(ControlStyles.UserPaint | //控件自行绘制,而不使用操作系统的绘制Cont…

2024 同一个网段,反弹shell四种方法【linux版本】bash、python、nc、villian反弹shell图解步骤

实验环境准备(同一个网段下,我是桥接的虚拟机) 一、bash反弹shell 二、python反弹shell 三、nc反弹shell 四、villain反弹shell 实验环境准备(同一个网段下,我是桥接的虚拟机) 一台kali的linux(攻击者)…

FPGA使用Verilog实现CAN通信

FPGA实现CAN通信(Verilog) 1.作者使用的方法是通过FPGA芯片(如Xilinx公司的型号为XC7K325TFFG676-2)控制SJA1000T芯片(CAN控制器芯片)实现CAN通信,如下图所示: 2.熟悉连接方式之后&…

已解决:spark代码中sqlContext.createDataframe空指针异常

这段代码是使用local模式运行spark代码。但是在获取了spark.sqlContext之后,用sqlContext将rdd算子转换为Dataframe的时候报错空指针异常 Exception in thread "main" org.apache.spark.sql.AnalysisException: java.lang.RuntimeException: java.lang.Nu…

jenkins用户在执行scp的时候如何做免密登录

一、背景 在jenkins job中执行scp的shell命令,当然不希望每次输入密码,另外处于出于安全考虑,也不建议在scp命令中指定。 所以,我们需要对远程机器进行免密登录。 本文遇到的问题是,在jenkins机器上执行scp已做到了…

HarmonyOS ArkUI(基于ArkTS) 开发布局 (中)

HarmonyOS ArkUI(基于ArkTS) 开发布局 (上) 四 层叠布局 (Stack) 层叠布局(StackLayout)用于在屏幕上预留一块区域来显示组件中的元素,提供元素可以重叠的布局。层叠布局通过Stack容器组件实现位置的固定定位与层叠&…

无线网络信号 6G、5G和2.4G 的一些小科普

无线网络信号划分为6G、5G和2.4G这几类信号,它们各自有不同的用途和区别: 1、 2.4G无线技术 - 用途:2.4G无线技术广泛应用于智能家居、物联网、WLAN和蓝牙设备等。它是一个全球性的工作频段,适用于低速率的应用,如普通…

什么是GCP kunernetes的Node Taints and Tolerations

在Kubernetes中,Node taints和Pod tolerations是两个相关的功能,它们用于控制Pods的调度,以确保Pods不会调度到不适当的节点上。以下是这两个概念的详细解释: Node Taints(节点污点) 定义:Node…

ROS进阶:使用URDF和Xacro构建差速轮式机器人模型

前言 本篇文章介绍的是ROS高效进阶内容,使用URDF 语言(xml格式)做一个差速轮式机器人模型,并使用URDF的增强版xacro,对机器人模型文件进行二次优化。 差速轮式机器人:两轮差速底盘由两个动力轮位于底盘左…

【Playwright + Python】系列(十)利用 Playwright 完美处理 Dialogs 对话框

哈喽,大家好,我是六哥!今天我来给大家分享一下如何使用playwight处理Dialogs对话框,面向对象为功能测试及零基础小白,这里我尽量用大白话的方式举例讲解,力求所有人都能看懂,建议大家先收藏&…

控制器ThinkPHP6

五、控制器中对数组值的返回 在做接口服务时,很多时候回使用数组作为返回值,那么数组如何返回成 json呢? 在 tp6 中返回json 很简单,直接使用 json 进行返回即可,例如: public function index(){$resarra…

基于Java Springboot城市交通管理系统

一、作品包含 源码数据库设计文档万字PPT全套环境和工具资源部署教程 二、项目技术 前端技术:Html、Css、Js、Vue、Element-ui 数据库:MySQL 后端技术:Java、Spring Boot、MyBatis 三、运行环境 开发工具:IDEA/eclipse 数…

Ubuntu24.04挂载磁盘

一、引言 由于几块磁盘每次开机时的编号都不一样,造成了很多麻烦,所有重新挂载磁盘试一试。 参考链接: ubuntu挂载磁盘或U盘Ubuntu添加新硬盘,挂载到根目录下的某个文件中 二、挂载磁盘 1. 查看盘名 sudo fdisk -l sda 代表第…

springboot003基于springboot的图书个性化推荐系统(源码+包运行+LW+技术指导)

项目描述 临近学期结束,还是毕业设计,你还在做java程序网络编程,期末作业,老师的作业要求觉得大了吗?不知道毕业设计该怎么办?网页功能的数量是否太多?没有合适的类型或系统?等等。这里根据疫情当下,你想解决的问…

【React】状态管理之Zustand

🌈个人主页: 鑫宝Code 🔥热门专栏: 闲话杂谈| 炫酷HTML | JavaScript基础 ​💫个人格言: "如无必要,勿增实体" 文章目录 状态管理之Zustand引言1. Zustand 的核心特点1.1 简单直观的 API1.2 无需 Provi…

【从零开始的LeetCode-算法】3210. 找出加密后的字符串

给你一个字符串 s 和一个整数 k。请你使用以下算法加密字符串: 对于字符串 s 中的每个字符 c,用字符串中 c 后面的第 k 个字符替换 c(以循环方式)。 返回加密后的字符串。 示例 1: 输入: s "dart&…