无法创建 BLE 管理器

问题描述 投票:0回答:3

您好,我是 React Native 的新手,在我的应用程序中,我正在尝试扫描 ble 设备并在屏幕上列出它们的名称。在代码源中,人们最常创建一个蓝牙低功耗管理器为

const manager = new BleManager();
,但是当我尝试此代码(当然还有其他方式)时,它会给出类似

的错误
TypeError: null is not an object (evaluating '_BleModule.BleModule.createClient')

This error is located at:
    in HelloWorldApp (created by ExpoRoot)
    in ExpoRoot
    in RCTView (created by View)
    in View (created by AppContainer)
    in RCTView (created by View)
    in View (created by AppContainer)
    in AppContainer
at node_modules\react-native\Libraries\LogBox\LogBox.js:149:8 in registerError
at node_modules\react-native\Libraries\LogBox\LogBox.js:60:8 in errorImpl
at node_modules\react-native\Libraries\LogBox\LogBox.js:34:4 in console.error
at node_modules\expo\build\environment\react-native-logs.fx.js:27:4 in error
at node_modules\react-native\Libraries\Core\ExceptionsManager.js:104:6 in reportException
at node_modules\react-native\Libraries\Core\ExceptionsManager.js:172:19 in handleException
at node_modules\react-native\Libraries\Core\ReactFiberErrorDialog.js:43:2 in showErrorDialog
at node_modules\react-native\Libraries\Renderer\implementations\ReactNativeRenderer-dev.js:15792:34 in logCapturedError        
at node_modules\react-native\Libraries\Renderer\implementations\ReactNativeRenderer-dev.js:15884:20 in update.callback
at node_modules\react-native\Libraries\Renderer\implementations\ReactNativeRenderer-dev.js:7199:2 in callCallback
at node_modules\react-native\Libraries\Renderer\implementations\ReactNativeRenderer-dev.js:7220:20 in commitUpdateQueue
at node_modules\react-native\Libraries\Renderer\implementations\ReactNativeRenderer-dev.js:16632:25 in commitLifeCycles        
at node_modules\react-native\Libraries\Renderer\implementations\ReactNativeRenderer-dev.js:19216:22 in commitLayoutEffects     
at node_modules\react-native\Libraries\Renderer\implementations\ReactNativeRenderer-dev.js:93:4 in invokeGuardedCallbackProd
at node_modules\react-native\Libraries\Renderer\implementations\ReactNativeRenderer-dev.js:318:2 in invokeGuardedCallback      
at node_modules\react-native\Libraries\Renderer\implementations\ReactNativeRenderer-dev.js:18952:29 in commitRootImpl
at node_modules\scheduler\cjs\scheduler.development.js:468:23 in unstable_runWithPriority
at node_modules\react-native\Libraries\Renderer\implementations\ReactNativeRenderer-dev.js:18791:17 in commitRoot
at node_modules\react-native\Libraries\Renderer\implementations\ReactNativeRenderer-dev.js:18192:12 in performSyncWorkOnRoot   
at node_modules\react-native\Libraries\Renderer\implementations\ReactNativeRenderer-dev.js:5911:33 in runWithPriority$argument_1
at node_modules\scheduler\cjs\scheduler.development.js:468:23 in unstable_runWithPriority
at node_modules\react-native\Libraries\Renderer\implementations\ReactNativeRenderer-dev.js:5906:23 in flushSyncCallbackQueueImpl
at node_modules\react-native\Libraries\Renderer\implementations\ReactNativeRenderer-dev.js:5893:28 in flushSyncCallbackQueue
at node_modules\react-native\Libraries\Renderer\implementations\ReactNativeRenderer-dev.js:17745:30 in scheduleUpdateOnFiber   
at node_modules\react-native\Libraries\Renderer\implementations\ReactNativeRenderer-dev.js:21484:23 in updateContainer
at node_modules\react-native\Libraries\Renderer\implementations\ReactNativeRenderer-dev.js:22144:17 in render
at node_modules\react-native\Libraries\ReactNative\renderApplication.js:58:4 in renderApplication
at node_modules\react-native\Libraries\ReactNative\AppRegistry.js:117:25 in runnables.appKey.run
at node_modules\react-native\Libraries\ReactNative\AppRegistry.js:202:4 in runApplication
at node_modules\react-native\Libraries\BatchedBridge\MessageQueue.js:414:4 in __callFunction
at node_modules\react-native\Libraries\BatchedBridge\MessageQueue.js:113:6 in __guard$argument_0
at node_modules\react-native\Libraries\BatchedBridge\MessageQueue.js:365:10 in __guard
at node_modules\react-native\Libraries\BatchedBridge\MessageQueue.js:112:4 in callFunctionReturnFlushedQueue

