react-native-ble-plx

3.2.1

Getting started

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.

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 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
  • PermissionsAndroid.PERMISSIONS.BLUETOOTH_SCAN (necessary for api 31+ )
  • PermissionsAndroid.PERMISSIONS.BLUETOOTH_CONNECT (necessary for api 31+ )

eg.

requestBluetoothPermission = async () => {
  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)
      return 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
      ])

      return (
        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
      )
    }
  }

  this.showErrorToast('Permission have not been granted')

  return false
}

With neverForLocation flag active, you can remove ACCESS_FINE_LOCATION permissions ask e.g.:

const result = await PermissionsAndroid.requestMultiple([
  PermissionsAndroid.PERMISSIONS.BLUETOOTH_SCAN,
  PermissionsAndroid.PERMISSIONS.BLUETOOTH_CONNECT
])

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

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(() => {
  const subscription = manager.onStateChange(state => {
    if (state === 'PoweredOn') {
      scanAndConnect()
      subscription.remove()
    }
  }, true)
  return () => subscription.remove()
}, [manager])

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() {
  manager.startDeviceScan(null, null, (error, device) => {
    if (error) {
      // Handle error (scanning will be stopped automatically)
      return
    }

    // Check if it is a device you are looking for based on advertisement data
    // or other criteria.
    if (device.name === 'TI BLE Sensor Tag' || device.name === 'SensorTag') {
      // Stop scanning as it's not necessary if you are scanning for one device.
      manager.stopDeviceScan()

      // Proceed with connection.
    }
  })
}

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

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

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 container for a value which can be read, written and monitored based on available capabilities. For example connection may look like this:

device
  .connect()
  .then(device => {
    return device.discoverAllServicesAndCharacteristics()
  })
  .then(device => {
    // Do work on device with services and characteristics
  })
  .catch(error => {
    // Handle errors
  })

Discovery of services and characteristics is required to be executed once per connection*. 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

After successful discovery of services you can call

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.

Main Classes

Classes described below are main building blocks for your BLE support. They are presented in order which aligns them with usage.

BleManager

BleManager is an entry point for react-native-ble-plx library. It provides all means to discover and work with Device instances. It should be initialized only once with new keyword and method destroy() should be called on its instance when user wants to deallocate all resources.

In case you want to properly support Background Mode, you should provide restoreStateIdentifier and restoreStateFunction in BleManagerOptions.

new BleManager(options: BleManagerOptions)
Parameters
options (BleManagerOptions = {})
Example
const manager = new BleManager();
// ... work with BLE manager ...
manager.destroy();
Instance Members
destroy
setLogLevel(logLevel)
logLevel()
cancelTransaction(transactionId)
enable(transactionId)
disable(transactionId)
state
onStateChange
startDeviceScan(UUIDs, options, listener)
stopDeviceScan
requestConnectionPriorityForDevice(deviceIdentifier, connectionPriority, transactionId)
readRSSIForDevice(deviceIdentifier, transactionId)
requestMTUForDevice(deviceIdentifier, mtu, transactionId)
devices(deviceIdentifiers)
connectedDevices(serviceUUIDs)
connectToDevice(deviceIdentifier, options)
cancelDeviceConnection(deviceIdentifier)
onDeviceDisconnected(deviceIdentifier, listener)
isDeviceConnected(deviceIdentifier)
discoverAllServicesAndCharacteristicsForDevice(deviceIdentifier, transactionId)
servicesForDevice(deviceIdentifier)
characteristicsForDevice(deviceIdentifier, serviceUUID)
descriptorsForDevice(deviceIdentifier, serviceUUID, characteristicUUID)
readCharacteristicForDevice(deviceIdentifier, serviceUUID, characteristicUUID, transactionId)
writeCharacteristicWithResponseForDevice(deviceIdentifier, serviceUUID, characteristicUUID, base64Value, transactionId)
writeCharacteristicWithoutResponseForDevice(deviceIdentifier, serviceUUID, characteristicUUID, base64Value, transactionId)
monitorCharacteristicForDevice(deviceIdentifier, serviceUUID, characteristicUUID, listener, transactionId)
readDescriptorForDevice(deviceIdentifier, serviceUUID, characteristicUUID, descriptorUUID, transactionId)
writeDescriptorForDevice(deviceIdentifier, serviceUUID, characteristicUUID, descriptorUUID, valueBase64, transactionId)

Device

Device instance which can be retrieved only by calling bleManager.startDeviceScan().

