RN使用蓝牙扫描

news2025/6/21 12:59:36

我项目需要用到蓝牙模块,蓝牙扫描到设备并且获取到电量显示到页面上,因此我做了如下demo,使用了react-native-ble-plx这个插件 点击进入官方文档官方文档

1.安卓环境配置(ios暂定,还没做ios,不过下面的方法是兼容的,自行配置ios权限)
android/app/src/main/AndroidManifest.xml加入以下权限代码

  <uses-permission android:name="android.permission.BLUETOOTH" />
  <uses-permission android:name="android.permission.BLUETOOTH_ADMIN" />
  <uses-permission android:name="android.permission.BLUETOOTH_SCAN" />

2.封装蓝牙相关模块工具包

import {BleManager} from 'react-native-ble-plx'; // 引入蓝牙模块
import {PermissionsAndroid, Platform} from 'react-native';
import {Buffer} from 'buffer';
// 下面这两个常量为了获取设备的电量信息(每个人设备不一样,这里仅作参考)
const batteryServiceUUID = '0000180f-0000-1000-8000-00805f9b34fb'; // 电量服务的UUID
const batteryLevelCharacteristicUUID = '00002a19-0000-1000-8000-00805f9b34fb'; // 电量特征的UUID

// 授权(位置,蓝牙扫描,蓝牙连接三个权限,但是我这目前有一个问题,蓝牙连接和蓝牙扫描权限会重复弹出两次框,这个bug后期再改)
const bleManager = new BleManager();
// 位置信息和蓝牙权限信息(请求权限)
export const requestPermissions = async callback => {
  if (Platform.OS === 'android') {
    try {
      let grantedLocation = await PermissionsAndroid.request(
        PermissionsAndroid.PERMISSIONS.ACCESS_COARSE_LOCATION,
      );
      let grantedBluetoothScan = await PermissionsAndroid.request(
        PermissionsAndroid.PERMISSIONS.BLUETOOTH_SCAN,
        {
          title: '请求蓝牙扫描权限',
          message: '我们需要您的蓝牙扫描权限来扫描设备。',
          buttonNeutral: '稍后询问',
          buttonNegative: '拒绝',
          buttonPositive: '同意',
        },
      );
      let grantedBluetoothConnect = await PermissionsAndroid.request(
        PermissionsAndroid.PERMISSIONS.BLUETOOTH_CONNECT,
        {
          title: '请求蓝牙连接权限',
          message: '我们需要您的蓝牙连接权限来扫描设备。',
          buttonNeutral: '稍后询问',
          buttonNegative: '拒绝',
          buttonPositive: '同意',
        },
      );

      if (
        grantedLocation === PermissionsAndroid.RESULTS.GRANTED &&
        grantedBluetoothConnect === PermissionsAndroid.RESULTS.GRANTED
      ) {
        console.log('已有权限');
        callback(true);
      } else {
        console.log('某些权限被拒绝');
        callback(false);
      }
    } catch (error) {
      console.error('权限请求错误:', error);
      callback(false);
    }
  } else {
    callback(false);
  }
};

// 扫描蓝牙设备
export const scanDevice = callback => {
  // 参数1可以为null不做限制,也可以为服务的uuid,例如['180D']:心率服务的uuid,这样就只扫描过滤出关于心率服务的设备的uuid,如下
  // bleManager.startDeviceScan(['180D'], null, (error, scannedDevice)
  bleManager.startDeviceScan(null, null, (error, scannedDevice) => {
    if (error) {
      console.error('扫描错误:', error);
      callback(ture, error);
      return;
    }
    console.log('scannedDevice', scannedDevice);
    callback(false, scannedDevice);
  });
};

// 返回信号强度(传入参数为上面的scannedDevice.rssi),每个数字都代表一个信号,我这就是0-5六个信号
export const BluetoothBatteryLevel = level => {
  let signal = 0;
  if (level < 0) {
    signal = Math.abs(level) / 10;
  } else {
    signal = 0;
  }
  if (signal > 0 && signal <= 3) {
    return 5;
  } else if (signal > 3 && signal <= 6) {
    return 4;
  } else if (signal > 6 && signal <= 9) {
    return 3;
  } else if (signal > 9 && signal <= 12) {
    return 2;
  } else if (signal > 12 && signal <= 15) {
    return 1;
  } else {
    return 0;
  }
};

// 断开某个连接的设备,传入的是scannedDevice.id设备编码,如E6:13:27:12:BF:92
export const disconnectAllDevices = async a => {
  try {
    if (bleManager) {
      await bleManager.cancelDeviceConnection(a);
    }
  } catch (error) {
    console.log('断开连接时出错:', error);
  }
};

