From da2e8601e45a6a4b65983c8ec5aab1d9b769b643 Mon Sep 17 00:00:00 2001 From: intent-kacper-cyranowski <143452571+intent-kacper-cyranowski@users.noreply.github.com> Date: Wed, 17 Jul 2024 10:44:03 +0200 Subject: [PATCH] Chore/add tutorials (#1221) * chore: add tutorials --- README.md | 3 +- INTRO.md => docs/GETTING_STARTED.md | 61 +++++++---- docs/TUTORIALS.md | 108 +++++++++++++++++++ docs/index.html | 157 ++++++++++++++++++++++++---- documentation.yml | 7 +- 5 files changed, 290 insertions(+), 46 deletions(-) rename INTRO.md => docs/GETTING_STARTED.md (74%) create mode 100644 docs/TUTORIALS.md diff --git a/README.md b/README.md index 68d7a55e..ac965350 100644 --- a/README.md +++ b/README.md @@ -27,6 +27,7 @@ It does NOT support: - bluetooth classic devices. - communicating between phones using BLE (Peripheral support) - [bonding peripherals](https://github.com/dotintent/react-native-ble-plx/wiki/Device-Bonding) +- [beacons](https://github.com/dotintent/react-native-ble-plx/wiki/=-FAQ:-Beacons) ## Table of Contents @@ -43,7 +44,7 @@ For old RN versions (<0.60) please check [old README](./docs/README_V1.md) (1.x) for the old instructions or [migration guide](./docs/MIGRATION_V1.md). | React Native | 3.1.2 | -|--------------| ------------------ | +| ------------ | ------------------ | | 0.74.1 | :white_check_mark: | | 0.69.6 | :white_check_mark: | | Expo 51 | :white_check_mark: | diff --git a/INTRO.md b/docs/GETTING_STARTED.md similarity index 74% rename from INTRO.md rename to docs/GETTING_STARTED.md index fcde8afc..c746f08a 100644 --- a/INTRO.md +++ b/docs/GETTING_STARTED.md @@ -1,31 +1,52 @@ -

- react-native-ble-plx +

+ react-native-ble-plx

This guide is an introduction to BLE stack and APIs exported by this library. All examples will be based on CC2541 SensorTag. -## Creating BLE Manager +### Install and prepare package -First step is to create BleManager instance which is an entry point to all available APIs. -Make sure to create it after application started its execution. For example we can keep it -as a static reference: +In the case of Expo, you will need to prepare a plugin config, detailed information can be found here: https://github.com/dotintent/react-native-ble-plx?tab=readme-ov-file#expo-sdk-43 +In the case of react native CLI you need to configure two environments: -```js +- [iOS](https://github.com/dotintent/react-native-ble-plx?tab=readme-ov-file#ios-example-setup) +- [Android](https://github.com/dotintent/react-native-ble-plx?tab=readme-ov-file#android-example-setup) + +### Creating BLE Manager + +First step is to create BleManager instance which is an entry point to all available APIs. It should be declared **OUTSIDE the life cycle of React**. Make sure to create it after application started its execution. We can keep it as a static reference by either creating our own abstraction (ex.1) or by simply creating a new instance (ex.2). + +#### Ex.1 + +```ts +import { BleManager } from 'react-native-ble-plx' + +// create your own singleton class +class BLEServiceInstance { + manager: BleManage + + constructor() { + this.manager = new BleManager() + } +} + +export const BLEService = new BLEServiceInstance() +``` + +#### Ex.2 + +```ts import { BleManager } from 'react-native-ble-plx' export const manager = new BleManager() ``` -Only _one_ instance of BleManager is allowed. When you don't need any BLE functionality you -can destroy created instance by calling `manager.destroy()` function. You can then recreate -`BleManager` later on. +Only _one_ instance of BleManager is allowed. When you don't need any BLE functionality you can destroy created instance by calling `manager.destroy()` function. You can then recreate `BleManager` later on. -Note that you may experience undefined behaviour when calling a function on one `BleManager` -and continuing with another instance. A frequently made error is to create a new instance -of the manager for every re-render of a React Native Component. +Note that you may experience undefined behavior when calling a function on one `BleManager` and continuing with another instance. A frequently made error is to create a new instance of the manager for every re-render of a React Native Component. -## Ask for permissions +### Ask for permissions Check if you requested following permissions @@ -82,7 +103,7 @@ return ( ) ``` -## Waiting for Powered On state +### Waiting for Powered On state When iOS application launches BLE stack is not immediately available and we need to check its status. To detect current state and following state changes we can use `onStateChange()` function: @@ -99,7 +120,7 @@ React.useEffect(() => { }, [manager]) ``` -## Scanning devices +### Scanning devices Devices needs to be scanned first to be able to connect to them. There is a simple function which allows only one callback to be registered to handle detected devices: @@ -128,11 +149,11 @@ It is worth to note that scanning function may emit one device multiple times. H when device is connected it won't broadcast and needs to be disconnected from central to be scanned again. Only one scanning listener can be registered. -### Bluetooth 5 Advertisements in Android +#### Bluetooth 5 Advertisements in Android To see devices that use Bluetooth 5 Advertising Extension you have to set the `legacyScan` variable to `false` in {@link #scanoptions|Scan options} when you are starting {@link #blemanagerstartdevicescan|BleManager.startDeviceScan()}, -## Connecting and discovering services and characteristics +### Connecting and discovering services and characteristics Once device is scanned it is in disconnected state. We need to connect to it and discover all services and characteristics it contains. Services may be understood @@ -160,7 +181,7 @@ It can be a long process depending on number of characteristics and services ava \* Extremely rarely, when peripheral's service/characteristic set can change during a connection an additional service discovery may be needed. -## Read, write and monitor values +### Read, write and monitor values After successful discovery of services you can call @@ -168,4 +189,4 @@ After successful discovery of services you can call - {@link #blemanagerwritecharacteristicwithresponsefordevice|BleManager.writeCharacteristicWithResponseForDevice()}, - {@link #blemanagermonitorcharacteristicfordevice|BleManager.monitorCharacteristicForDevice()} -and other functions which are described in detail in documentation. +and other functions which are described in detail in documentation. You can also check our _example app_ which is available in the repository. diff --git a/docs/TUTORIALS.md b/docs/TUTORIALS.md new file mode 100644 index 00000000..2c04b6ae --- /dev/null +++ b/docs/TUTORIALS.md @@ -0,0 +1,108 @@ +### Monitoring device connection + +`onDeviceDisconnected` method allows you to monitor the disconnection of a device, more about the method can be found {@link #here|BleManager.onDeviceDisconnected()}. Using it you can implement your own logic to handle the disconnection event. For example, you can try to reconnect to the device or show a notification to the user. + +Note: connection will be monitored only when app is in foreground. + +```ts +const setupOnDeviceDisconnected = (deviceIdToMonitor: String) => { + bleManagerInstance.onDeviceDisconnected(deviceIdToMonitor, disconnectedListener) +} + +const disconnectedListener = (error: BleError | null, device: Device | null) => { + if (error) { + console.error(JSON.stringify(error, null, 4)) + } + if (device) { + console.info(JSON.stringify(device, null, 4)) + + // reconnect to the device + device.connect() + } +} +``` + +### Reading and writing to characteristics + +#### Prepare the connection with your device + +The first thing you need to do is connect to your device. Once the connection is established you can only perform basic operations without the possibility of interacting with the services on the device. To be able to interact with the services on the device, so you need to call an additional command `device.discoverAllServicesAndCharacteristics()` because even though you know what service and characteristics are on the device, they all must be visible to the GATT client, which handles all operations. + +```js +device + .connect() + .then(device => { + return device.discoverAllServicesAndCharacteristics() + }) + .then(device => { + // A fully functional connection you can use, now you can read, write and monitor values + }) + .catch(error => { + // Handle errors + }) +``` + +#### Reading from a characteristic + +To read a value from a characteristic, you need to call the `readCharacteristic` method on the device object. The method returns a promise that resolves to the characteristic value. + +```js +device + .readCharacteristicForService(serviceUUID, characteristicUUID) + .then(characteristic => { + console.log('Read characteristic value:', characteristic.value) + }) + .catch(error => { + console.error('Read characteristic error:', error) + }) +``` + +#### Writing to a characteristic + +To write a value to a characteristic, you need to call the `writeCharacteristicWithResponse` or `writeCharacteristicWithoutResponse` method on the device object. The method returns a promise that resolves when the write operation is completed. + +```js +device + .writeCharacteristicWithResponseForService(serviceUUID, characteristicUUID, value) + .then(() => { + console.log('Write characteristic success') + }) + .catch(error => { + console.error('Write characteristic error:', error) + }) +``` + +### Connecting to a device that is already connected to the OS + +If you want to connect to a device that isn't discoverable because it is already connected to the system, you can use the `getConnectedDevices` method to get a list of connected devices. Then you can use the `connect` method on the device object to connect to the device, after making sure that the device is not already connected. + +```js +bleManagerInstance + .getConnectedDevices([serviceUUIDs]) + .then(devices => { + const device = devices.find(d => d.id === deviceIdWeWantToConnectTo) + + if (device && !device.isConnected) { + device.connect() + } + }) + .catch(error => { + console.error('Get connected devices error:', error) + }) +``` + +### Collecting native logs + +If you encounter any issues with the library, you can enable native logs to get more information about what is happening under the hood. To enable native logs, you need to set the `logLevel` property on the BleManager instance to `LogLevel.Verbose`. + +```js +bleManagerInstance.setLogLevel(LogLevel.Verbose) +``` + +#### Android + +To collect native logs on Android, you can open the Logcat in Android Studio and set filters to `package:mine (tag:BluetoothGatt | tag:ReactNativeJS | RxBle)`. + +#### iOS + +To collect native logs on iOS, you can open the Xcode console. diff --git a/docs/index.html b/docs/index.html index 720d0885..5135673a 100644 --- a/docs/index.html +++ b/docs/index.html @@ -29,9 +29,19 @@

react-native-ble-plx

  • - Introduction + Getting started + + + +
  • + + +
  • + Tutorials @@ -1957,30 +1967,45 @@

    react-native-ble-plx

    -

    - Introduction +

    + Getting started

    -

    - react-native-ble-plx +

    + react-native-ble-plx

    This guide is an introduction to BLE stack and APIs exported by this library. All examples will be based on CC2541 SensorTag.

    -

    Creating BLE Manager

    -

    First step is to create BleManager instance which is an entry point to all available APIs. -Make sure to create it after application started its execution. For example we can keep it -as a static reference:

    +

    Install and prepare package

    +

    In the case of Expo, you will need to prepare a plugin config, detailed information can be found here: https://github.com/dotintent/react-native-ble-plx?tab=readme-ov-file#expo-sdk-43 +In the case of react native CLI you need to configure two environments:

    + +

    Creating BLE Manager

    +

    First step is to create BleManager instance which is an entry point to all available APIs. It should be declared OUTSIDE the life cycle of React. Make sure to create it after application started its execution. We can keep it as a static reference by either creating our own abstraction (ex.1) or by simply creating a new instance (ex.2).

    +

    Ex.1

    +
    import { BleManager } from 'react-native-ble-plx'
    +
    +// create your own singleton class
    +class BLEServiceInstance {
    +  manager: BleManage
    +
    +  constructor() {
    +    this.manager = new BleManager()
    +  }
    +}
    +
    +export const BLEService = new BLEServiceInstance()
    +

    Ex.2

    import { BleManager } from 'react-native-ble-plx'
     
     export const manager = new BleManager()
    -

    Only one instance of BleManager is allowed. When you don't need any BLE functionality you -can destroy created instance by calling manager.destroy() function. You can then recreate -BleManager later on.

    -

    Note that you may experience undefined behaviour when calling a function on one BleManager -and continuing with another instance. A frequently made error is to create a new instance -of the manager for every re-render of a React Native Component.

    -

    Ask for permissions

    +

    Only one instance of BleManager is allowed. When you don't need any BLE functionality you can destroy created instance by calling manager.destroy() function. You can then recreate BleManager later on.

    +

    Note that you may experience undefined behavior when calling a function on one BleManager and continuing with another instance. A frequently made error is to create a new instance of the manager for every re-render of a React Native Component.

    +

    Ask for permissions

    Check if you requested following permissions

    • PermissionsAndroid.PERMISSIONS.ACCESS_FINE_LOCATION
    • @@ -2028,7 +2053,7 @@

      Ask for permissions

      result['android.permission.BLUETOOTH_CONNECT'] === PermissionsAndroid.RESULTS.GRANTED && result['android.permission.BLUETOOTH_SCAN'] === PermissionsAndroid.RESULTS.GRANTED ) -

      Waiting for Powered On state

      +

      Waiting for Powered On state

      When iOS application launches BLE stack is not immediately available and we need to check its status. To detect current state and following state changes we can use onStateChange() function:

      React.useEffect(() => {
      @@ -2040,7 +2065,7 @@ 

      Waiting for Powered On state

      }, true) return () => subscription.remove() }, [manager])
      -

      Scanning devices

      +

      Scanning devices

      Devices needs to be scanned first to be able to connect to them. There is a simple function which allows only one callback to be registered to handle detected devices:

      function scanAndConnect() {
      @@ -2063,9 +2088,9 @@ 

      Scanning devices

      It is worth to note that scanning function may emit one device multiple times. However when device is connected it won't broadcast and needs to be disconnected from central to be scanned again. Only one scanning listener can be registered.

      -

      Bluetooth 5 Advertisements in Android

      +

      Bluetooth 5 Advertisements in Android

      To see devices that use Bluetooth 5 Advertising Extension you have to set the legacyScan variable to false in Scan options when you are starting BleManager.startDeviceScan(),

      -

      Connecting and discovering services and characteristics

      +

      Connecting and discovering services and characteristics

      Once device is scanned it is in disconnected state. We need to connect to it and discover all services and characteristics it contains. Services may be understood as containers grouping characteristics based on their meaning. Characteristic is a @@ -2086,14 +2111,100 @@

      Connecting and discovering services and characteristics

      It can be a long process depending on number of characteristics and services available.

      * Extremely rarely, when peripheral's service/characteristic set can change during a connection an additional service discovery may be needed.

      -

      Read, write and monitor values

      +

      Read, write and monitor values

      After successful discovery of services you can call

      -

      and other functions which are described in detail in documentation.

      +

      and other functions which are described in detail in documentation. You can also check our example app which is available in the repository.

      + + +
    + + + +
    + +

    + Tutorials +

    + + +

    Monitoring device connection

    +

    onDeviceDisconnected method allows you to monitor the disconnection of a device, more about the method can be found BleManager.onDeviceDisconnected(). Using it you can implement your own logic to handle the disconnection event. For example, you can try to reconnect to the device or show a notification to the user.

    +

    Note: connection will be monitored only when app is in foreground.

    +
    const setupOnDeviceDisconnected = (deviceIdToMonitor: String) => {
    +  bleManagerInstance.onDeviceDisconnected(deviceIdToMonitor, disconnectedListener)
    +}
    +
    +const disconnectedListener = (error: BleError | null, device: Device | null) => {
    +  if (error) {
    +    console.error(JSON.stringify(error, null, 4))
    +  }
    +  if (device) {
    +    console.info(JSON.stringify(device, null, 4))
    +
    +    // reconnect to the device
    +    device.connect()
    +  }
    +}
    +

    Reading and writing to characteristics

    +

    Prepare the connection with your device

    +

    The first thing you need to do is connect to your device. Once the connection is established you can only perform basic operations without the possibility of interacting with the services on the device. To be able to interact with the services on the device, so you need to call an additional command device.discoverAllServicesAndCharacteristics() because even though you know what service and characteristics are on the device, they all must be visible to the GATT client, which handles all operations.

    +
    device
    +  .connect()
    +  .then(device => {
    +    return device.discoverAllServicesAndCharacteristics()
    +  })
    +  .then(device => {
    +    // A fully functional connection you can use, now you can read, write and monitor values
    +  })
    +  .catch(error => {
    +    // Handle errors
    +  })
    +

    Reading from a characteristic

    +

    To read a value from a characteristic, you need to call the readCharacteristic method on the device object. The method returns a promise that resolves to the characteristic value.

    +
    device
    +  .readCharacteristicForService(serviceUUID, characteristicUUID)
    +  .then(characteristic => {
    +    console.log('Read characteristic value:', characteristic.value)
    +  })
    +  .catch(error => {
    +    console.error('Read characteristic error:', error)
    +  })
    +

    Writing to a characteristic

    +

    To write a value to a characteristic, you need to call the writeCharacteristicWithResponse or writeCharacteristicWithoutResponse method on the device object. The method returns a promise that resolves when the write operation is completed.

    +
    device
    +  .writeCharacteristicWithResponseForService(serviceUUID, characteristicUUID, value)
    +  .then(() => {
    +    console.log('Write characteristic success')
    +  })
    +  .catch(error => {
    +    console.error('Write characteristic error:', error)
    +  })
    +

    Connecting to a device that is already connected to the OS

    +

    If you want to connect to a device that isn't discoverable because it is already connected to the system, you can use the getConnectedDevices method to get a list of connected devices. Then you can use the connect method on the device object to connect to the device, after making sure that the device is not already connected.

    +
    bleManagerInstance
    +  .getConnectedDevices([serviceUUIDs])
    +  .then(devices => {
    +    const device = devices.find(d => d.id === deviceIdWeWantToConnectTo)
    +
    +    if (device && !device.isConnected) {
    +      device.connect()
    +    }
    +  })
    +  .catch(error => {
    +    console.error('Get connected devices error:', error)
    +  })
    +

    Collecting native logs

    +

    If you encounter any issues with the library, you can enable native logs to get more information about what is happening under the hood. To enable native logs, you need to set the logLevel property on the BleManager instance to LogLevel.Verbose.

    +
    bleManagerInstance.setLogLevel(LogLevel.Verbose)
    +

    Android

    +

    To collect native logs on Android, you can open the Logcat in Android Studio and set filters to package:mine (tag:BluetoothGatt | tag:ReactNativeJS | RxBle).

    +

    iOS

    +

    To collect native logs on iOS, you can open the Xcode console.

    diff --git a/documentation.yml b/documentation.yml index 69238532..00c1e34e 100644 --- a/documentation.yml +++ b/documentation.yml @@ -1,6 +1,8 @@ toc: - - name: Introduction - file: INTRO.md + - name: Getting started + file: ./docs/GETTING_STARTED.md + - name: Tutorials + file: ./docs/TUTORIALS.md - name: Main Classes description: | Classes described below are main building blocks for your BLE support. They @@ -42,3 +44,4 @@ toc: - TransactionId - Subscription - Base64 + - RefreshGattMoment