How to Use BLE in React Native to Connect RPM Devices (Basic Setup)

Astha NiharikaAstha Niharika
5 min read

“A clean, step-by-step React Native BLE tutorial to connect and read data from RPM devices using react-native-ble-plx.”

Bluetooth Low Energy (BLE) is the go-to protocol for wirelessly connecting devices like fitness trackers, smartwatches, and Remote Patient Monitoring (RPM) tools with mobile apps. In React Native, the react-native-ble-plx library provides a powerful yet flexible way to implement BLE functionality.

But let’s be honest…

I know the pain. You go through the docs, scroll endlessly, and yet you’re still sitting there wondering:
“Which functions do I even use? How do I start? Where do I go from here?”

Yeah… been there.

That’s exactly why I wrote this blog — to cut through the confusion and give you a simple, basic implementation with the least amount of code needed to connect your React Native app to an RPM (Remote Patient Monitoring) device using BLE.

No fluff. No deep dives. Just the cleanest path to getting your app talking to a device. Let’s go.

What You'll Need

  • A React Native project set up on your computer

  • A BLE-enabled blood pressure monitor

  • A smartphone with for testing

Step 1: Set Up Your React Native Project

First, make sure you have a React Native app set up. If not, you can start fresh using:

npx react-native init BLEExample

#or if you use yarn 
yarn add react-native-ble-plx

Navigate into your project directory:

cd BLEExample

Now install the BLE library:

npm install --save react-native-ble-plx

#yarn 
yarn add react-native-ble-plx

Step 2 : Setting Up Permissions

Before your app can start scanning for RPM devices over Bluetooth, you need to get those pesky permissions sorted.

Android Permissions

On Android, BLE requires location and Bluetooth permissions. And depending on the Android version, the required permissions change. So yeah... if you want your app to work across all Android versions, you'll need to handle all these cases cleanly.

npm install --save react-native-permissions

#yarn
yarn add react-native-permissions

Then, make sure to add the permissions to your AndroidManifest.xml:

<!-- Required for BLE -->
<uses-permission android:name="android.permission.BLUETOOTH" />
<uses-permission android:name="android.permission.BLUETOOTH_ADMIN" />
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<uses-permission android:name="android.permission.BLUETOOTH_SCAN" />
<uses-permission android:name="android.permission.BLUETOOTH_CONNECT" />

iOS Permissions

iOS keeps it a little saner. You don't need react-native-permissions unless you want more control—but you must update your Info.plist or your app will crash or get rejected.

Add these to ios/YourApp/Info.plist:

<key>NSBluetoothAlwaysUsageDescription</key>
<string>This app uses Bluetooth to connect to medical devices.</string>
<key>NSLocationWhenInUseUsageDescription</key>
<string>This app needs location access for BLE to function properly.</string>

Then run:

cd ios && pod install

Step 3: Scanning For Devices

Next job is to scan for available BLE devices nearby. The react-native-ble-plx library gives you a BleManager class to handle all BLE operations—kind of like your Bluetooth command center.

import { BleManager } from 'react-native-ble-plx';

const manager = new BleManager();

Now, to scan :

 manager.startDeviceScan(null, null, (error, device) => {
    if (error) {
      console.error('Scan error:', error);
      return;
    }

    // Filter (Optional) : Only show devices with a name
    if (device && device.name) {
      console.log('Found device with name:', device.name);
    }
  });
  setTimeout(() => {
    bleManager.stopDeviceScan();
    console.log('Stopped scanning');
  }, 10000); // 10 seconds
};
  • null, null means you're scanning for all BLE devices (you can filter with UUIDs if needed).

  • device.name might be null for some legit devices; in that case, try checking device.localName if needed.

  • You can store these devices in state if you’re displaying them in UI (e.g., useState in React).

Step 4 : Connecting to a Device

const connectToDevice = async (deviceId) => {
    const connectedDevice = await bleManager.connectToDevice(deviceId);
    console.log('Connected to device:', connectedDevice.name || connectedDevice.localName);
};

Step 4: Discover Services and Characteristics

When you connect to a device, you don't instantly know what it can do. You have to ask the device to reveal:

  • What services it offers

  • What characteristics are inside each service

  • What kind of operations you can perform on them

This is what await connectedDevice.discoverAllServicesAndCharacteristics() does. Without this, you can't:

  • Read RPM values

  • Write settings

  • Subscribe to real-time data

const discoverDeviceServices = async (device) => {
    const discoveredDevice = await device.discoverAllServicesAndCharacteristics();
    const services = await discoveredDevice.services();

    for (const service of services) {
      const characteristics = await service.characteristics();
      // You can now read, write, or subscribe to characteristics if needed
    }

    return discoveredDevice;
};

Step 5: Accessing Characteristics to Read or Subscribe to Data

You’ll need:

  • A Service UUID (which contains your desired data)

  • A Characteristic UUID (the exact point to read from or subscribe to)

    ( These are usually given by your device’s documentation or vendor.)

const readOrSubscribeToData = async (device, serviceUUID, characteristicUUID) => {
  const services = await device.services();

  for (const service of services) {
    if (service.uuid === serviceUUID) {
      const characteristics = await service.characteristics();

      for (const char of characteristics) {
        if (char.uuid === characteristicUUID) {
          // To read once
          const value = await char.read();
          const decodedValue = Buffer.from(value.value, 'base64').toString('utf-8');

          // OR: To subscribe for live updates
          char.monitor((error, characteristic) => {
            if (characteristic?.value) {
              const liveValue = Buffer.from(characteristic.value, 'base64').toString('utf-8');
              // Do something with liveValue
            }
          });
        }
      }
    }
  }
};

💥 And BAM — That’s It!

Your app can now read data from RPM devices.


Step 6: Read , Write & Monitor BLE Characteristics

isNotifiable : Real-time BLE Updates

This is for live health data like:

  • SpO2 readings from an oximeter

  • Heart rate from a chest strap

  • Step count from a fitness tracker

manager.monitorCharacteristicForDevice(
  device.id,
  characteristic.serviceUUID,
  characteristic.uuid,
  (error, char) => {
    if (char?.value) {
      const decodedValue = Buffer.from(char.value, 'base64').toString('utf-8');
      const data = JSON.parse(decodedValue);
      setOximeterData({ spo2: data.spo2 || '', pulseRate: data.pulseRate || '' });
    }
  }
);

isReadable : One-Time Value Fetch

  • Device name

  • Firmware version

  • Battery level

connectedDevice.readCharacteristicForService(
  characteristic.serviceUUID,
  characteristic.uuid
).then((char) => {
  const decodedValue = Buffer.from(char.value, 'base64').toString('utf-8');
});

isWritable : Sending commands to Your Device

If you need to send data to the device — like changing a setting or triggering a measurement — then you’ll use isWritableWithResponse or isWritableWithoutResponse.

await characteristic.writeWithResponse('your_base64_encoded_value');

Note: Not every characteristic supports writing — and some expect specific formats, so always check your device documentation.


For more advanced features, detailed configuration, and platform-specific nuances, you can refer to the official documentation here:

You can also check out my implementation : GitHub

0
Subscribe to my newsletter

Read articles from Astha Niharika directly inside your inbox. Subscribe to the newsletter, and don't miss out.

Written by

Astha Niharika
Astha Niharika