new Device(nativeDevice: NativeDevice, manager: BleManager)
Parameters
nativeDevice (NativeDevice) Native device properties
manager (BleManager) BleManager handle
Instance Members
id
name
rssi
mtu
manufacturerData
rawScanRecord
serviceData
serviceUUIDs
localName
txPowerLevel
solicitedServiceUUIDs
isConnectable
overflowServiceUUIDs
requestConnectionPriority(connectionPriority, transactionId)
readRSSI(transactionId)
requestMTU(mtu, transactionId)
connect(options)
cancelConnection()
isConnected()
onDisconnected(listener)
discoverAllServicesAndCharacteristics(transactionId)
services()
characteristicsForService(serviceUUID)
descriptorsForService(serviceUUID, characteristicUUID)
readCharacteristicForService(serviceUUID, characteristicUUID, transactionId)
writeCharacteristicWithResponseForService(serviceUUID, characteristicUUID, valueBase64, transactionId)
writeCharacteristicWithoutResponseForService(serviceUUID, characteristicUUID, valueBase64, transactionId)
monitorCharacteristicForService(serviceUUID, characteristicUUID, listener, transactionId)
readDescriptorForService(serviceUUID, characteristicUUID, descriptorUUID, transactionId)
writeDescriptorForService(serviceUUID, characteristicUUID, descriptorUUID, valueBase64, transactionId)

Service

Service object.

new Service(nativeService: NativeService, manager: BleManager)
Parameters
nativeService (NativeService) NativeService properties to be copied.
manager (BleManager) Current BleManager instance.
Instance Members
id
uuid
deviceID
isPrimary
characteristics()
descriptorsForCharacteristic(characteristicUUID)
readCharacteristic(characteristicUUID, transactionId)
writeCharacteristicWithResponse(characteristicUUID, valueBase64, transactionId)
writeCharacteristicWithoutResponse(characteristicUUID, valueBase64, transactionId)
monitorCharacteristic(characteristicUUID, listener, transactionId)
readDescriptorForCharacteristic(characteristicUUID, descriptorUUID, transactionId)
writeDescriptorForCharacteristic(characteristicUUID, descriptorUUID, valueBase64, transactionId)

Characteristic

Characteristic object.

new Characteristic(nativeCharacteristic: NativeCharacteristic, manager: BleManager)
Parameters
nativeCharacteristic (NativeCharacteristic) NativeCharacteristic
manager (BleManager) BleManager
Instance Members
id
uuid
serviceID
serviceUUID
deviceID
isReadable
isWritableWithResponse
isWritableWithoutResponse
isNotifiable
isNotifying
isIndicatable
value
descriptors()
read(transactionId)
writeWithResponse(valueBase64, transactionId)
writeWithoutResponse(valueBase64, transactionId)
monitor(listener, transactionId)
readDescriptor(descriptorUUID, transactionId)
writeDescriptor(descriptorUUID, valueBase64, transactionId)

Descriptor

Descriptor object.

new Descriptor(nativeDescriptor: NativeDescriptor, manager: BleManager)
Parameters
nativeDescriptor (NativeDescriptor) NativeDescriptor
manager (BleManager) BleManager
Instance Members
id
uuid
characteristicID
characteristicUUID
serviceID
serviceUUID
deviceID
value
read(transactionId)
write(valueBase64, transactionId)

Utils

Utility functions and classes.

fullUUID

Converts UUID to full 128bit, lowercase format which should be used to compare UUID values.

fullUUID(uuid: UUID): UUID
Parameters
uuid (UUID) 16bit, 32bit or 128bit UUID.
Returns
UUID: 128bit lowercase UUID.

BLE Error

Types and classes related to BLE errors.

BleError

BleError is an error class which is guaranteed to be thrown by all functions of this library. It contains additional properties which help to identify problems in platform independent way.

new BleError(nativeBleError: (NativeBleError | string), errorMessageMapping: BleErrorCodeMessageMapping)

Extends Error

Parameters
nativeBleError ((NativeBleError | string))
errorMessageMapping (BleErrorCodeMessageMapping)
Instance Members
errorCode
attErrorCode
iosErrorCode
androidErrorCode
reason

BleErrorCode

Platform independent error code map adjusted to this library's use cases.

