Tag: iot

  • Unlocking Seamless Communication: BLE Integration with React Native for Device Connectivity

    In today’s interconnected world, where smart devices have become an integral part of our daily lives, the ability to communicate with Bluetooth Low Energy (BLE) enabled devices opens up a myriad of possibilities for innovative applications. In this blog, we will explore the exciting realm of communicating with BLE-enabled devices using React Native, a popular cross-platform framework for mobile app development. Whether you’re a seasoned React Native developer or just starting your journey, this blog will equip you with the knowledge and skills to establish seamless communication with BLE devices, enabling you to create powerful and engaging user experiences. So, let’s dive in and unlock the potential of BLE communication in the world of React Native!

    BLE (Bluetooth Low Energy)

    Bluetooth Low Energy (BLE) is a wireless communication technology designed for low-power consumption and short-range connectivity. It allows devices to exchange data and communicate efficiently while consuming minimal energy. BLE has gained popularity in various industries, from healthcare and fitness to home automation and IoT applications. It enables seamless connectivity between devices, allowing for the development of innovative solutions. With its low energy requirements, BLE is ideal for battery-powered devices like wearables and sensors. It offers simplified pairing, efficient data transfer, and supports various profiles for specific use cases. BLE has revolutionized the way devices interact, enabling a wide range of connected experiences in our daily lives.

    Here is a comprehensive overview of how mobile applications establish connections and facilitate communication with BLE devices.

    What will we be using?

    react-native - 0.71.6
    react - 18.0.2
    react-native-ble-manager - 10.0.2

    Note: We are assuming you already have the React Native development environment set up on your system; if not, please refer to the React Native guide for instructions on setting up the RN development environment.

    What are we building?

    Together, we will construct a sample mobile application that showcases the integration of Bluetooth Low Energy (BLE) technology. This app will search for nearby BLE devices, establish connections with them, and facilitate seamless message exchanges between the mobile application and the chosen BLE device. By embarking on this project, you will gain practical experience in building an application that leverages BLE capabilities for effective communication. Let’s commence this exciting journey of mobile app development and BLE connectivity!

    Setup

    Before setting up the react-native-ble manager, let’s start by creating a React Native application using the React Native CLI. Follow these steps:

    Step 1: Ensure that you have Node.js and npm (Node Package Manager) installed on your system.

    Step 2: Open your command prompt or terminal and navigate to the directory where you want to create your React Native project.

    Step 3: Run the following command to create a new React Native project:

    npx react-native@latest init RnBleManager

    Step 4: Wait for the project setup to complete. This might take a few minutes as it downloads the necessary dependencies.

    Step 5: Once the setup is finished, navigate into the project directory:

    cd RnBleManager

    Step 6: Congratulations! You have successfully created a new React Native application using the React Native CLI.

    Now you are ready to set up the react-native-ble manager and integrate it into your React Native project.

    Installing react-native-ble-manager

    If you use NPM -
    npm i --save react-native-ble-manager
    
    With Yarn -
    yarn add react-native-ble-manager

    In order to enable Android applications to utilize Bluetooth and location services for detecting and communicating with BLE devices, it is essential to incorporate the necessary permissions within the Android platform.

    Add these permissions in the AndroidManifest.xml file in android/app/src/main/AndroidManifest.xml

    Integration

    At this stage, having successfully created a new React Native application, installed the react-native-ble-manager, and configured it to function seamlessly on Android, it’s time to proceed with integrating the react-native-ble-manager into your React Native application. Let’s dive into the integration process to harness the power of BLE functionality within your app.

    BleConnectionManager

    To ensure that our application can access the BLE connection state and facilitate communication with the BLE device, we will implement BLE connection management in the global state. This will allow us to make the connection management accessible throughout the entire codebase. To achieve this, we will create a ContextProvider called “BleConnectionContextProvider.” By encapsulating the BLE connection logic within this provider, we can easily share and access the connection state and related functions across different components within the application. This approach will enhance the efficiency and effectiveness of managing BLE connections. Let’s proceed with implementing the BleConnectionContextProvider to empower our application with seamless BLE communication capabilities.

    This context provider will possess the capability to access and manage the current BLE state, providing a centralized hub for interacting with the BLE device. It will serve as the gateway to establish connections, send and receive data, and handle various BLE-related functionalities. By encapsulating the BLE logic within this context provider, we can ensure that all components within the application have access to the BLE device and the ability to communicate with it. This approach simplifies the integration process and facilitates efficient management of the BLE connection and communication throughout the entire application.

    Let’s proceed with creating a context provider equipped with essential state management functionalities. This context provider will effectively handle the connection and scanning states, maintain the BLE object, and manage the list of peripherals (BLE devices) discovered during the application’s scanning process. By implementing this context provider, we will establish a robust foundation for seamlessly managing BLE connectivity and communication within the application.

    NOTE: Although not essential for the example at hand, implementing global management of the BLE connection state allows us to demonstrate its universal management capabilities.

    ....
    BleManager.disconnect(BLE_SERVICE_ID)
      .then(() => {
        dispatch({ type: "disconnected", payload: { peripheral } })
      })
      .catch((error) => {
        // Failure code
        console.log(error);
      });
    ....

    Prior to integrating the BLE-related components, it is crucial to ensure that the mobile app verifies whether the:

    1. Location permissions are granted and enabled
    2. Mobile device’s Bluetooth is enabled

    To accomplish this, we will implement a small method called requestPermissions that grants all the necessary permissions to the user. We will then call this method as soon as our context provider initializes within the useEffect hook in the BleConnectionContextProvider. Doing so ensures that the required permissions are obtained by the mobile app before proceeding with the integration of BLE functionalities.

    import {PermissionsAndroid, Platform} from "react-native"
    import BleManager from "react-native-ble-manager"
    
      const requestBlePermissions = async (): Promise<boolean> => {
        if (Platform.OS === "android" && Platform.Version < 23) {
          return true
        }
        try {
          const status = await PermissionsAndroid.requestMultiple([
            PermissionsAndroid.PERMISSIONS.ACCESS_FINE_LOCATION,
            PermissionsAndroid.PERMISSIONS.BLUETOOTH_CONNECT,
            PermissionsAndroid.PERMISSIONS.BLUETOOTH_SCAN,
            PermissionsAndroid.PERMISSIONS.BLUETOOTH_ADVERTISE,
          ])
          return (
            status[PermissionsAndroid.PERMISSIONS.BLUETOOTH_CONNECT] == "granted" &&
            status[PermissionsAndroid.PERMISSIONS.BLUETOOTH_SCAN] == "granted" &&
            status[PermissionsAndroid.PERMISSIONS.ACCESS_FINE_LOCATION] == "granted"
          )
        } catch (e) {
          console.error("Location Permssions Denied ", e)
          return false
        }
      }
    
    // effects
    useEffect(() => {
      const initBle = async () => {
        await requestBlePermissions()
        BleManager.enableBluetooth()
      }
      
      initBle()
    }, [])

    After granting all the required permissions and enabling Bluetooth, the next step is to start the BleManager. To accomplish this, please add the following line of code after the enableBle command in the aforementioned useEffect:

    // initialize BLE module
    BleManager.start({ showAlert: false })

    By including this code snippet, the BleManager will be initialized, facilitating the smooth integration of BLE functionality within your application.

    Now that we have obtained the necessary permissions, enabled Bluetooth, and initiated the Bluetooth manager, we can proceed with implementing the functionality to scan and detect BLE peripherals. 

    We will now incorporate the code that enables scanning for BLE peripherals. This will allow us to discover and identify nearby BLE devices. Let’s dive into the implementation of this crucial step in our application’s BLE integration process.

    To facilitate scanning and stopping the scanning process for BLE devices, as well as handle various events related to the discovered peripherals, scan stop, and BLE disconnection, we will create a method along with the necessary event listeners.

    In addition, state management is essential to effectively handle the connection and scanning states, as well as maintain the list of scanned devices. To accomplish this, let’s incorporate the following code into the BleConnectionConextProvider. This will ensure seamless management of the aforementioned states and facilitate efficient tracking of scanned devices.

    Let’s proceed with implementing these functionalities to ensure smooth scanning and handling of BLE devices within our application.

    export const BLE_NAME = "SAMPlE_BLE"
    export const BLE_SERVICE_ID = "5476534d-1213-1212-1212-454e544f1212"
    export const BLE_READ_CHAR_ID = "00105354-0000-1000-8000-00805f9b34fb"
    export const BLE_WRITE_CHAR_ID = "00105352-0000-1000-8000-00805f9b34fb"
    
    export const BleContextProvider = ({
      children,
    }: {
      children: React.ReactNode
    }) => {
      // variables
      const BleManagerModule = NativeModules.BleManager
      const bleEmitter = new NativeEventEmitter(BleManagerModule)
      const { setConnectedDevice } = useBleStore()
    
      // State management
      const [state, dispatch] = React.useReducer(
        (prevState: BleState, action: any) => {
          switch (action.type) {
            case "scanning":
              return {
                ...prevState,
                isScanning: action.payload,
              }
            case "connected":
              return {
                ...prevState,
                connectedBle: action.payload.peripheral,
                isConnected: true,
              }
            case "disconnected":
              return {
                ...prevState,
                connectedBle: undefined,
                isConnected: false,
              }
            case "clearPeripherals":
              let peripherals = prevState.peripherals
              peripherals.clear()
              return {
                ...prevState,
                peripherals: peripherals,
              }
            case "addPerpheral":
              peripherals = prevState.peripherals
              peripherals.set(action.payload.id, action.payload.peripheral)
              const list = [action.payload.connectedBle]
              return {
                ...prevState,
                peripherals: peripherals,
              }
            default:
              return prevState
          }
        },
        initialState
      )
    
      // methods
      const getPeripheralName = (item: any) => {
        if (item.advertising) {
          if (item.advertising.localName) {
            return item.advertising.localName
          }
        }
    
        return item.name
      }
    
      // start to scan peripherals
      const startScan = () => {
        // skip if scan process is currenly happening
        console.log("Start scanning ", state.isScanning)
        if (state.isScanning) {
          return
        }
    
        dispatch({ type: "clearPeripherals" })
    
        // then re-scan it
        BleManager.scan([], 10, false)
          .then(() => {
            console.log("Scanning...")
            dispatch({ type: "scanning", payload: true })
          })
          .catch((err) => {
            console.error(err)
          })
      }
    
      const connectBle = (peripheral: any, callback?: (name: string) => void) => {
        if (peripheral && peripheral.name && peripheral.name == BLE_NAME) {
          BleManager.connect(peripheral.id)
            .then((resp) => {
              dispatch({ type: "connected", payload: { peripheral } })
              // callback from the caller
              callback && callback(peripheral.name)
              setConnectedDevice(peripheral)
            })
            .catch((err) => {
              console.log("failed connecting to the device", err)
            })
        }
      }
    
      // handle discovered peripheral
      const handleDiscoverPeripheral = (peripheral: any) => {
        console.log("Got ble peripheral", getPeripheralName(peripheral))
    
        if (peripheral.name && peripheral.name == BLE_NAME) {
          dispatch({
            type: "addPerpheral",
            payload: { id: peripheral.id, peripheral },
          })
        }
      }
    
      // handle stop scan event
      const handleStopScan = () => {
        console.log("Scan is stopped")
        dispatch({ type: "scanning", payload: false })
      }
    
      // handle disconnected peripheral
      const handleDisconnectedPeripheral = (data: any) => {
        console.log("Disconnected from " + data.peripheral)
    
        //
        dispatch({ type: "disconnected" })
      }
    
      const handleUpdateValueForCharacteristic = (data: any) => {
        console.log(
          "Received data from: " + data.peripheral,
          "Characteristic: " + data.characteristic,
          "Data: " + toStringFromBytes(data.value)
        )
      }
    
      // effects
      useEffect(() => {
        const initBle = async () => {
          await requestBlePermissions()
          BleManager.enableBluetooth()
        }
    
        initBle()
    
        // add ble listeners on mount
        const BleManagerDiscoverPeripheral = bleEmitter.addListener(
          "BleManagerDiscoverPeripheral",
          handleDiscoverPeripheral
        )
        const BleManagerStopScan = bleEmitter.addListener(
          "BleManagerStopScan",
          handleStopScan
        )
        const BleManagerDisconnectPeripheral = bleEmitter.addListener(
          "BleManagerDisconnectPeripheral",
          handleDisconnectedPeripheral
        )
        const BleManagerDidUpdateValueForCharacteristic = bleEmitter.addListener(
          "BleManagerDidUpdateValueForCharacteristic",
          handleUpdateValueForCharacteristic
        )
      }, [])
    
    // render
      return (
        <BleContext.Provider
          value={{
            ...state,
            startScan: startScan,
            connectBle: connectBle,
          }}
        >
          {children}
        </BleContext.Provider>
      )
    }

    NOTE: It is important to note the properties of the BLE device we intend to search for and connect to, namely BLE_NAME, BLE_SERVICE_ID, BLE_READ_CHAR_ID, and BLE_WRITE_CHAR_ID. Familiarizing yourself with these properties beforehand is crucial, as they enable you to restrict the search to specific BLE devices and facilitate connection to the desired BLE service and characteristics for reading and writing data. Being aware of these properties will greatly assist you in effectively working with BLE functionality.

    For instance, take a look at the handleDiscoverPeripheral method. In this method, we filter the discovered peripherals based on their device name, matching it with the predefined BLE_NAME we mentioned earlier. As a result, this approach allows us to obtain a list of devices that specifically match the given name, narrowing down the search to the desired devices only. 

    Additionally, you have the option to scan peripherals using the service IDs of the Bluetooth devices. This means you can specify specific service IDs to filter the discovered peripherals during the scanning process. By doing so, you can focus the scanning on Bluetooth devices that provide the desired services, enabling more targeted and efficient scanning operations.

    Excellent! We now have all the necessary components in place for scanning and connecting to the desired BLE device. Let’s proceed by adding the user interface (UI) elements that will allow users to initiate the scan, display the list of scanned devices, and enable connection to the selected device. By implementing these UI components, we will create a seamless user experience for scanning, device listing, and connection within our application.

    Discovering and Establishing Connections with BLE Devices

    Let’s create a new UI component/Page that will handle scanning, listing, and connecting to the BLE device. This page will have:

    • A Scan button to call the scan function
    • A simple FlatList to list the selected BLE devices and
    • A method to connect to the selected BLE device when the user clicks on any BLE item row from the list

    Create HomeScreen.tsx in the src folder and add the following code: 

    import React, {useCallback, useEffect, useMemo} from 'react';
    import {
      ActivityIndicator,
      Alert,
      Button,
      FlatList,
      StyleSheet,
      Text,
      TouchableOpacity,
      View,
    } from 'react-native';
    import {useBleContext} from './BleContextProvider';
    
    interface HomeScreenProps {}
    
    const HomeScreen: React.FC<HomeScreenProps> = () => {
      const {
        isConnected,
        isScanning,
        peripherals,
        connectedBle,
        startScan,
        connectBle,
      } = useBleContext();
    
      // Effects
      const scannedbleList = useMemo(() => {
        const list = [];
        if (connectedBle) list.push(connectedBle);
        if (peripherals) list.push(...Array.from(peripherals.values()));
        return list;
      }, [peripherals, isScanning]);
    
      useEffect(() => {
        if (!isConnected) {
          startScan && startScan();
        }
      }, []);
    
      // Methods
      const getRssi = (rssi: number) => {
        return !!rssi
          ? Math.pow(10, (-69 - rssi) / (10 * 2)).toFixed(2) + ' m'
          : 'N/A';
      };
    
      const onBleConnected = (name: string) => {
        Alert.alert('Device connected', `Connected to ${name}.`, [
          {
            text: 'Ok',
            onPress: () => {},
            style: 'default',
          },
        ]);
      };
      const BleListItem = useCallback((item: any) => {
        // define name and rssi
        return (
          <TouchableOpacity
            style={{
              flex: 1,
              flexDirection: 'row',
              justifyContent: 'space-between',
              padding: 16,
              backgroundColor: '#2A2A2A',
            }}
            onPress={() => {
              connectBle && connectBle(item.item, onBleConnected);
            }}>
            <Text style={{textAlign: 'left', marginRight: 8, color: 'white'}}>
              {item.item.name}
            </Text>
            <Text style={{textAlign: 'right'}}>{getRssi(item.item.rssi)}</Text>
          </TouchableOpacity>
        );
      }, []);
    
      const ItemSeparator = useCallback(() => {
        return <View style={styles.divider} />;
      }, []);
    
      // render
      // Ble List and scan button
      return (
        <View style={styles.container}>
          {/* Loader when app is scanning */}
          {isScanning ? (
            <ActivityIndicator size={'small'} />
          ) : (
            <>
              {/* Ble devices List View */}
              {scannedbleList && scannedbleList.length > 0 ? (
                <>
                  <Text style={styles.listHeader}>Discovered BLE Devices</Text>
                  <FlatList
                    data={scannedbleList}
                    renderItem={({item}) => <BleListItem item={item} />}
                    ItemSeparatorComponent={ItemSeparator}
                  />
                </>
              ) : (
                <View style={styles.emptyList}>
                  <Text style={styles.emptyListText}>
                    No Bluetooth devices discovered. Please click scan to search the
                    BLE devices
                  </Text>
                </View>
              )}
    
              {/* Scan button */}
              <View style={styles.btnContainer}>
                <Button
                  title="Scan"
                  color={'black'}
                  disabled={isConnected || isScanning}
                  onPress={() => {
                    startScan && startScan();
                  }}
                />
              </View>
            </>
          )}
        </View>
      );
    };
    
    const styles = StyleSheet.create({
      container: {
        flex: 1,
        flexDirection: 'column',
      },
      listHeader: {
        padding: 8,
        color: 'black',
      },
      emptyList: {
        flex: 1,
        justifyContent: 'center',
        alignItems: 'center',
      },
      emptyListText: {
        padding: 8,
        textAlign: 'center',
        color: 'black',
      },
      btnContainer: {
        marginTop: 10,
        marginHorizontal: 16,
        bottom: 10,
        alignItems: 'flex-end',
      },
      divider: {
        height: 1,
        width: '100%',
        marginHorizontal: 8,
        backgroundColor: '#1A1A1A',
      },
    });
    
    export default HomeScreen;

    Now, open App.tsx and replace the complete code with the following changes: 
    In App.tsx, we removed the default boilerplate code, react-native cli generated while creating the project with our own code, where we added the BleContextProvider and HomeScreen to the app.

    import React from 'react';
    import {SafeAreaView, StatusBar, useColorScheme, View} from 'react-native';
    
    import {Colors} from 'react-native/Libraries/NewAppScreen';
    import {BleContextProvider} from './BleContextProvider';
    import HomeScreen from './HomeScreen';
    
    function App(): JSX.Element {
      const isDarkMode = useColorScheme() === 'dark';
    
      const backgroundStyle = {
        backgroundColor: isDarkMode ? Colors.darker : Colors.lighter,
      };
    
      return (
        <SafeAreaView style={backgroundStyle}>
          <StatusBar
            barStyle={isDarkMode ? 'light-content' : 'dark-content'}
            backgroundColor={backgroundStyle.backgroundColor}
          />
          <BleContextProvider>
            <View style={{height: '100%', width: '100%'}}>
              <HomeScreen />
            </View>
          </BleContextProvider>
        </SafeAreaView>
      );
    }
    
    export default App;

    Running the application on an Android device: Upon launching the app, you will be presented with an empty list message accompanied by a scan button. Simply tap the scan button to retrieve a list of available BLE peripherals within the range of your mobile device. By selecting a specific BLE device from the list, you can establish a connection with it.

    Awesome! Now we are able to scan, detect, and connect to the BLE devices, but there is more to it than just connecting to the BLE devices. We can write to and read the required information from BLE devices, and based on that information, mobile applications OR backend services can perform several other operations.

    For example, if you are wearing and connected to a BLE device that monitors your blood pressure every one hour, and if it goes beyond the threshold, it can trigger a call to a doctor or family members to check and take precautionary measures as soon as possible.

    Communicating with BLE devices

    For seamless communication with a BLE device, the mobile app must possess precise knowledge of the services and characteristics associated with the device. A BLE device typically presents multiple services, each comprising various distinct characteristics. These services and characteristics can be collaboratively defined and shared by the team responsible for manufacturing the BLE device.

    In BLE communication, comprehending the characteristics and their properties is crucial, as they serve distinct purposes. Certain characteristics facilitate writing data to the BLE device, while others enable reading data from it. Gaining a comprehensive understanding of these characteristics and their properties is vital for effectively interacting with the BLE device and ensuring seamless communication.

    Reading data from BLE device when BLE sends data

    Once the mobile app successfully establishes a connection with the BLE device, it initiates the retrieval of available services. It activates the listener to begin receiving notifications from the BLE device. This process takes place within the callback of the “connect BLE” method, ensuring that the app seamlessly retrieves the necessary information and starts listening for important updates from the connected BLE device.

    const connectBle = (peripheral: any, callback?: (name: string) => void) => {
        if (peripheral && peripheral.name && peripheral.name == BLE_NAME) {
          BleManager.connect(peripheral.id)
            .then((resp) => {
              dispatch({ type: "connected", payload: { peripheral } })
              // callback from the caller
              callback && callback(peripheral.name)
              setConnectedDevice(peripheral)
    
              // retrieve services and start read notification
              BleManager.retrieveServices(peripheral.id).then((resp) => {
                BleManager.startNotification(
                  peripheral.id,
                  BLE_SERVICE_ID,
                  BLE_READ_CHAR_ID
                )
                  .then(console.log)
                  .catch(console.error)
              })
            })
            .catch((err) => {
              console.log("failed connecting to the device", err)
            })
        }
      }

    Consequently, the application will promptly receive notifications whenever the BLE device writes data to the designated characteristic within the specified service.

    Reading and writing data to BLE from a mobile device

    To establish communication between the mobile app and the BLE device, we will implement new methods within BleContextProvider. These methods will facilitate the reading and writing of data to the BLE device. By exposing these methods in BleContextProvider’s reducer, we ensure that the app has a reliable means of interacting with the BLE device and can seamlessly exchange information as required.

    interface BleState {
      isConnected: boolean
      isScanning: boolean
      peripherals: Map<string, any>
      list: Array<any>
      connectedBle: Peripheral | undefined
      startScan?: () => void
      connectBle?: (peripheral: any, callback?: (name: string) => void) => void
      readFromBle?: (id: string) => void
      writeToble?: (
        id: string,
        content: string,
        callback?: (count: number, buttonNumber: ButtonNumber) => void
      ) => void
    }
    
    export const BleContextProvider = ({
      children,
    }: {
      children: React.ReactNode
    }) => {
        ....
        
        const writeToBle = (
        id: string,
        content: string,
        count: number,
        buttonNumber: ButtonNumber,
        callback?: (count: number, buttonNumber: ButtonNumber) => void
      ) => {
        BleManager.retrieveServices(id).then((response) => {
          BleManager.writeWithoutResponse(
            id,
            BLE_SERVICE_ID,
            BLE_WRITE_CHAR_ID,
            toByteArray(content)
          )
            .then((res) => {
              callback && callback(count, buttonNumber)
            })
            .catch((res) => console.log("Error writing to BLE device - ", res))
        })
      }
    
      const readFromBle = (id: string) => {
        BleManager.retrieveServices(id).then((response) => {
          BleManager.read(id, BLE_SERVICE_ID, BLE_READ_CHAR_ID)
            .then((resp) => {
              console.log("Read from BLE", toStringFromBytes(resp))
            })
            .catch((err) => {
              console.error("Error Reading from BLE", err)
            })
        })
      }
      ....
    
      // render
      return (
        <BleContext.Provider
          value={{
            ...state,
            startScan: startScan,
            connectBle: connectBle,
            writeToble: writeToBle,
            readFromBle: readFromBle,
          }}
        >
          {children}
        </BleContext.Provider>
      )    
    }

    NOTE: Before a write, read, or start notification, you need to call retrieveServices method every single time.

    Disconnecting BLE connection

    Once you are done with the BLE services, you can disconnect the BLE connection using the disconnectBLE method provided in the library.

    ....
    BleManager.disconnect(BLE_SERVICE_ID)
      .then(() => {
        dispatch({ type: "disconnected", payload: { peripheral } })
      })
      .catch((error) => {
        // Failure code
        console.log(error);
      });
    ....

    Additionally, the React Native BLE Manager library offers various other methods that can enhance the application’s functionality. These include the createBond method, which facilitates the pairing of the BLE device with the mobile app, the stopNotification method, which ceases receiving notifications from the device, and the readRSSI method, which retrieves the received signal strength indicator (RSSI) of the device. For a more comprehensive understanding of the library and its capabilities, I recommend exploring further details on the React Native BLE Manager library documentation here: https://www.npmjs.com/package/react-native-ble-manager

    Conclusion

    We delved into the fascinating world of communicating with BLE (Bluetooth Low Energy) using the React Native BLE Manager library. Then we explored the power of BLE technology and how it can be seamlessly integrated into React Native applications to enable efficient and low-power communication between devices.

    Using the React Native BLE Manager library, we explored essential functionalities such as scanning for nearby BLE devices, establishing connections, discovering services and characteristics, and exchanging data. We also divided into more advanced features like managing connections and handling notifications for a seamless user experience.

    It’s important to remember that BLE technology is continually evolving, and there may be additional libraries and frameworks available for BLE communication in the React Native ecosystem. As you progress on your journey, I encourage you to explore other resources, keep up with the latest advancements, and stay connected with the vibrant community of developers working with BLE and React Native.

    I hope this blog post has inspired you to explore the immense potential of BLE communication in your React Native applications. By harnessing the power of BLE, you can create innovative, connected experiences that enhance the lives of your users and open doors to new possibilities.

    Thank you for taking the time to read through this blog!

  • MQTT Protocol Overview – Everything You Need To Know

    MQTT is the open protocol. This is used for asynchronous message queuing. This has been developed and matured over several years. MQTT is a machine to machine protocol. It’s been widely used with embedded devices. Microsoft is having its own MQTT tool with huge support. Here, we are going to overview the MQTT protocol & its details.

    MQTT Protocol:

    MQTT is a very simple publish / subscribe protocol. It allows you to send messages on a topic (channels) passed through a centralized message broker.

    The MQTT module of API will take care of the publish/ subscribe mechanism along with additional features like authentication, retaining messages and sending duplicate messages to unreachable clients.

    There are three parts of MQTT architecture –

    • MQTT Broker – All messages passed from the client to the server should be sent via the broker.
    • MQTT Server – The API acts as an MQTT server. The MQTT server will be responsible for publishing the data to the clients.
    • MQTT Client – Any third party client who wishes to subscribe to data published by API, is considered as an MQTT Client.

    The MQTT Client and the MQTT Server need to connect to the Broker in order to publish or subscribe messages.

    MQTT Communication Program

    Suppose our API is sending sensor data to get more ideas on MQTT.
    API gathers the sensor data through the Monitoring module, and the MQTT module publishes the data to provide different channels. On the successful connection of external client to the MQTT module of the API, the client would receive sensor data on the subscribed channel.

    Below diagram shows the flow of data from the API Module to the External clients.

    MQTT Broker – EMQTT:

    EMQTT (Erlang MQTT Broker) is a massively scalable and clusterable MQTT V3.1/V3.1.1 broker, written in Erlang/OTP.

    Main responsibilities of a Broker are-

    • Receive all messages
    • Filter messages
    • Decide which are interested clients
    • Publish messages to all the subscribed clients

    All messages published are passed through the broker. The broker generates the Client ID and Message ID, maintains the message queue, and publishes the message.

    There are several brokers that can be used. Default EMQTT broker developed in ErLang.

    MQTT Topics:

    A topic is a string(UTF-8). Using this string, Broker filters messages for all connected clients. One topic may consist of one or more topic levels. Forward slash(topic level separator) is used for separating each topic level.

     

    When API starts, the Monitoring API will monitor the sensor data and publish it in a combination of topics. The third party client can subscribe to any of those topics, based on the requirement.

    The topics are framed in such a way that it provides options for the user to subscribe at level 1, level 2, level 3, level 4, or individual sensor level data.

    While subscribing to each level of sensor data, the client needs to specify the hierarchy of the IDs. For e.g. to subscribe to level 4 sensor data, the client needs to specify level 1 id/ level 2 id/ level 3 id/ level 4 id.

    The user can subscribe to any type of sensor by specifying the sensor role as the last part of the topic.

    If the user doesn’t specify the role, the client will be subscribed to all types of sensors on that particular level.

    The user can also specify the sensor id that they wish to subscribe to. In that case, they need to specify the whole hierarchy of the sensor, starting from project id and ending with sensor id.

    Following is the list of topics exposed by API on startup.

     

    Features supported by MQTT:

    1. Authentication:

    EMQTT provides authentication of every user who intends to publish or subscribe to particular data. The user id and password is stored in the API database, into a separate collection called ‘mqtt

    While connecting to EMQTT broker, we provide the username name and password, and the MQTT Broker will validate the credentials based on the values present in the database.

    2. Access Control:

    EMQTT determines which user is allowed to access which topics. This information is stored in MongoDB under the table ‘mqtt_acl’

    By default, all users are allowed to access all topics by specifying ‘#’ as the allowed topic to publish and subscribe for all users.

    3. QoS:

    The Quality of Service (QoS) level is the Quality transfer of messages which ensures the delivery of messages between sending body & receiving body. There are 3 QoS levels in MQTT:

    • At most once(0) –The message is delivered at most once, or it is not delivered at all.
    • At least once(1) – The message is always delivered at least once.
    • Exactly once(2) – The message is always delivered exactly once.

    4. Last Will Message:

    MQTT uses the Last Will & Testament(LWT) mechanism to notify ungraceful disconnection of a client to other clients. In this mechanism, when a client is connecting to a broker, each client specifies its last will message which is a normal MQTT message with QoS, topic, retained flag & payload. This message is stored by the Broker until it it detects that the client has disconnected ungracefully.

    5. Retain Message:

    MQTT also has a feature of Message Retention. It is done by setting TRUE to retain the flag. It then retained the last message & QoS for the topic. When a client subscribes to a topic, the broker matches the topic with a retained message. Clients will receive messages immediately if the topic and the retained message are matched. Brokers only store one retained message for each topic.

    6. Duplicate Message:

    If a publisher doesn’t receive the acknowledgement of the published packet, it will resend the packet with DUP flag set to true. A duplicate message contains the same Message ID as the original message.

    7. Session:

    In general, when a client connects with a broker for the first time, the client needs to create subscriptions for all topics for which they are willing to receive data/messages from the broker. Suppose a session is not maintained, or there is no persistent session, or the client lost a connection with the broker, then users have to resubscribe to all the topics after reconnecting to the broker. For the clients with limited resources, it would be very tedious to subscribe to all topics again. So brokers use a persistent session mechanism, in which it saves all information relevant to the client. ‘clientId’ provided by client is used as ‘session identifier’, when the client establishes a connection with the broker.

    Features not-supported by MQTT:

    1. Not RESTful:

    MQTT does not allow a client to expose RESTful API endpoints. The only way to communicate is through the publish /subscribe mechanism.

    2. Obtaining Subscription List:

    The MQTT Broker doesn’t have the Client IDs and the subscribed topics by the clients. Hence, the API needs to publish all data to all possible combinations of topics. This would lead to a problem of network congestion in case of large data.

    MQTT Wildcards:

    MQTT clients can subscribe to one or more topics. At a time, one can subscribe to a single topic only. So we can use the following two wildcards to create a topic which can subscribe to many topics to receive data/message.

    1. Plus sign(+):

    This is a single level wildcard. This is used to match specific topic level. We can use this wildcard when we want to subscribe at topic level.

    Example: Suppose we want to subscribe for all Floor level ‘AL’(Ambient light) sensors, we can use Plus (+) sign level wild card instead of a specific zone level. We can use following topic:

    <project_id>/<building_id>/<floor_id>/+/AL</floor_id></building_id></project_id>

    2. Hash Sign(#):

    This is a multi level wildcard. This wildcard can be used only at the end of a topic. All data/messages get subscribed which match to left-hand side of the ‘#’ wildcard.

    Example: In case we want to receive all the messages related to all sensors for floor1 , we can use Hash sing(#) multi level wildcard after floor name & the slash( / ). We can use following topic-

    <level 1_id=””>/<level 2_id=””>/<level 3_id=””>/#</level></level></level>

    MQTT Test tools:

    Following are some popular open source testing tools for MQTT.

    1. MQTT Lens
    2. MQTT SPY
    3. MQTT FX

    Difference between MQTT & AMQP:

    MQTT is designed for lightweight devices like Embedded systems, where bandwidth is costly and the minimum overhead is required. MQTT uses byte stream to exchange data and control everything. Byte stream has optimized 2 byte fixed header, which is prefered for IoT.

    AMQP is designed with more advanced features and uses more system resources. It provides more advanced features related to messaging, topic-based publish & subscribe messaging, reliable queuing, transactions, flexible routing and security.

    Difference between MQTT & HTTP:

    MQTT is data-centric, whereas HTTP is document-centric. HTTP is a request-response protocol for client-server, on the other hand, MQTT uses publish-subscribe mechanism. Publish/subscribe model provides clients with the independent existence from one another and enhances the reliability of the whole system. Even if any of the client is out of network, the system keeps itself up and running

    As compared to HTTP, MQTT is lightweight (very short message header and the smallest packet message size of 2 bytes), and allows to compose lengthy headers and messages.

    MQTT Protocol ensures high delivery guarantees compared to HTTP.

    There are 3 levels of Quality of Services:

    at most once: it guarantees that message will be delivered with the best effort.

    at least once: It guarantees that message will be delivered at a minimum of one time. But the message can also be delivered again..

    exactly once: It guarantees that message will be delivered one and only one time.

    Last will & testament and Retained messages are the options provided by MQTT to users. With Last Will & Testament, in case of unexpected disconnection of a client, all subscribed clients will get a message from the broker. Newly subscribed clients will get immediate status updates via Retained message.

    HTTP Protocol has none of these abilities.

    Conclusion:

    MQTT is one of its kind message queuing protocols, best suited for embedded hardware devices. On the software level, it supports all major operating systems and platforms. It has proven its certainty as an ISO standard in IoT platforms because of its more pragmatic security and message reliability.