This article explores how to create a react native application that is based on Polidea’s react-native Bluetooth low energy library. The app can discover Bluetooth devices, query for their services, determine the characteristics for each service, and interact with the read and write characteristics. My goal is to help you understand how to interact with preconfigured Bluetooth low energy (BLE) devices. To demonstrate this functionality, the app will write a text string using the standard Nordic UART RX/TX connection profile.

I am using the Arduino compatible Adafruit Flora platform with the bluefruit LE add on, which is tailored to wearables. This app is loosely based on the AdaFruit iOS Bluefruit_LE_Connect app, which I used to understand how they implemented the UART transmission functions. I try to go into as much detail as possible for you to understand the concepts of scanning for Bluetooth devices and services. I have a link to the source code I wrote at the end of the article, and I do recommend you downloading the code and giving it a try, as well as debugging it however you choose, I prefer to use react native debugger. This article assumes that you have built a react native app without using Expo and have gone through the steps to get it on your phone. If this is your first react native application or your attempt at learning redux, you may get frustrated.

The flow laid out here is a standard way to communicate with most BLE devices: you start by searching for devices, then connect to a device and determine it’s services, choose a service to determine the characteristics, and then interact with a specific characteristic. While not specifically included as a standard, from a microcontroller perspective, the Nordic UART service has become pretty common, because you can use it to send commands as text strings that are easily translated on your device and available to perform functions, assuming you can program your device with an IDE like Arduino or Raspberry Pi. The Adafruit app uses this in its controller for things such as changing the color of an LED. The app sends a string such as “!C00FF00” the C is used to denote the command, and the next values are interpreted as the hex value used to change the color. Here’s a snippet of the code on the device:

This app is based on my work in previous articles, so it uses a similar architecture: we will use React Native BLE PLX from Polidea as the library to perform all Bluetooth communication, Redux for state management, Redux Thunk to control and interact with the Bluetooth manager and devices, Native Base for UI elements, React Native Flat List to display the list of BLE devices, services, and characteristics, Activity Indicator to display a spinner while we query the Bluetooth devices, and React Navigation for managing the application screens. I have also taken the liberty to improve certain aspects of the previous code: I have updated the react-navigation library to the latest version, I have used immutability helpers to reduce the complexity of my reducers, and I added a data activity UI Component used to display a spinning wheel while communicating with the Bluetooth device.

The basic flow of the app is shown in the diagram below:

Bluetooth on iOS

To develop a Bluetooth app, you will not be able to use Expo, nor will you be able to use a simulator on your computer. I have outlined these steps in a previous article, please read the section “How to Bluetooth with React Native” to learn the steps required. An additional step that I would like to outline for iOS development is that you need to modify your info.plist within Xcode to allow a user to accept that your application uses Bluetooth. If not, you will get the following error when running your application:

BLEDiscoveryApp[21467:5688265] [access] This app has crashed because it attempted to access privacy-sensitive data without a usage description. The app's Info.plist must contain an NSBluetoothAlwaysUsageDescription key with a string value explaining to the user how the app uses this data.

To do this, open your workspace in Xcode. In the project explorer, click on the project, which is found at the top, then choose the Target App you are building for. Once that is done, click on the info tab across the top. To add a new key, click on the plus sign next to an item where you want to add a new key. This will add a new item below. You can either scroll to the privacy section, or you can cut and paste “NSBluetoothAlwaysUsageDescription”. For the value you give it a string you’d like presented to the user when they start the app for the first time, I chose “We use Bluetooth to find devices”.

Redux: State

The state for this application is designed to make displaying data within the FlatList components simple: each screen in the app uses a component tied to an object in the redux store using the data prop in a FlatList: the Device Discovery screen uses the BLEList object, the services screen uses the connectedDeviceServices object, and the characteristics screen uses the connectedServiceCharacteristics.

In addition to populating the lists on each screen, I use the store to save information about which device, service, and characteristic a user chose. This is required to properly communicate with the device and the underlying services. To accomplish this, when a user taps on a device, service, and characteristic within their list, I store them as connectedDevice, selectedService and selectedCharacteristic respectively.

Redux: Reducers

As I mentioned earlier, I have added the awesome library from Moshe Kolodny called immutability-helper that greatly simplifies my reducer. In my previous app, I was explicitly returning the entire state in each reducer. This became prone to errors and cumbersome each time I added a new object to the store. To fix this, the immutability helper functions allow me to modify the specific object in my reducer, without having to explicitly copy each object.

Because the library allows you to mutate deep copies of your redux store with a single method, I could reduce the number of state objects, but, to keep things easy to understand, I am using the update function with the $set parameter to mutate my state with the objects returned from the Bluetooth manager. It’s a bit like using the spread function, but simpler to read.

reducers

Redux: Actions

Most of the actions are dispatched from one of the thunks and are used to exclusively update the state. To make it easier to understand, the name of my actions correspond to the object they are modifying.

actions

Redux: Thunk

Understanding the flow of thunks and actions within the application can get overwhelming. The basic flow of each component is the same, so I will highlight what happens within the first screen to populate the device list, and I hope this will help you understand how to decipher the other components, and their respective thunks and actions.