// 获取蓝牙设备电量(传入的是整个scannedDevice)
export const connectAndDiscoverServices = async (device, callback) => {
  try {
    if (device) {
      bleManager.stopDeviceScan(); // 停止扫描
      device
        .connect() // 连接设备
        .then(device => {
          console.log('设备已连接:', device.id);
          return device.discoverAllServicesAndCharacteristics();
        })
        .then(device => {
          return device.services();
        })
        .then(services => {
          // 下面这堆逻辑是处理蓝牙设备电量的
          services.forEach(service => {
            if (service.uuid === batteryServiceUUID) {
              service.characteristics().then(characteristics => {
                characteristics.forEach(characteristic => {
                  if (characteristic.uuid === batteryLevelCharacteristicUUID) {
                    characteristic
                      .read()
                      .then(readData => {
                        console.log('电量:', readData.value);
                        // Step 1: 使用 base64 解码
                        // 这里的Buffer自行安装 yarn add buffer 导入方法如上所示
                        const decodedBytes = Buffer.from(
                          readData.value,
                          'base64',
                        );
                        // Step 2: 将字节数组转换为十六进制字符串
                        const hexString = decodedBytes.toString('hex');
                        // Step 3: 将十六进制字符串解析为十进制数值
                        const decimalValue = parseInt(hexString, 16);
                        console.log('解析后的十进制数值:', decimalValue);
                        callback(true, decimalValue, device); // 将电量信息返回
                        // 这里的 readData.value 可能需要根据设备的具体协议进行解析
                        // 断开连接
                        disconnectAllDevices(device.id);
                      })
                      .catch(error => {
                        console.error('读取电量信息出错:', error);
                        // 断开连接
                        disconnectAllDevices(device.id);
                        callback(false, error);
                      });
                  }
                });
              });
            }
          });
        })
        .catch(error => {
          console.error('连接设备出错:', error);
        });
    }
  } catch (error) {
    console.error('连接设备时出错:', error);
    return null;
  }
};

// 停止扫描
export const stopScan = () => {
  bleManager.stopDeviceScan();
};

使用demo

import {Button, View, TouchableOpacity, Text} from 'react-native';
import {
  requestPermissions, // 请求设备权限
  scanDevice, // 开始扫描设备
  BluetoothBatteryLevel, // 返回信号强度
  connectAndDiscoverServices, // 连接设备获取设备电量,然后断开连接(断开连接逻辑在工具包里面)
  stopScan, // 停止扫描
} from './uitls';
import {useState} from 'react';

export default function App() {
  const [dataArr, setDataArr] = useState([]); // 设备列表
  const [dianliang, setDianLiang] = useState(0); // 电量

  // 更新设备数据(已经到扫描到的不会重复添加)
  function devicesArr(str) {
    let newData = str;
    let found = false;
    setDataArr(arr => {
      arr = arr.map(item => {
        if (item.name === newData.name) {
          found = true;
          return {...item, xinhao: newData.xinhao};
        }
        return item;
      });
      if (!found) {
        arr.push(newData);
      }
      return arr;
    });
  }

  // 扫描设备
  const scanDevices = async () => {
    // 请求权限
    await requestPermissions(res => {
      if (!res) {
        console.log('未获得必要权限');
        return;
      }
      console.log('开始扫描设备');
      scanDevice((error, scannedDevice) => {
        if (error) {
          console.error('扫描错误:', error);
          return;
        }
        devicesArr({
          name: scannedDevice.name,
          xinhao: BluetoothBatteryLevel(scannedDevice.rssi),
          devices: scannedDevice,
        });
      });
    });
  };

  // 连接设备获取电量,然后断开设备
  async function connectDevice(index, item) {
    await connectAndDiscoverServices(
      item.devices,
      (flag, dianliang, device) => {
        // dianliang 是设备电量 , flag代表扫描状态, device代表当前设备信息
        // 断开设备的逻辑在工具文件内
        if (flag) {
          console.log('flag,dianliang,device', flag, dianliang, device);
          setDianLiang(dianliang);
        }
      },
    );
  }

  return (
    <View>
      <Button
        title="点击获取蓝牙权限"
        onPress={() => {
          requestPermissions(res => {
            console.log('是否获取到了权限', res);
          });
        }}></Button>
      <Button title="开始扫描" onPress={scanDevices}></Button>
      <Button title="停止扫描" onPress={stopScan}></Button>
      <Text>选中的设备电量为:{dianliang}</Text>
      {dataArr.map((item, index) => {
        return (
          <TouchableOpacity key={index}>
            <View
              style={{
                flexDirection: 'row',
                justifyContent: 'space-around',
                alignItems: 'center',
                height: 50,
              }}>
              <Text>信号强度为:{item.xinhao}</Text>
              <Text>{item.name}</Text>
              <TouchableOpacity
                onPress={() => {
                  connectDevice(index, item);
                }}
                style={{
                  flexDirection: 'row',
                  flexDirection: 'row',
                  width: 80,
                  height: 35,
                  borderRadius: 10,
                  justifyContent: 'space-around',
                  alignItems: 'center',
                  borderWidth: 1,
                }}>
                <Text
                  style={{
                    fontSize: 15,
                  }}>
                  获取当前电量完成后断开蓝牙
                </Text>
              </TouchableOpacity>
            </View>
            <View style={{borderWidth: 0.2, borderColor: '#CCCCCC'}}></View>
          </TouchableOpacity>
        );
      })}
    </View>
  );
}

