Cumulations Logo

Working with BLE devices while building Android apps is always a challenging task especially when there is not much documentation available. In one of our current project, we were supposed to communicate with MetaWear device to read its sensor data. Unfortunately, MetaWear doesn’t provide any gatt documentation to interact with its BLE device. They have provided open source library for Android (not open sourced for iOS) and API documentation on how to use it. But we were not supposed to use any libraries for 2 reasons.

  • It would increase our app size and method counts (more than 4K methods) drastically.
  • Our interaction with the hardware is by using BluetoothGatt object of Android framework, MetaWear API doesn’t expose its BluetoothGatt object which is not really an option for us.

Our task was to read data from pulse rate, gyroscope, accelerometer sensors of the MetaWear. In BLE we can read data from hardware either by reading gatt characteristics or by receiving notification from the gatt characteristics. For this, we should be knowing about the UUIDs of service and characteristics. In case if data is received by notification we should know about data bytes that should be written to start/stop the notification. But as mentioned above MetaWear has not provided any documentation about the gatt profiles, our Android, and iOS developers were restricted to not use MetaWear library. Luckily Android library was open sourced, so we modified their library and added thousands of logs to understand the code flow and the data that is exchanged between hardware and phone.

After days of hard work which involved in understanding each and every line of logs, we were able to decode MetaWear APIs and read sensors data from the device without using their library. In below section, we will help you to understand how to read data from the pulserate sensor using MetaWear APIs and without using MetaWear APIs.

  • Using MetaWear APIs
      Below is the step by step procedure to read pulse rate data using MetaWear API
      1. Enable pulse sensor: switch on pulse sensor light before reading data                                
    	     gpio = mwBoard.getModule(Gpio.class);
    	     gpio.clearDigitalOut((byte) 1);

     2. Enable notification to read data

      gpio.routeData()
          .fromAnalogIn(GPIO_PIN, Gpio.AnalogReadMode.ADC)
          .stream(HEART_RATE)
               	       .commit().onComplete(gpioHandler);
      private Timer.Controller heartRateController;
      private final AsyncOperation.CompletionHandler gpioHandler = new AsyncOperation.CompletionHandler() {
    	@Override
    	public void success(RouteManager result) {
       	result.subscribe(HEART_RATE, dataHandler);
       	try {
           	AsyncOperation taskResult = mwBoard.getModule(Timer.class).scheduleTask(
                   	new Timer.Task() {
                       	@Override
                       	public void commands() {
                           	gpio.readAnalogIn(GPIO_PIN, Gpio.AnalogReadMode.ADC);
                       	}
                   	}, 10000, false);
           	taskResult.onComplete(
                   	new AsyncOperation.CompletionHandler() {
                       	@Override
                       	public void success(Timer.Controller result) {
                           	heartRateController = result;
                           	result.start();
                       	}
                   	}
           	);
       	} catch (UnsupportedModuleException e) {
           	Log.e("HeartRateSensorFragment", e.toString());
       	}
    	}
    };

     3. Process raw data received from the hardware

    private final RouteManager.MessageHandler dataHandler = new RouteManager.MessageHandler() {
    	@Override
    	public void process(Message message) {
    	// raw data is received here.
    	}
    };

    4. Disable pulse sensor and stop reading data from the hardware

    gpio.setDigitalOut((byte) 1);
    heartRateController.stop();
  • Without using MetaWear APIs
         In this case, we have more steps in addition to using MetaWear APIs as described below.
       1. Switch ON pulse sensor: Write byte array 050201 to switch on pulse sensor light.
             Input - 050201, where 05 is GPIO module, 02 is flag to switch ON and 01 is analog opcode.
             service uuid: 326a9000-85cb-9195-d9dd-464cfbbae75a
             characteristic uuid: 326a9001-85cb-9195-d9dd-464cfbbae75a
             write type: 1
       2. Enable timer register : Write byte array 0C02E8030000FFFF01 to enable timer, so that sensor data is received
             for the specified sampling duration.
             Input - 0C02E8030000FFFF01, where 0C is timer module, 02 is timer_entry opcode, E8030000FFFF01 is
             param which consists of period, repetitions and delay
             service uuid: 326a9000-85cb-9195-d9dd-464cfbbae75a
             characteristic uuid: 326a9001-85cb-9195-d9dd-464cfbbae75a
             write type: 1
       3. Receive tickerId:
            
    After step 1, onCharacteristicsChanged will be called and app will receive tickerId in response.
            Output: 0C0205, where 0C is module, 02 is opcode and 05 is tickerId
            service uuid: 326a9000-85cb-9195-d9dd-464cfbbae75a
            characteristic uuid: 326a9006-85cb-9195-d9dd-464cfbbae75a
       4. Enable heartrate register : This is 2 step process where we need to write different byte arrays to enable a register; heartrate data that is received by the phone is stored/cached in this register which acts as
            a placeholder.
            a. Write event entry data
                Input - 0A020C0600058701, where 0A is EVENT module, 02 is EVENT_ENTRY opcode, 0C is timer module,
                06 is notify opcode, 00 is tickerId, 05 is GPIO module (GP input/output pin), 87=80|07 where 07
                is READ_AI_ADC opcode (analog input), 01 is param
               service uuid: 326a9000-85cb-9195-d9dd-464cfbbae75a
               characteristic uuid: 326a9001-85cb-9195-d9dd-464cfbbae75a
               write type: 1

           b. Write event param data
               Input - 0A0300, where 0A is EVENT module, 03 is command params  opcode
               service uuid: 326a9000-85cb-9195-d9dd-464cfbbae75a
               characteristic uuid: 326a9001-85cb-9195-d9dd-464cfbbae75a
               write type: 1

       5. Receive confirmed tickerId
           After step 4, onCharacteristicsChanged will be called and an app will receive tickerId in response.
           Output: 0A0205, where 0A is EVENT module, 02 is EVENT_ENTRY opcode and 05 is tickerId which will be
           equal to the one received in step 2.
           service uuid: 326a9000-85cb-9195-d9dd-464cfbbae75a
           characteristic uuid: 326a9006-85cb-9195-d9dd-464cfbbae75a

            6. Send start command
                Input - 0C0300, where 0C is timer module, 03 is start opcode and 05 is tickerId
                service uuid: 326a9000-85cb-9195-d9dd-464cfbbae75a
                characteristic uuid: 326a9001-85cb-9195-d9dd-464cfbbae75a
                write type: 1

            7. Receive heartrate value
                 Output: 0587006603, 05 is GPIO module, 87=80|07 where 07 is READ_AI_ADC opcode (analog input), last
                 2 bytes (6603) is the temperature value.
                 Since data is in little endian format hex value is 0366, equivalent decimal value is 870; this number is sent
                 through multiple methods to calculate heartrate
                 value per minute.
                 service uuid: 326a9000-85cb-9195-d9dd-464cfbbae75a
                 characteristic uuid: 326a9006-85cb-9195-d9dd-464cfbbae75a

            8. Stop receiving heartrate value
                Input - 0C0400, where 0C is timer module, 04 is stop opcode and 00 is tickerId
                service uuid: 326a9000-85cb-9195-d9dd-464cfbbae75a
                characteristic uuid: 326a9001-85cb-9195-d9dd-464cfbbae75a
                write type: 1

             9.Stop receiving heartrate value
                Input - 050101, where 05 is GPIO module, 01 is flag to switch OFF and 01 is analog opcode.
                service said: 326a9000-85cb-9195-d9dd-464cfbbae75a
                characteristic uuid: 326a9001-85cb-9195-d9dd-464cfbbae75a
                write type: 1

In this article we understood problems faced while reading data from MetaWear sensors and how to solve it with an example of pulse sensor. In next article we will understand how we can read data from motion sensors like gyro and accelerometer, keep watching this space.