I use thunks to control the communication of my application with the BLE device. Each function performs a specific tasks based on the application window and user interactions. This makes it simpler to understand each task the BLE needs to perform, as well as making it simpler to attach them to a specific user interaction. The results of these promises are stored in the redux store using one of the actions described above. Looking at the diagram above, when the BLEList component is initialized (the constructor is called), I make a call the thunk startScan().

This function makes sure that your phone’s Bluetooth is in a powered-on state, and then calls the scan() thunk, which finds all of the devices within range of your phone. If you do not check for a powered-on state before you scan for devices, you will get errors.

The scan() function interacts with the Bluetooth device manager to perform the startDeviceScan function. I change the status in the redux store to “scanning,” which is used as a visual indicator on the app, and each time a device is found the addBLE action is called, given the found device as an input. This calls the ADD_BLE reducer, which appends the device to the BLEList array. Once the device is added to the array, the Flatlist component is updated to reflect the new device.

The application stays in the state listed above until the user taps on a device that they wish to connect to. As outlined on the right-hand side of the Device Discovery diagram above, this initiates a handleClick event which begins the operations to have the device manager connect to the device, update the device object within the store, discover the set of services in the device, and finally update the store with the services offered.

The Screens and components follow a similar pattern to the BLE List flow I described above. For instance, when a user selects a service, a handleClick event is fired on the services screen which navigates a user to the characteristics component, which calls the “getServiceCharacteristics” thunk. This tells the Bluetooth device manager to find the services for the connected device, then call the “connectedServiceCharacteristics” action, which calls the “CONNECTED_CHARACTERISTICS” reducer, and finally updates the store.

Interacting with Characteristics

The code I described up to this point should work with any Bluetooth device that is within the connection distance of your phone. I find it amazing how many devices that is, especially in your home! I found that my electric toothbrush even had Bluetooth! Now that we are looking into actually interacting with a specific characteristic, you are on your own to learn how to interact with your specific device. If the devices follow any of the standardized GATT service definitions, you should be able to write or find some code to work with them. My goal in writing this application was to communicate with Arduino based Bluetooth devices and be able to send them special commands to help me with my projects, which mostly are around wearables and LEDs.

To demonstrate this ability, the last function that I would like to point out is the “writeCharacteristic” thunk, which performs the job of sending data to your connected device. This function may have to be tweaked depending on the device you are communicating with: I am working with an Arduino based Bluetooth connection, and some of the settings, such as packetsize are written into the Arduino code. You can try to adjust this number to a smaller value if you are not seeing results. This function takes in an ASCII string, converts it to a uint8 array, then transmits the data as 20-byte packets until all information is sent. To accomplish this I am using the Bluetooth function called: “writeCharacteristicWithoutResponseForService,” which will allow for sending packets without a response for the device. The Arduino device is looking for a “

,” or char code(10) to signify the end of a string. I append this value in the handleClick event:

Helper Functions

There are a few functions I use that help with the transmission of a string over Bluetooth. The first is a function that converts a string to a uint8array:

The second is a function that adds a cyclic redundancy check (CRC) byte to the end of my transmission and is used to ensure the packet of data arrived at the device correctly. The transmission of data over Bluetooth like this can get problematic, so it’s a good practice to include a CRC value at the end of your transmission, which your device can use to ensure successful transmission. I did not use one because the Arduino code I am using does not require it.

React Navigation

Another improvement from my previous code was that I upgrade the react-navigation library to the latest version, which simplified some things and thought I should highlight those changes. Most of the changes are handled in app.js. We now import new libraries and create a StackNavigator, and return a Navigation container that uses a stack of screens used for navigation.

The biggest difference within the components is the removal of the withNavigation wrapper. The navigation stack is included in the redux store and you can simply call the navigation function and pass it the name of one of the screens defined above.

UI Components

The UI Components for showing the list of devices, services, and characteristics all rely on a Flatlist as well as one of the arrays in the redux state, so I will highlight one of them and the others you can look at the code to figure out the specifics for each one.

BLEList

I would like to also highlight the component used to determine if the characteristic is a read or write, and I will show you the UI for writing data to the device.

Data Activity Indicator

A nice feature of the FlatList component is the ability to attach another component that is shown when there are no items to be displayed. Using the “ListEmptyComponent” attribute, I have created a special component that uses the React Native Activity Indicator, which is a spinning icon, to be shown when the application is searching for devices, services, and characteristics. This helps give the user some feedback as to what is going on, which is much better than a blank screen.

Summary

My motivation for this app was to help me better understand Bluetooth communication in general, instead of focusing on my specific problem set of controlling a device to perform my specific tasks. In reality, if you know the device, service, and characteristics that you are working with, you would not need to go through each of the steps deliberately asking for the user’s interaction. That being said, it is required to perform the actions of discovery with the Device manager so that it understands how to communicate, so, you will need to programmatically perform each step so that you will not run into errors. If you do not do this, the Device Manager will complain that the service or characteristics do not exist, even though you know they do.

Code

About Me

Learn More

Learn about cyclic redundancy checks: