如何使用React Native BLE管理器在移动应用中整合BLE技术

592 阅读7分钟

如何使用react原生BLE管理器在移动应用中整合BLE技术。了解如何使用中心和外围设备进行基本操作。

所需的库和设备信息

我们使用 react native ble manager 库,将react-native移动应用与BLE技术整合在一起。我使用Mansa智能灯泡创建了一个演示应用程序并进行了操作。我使用 react原生颜色选择器 库来显示一个颜色选择器。

你如何实现它?

1.到你的项目中,运行以下命令来安装库

yarn add react-native-ble-manager react-native-color-picker

2.对于iOS,使用下面的命令安装pods。

cd ios && pod install OR cd ios && 

3.在Android平台上,你需要 按照给定的格式更新 AndroidManifest.xml 文件。了解更多关于(Android-Configuration)

<manifest xmlns:android="http://schemas.android.com/apk/res/android"

    xmlns:tools="http://schemas.android.com/tools"

    package="YOUR_PACKAGE_NAME">

    <uses-permission android:name="android.permission.BLUETOOTH" android:maxSdkVersion="30" />

    <uses-permission android:name="android.permission.BLUETOOTH_ADMIN" android:maxSdkVersion="30" />

    <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" android:maxSdkVersion="28"/>

    <uses-permission-sdk-23 android:name="android.permission.ACCESS_FINE_LOCATION" tools:targetApi="Q"/>

    <!-- Only when targeting Android 12 or higher -->

    <!-- Please make sure you read the following documentation to have a

         better understanding of the new permissions.

         https://developer.android.com/guide/topics/connectivity/bluetooth/permissions#assert-never-for-location

         -->

    <!-- If your app doesn't use Bluetooth scan results to derive physical location information,

         you can strongly assert that your app

         doesn't derive physical location. -->

    <uses-permission android:name="android.permission.BLUETOOTH_CONNECT" 

                     android:usesPermissionFlags="neverForLocation"

                     tools:targetApi="s" />

    <!-- Needed only if your app looks for Bluetooth devices. -->

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

    <!-- Needed only if your app makes the device discoverable to Bluetooth devices. -->

    <uses-permission android:name="android.permission.BLUETOOTH_ADVERTISE" />

4.对于iOS,你需要在info.plist文件中添加NSBluetoothAlwaysUsageDescription字符串键。

5.首先,在执行任何种类的操作或行动之前,我们必须初始化BLE模块。

  useEffect(() => {

        BleManager.start({ showAlert: false, forceLegacy: true });

    }, []);

6.在初始化BLE模块后,你需要检查蓝牙许可。如果该许可不可用,你需要申请并获得它。

const checkForBluetoothPermission = () => {

        if (Platform.OS === 'android' && Platform.Version >= 23) {

            let finalPermission = Platform.Version >= 29

                ? PermissionsAndroid.PERMISSIONS.ACCESS_FINE_LOCATION

                : PermissionsAndroid.PERMISSIONS.ACCESS_COARSE_LOCATION;

            PermissionsAndroid.check(finalPermission).then((result) => {

                if (result) {

                    enableBluetoothInDevice()

                } else {

                    PermissionsAndroid.request(finalPermission).then((result) => {

                        if (result) {

                            enableBluetoothInDevice()

                        } else {

                            console.log("User refuse");

                        }

                    });

                }

            });

        }

        else {

            console.log("IOS");

            enableBluetoothInDevice()

        }

    }

7.如果有许可,在开始扫描附近的BLE外围设备之前,您应该启用蓝牙。

const enableBluetoothInDevice = () => {

        BleManager.enableBluetooth()

            .then(() => {

                setBluetoothtoggle(true)

                startScan()

            })

            .catch((error) => {

                console.log("Error--->", error);

            });

    }
const startScan = () => {

        if (!isScanning) {

            BleManager.scan([], 10, true).then((results) => {

                console.log('Scanning...');

                setIsScanning(true);

            }).catch(err => {

                console.error(err);

            });

        }

    }

8.扫描后,您可以得到附近可用的BLE外围设备的列表。你可以根据需要使用FlatList来显示列表中的元素。为了这个演示的目的,我创建了以下屏幕。

9.现在我们可以连接到列表中的任何外围设备,并执行不同种类的操作。所以首先我们需要检查外围设备是否与我们的应用程序(中央设备)连接。如果一个设备没有连接,那么我们需要连接,然后我们就可以继续了。

const connectBLEDevice = (item, index) => {

        toggleConnecting(true, index)   

        BleManager.isPeripheralConnected(item.id, []).then((res) => {

            if (res == false) {

                BleManager.connect(item.id)

                    .then((res7) => {

                        redirectUserToNext(item, index)

                    }).catch((error) => {

                        console.log("Error---BLE connect--->", error);

                        toggleConnecting(false, index)

                        ToastAndroid.show("Something went wrong while connecting..", ToastAndroid.SHORT)

                    })

            }

            else {

                redirectUserToNext(item, index)

            }

        }).catch((error) => {

            toggleConnecting(false, index)

            ToastAndroid.show("Something went wrong while connecting..", ToastAndroid.SHORT)

        })

    }

