Bluetooth Low Energy Client on a React Native Application
Code walkthrough for a React Native application running as a Bluetooth Low Energy (BLE) client of the ESP32 BLE server.
June 08, 2020
Make sure the mobile device you are going to use has BLE as a feature and do not forget to always turn on Bluetooth. Because I did.
What is covered
Code repository
Primers
- Bluetooth Low Energy Server on ESP32 development board
- Getting Started with ESP32 Bluetooth Low Energy (BLE) on Arduino IDE
- React Native - Native Modules
Installing react-native-ble-manager
As you may see on the readme file of the fantastic react-native-ble-manager package created by Innove.it, project led by Marco Sinigaglia (marcosinigaglia github account), you have to add some user-permission
keys to AndroidManifest.xml
file and to iOS Info.plist
the NSBluetoothAlwaysUsageDescription
key.
Adding listeners for specific BLE events
Please open /src/App.js file to see how the react native NativeModule
called BleManager
(line 30) is leveraged. Once we instantiate that module under BleManagerModule
, we also need an instance of the NativeEventEmitter
to be able to subscribe from JavaScript code to native events.
Under componentDidMount
lifecycle method we are adding listeners for:
AppState
change withthis.handleAppStateChange
to be able to detect when the app is becoming active from background (line 43)BleManagerDiscoverPeripheral
native event withthis.handleDiscoverPeripheral
(lines 52-55)BleManagerStopScan
native event withthis.handleStopScan
(lines 57-60)BleManagerDisconnectPeripheral
native event withthis.handleDisconnectedPeripheral
(lines 62-65)BleManagerDidUpdateValueForCharacteristic
native event withthis.handleUpdateValueForCharacteristic
(lines 67-70)
Beside setting up those event listeners we call this.startScan
method on line 72.
We need to clean up things under componentWillUnmount
lifecycle method to prevent memory leaks, so we remove all listeners from above on lines (92-98).
Adding handlers for each BLE related listener
So, we now need to implement 5 methods to handle those 5 events we subscribed to. But we also need 3 more helper functions: startScan
(lines 153-164) for discovering BLE peripherals around our mobile device like ESP32 server, writeNewSettings
(lines 177-212) to send data to the the ESP32 server, hookUpSensorNotifications
(lines 214-257) to be able to receive BLE notifications for the data streams from the 3 Arduino sensors.
handleAppStateChange
(lines 75-90) is making sure that each time our app is becoming active from background looks for the connected peripherals.
handleStopScan
(lines 129-151) is calling again the startScan
method if there are no peripherals under that particular state key. It is also taking care of connecting to the ESP32 detected BLE server if that key under App
component shows otherwise by calling this.hookUpSensorNotifications
passing the detected device
argument.
Please note that each time we scan for BLE devices around, we look for a particular device UUID, in other words the UUID of the ESP32 board.
handleDiscoverPeripheral
(lines166-175) is storing each detected peripheral under the peripherals
state key which is a JavaScript Map
object to collect all these BLE enabled devices.
handleDisconnectedPeripheral
(lines 100-110) looks to state for the particular peripheral object received from the native module and if it finds it, it sets its connected
key to false
.
Reading and writing BLE data
handleUpdateValueForCharacteristic
(lines 112-127) is receiving the data passed by the native module as a stream of bytes (the usual way of formatting data not only for BLE communication but also for I2C), it decodes it using the decode
function coming from /src/utils/utf8Convertor.js. The result is a string of x
divided values that need to be split and parsed as integers before storing them into the component state.
writeNewSettings
(lines 177-212) is responsible for receiving the new settingsArray
from a child component of the App
component. If what it is getting is something new, it retrieveServices
first for the ESP32 device to make sure the connection is still alive, then writes those values after it changes them into a joined string then into a stream of bytes.
The render
method is looking first for the state value under the isConnected
key to conditionally render a preloader or the main Navigation
component by passing to it sensorData
and the writeNewSettings
as props.