这是我尝试创建管理器的代码:

import { StatusBar } from 'expo-status-bar';
import React, { Component } from 'react';
import { StyleSheet, Text, View } from 'react-native';
import { BleManager } from 'react-native-ble-plx';

const HelloWorldApp = () => {
  const manager = new BleManager();

我无法解决我的错误在哪里。请问有人可以帮我解决这个问题吗?

注意:我还使用

npm install --save react-native-ble-plx
命令安装了 ble-plx 软件包。

注2:我使用expo创建了我的项目。

react-native expo bluetooth-lowenergy react-native-ble-plx
3个回答
1
投票

在使用蓝牙之前,您可能需要征求用户的许可。将这些添加到您的 android-manifest.xml 中:

<uses-feature android:name="android.hardware.bluetooth" android:required="true"/>
<uses-feature android:name="android.hardware.bluetooth_le" android:required="true"/>

0
投票

您因博览会而收到此错误。 Expo 不支持 ble-manager ble-plx 软件包。您需要通过

创建一个本机项目
npx react-native init AwesomeProject

0
投票

import React, {useState, useEffect, useCallback} from 'react';
import {
  View,
  Text,
  Button,
  FlatList,
  StyleSheet,
  Alert,
  TouchableOpacity,
  TextInput,
  Platform,
  PermissionsAndroid,
  NativeEventEmitter,
  NativeModules,
} from 'react-native';
import BleManager from 'react-native-ble-manager';
import {Buffer} from 'buffer';

const BluetoothScanner = () => {
  const [devices, setDevices] = useState([]);
  const [connectedDevice, setConnectedDevice] = useState(null);
  const [services, setServices] = useState([]);
  const [characteristics, setCharacteristics] = useState([]);
  const [selectedCharacteristic, setSelectedCharacteristic] = useState(null);
  const [readValue, setReadValue] = useState('');
  const [writeValue, setWriteValue] = useState('');

  useEffect(() => {
    BleManager.start({showAlert: false});

    const requestPermissions = async () => {
      if (Platform.OS === 'android') {
        const granted = await PermissionsAndroid.requestMultiple([
          PermissionsAndroid.PERMISSIONS.ACCESS_FINE_LOCATION,
          PermissionsAndroid.PERMISSIONS.BLUETOOTH_SCAN,
          PermissionsAndroid.PERMISSIONS.BLUETOOTH_CONNECT,
        ]);
        if (
          granted['android.permission.ACCESS_FINE_LOCATION'] !==
            PermissionsAndroid.RESULTS.GRANTED ||
          granted['android.permission.BLUETOOTH_SCAN'] !==
            PermissionsAndroid.RESULTS.GRANTED ||
          granted['android.permission.BLUETOOTH_CONNECT'] !==
            PermissionsAndroid.RESULTS.GRANTED
        ) {
          Alert.alert(
            'Permission Required',
            'Please grant all permissions to use Bluetooth features.',
          );
        }
      }
    };

    requestPermissions();

    const handleDiscoverPeripheral = peripheral => {
      if (peripheral && peripheral.name) {
        setDevices(prevDevices => {
          if (!prevDevices.find(d => d.id === peripheral.id)) {
            return [...prevDevices, peripheral];
          }
          return prevDevices;
        });
      }
    };

    const handleStopScan = () => {
      console.log('Scan is stopped');
    };

    const handleDisconnectedPeripheral = data => {
      Alert.alert(
        'Disconnected',
        `Disconnected from device ${data.peripheral}`,
      );
      setConnectedDevice(null);
      setServices([]);
      setCharacteristics([]);
    };

    const bleManagerEmitter = new NativeEventEmitter(NativeModules.BleManager);

    const discoverListener = bleManagerEmitter.addListener(
      'BleManagerDiscoverPeripheral',
      handleDiscoverPeripheral,
    );

    const stopListener = bleManagerEmitter.addListener(
      'BleManagerStopScan',
      handleStopScan,
    );

    const disconnectListener = bleManagerEmitter.addListener(
      'BleManagerDisconnectPeripheral',
      handleDisconnectedPeripheral,
    );

    return () => {
      discoverListener.remove();
      stopListener.remove();
      disconnectListener.remove();
    };
  }, []);

  const scanAndConnect = useCallback(() => {
    console.log('Starting scan');
    setDevices([]);
    BleManager.scan([], 5, true)
      .then(() => {
        console.log('Scanning...');
      })
      .catch(error => {
        console.warn('Error during scan:', error);
      });
  }, []);

  const connectToDevice = useCallback(async deviceId => {
    try {
      await BleManager.connect(deviceId);
      console.log('Connected to device:', deviceId);
      setConnectedDevice(deviceId);
      await BleManager.retrieveServices(deviceId);
      fetchServices(deviceId);
      Alert.alert('Connected', `Successfully connected to device ${deviceId}`);
    } catch (error) {
      console.warn('Error connecting to device:', error);
      Alert.alert('Error', 'Failed to connect to device.');
    }
  }, []);

  const fetchServices = useCallback(async deviceId => {
    try {
      const servicesData = await BleManager.retrieveServices(deviceId);
      console.log('Fetched services:', servicesData.characteristics);
      const filteredCharacteristics = servicesData.characteristics.filter(
        char => char.service !== '1800' && char.service !== '1801',
      );
      setServices(servicesData.services);
      setCharacteristics(filteredCharacteristics);
    } catch (error) {
      console.warn('Error fetching services:', error);
    }
  }, []);

  const handleServiceSelect = useCallback(
    serviceUUID => {
      const serviceCharacteristics = characteristics.filter(
        char => char.service === serviceUUID,
      );
      setCharacteristics(serviceCharacteristics);
    },
    [characteristics],
  );

  const readCharacteristic = useCallback(
    async characteristic => {
      if (connectedDevice) {
        try {
          console.log(
            `Attempting to read characteristic ${characteristic.characteristic} from service ${characteristic.service}`,
          );

          // Read the characteristic
          const readData = await BleManager.read(
            connectedDevice,
            characteristic.service,
            characteristic.characteristic,
          );

          console.log('Read data:', readData);

          // Convert the read data to Buffer
          const buffer = Buffer.from(readData);

          // Optionally, process the buffer based on data type
          let parsedData;

          // Example for different types of data
          // 1. Read UInt8
          if (characteristic.dataType === 'UInt8') {
            parsedData = buffer.readUInt8(0);
          }
          // 2. Read Int16 (little-endian)
          else if (characteristic.dataType === 'Int16') {
            parsedData = buffer.readInt16LE(0);
          }
          // 3. Read Float32 (little-endian)
          else if (characteristic.dataType === 'Float32') {
            parsedData = buffer.readFloatLE(0);
          }
          // 4. Read String (assuming UTF-8 encoding)
          else if (characteristic.dataType === 'String') {
            parsedData = buffer.toString('utf8');
          }
          // 5. Handle other data types or default case
          else {
            parsedData = `${readData}`;
          }

          setReadValue(parsedData.toString());
        } catch (error) {
          console.warn('Error reading characteristic:', error);
          Alert.alert('Error', 'Failed to read characteristic.');
        }
      } else {
        Alert.alert('Error', 'No device connected.');
      }
    },
    [connectedDevice],
  );

  // Convert a hexadecimal string to an ArrayBuffer
  function hexStringToByteArray(hexString) {
    const hex = hexString.replace(/\s+/g, ''); // Remove spaces
    const length = hex.length / 2;
    const byteArray = [];

    for (let i = 0; i < length; i++) {
      byteArray.push(parseInt(hex.substr(i * 2, 2), 16));
    }

    return byteArray;
  }

  // Convert an array of integers to an ArrayBuffer
  function intArrayToByteArray(intArray) {
    // Ensure all integers are in the 0-255 range
    return intArray.map(value => value & 255);
  }

  // Write data to a characteristic
  const writeCharacteristic = useCallback(async () => {
    if (selectedCharacteristic && writeValue && connectedDevice) {
      try {
        let byteArray;

        // Check and convert writeValue based on its type
        if (/^[0-9A-Fa-f]+$/.test(writeValue)) {
          // Hexadecimal string
          byteArray = hexStringToByteArray(writeValue);
        } else if (
          Array.isArray(writeValue) &&
          writeValue.every(Number.isInteger)
        ) {
          // Array of integers
          byteArray = intArrayToByteArray(writeValue);
        } else {
          throw new Error('Invalid writeValue format');
        }

        console.log('Byte Array:', byteArray);

        // Write to BLE characteristic
        await BleManager.write(
          connectedDevice,
          selectedCharacteristic.service,
          selectedCharacteristic.characteristic,
          byteArray,
          20, // maxByteSize, optional
        );
        Alert.alert('Success', 'Value written successfully');
      } catch (error) {
        console.warn('Error writing characteristic:', error);
        Alert.alert('Error', 'Failed to write characteristic.');
      }
    } else {
      Alert.alert('Error', 'No characteristic selected or value not provided.');
    }
  }, [connectedDevice, selectedCharacteristic, writeValue]);

  const disconnectFromDevice = useCallback(async () => {
    if (connectedDevice) {
      try {
        await BleManager.disconnect(connectedDevice);
        setConnectedDevice(null);
        setServices([]);
        setCharacteristics([]);
        setWriteValue('');
        setReadValue('');
        setSelectedCharacteristic(null);
        Alert.alert(
          'Disconnected',
          `Successfully disconnected from device ${connectedDevice}`,
        );
      } catch (error) {
        console.warn('Error disconnecting from device:', error);
        Alert.alert('Error', 'Failed to disconnect from device.');
      }
    }
  }, [connectedDevice]);

  return (
    <View style={styles.container}>
      <Text style={styles.header}>Bluetooth Scanner</Text>
      <Button title="Scan for Devices" onPress={scanAndConnect} />
      <FlatList
        data={devices}
        renderItem={({item}) => (
          <View style={styles.deviceContainer}>
            <Text style={styles.device}>{`Device found: ${item.name}`}</Text>
            {connectedDevice === item.id ? (
              <TouchableOpacity
                style={styles.button}
                onPress={disconnectFromDevice}>
                <Text style={styles.buttonText}>Disconnect</Text>
              </TouchableOpacity>
            ) : (
              <TouchableOpacity
                style={styles.button}
                onPress={() => connectToDevice(item.id)}>
                <Text style={styles.buttonText}>Connect</Text>
              </TouchableOpacity>
            )}
          </View>
        )}
        keyExtractor={item => item.id}
      />
      {connectedDevice && (
        <>
          {/* <Text style={styles.header}>Services</Text> */}
          {/* <FlatList
            data={services}
            renderItem={({item}) => (
              <TouchableOpacity
                style={styles.button}
                onPress={() => handleServiceSelect(item.uuid)}>
                <Text style={styles.buttonText}>{`Service: ${item.uuid}`}</Text>
              </TouchableOpacity>
            )}
            keyExtractor={item => item.uuid}
          /> */}
          <Text style={styles.header}>Characteristics</Text>
          <FlatList
            data={characteristics}
            renderItem={({item}) => (
              <View style={styles.characteristicContainer}>
                <Text
                  style={
                    styles.device
                  }>{`Characteristic: ${item.characteristic}`}</Text>

                {item.properties.Read && (
                  <TouchableOpacity
                    style={styles.button}
                    onPress={() => {
                      setSelectedCharacteristic(item);
                      readCharacteristic(item);
                    }}>
                    <Text style={styles.buttonText}>Read</Text>
                  </TouchableOpacity>
                )}

                {item.properties.Write && (
                  <TouchableOpacity
                    style={styles.button}
                    onPress={() => {
                      setSelectedCharacteristic(item);
                      setReadValue(null);
                    }}>
                    <Text style={styles.buttonText}>Write</Text>
                  </TouchableOpacity>
                )}
              </View>
            )}
            keyExtractor={item => item.characteristic}
          />
          {selectedCharacteristic && readValue === null && (
            <>
              <Text style={styles.header}>Characteristic Value</Text>

              <TextInput
                style={styles.input}
                placeholderTextColor={'gray'}
                placeholder="Enter value to write"
                value={writeValue}
                onChangeText={setWriteValue}
              />
              <Button
                title="Write Characteristic"
                onPress={writeCharacteristic}
              />
            </>
          )}
          {selectedCharacteristic && readValue && (
            <View
              style={{
                padding: 8,
                backgroundColor: 'black',
              }}>
              <Text
                style={{
                  color: 'white',
                }}>{`Read Value: ${readValue}`}</Text>
            </View>
          )}
        </>
      )}
    </View>
  );
};

const styles = StyleSheet.create({
  container: {
    flex: 1,
    padding: 16,
  },
  header: {
    fontSize: 18,
    fontWeight: 'bold',
    marginVertical: 8,
    color: 'black',
  },
  deviceContainer: {
    flexDirection: 'row',
    alignItems: 'center',
    justifyContent: 'space-between',
    padding: 8,
    borderBottomWidth: 1,
    borderBottomColor: '#ccc',
  },

  device: {
    fontSize: 16,
    color: 'black',
  },
  button: {
    
    padding: 8,
    backgroundColor: '#007AFF',
    borderRadius: 4,
  },
  buttonText: {
    color: '#fff',
  },
  characteristicContainer: {
    flexDirection: 'row',
    alignItems: 'center',
    justifyContent: 'space-between',
    padding: 8,
    borderBottomWidth: 1,
    borderBottomColor: '#ccc',
  },
  input: {
    height: 40,
    borderColor: 'gray',
    borderWidth: 1,
    marginBottom: 12,
    padding: 10,
    color: 'black',
  },
});

export default BluetoothScanner;

这是为了让 React Native 连接附近的设备并使用广告和蓝牙管理器获取其服务和特性

© www.soinside.com 2019 - 2024. All rights reserved.