General
IoT

Decoding MetaWear APIs Using BLE To Read Pulse Sensor

Pradeep V R

21 Nov 2016

Decoding MetaWear APIs Using BLE To Read Pulse Sensor | Cumulations

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.