BleErrorCode
Static Members
UnknownError
BluetoothManagerDestroyed
OperationCancelled
OperationTimedOut
OperationStartFailed
InvalidIdentifiers
BluetoothUnsupported
BluetoothUnauthorized
BluetoothPoweredOff
BluetoothInUnknownState
BluetoothResetting
BluetoothStateChangeFailed
DeviceConnectionFailed
DeviceDisconnected
DeviceRSSIReadFailed
DeviceAlreadyConnected
DeviceNotFound
DeviceNotConnected
DeviceMTUChangeFailed
ServicesDiscoveryFailed
IncludedServicesDiscoveryFailed
ServiceNotFound
ServicesNotDiscovered
CharacteristicsDiscoveryFailed
CharacteristicWriteFailed
CharacteristicReadFailed
CharacteristicNotifyChangeFailed
CharacteristicNotFound
CharacteristicsNotDiscovered
CharacteristicInvalidDataFormat
DescriptorsDiscoveryFailed
DescriptorWriteFailed
DescriptorReadFailed
DescriptorNotFound
DescriptorsNotDiscovered
DescriptorInvalidDataFormat
DescriptorWriteNotAllowed
ScanStartFailed
LocationServicesDisabled

BleErrorCodeMessage

Mapping of error codes to error messages

BleErrorCodeMessage

BleATTErrorCode

Error codes for ATT errors.

BleATTErrorCode
Static Members
Success
InvalidHandle
ReadNotPermitted
WriteNotPermitted
InvalidPdu
InsufficientAuthentication
RequestNotSupported
InvalidOffset
InsufficientAuthorization
PrepareQueueFull
AttributeNotFound
AttributeNotLong
InsufficientEncryptionKeySize
InvalidAttributeValueLength
UnlikelyError
InsufficientEncryption
UnsupportedGroupType
InsufficientResources

BleAndroidErrorCode

Android specific error codes.

BleAndroidErrorCode
Static Members
NoResources
InternalError
WrongState
DbFull
Busy
Error
CmdStarted
IllegalParameter
Pending
AuthFail
More
InvalidCfg
ServiceStarted
EncrypedNoMitm
NotEncrypted
Congested

BleIOSErrorCode

iOS specific error codes.

BleIOSErrorCode
Static Members
Unknown
InvalidParameters
InvalidHandle
NotConnected
OutOfSpace
OperationCancelled
ConnectionTimeout
PeripheralDisconnected
UuidNotAllowed
AlreadyAdvertising
ConnectionFailed
ConnectionLimitReached
UnknownDevice

Flow Types

All Flow aliases and Flow types used in this library.

LogLevel

Native module logging log level. By default it is set to None.

LogLevel
Static Members
None
Verbose
Debug
Info
Warning
Error

ConnectionPriority

Connection priority of BLE link determining the balance between power consumption and data throughput.

ConnectionPriority
Static Members
Balanced
High
LowPower

State

Device Bluetooth Low Energy state. It's keys are used to check #blemanagerstate values received by BleManager

State
Static Members
Unknown
Resetting
Unsupported
Unauthorized
PoweredOff
PoweredOn

BleErrorCodeMessageMapping

Type of error code mapping table

BleErrorCodeMessageMapping

Type: {}

BleManagerOptions

Options which can be passed to when creating BLE Manager

BleManagerOptions
Instance Members
restoreStateIdentifier
restoreStateFunction(restoredState)
errorCodesToMessagesMapping

BleRestoredState

Object representing information about restored BLE state after application relaunch.

BleRestoredState
Instance Members
connectedPeripherals

ScanOptions

Options which can be passed to scanning function

ScanOptions
Instance Members
allowDuplicates
scanMode
callbackType
legacyScan

ScanCallbackType

Scan callback type for Bluetooth LE scan.

ScanCallbackType
Static Members
AllMatches
FirstMatch
MatchLost

ScanMode

Scan mode for Bluetooth LE scan.

ScanMode
Static Members
Opportunistic
LowPower
Balanced
LowLatency

ConnectionOptions

Connection specific options to be passed before connection happen. [Not used]

ConnectionOptions
Instance Members
autoConnect
requestMTU
refreshGatt
timeout

DeviceId

Bluetooth device id.

DeviceId

Type: string

Identifier

Unique identifier for BLE objects.

Identifier

Type: number

UUID

Bluetooth UUID

UUID

Type: string

TransactionId

Transaction identifier. All transaction identifiers in numeric form are reserved for internal use.

TransactionId

Type: string

Subscription

Subscription

Subscription

Base64

Base64 value

Base64

Type: string

RefreshGattMoment

[Android only] ConnectionOptions parameter to describe when to call BluetoothGatt.refresh()

RefreshGattMoment

Type: "OnConnected"