效果图

在这里插入图片描述

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

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

相关文章

API管理平台:你用的到底是哪个?

Apifox是不开源的&#xff0c;在github的项目只是readme文件&#xff0c;私有化需要付费。当然saas版目前是免费使用的。 一、Swagger 为了让Swagger界面更加美观&#xff0c;有一些项目可以帮助你实现这一目标。以下是一些流行的项目&#xff0c;它们提供了增强的UI和额外的功…

代码签名证书:确保软件安全与可信性的关键工具

在数字化时代&#xff0c;软件已成为各行各业的核心驱动力。然而&#xff0c;随着网络威胁日益复杂&#xff0c;用户对于软件来源的可靠性、内容的完整性和发布者的可信度提出了更高要求。为满足这一需求&#xff0c;代码签名证书应运而生&#xff0c;它通过先进的加密技术&…

【SpringBoot】-- mapstruct进行类型转换时Converter实现类不能自动生成代码问题解决

问题描述 我的问题如下&#xff1a; 应该在红色区域生成对应的转换细节&#xff0c;但是这里只返回了一个空对象 问题解决 加入lombok-mapstruct-binding依赖,也要注意依赖引用顺序问题 <dependency><groupId>org.projectlombok</groupId><artifactId&…

【智能优化算法详解】粒子群算法PSO量子粒子群算法QPSO

1.粒子群算法PSO 博主言简意赅总结-算法思想&#xff1a;大方向下个体自学习探索群体交流共享 对比适应度找到最优点 背景 粒子群算法&#xff0c;也称粒子群优化算法或鸟群觅食算法&#xff08;Particle Swarm Optimization&#xff09;&#xff0c; 缩写为 PSO。粒子群…

小白部署springboot+vue网站到服务器踩坑总结【维护更新篇】

目录 前言如何更新前端nginx安装和启动言归正传&#xff0c;更新前端 如何更新后端杀死进程 前言 在上一篇文章里详细介绍了怎样部署一个前后端分离的网站&#xff0c;链接: 链接在此 但是部署完之后&#xff0c;在本地的开发更新后&#xff0c;重新上传到服务器上又遇到了一些…

mybatis(5)参数处理+语句查询

参数处理&#xff0b;语句查询 1、简单单个参数2、Map参数3、实体类参数4、多参数5、Param注解6、语句查询6.1 返回一个实体类对象6.2 返回多个实体类对象 List<>6.3 返回一个Map对象6.4 返回多个Map对象 List<Map>6.5 返回一个大Map6.6 结果映射6.6.1 使用resultM…

[图解]DDD领域驱动设计伪创新-聚合根01

0 00:00:00,070 --> 00:00:06,010 今天我们来说一下领域驱动设计的另外一个伪创新&#xff0c;聚合根 1 00:00:06,870 --> 00:00:09,440 这个我们分为几个部分来讲 2 00:00:12,610 --> 00:00:15,580 前面说过很多遍&#xff0c;伪创新 3 00:00:16,760 --> 00:00…

登陆qq,经常收到qq游戏中心的推送信息,关闭推送信息

手动关闭推送信息的步骤&#xff1a; 1.点开左侧游戏中心 2、在打开界面&#xff0c;点击左下角自己的头像 3、打开设置中心&#xff0c;关闭所有的推送 4、完成关闭&#xff0c;不会推送了

基于springboot实现教学资源库系统项目【项目源码+论文说明】计算机毕业设计

基于springboot实现教学资源库系统演示 摘要 社会的进步&#xff0c;教育行业发展迅速&#xff0c;人们对教育越来越重视&#xff0c;在当今网络普及的情况下&#xff0c;教学模式也开始逐渐网络化&#xff0c;各大高校开始网络教学模式。 本文研究的教学资源库系统基于Sprin…

