RN使用蓝牙扫描

本文介绍了如何在ReactNative项目中使用`react-native-ble-plx`插件进行蓝牙设备扫描,获取设备电量,并处理权限请求和设备连接。开发者需在AndroidManifest.xml中添加相关权限,并封装蓝牙操作函数以实现实时电量显示。
摘要由CSDN通过智能技术生成

我项目需要用到蓝牙模块,蓝牙扫描到设备并且获取到电量显示到页面上,因此我做了如下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 === 'ios') {
    return true;
  }
  if (
    Platform.OS === 'android' &&
    PermissionsAndroid.PERMISSIONS.ACCESS_FINE_LOCATION
  ) {
    const apiLevel = parseInt(Platform.Version.toString(), 10);

    if (apiLevel < 31) {
      const granted = await PermissionsAndroid.request(
        PermissionsAndroid.PERMISSIONS.ACCESS_FINE_LOCATION,
      );
      callback(granted === PermissionsAndroid.RESULTS.GRANTED)
    }
    if (
      PermissionsAndroid.PERMISSIONS.BLUETOOTH_SCAN &&
      PermissionsAndroid.PERMISSIONS.BLUETOOTH_CONNECT
    ) {
      const result = await PermissionsAndroid.requestMultiple([
        PermissionsAndroid.PERMISSIONS.BLUETOOTH_SCAN,
        PermissionsAndroid.PERMISSIONS.BLUETOOTH_CONNECT,
        PermissionsAndroid.PERMISSIONS.ACCESS_FINE_LOCATION,
      ]);

      callback(
        result['android.permission.BLUETOOTH_CONNECT'] ===
          PermissionsAndroid.RESULTS.GRANTED &&
          result['android.permission.BLUETOOTH_SCAN'] ===
            PermissionsAndroid.RESULTS.GRANTED &&
          result['android.permission.ACCESS_FINE_LOCATION'] ===
            PermissionsAndroid.RESULTS.GRANTED,
      );
      return
    }
  }

  this.showErrorToast('Permission have not been granted');
  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 => {
      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>
  );
}

效果图

在这里插入图片描述

  • 10
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

萧寂173

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值