Skip to content
Metawear edited this page Jul 6, 2015 · 8 revisions

Data routes are the new mechanism for manipulating sensor data. They replace the old callback driven setup with a Java DSL for expressing what to do with sensor data. You will be using these routes to stream or log data, performing on board data processing, and programming the board to react to sensor activity.

Routes are created by calling MetaWearBoard.routeData. From there, you use the RouteBuilder the method returns to select the origin of your data i.e. which sensor will serve as the data source for your route. After selecting your data source, you can then attach various signal components to your route to manipulate the data flow before finally committing the route to the board.

This guide will cover the basics of data routes and will provide you with the foundation needed to build simple routes. More advanced features and complex examples will be covered in later guides.

Receiving Data

For starters, lets create a data route that will stream button presses to your device using a subscriber, which is a data listener that streams data to your mobile device.

mwBoard.routeData().fromSwitch().subscribe(new DataSignal.MessageProcessor() {
    @Override
    public void process(final Message msg) {
        Log.i("ExampleActivity", String.format("Switch %s" , 
                msg.getData(Boolean.class) ? "Pressed" : "Released"));
    }
}).commit();

Attaching a subscriber to the route is done by calling subscribe. The subscribe function takes in a MessageProcessor object which processes each message received from your data source. In a similar vein, you can replace the subscriber with a logger to your route to instead store data to the board's flash memory and retrieve it later.

mwBoard.routeData().fromSwitch().log(new DataSignal.MessageProcessor() {
    @Override
    public void process(final Message msg) {
        Log.i("ExampleActivity", String.format(
                "%tY%<tm%<td-%<tH:%<tM:%<tS.%<tL": Switch %s" ,
                msg.getTimestamp(),
                msg.getData(Boolean.class) ? "Pressed" : "Released"));
    }
}).commit();

Messages

In the MessageProcessor class that you override, the process function has one parameter of type Message. The Message class is a generic container that wraps the raw sensor data and also attaches a few attributes to the data. Extracting data is done by calling getData and passing in the type that the data should be interpreted as.

In the above switch example, switch sensor data can be interpreted as both a boolean or a byte thus the valid parameters for the getData function are either Boolean.class or Byte.class. For more specifics on the different Message classes, check out the documentation for the Message subclass for the specific sensor you are working with.

Data Filter

Another signal component you can add to the route are data filters. Data filters act like gates, controlling the flow of data in your route by only allowing data through that satisfies their condition. Lets further expand on our switch example by adding a comparison filter to remove button releases from our subscriber.

import com.mbientlab.metawear.processor.Comparison;

mwBoard.routeData().fromSwitch()
    ///< Button presses correspond to a 1 in the sensor data
    .filter(new Comparison(Comparison.Operation.EQ, 1))
    .subscribe(new DataSignal.MessageProcessor() {
        @Override
        public void process(final Message msg) {
            ///< Add this here to ensure the subscriber only receives press events
            if (!msg.getData(Boolean.class)) {
                Log.e("ExampleActivity", "This log call should never be executed");
            }
            Log.i("ExampleActivity", "Button pressed");
        }
    })
.commit();

Filters are configured either with DataFilter tokens or a String URI. The above sample code uses the token version of the filter function by passing in a Comparison token. Alternatively, the filter function could have been called as follows:

.filter("comparison?operation=eq&reference=1")

Specifics on the URI format are provided [here](Data Processor URI Scheme).

Data Transformer

Unlike data filters, transformers modify the incoming data and possibly convert it into different forms. Transformers are also configured with either DataTransformer tokens or a String URI. If you are using the String URI method, check the documentation of the transformer you are using for more details on the URI format. Lets replace the filter with an accumulator transformer, which we will use to count the number of button presses.

import com.mbientlab.metawear.processor.Accumulator;

mwBoard.routeData().fromSwitch()
    .transformer(new Accumulator())
    .subscribe(new DataSignal.MessageProcessor() {
        @Override
        public void process(final Message msg) {
            Log.i("ExampleActivity", String.format("Button press #%d", 
                    msg.getData(Byte.class)));
        }
    })
.commit();

You may have noticed, that the getData method is now called with Byte.class instead of Boolean.class. This is because interpreting the accumulated data as a boolean is nonsensical given our goal of counting button presses. In this context, interpreting the data as a byte makes more sense. You will need to think about what you expect your data to look like when adding transformers in your route to make the appropriate type casting on the received data.

Clone this wiki locally