锂电池升降压转换利器:PW2224转换器,实现3.3V高效持续输出

描述&#xff1a; PW2224是一款专为锂电池供电设备设计的高效单电感降压-升压转换器。这款转换器能够在3V至4.2V的锂电池输入电压范围内工作&#xff0c;实现升降压模式自动切换&#xff0c;稳定输出3.3V电压&#xff0c;并持续提供高达1A的负载电流。此外&#xff0c;PW2224的…

Linux C++ 029-STL之queue容器

Linux C 029-STL之queue容器 本节关键字&#xff1a;Linux、C、queue 相关库函数&#xff1a;push、pop、back、front queue基本概念 概念&#xff1a;queue是一种先进先出&#xff08;First In Fisrst Out&#xff0c;FIFO&#xff09;的数据结构&#xff0c;它有两个端口 关…

数据可视化-ECharts Html项目实战(11)

在之前的文章中&#xff0c;我们学习了如何在ECharts中特殊图表的双y图以及自定义形状词云图。想了解的朋友可以查看这篇文章。同时&#xff0c;希望我的文章能帮助到你&#xff0c;如果觉得我的文章写的不错&#xff0c;请留下你宝贵的点赞&#xff0c;谢谢。 数据可视化-ECh…

栈与队列2s总结(不含单调栈)

6.栈与队列 栈与队列理论基础 队列是先进先出&#xff0c;栈是先进后出。 C中stack 是容器么&#xff1f; 我们使用的stack是属于哪个版本的STL&#xff1f; 我们使用的STL中stack是如何实现的&#xff1f; stack 提供迭代器来遍历stack空间么&#xff1f; 栈和队列是STL…

甘特图在生产进度管理中的应用

生产进度管理在生产制造过程中起着至关重要的作用。 它主要关注对生产进程的掌控和安排&#xff0c;确保生产活动能够按照预定的计划和时间顺利进行&#xff0c;以达到按时交付产品的目标。 在生产进度管理中&#xff0c;首先需要制定一个详细且合理的生产计划&#xff0c;明…

Ubuntu20.04安装ROS过程记录以及常见报错处理

官网安装步骤如下&#xff1a; http://wiki.ros.org/cn/noetic/Installation/Ubuntu#A.2BXwBZy1uJiMU- 第一个&#xff1a;添加ROS软件源 sudo sh -c echo "deb http://packages.ros.org/ros/ubuntu $(lsb_release -sc) main" > /etc/apt/sources.list.d/ros-la…

第十二届蓝桥杯省赛真题(C/C++大学B组)

目录 #A 空间 #B 卡片 #C 直线 #D 货物摆放 #E 路径 #F 时间显示 #G 砝码称重 #H 杨辉三角形 #I 双向排序 #J 括号序列 #A 空间 #include <bits/stdc.h> using namespace std;int main() {cout<<256 * 1024 * 1024 / 4<<endl;return 0; } #B 卡片…

【正点原子Linux连载】第二十九章 Linux RTC驱动实验摘自【正点原子】ATK-DLRK3568嵌入式Linux驱动开发指南

1&#xff09;实验平台&#xff1a;正点原子ATK-DLRK3568开发板 2&#xff09;平台购买地址&#xff1a;https://detail.tmall.com/item.htm?id731866264428 3&#xff09;全套实验源码手册视频下载地址&#xff1a; http://www.openedv.com/docs/boards/xiaoxitongban 第二十…

自动驾驶定位算法-递归贝叶斯滤波(Bayes Filter)

自动驾驶定位算法-递归贝叶斯滤波(Bayes Filter) 附赠自动驾驶学习资料和量产经验&#xff1a;链接 贝叶斯滤波器(Bayes Filter)是无人驾驶汽车中高精定位相关的基础技术&#xff0c;同时也是机器人技术中的基础算法。 如上图&#xff0c;开始机器人不知道自己在哪里&#xff…

图解二叉树遍历方法-前序遍历、中序遍历、后序遍历

一、几个概念 二叉树&#xff08;binary tree&#xff09;&#xff1a;是 n&#xff08;n > 0&#xff09;个结点&#xff08;每个结点最多只有2棵子树&#xff09;的有限集合&#xff0c;该集合可为空集&#xff08;称为空二叉树&#xff09;&#xff0c;或由一个根节点和…

RabbitMQ Docker 安装与应用

1.官方镜像 该镜像包含用户操作界面 2.Docker运行&#xff0c;并设置开机自启动 docker run -d --restartalways --name rabbitmq -p 5672:5672 -p 15672:15672 rabbitmq:3.10-management 默认登录账户和密码 guest 3、使用 队列和交换机绑定