MetaWearBoard

The MetaWearBoard class is the central class for communicating with your MetaWear board; it is similar to the old MetaWearController class from previous APIs.

To retrieve a MetaWearBoard object, you will need a reference to the MetaWearBleService.LocalBinder class and the BluetoothDevice object that corresponds to the MetaWear board you are using. You can create a BluetoothDevice object with BluetoothAdapter.getRemoteDevice if you know the MAC address or initiate a Bluetooth LE scan to find your board.

import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothManager;
// Other required imports

public class MainActivity extends Activity implements ServiceConnection {
    private final String MW_MAC_ADDRESS= "EC:2C:09:81:22:AC";
    private MetaWearBoard mwBoard;

    public void retrieveBoard() {
        final BluetoothManager btManager=
                (BluetoothManager) getSystemService(Context.BLUETOOTH_SERVICE);
        final BluetoothDevice remoteDevice=
                btManager.getAdapter().getRemoteDevice(MW_MAC_ADDRESS);

        // Create a MetaWear board object for the Bluetooth Device
        mwBoard= binder.getMetaWearBoard(remoteDevice);
    }
}

Connection State

The device’s connection state is controlled with the connect and disconnect functions. Notifications about the connection state are handled by the ConnectionStateHandler class. You register a handler by calling setConnectionStateHandler.

import android.util.Log;
import static com.mbientlab.metawear.MetaWearBoard.ConnectionStateHandler;

public class MainActivity extends Activity implements ServiceConnection {
    private final ConnectionStateHandler stateHandler= new ConnectionStateHandler() {
        @Override
        public void connected() {
            Log.i("MainActivity", "Connected");
        }

        @Override
        public void disconnected() {
            Log.i("MainActivity", "Connected Lost");
        }

        @Override
        public void failure(int status, Throwable error) {
            Log.e("MainActivity", "Error connecting", error);
        }
    };

    public void connectBoard() {
        mwBoard.setConnectionStateHandler(stateHandler);
        mwBoard.connect();
    }
}

Asynchronous Operations

Instead of registering callback functions to asynchronously receive responses from MetaWear or Bluetooth LE requests, asynchronous operations are encapsulated by the AsyncOperation class. This class acts as an observer of the task, notifying the user asynchronously when the task is complete.

To see this class in action, call MetaWearBoard.readRssi; the method returns an AsyncOperation holding an integer result.

AsyncOperation<Integer> result= mwBoard.readRssi();

With our AsyncOperation object in hand, we now register a handler to listen for the result of this request. If the request has already been completed when the handler is registered, it will immediately be executed and discarded. Otherwise, it will be queued and executed later when the task has finished.

The CompletionHandler class has two methods: success and failure. The former is called if the request was successful and passes the result to the caller, Conversely, the latter is called if the operation or success callback function failed, passing the error to the user.

import com.mbientlab.metawear.AsyncResult;

public class MainActivity extends Activity implements ServiceConnection {
    public void updateRssi() {
        AsyncResult<Integer> result= mwBoard.readRssi();
        result.onComplete(new AsyncResult.CompletionHandler<Integer>() {
            @Override
            public void success(final Integer result) {
                Log.i("MainActivity", String.format("Rssi (%d dBm)", result));
            }

            @Override
            public void failure(Throwable error) {
                Log.e("AsyncResult Example", "Error reading RSSI value", error);
            }
        });
    }
}

Modules

Modules are on-board sensors or features supported by the firmware. To interact with the underlying MetaWear modules, you will first need to establish a connection with the board. Once a successful connection has been established, you can then call MetaWearBoard.getModule. If the board does not support the requested module, an UnsupportedModuleException will be thrown. This is a checked exception, meaning users must handle the exception.

import com.mbientlab.metawear.module.Led;
import com.mbientlab.metawear.UnsupportedModuleException;