10.你也可以给你的生命周期方法添加一个监听器,以检测外围设备,停止扫描,并在更新发生时为你的BLE设备获得一个分数。事件的名称是分配给所使用的库的。

useEffect(() => {

        BleManager.start({ showAlert: false, forceLegacy: true });

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

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

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

        const ble4 = bleManagerEmitter.addListener('BleManagerDidUpdateValueForCharacteristic', handleUpdateValueForCharacteristic);

        checkForBluetoothPermission()

        return (() => {

            ble1.remove()

            ble2.remove()

            ble3.remove()

            ble4.remove()

        })

    }, []);

11.你现在可以使用BLE设备实现读和写了。描述了读和写的操作。首先,你需要获得 执行读写操作所需的 所有服务和 特性 。如果所需的服务和 特性 不可用,您将无法在BLE设备上执行操作。

12.现在您应该看到您的BLE设备上的可用服务列表。在这里,你需要为外围设备传递一个唯一的ID。Blemanager库有一个叫做retriveServices的方法。

const getAllServiceForBLEDevice = () => {

        let item = route.params && route.params.peripheral ? route.params.peripheral : null

        var tempdata = [];

        BleManager.retrieveServices(item.id).then((res1) => {

            console.log("Res1===>", res1);

        }).catch((err) => {console.log("err-->", err);})

    }

13.在获取服务后,你可以执行所需的逻辑。而在这里,我们检查所需的服务和 特性是否 在指定的列表中可用。这里我们定义了使用Smart Bulb执行写操作所需的服务UUID和属性UUID。为了做到这一点,它检测它是否可用。你需要获得执行操作的硬件所需的所有信息和参数。

const serviceUUIDForWriteBlubColor = "ffb0"

const characteristicUUIDForWriteBlubColor = "ffb2"

    const getAllServiceForBLEDevice = () => {

        let item = route.params && route.params.peripheral ? route.params.peripheral : null

        var tempdata = [];

        BleManager.retrieveServices(item.id).then((res1) => {

            console.log("Res1===>", res1);

            let data = res1.characteristics;

            var seen = {};

            tempdata = data.filter(function (entry) {

                var previous;

                if (seen.hasOwnProperty(entry.service)) {

                                       previous = seen[entry.service];

                    previous.characteristicList.push({

                        characteristic: entry.characteristic,

                        properties: entry.properties,

                    });

                    return false;

                }

                if (!Array.isArray(entry.characteristicList)) {

                    entry.characteristicList = [{

                        characteristic: entry.characteristic,

                        properties: entry.properties,

                    }];

                }

                seen[entry.service] = entry;

                delete entry.characteristic;

                delete entry.properties;

                delete entry.descriptors;

                return true;

            });

            console.log("tempdata-0----->", tempdata);

            setServiceAndCharList(tempdata)

            let isListContainBlubChangeColorService = tempdata.filter((obj) => obj.service == serviceUUIDForWriteBlubColor);

            console.log("isListContainBlubChangeColorService---->", isListContainBlubChangeColorService);

            if (isListContainBlubChangeColorService.length > 0) {

                let isListContainBlubChangeColorChar = isListContainBlubChangeColorService[0].characteristicList.filter((obj) => obj.characteristic == characteristicUUIDForWriteBlubColor);

                console.log("isListContainBlubChangeColorChar---->", isListContainBlubChangeColorChar);

                if (isListContainBlubChangeColorChar.length) {

                    setAvaibility(true)

                }

                else {

                    setAvaibility(false)

                }

            }

            else {

                setAvaibility(false)

            }

        }).catch((err) => {

            console.log("err-->", err);

        })

    }

14.现在我们需要用写操作来改变灯泡的颜色。 在这里,我创建了以下屏幕。 在这里面,我使用了一个颜色选择器来选择颜色来改变灯泡的颜色。我还添加了一些主要颜色的色调来改变灯泡的颜色。

15.现在,当我们挑选颜色时,首先我们需要检查我们需要的服务和特性是否可用,然后我们可以继续,否则就不行。当我们从选色器或预定义的颜色阴影中挑选颜色时,它将是HEX格式的。 因此,需要将其转换为RGB格式。

