The course, Deployment of Machine Learning Models is now live on Udemy

MetaWear Guide Series Part 9

A Better Connection I
28 February 2016

This is part 9 of a multipart series showing you how to get started with the MetaWear platform. View the contents of the series to easily skip forwards or backwards

One of the reasons why using the MetaWear sample Android app is a big jump for those new to Android is that it involves multiple activities and especially because it uses fragments which build upon other fragments. This can make some parts of it seem like a black box without some serious digging. In this section, I’ll unpack some of the more basic parts of the sample app. As a result, there will be a slight jump in complexity…But if you’ve made it this far, you’re probably ready.

We are going to add the Sample app’s ScannerActivity.java to our project. This will allow us to select from multiple different boards, and eventually to manage our bluetooth connections more effectively.

To begin, we need to add a new dependency to our app build.grade file:

dependencies {
    compile fileTree(dir: 'libs', include: ['*.jar'])
    compile 'com.mbientlab:metawear:2.4.0'
    compile 'com.mbientlab.bletoolbox:scanner:0.2.0' //add this

Be sure to sync the project after adding the new dependency.

pt9-gradle

Next, create the new activity. In Android Studio, go to File –> New –> Activity –> Blank Activity

Call the activity, ScannerActivity. The associated layout name will automatically update.

We add the ScannerActivity to our AndroidManifest.xml

<activity
    android:name=".MyActivity"
    android:label="@string/app_name" >
    <intent-filter>
        <action android:name="android.intent.action.MAIN" />
    </intent-filter>
</activity>
<activity
    android:name=".ScannerActivity"
    android:label="@string/title_activity_scanner" >
    <intent-filter>
        <action android:name="android.intent.action.MAIN" />
        <category android:name="android.intent.category.LAUNCHER" />
    </intent-filter>
</activity>

Note that scanner activity is now the LAUNCHER activity, and that we have removed that action from .MyActivity. If you run your app you should see the ‘Hello World’ message.

Let’s update our activty_scanner.xml layout file. The ScannerActivity will require the ids we update here:

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/scanner_fragment_layout"
    android:orientation="vertical"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:textAppearance="?android:attr/textAppearanceLarge"
        android:text="Select Device"
        android:id="@+id/ble_scan_title"
        android:layout_gravity="center_horizontal" />

    <fragment xmlns:tools="http://schemas.android.com/tools" android:id="@+id/scanner_fragment"
        android:name="com.mbientlab.bletoolbox.scanner.BleScannerFragment"
        tools:layout="@layout/blescan_device_list" android:layout_width="match_parent"
        android:layout_height="match_parent" />

</LinearLayout>

You’ll note (and Android Studio will tell you) that a few strings need adding. Add these to your strings.xml file:

<string name="title_connecting">Connecting...</string>
<string name="message_wait">Please wait</string>
<string name="label_cancel">Cancel</string>

OK, now we’re ready to get to work on the ScannerActivity.

By checking against the branch version-0.8, make sure you’ve got all the imports required (there are quite a few).

We will be implementing both a ServiceConnection and a ScannerCommunicationBus in our activity, so the syntax is as follows:

public class ScannerActivity extends AppCompatActivity implements ScannerCommunicationBus, ServiceConnection {
	//activity code

}

Both of these have certain required methods. From part 3 You should already be familiar with those required for the ServiceConnection:

@Override
public void onServiceConnected(ComponentName componentName, IBinder iBinder) {
    serviceBinder = (MetaWearBleService.LocalBinder) iBinder;
}

@Override
public void onServiceDisconnected(ComponentName componentName) {

}

And there are a few standard activity setup methods to complete also:

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_scanner);

    getApplicationContext().bindService(new Intent(this, MetaWearBleService.class), this, BIND_AUTO_CREATE);
}

@Override
public void onDestroy() {
    super.onDestroy();

    ///< Unbind the service when the activity is destroyed
    getApplicationContext().unbindService(this);
}

Now we need to implement the methods for ScannerCommunicationBus. First, we’ll need a few variables (including our TAG variable for logging):

private static final String TAG = "MetaWear";
private final static UUID[] serviceUuids;
private MetaWearBleService.LocalBinder serviceBinder;
public static final int REQUEST_START_APP= 1;
private MetaWearBoard mwBoard;
static {
    serviceUuids= new UUID[] {
            MetaWearBoard.METAWEAR_SERVICE_UUID,
            MetaWearBoard.METABOOT_SERVICE_UUID
    };
}

And then the methods themselves:

@Override
    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
        switch(requestCode) {
            case REQUEST_START_APP:
                ((BleScannerFragment) getFragmentManager().findFragmentById(R.id.scanner_fragment)).startBleScan();
                break;
        }
        super.onActivityResult(requestCode, resultCode, data);
    }

    @Override
    public UUID[] getFilterServiceUuids() {
        return serviceUuids;
    }

    @Override
    public long getScanDuration() {
        return 10000L;
    }

    @Override
    public void onDeviceSelected(BluetoothDevice bluetoothDevice) {
        Log.i(TAG, "Device selected");
    }

At this point, we have a basic structure. If you run the app, you will see the scanner activity with a scan button, but nothing will happen when you press the button. Let’s add more code:

@Override
public void onDeviceSelected(final BluetoothDevice btDevice) {
    mwBoard= serviceBinder.getMetaWearBoard(btDevice);

    final ProgressDialog connectDialog = new ProgressDialog(this);
    connectDialog.setTitle(getString(R.string.title_connecting));
    connectDialog.setMessage(getString(R.string.message_wait));
    connectDialog.setCancelable(false);
    connectDialog.setCanceledOnTouchOutside(false);
    connectDialog.setIndeterminate(true);
    connectDialog.setButton(DialogInterface.BUTTON_NEGATIVE, getString(R.string.label_cancel), new DialogInterface.OnClickListener() {
        @Override
        public void onClick(DialogInterface dialogInterface, int i) {
            mwBoard.disconnect();
        }
    });
    connectDialog.show();

    mwBoard.setConnectionStateHandler(new MetaWearBoard.ConnectionStateHandler() {
        @Override
        public void connected() {
            connectDialog.dismiss();
            Log.i(TAG, String.format("Accelerometer: %s", mwBoard.getMacAddress()));
        }

        @Override
        public void disconnected() {
            mwBoard.connect();
        }

        @Override
        public void failure(int status, Throwable error) {
            mwBoard.connect();
        }
    });
    mwBoard.connect();
}

We now have enough code to search for the boards over bluetooth without manually entering the MAC address. If you run the app and hit “Scan”, then a list of the MetaWear boards within range will be displayed:

pt9-boards

The line of code in our log above mwBoard.getMacAddress() also ensures that when you select a board, its MAC address is displayed in the log:

pt9-mac

Since this post is already quite long, I’ll break it into two sections. In section II, I will show you the next steps after selecting a board using our new ScannerActivity.

Note You can view all these changes in the github repository on the branch version-0.8

Next post in the series –>

Category