public class MainActivity extends Activity implements ServiceConnection {
    // Only run this function once a connection to the board has been made
    public void turnOnLed() {
        try {
            // Do not need to type cast result to Led class
            Led ledCtrllr= mwBoard.getModule(Led.class);
            ledCtrllr.writeChannelAttributes(Led.ColorChannel.BLUE)
                    .setRiseTime((short) 0).setPulseDuration((short) 1000)
                    .setRepeatCount((byte) -1).setHighTime((short) 500)
                    .setHighIntensity((byte) 16).setLowIntensity((byte) 16)
                    .commit();
            ledCtrllr.playLed(false);
        } catch (UnsupportedModuleException e) {
            Toast.makeText(this, "Led module not supported on this board / firmware",
                Toast.LENGTH_LONG).show();
            Log.e("MainActivity", "No Led on the board", e);
        }
    }
}

For users who know exactly what modules their MetaWears have, the lookupModule can be used to retrieve modules without needing to handle exceptions. This function will simply return null if a module cannot be found.

Led ledCtrllr;
if ((ledCtrllr= mwBoard.getModule(Led.class)) != null) {
    ledCtrllr.writeChannelAttributes(Led.ColorChannel.BLUE)
        .setRiseTime((short) 0).setPulseDuration((short) 1000)
        .setRepeatCount((byte) -1).setHighTime((short) 500)
        .setHighIntensity((byte) 16).setLowIntensity((byte) 16)
        .commit();
    ledCtrllr.playLed(false);
}

Serialization

The internal state of the class can be converted into a byte array, which can then be saved onto the device. You can use this to persist the state across app crashes or combine it with the Macro system to rebuild the class state after a programmed command executes. To serialize the state, call serializeState and conversely, call deserializeState to restore the class’ state.

import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;

import android.content.SharedPreferences;

public class MainActivity extends Activity implements ServiceConnection {
    private static final String SHARED_PREF_KEY= "MainActivity.SHARED_PREF_KEY";

    public void serializeToFile() throws IOException {
        String filename= String.format("metawearboard_%s", mwBoard.getMacAddress());
        File file = new File(context.getFilesDir(), );
        FileOutputStream fos= new FileOutputStream(file);

        fos.write(mwBoard.serializeState());
        fos.close();
    }

    public void deserializeFromFile() throws IOException {
        String filename= String.format("metawearboard_%s", mwBoard.getMacAddress());
        File fPtr= new File(filename);
        FileInputStream fis= new FileInputStream(fPtr);

        // only works with files < 2GB
        byte[] buffer= new byte[(int) fPtr.length()];
        fis.read(buffer);

        mwBoard.deserializeState(buffer);
    }

    public void serializeToSharedPreferences() {
        SharedPreferences.Editor editor= getSharedPreferences(SHARED_PREF_KEY,
                MODE_PRIVATE).edit();

        editor.putString(mwBoard.getMacAddress(), new String(mwBoard.serializeState()));
        editor.apply();
    }

    public void deserializeFromSharedPreferences() {
        SharedPreferences sharedPref= getSharedPreferences(SHARED_PREF_KEY, MODE_PRIVATE);
        String stateStr= sharedPref.getString(mwBoard.getMacAddress(), "");

        if (!stateStr.isEmpty()) {
            mwBoard.deserializeState(stateStr);
        } else {
            Log.i("MainActivity", "Cannot find state for this board");
        }
    }

}

Updating Firmware

As of api v2.6.0, the updateFirmware function is deprecated. Instead, the api provides the downloadLatestFirmware function which will download the latest firmware release to your mobile device. The firmware file can then be uploaded to your MetaWear using the Nordic DFU library.

mwBoard.downloadLatestFirmware().onComplete(new AsyncOperation.CompletionHandler<File>() {
    @Override
    public void success(File result) {
        if (mwBoard.inMetaBootMode()) {
            mwBoard.disconnect();
        } else {
            mwBoard.lookupModule(Debug.class).jumpToBootloader();
        }
    }
});

Checkout the Nordic DFU library GitHub page or the MetaWear Android app source code for information and examples on how to use the library.