export function hexToRgb(hex) {

    var result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex);

    return result ? {

        r: parseInt(result[1], 16),

        g: parseInt(result[2], 16),

        b: parseInt(result[3], 16)

    } : null;

}
const fullBrightNessHexValue = 49; // 1

  const zeroBrightNessHexValue = 48; // 0

    const onColorPicked = (color) => {

        if (isServiceAndCharAvailable) {

            let item = route.params && route.params.peripheral ? route.params.peripheral : null

            let hexToRgbValue = hexToRgb(color)

            let { r, g, b } = hexToRgbValue

            let tempObj = {

                id: item.id,

                name: item.name,

                service: serviceUUIDForWriteBlubColor,

                characteristic: characteristicUUIDForWriteBlubColor,

                writeValueData: [fullBrightNessHexValue, r, g, b]

            }

            setLastColor(color)

            readAndWriteData(tempObj)

        }

        else {

            ToastAndroid.show("Bulb change color service is not available.", ToastAndroid.SHORT)

        }

    }

16.我创建了一个obj来传递执行写操作。你需要传递一个长度为4的字节数组(writeValueData)。这第一个元素需要你传递十六进制代码的亮度值,其他三个参数是R、G、B的。

17.然后再次检查BLE外围设备是否与应用程序连接。如果连接了,就可以继续,如果没有连接,就不能继续。

const readAndWriteData = (peripheral, isRead, isToggleBlub) => {

        BleManager.isPeripheralConnected(peripheral.id, []).then((res) => {

            if (res == false) {

                BleManager.connect(peripheral.id)

                    .then((res7) => {

                        if (isRead) readCharData(peripheral)

                        else writeCharData(peripheral, isToggleBlub)

                    })

                    .catch((error) => { console.log("error---456464454->", error); });

            }

            else {

                if (isRead) readCharData(peripheral)

                else writeCharData(peripheral, isToggleBlub)

            }

        }).catch((error) => { console.log("Error--->", error) })

    }

18.接下来,让我们使用BleManager库的写入方法进行最后的写入。要写,你必须传递一个唯一的外设ID、服务UDID、属性UDID和字节数组。(我已经有了这个项目)。

const writeCharData = (peripheral, isToggleBlub) => {

        try {

            BleManager.write(peripheral.id,

                peripheral.service,

                peripheral.characteristic,

                peripheral.writeValueData  

            ).then((response) => {

                if (isToggleBlub == "1") {

                    ToastAndroid.show("Blub is now Turned On", ToastAndroid.SHORT)

                }

                else if (isToggleBlub == "2") {

                    ToastAndroid.show("Blub is now Turned Off", ToastAndroid.SHORT)

                }

                else {

                }

            }).catch(error => {

                console.log("Error--->", error);

                ToastAndroid.show(JSON.stringify(error), ToastAndroid.SHORT)

            })

        } catch (error) {

            console.log("Error---123123123-<", error);

        }

    }

19.如果一切正常,写完后灯泡会变色。然后进行读操作。要做到这一点,使用BleManager的读取方法来获得灯泡的名称。为了得到智能灯泡的名称,你需要传递外围设备的唯一ID、serviceUDID和特性UDID。为了从读取方法中获得字节数组中的响应,我们首先需要将其转换为人类可读的格式。然后就可以查看转换后得到的名称。

    const serviceUUIDForWriteBlubColor = "ffb0"
    
    const characteristicUUIDForWriteBlubColor = "ffb2"

    const characteristicUUIDForChangeBlubName = "ffb7"

            BleManager.read(item.id, serviceUUIDForWriteBlubColor, characteristicUUIDForChangeBlubName).then((characteristic) => {

                const bytesString = String.fromCharCode(...characteristic)

                setBlubName(bytesString)

            }).catch((error) => {

                console.log("Error--write name->", error);

            })

20.现在,让我们用一张图来看看整个场景,这是各种技术学科的正常流程。这也是所有开发者的共同流程。

  1. 有两种类型的设备,中心设备和外围设备。
  2. 当你打开外围设备时,广告就会开始。
  3. 当应用程序在中央设备上打开时,它首先检查权限,询问是否可用。然后,在获得许可后,开始扫描几秒钟。
  4. 中央设备现在将搜索广告包,并找到附近可用的BLE外围设备列表。
  5. 现在,为了在外围设备上执行BLE操作,你需要检查所选外围设备是否连接到应用程序。如果没有连接,你需要先进行连接。
  6. 一旦建立了连接,就需要获得所有的资源,在此基础上,需要获得扫盲过程所需的服务。
  7. 如果有服务,你需要查看与该服务相关的所有特性,在此基础上,找到读写所需的特性。
  8. 如果没有性能或功能,你应该相应地处理这个错误。
  9. 一旦你找到了该特性,你就可以进行读、写和通知操作。

总结

这篇文章对那些想学习使用react原生BLE管理器将BLE技术集成到移动应用中的开发者来说可能很有用。首先,你需要BLE硬件或BLE模拟器应用来执行操作。然而,最重要的还是,我们需要BLE的硬件相关信息,因为我们需要一些初步信息来执行BLE操作。B. 服务和特征UDID,这是一种用于书写的数据格式。因此,通过BLE技术,你可以使用React native来创建有趣的应用程序。最后的流程图提供了对BLE特性的基本理解。