Nod Developer Center

Getting Started

  • This guide covers creating a minimal Android app (using Android Studio for Linux) with the OpenSpatial SDK.
  • These steps apply to Android Studio on other platforms with minimal changes and should be broadly applicable to other IDEs

STEP 0

Install them in convenient locations.

STEP 1

  • Open up Android Studio
  • Start a new project, give it a name (For example – “TargetEuler”), select “Blank Activity”

STEP 2

  • Edit AndroidManifest.xml to add
<uses-permission android:name="android.permission.BLUETOOTH"/>
<service android:name="net.openspatial.OpenSpatialService"/>

Location is important:

  • service goes INSIDE <application> but OUTSIDE <activity>
  • uses-permission goes OUTSIDE <application>

STEP 3

  • Find where Android Studio is keeping your project
  • For me, this is ~/AndroidStudioProjects/TargetEuler
  • Open a command prompt, and do:
cp PATH_TO_OPENSPATIAL/android-sdk/sdk/release/OpenSpatialSDKv0.6.9.jar PATH_TO_PROJECTS/TargetEuler/app/libs
  • If by the time you read this, there is a later release than 0.6.9, go ahead and use that one if you want
  • Back in Android Studio, go to the “Project” window, and select “Project” view
  • Find OpenSpatialSDKv0.6.9.jar in the libs/ directory, right click, and select ‘Add as Library’.

STEP 4

  • The IDE should have populated a bunch of boilerplate already, and it should populate (or prompt to populate) more as we go along. Our activity is:
public class MainActivity extends AppCompatActivity {
// ...
}
  • Add a couple fields:
public static final String TAG = "TargetEuler";
OpenSpatialService mOpenSpatialService;
  • Override a couple methods:
@Override
  public void onCreate(Bundle savedInstanceState) {
      super.onCreate(savedInstanceState);
      setContentView(R.layout.activity_main);
      bindService(new Intent(this, OpenSpatialService.class), mOpenSpatialServiceConnection, BIND_AUTO_CREATE);
  }

@Override
  public void onDestroy() {
      super.onDestroy();
      unbindService(mOpenSpatialServiceConnection);
  }
  • Add a tiny little method that we’ll use later:
public void updateAngle(String device, EulerData angle) {
      String logline = device + " " + angle.getRoll()
              + " " + angle.getPitch()
              + " " + angle.getYaw();
      Log.d(TAG, logline);
}
  • You probably noticed we don’t have a mOpenSpatialServiceConnection yet. Let’s add that now:
private ServiceConnection mOpenSpatialServiceConnection = new ServiceConnection() {
};
  • We need to override a couple fields, so let’s have the IDE generate that code now
  • We’ll also add some cleanup and startup code while we’re here:
private ServiceConnection mOpenSpatialServiceConnection = new ServiceConnection() {

        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {
           mOpenSpatialService =
               ((OpenSpatialService.OpenSpatialServiceBinder)service).getService();
           // CODE FROM STEP FIVE GOES HERE
           mOpenSpatialService.getConnectedDevices();
        }

        @Override
        public void onServiceDisconnected(ComponentName name) {
           mOpenSpatialService = null;
        }

    }

STEP 5

  • Now we have to initialize the mOpenSpatialService object. The first part is:
mOpenSpatialService.initialize(TAG, new OpenSpatialInterface {} );
  • At some point as you’re entering this, Android Studio may automatically create overridden methods, if it does not, have it do so now. So it should look like:
mOpenSpatialService.initialize(TAG, new OpenSpatialService.OpenSpatialServiceCallback() {

    @Override
    public void onDeviceConnected(BluetoothDevice bluetoothDevice) {

    }

    @Override
    public void onDeviceDisconnected(BluetoothDevice bluetoothDevice) {

    }

    @Override
    public void onGetParameterResponse(BluetoothDevice bluetoothDevice,
                                       DataType dataType,
                                       DeviceParameter deviceParameter,
                                       ResponseCode responseCode,
                                       short[] data) {

    }

    @Override
    public void onSetParameterResponse(BluetoothDevice bluetoothDevice,
                                       DataType dataType,
                                       DeviceParameter deviceParameter,
                                       ResponseCode responseCode,
                                       short[] data) {

    }

    @Override
    public void onGetIdentifierResponse(BluetoothDevice bluetoothDevice,
                                        DataType dataType,
                                        byte index,
                                        ResponseCode responseCode,
                                        String identifier) {

    }

    @Override
    public void onGetParameterRangeResponse(BluetoothDevice bluetoothDevice,
                                            DataType dataType,
                                            DeviceParameter deviceParameter,
                                            ResponseCode responseCode,
                                            Number low, Number high) {

    }

    @Override
    public void onDataEnabledResponse(BluetoothDevice bluetoothDevice,
                                      DataType dataType,
                                      ResponseCode responseCode) {

    }

    @Override
    public void onDataDisabledResponse(BluetoothDevice bluetoothDevice,
                                       DataType dataType,
                                       ResponseCode responseCode) {

    }

    @Override
    public void onDataReceived(OpenSpatialData openSpatialData) {

    }
}
  • The only thing we’re going to worry about for now is onDeviceConnected, you can leave the others as stubs

STEP 6

  • In onDeviceConnected we will enable data reporting for the data types we are interested in
  • We’re only going to ask for EULER_ANGLE data
  • Here’s the code:
@Override
public void onDeviceConnected(BluetoothDevice bluetoothDevice) {
    mOpenSpatialService.enableData(bluetoothDevice, DataType.EULER_ANGLES);
}
  • The device has now been notified that you are interested in receiving euler angle reports. The response from the device will come in the onDataEnabled callback
  • When an event is received it reports using the onDataReceived callback.
  • When we get some data we want to do something with it. This code sends the data to our updateAngle method:
@Override
public void onDataReceived(OpenSpatialData openSpatialData) {
    String deviceName = openSpatialData.device.getName();
    if (openSpatialData.dataType.equals(DataType.EULER_ANGLES)) {
        EulerData eulerData = (EulerData) openSpatialData;
        updateAngle(deviceName, eulerData);
    }
}

STEP 7

  • Plug in your android device, ensure that your OpenSpatial device shows up in the Nod app, and start the program
  • You should see a white screen that pops up with an uninspiring message. That’s not where the excitement is
  • Take a look at the logcat output, either with “adb logcat” or using the IDE log, and you’ll see lots of lines like:
11-19 16:42:46.769  17040-17040/com.hellonod.targeteuler D/TargetEuler: nod-09b-Re-1222 -0.6107177734375 1.21044921875 -2.7442626953125
11-19 16:42:46.779  17040-17040/com.hellonod.targeteuler D/TargetEuler: nod-09b-Re-1222 -0.6099853515625 1.2105712890625 -2.7437744140625
11-19 16:42:46.859  17040-17040/com.hellonod.targeteuler D/TargetEuler: nod-09b-Re-1222 -0.61083984375 1.21044921875 -2.74462890625
11-19 16:42:46.949  17040-17040/com.hellonod.targeteuler D/TargetEuler: nod-09b-Re-1222 -0.6107177734375 1.2103271484375 -2.74462890625
11-19 16:42:47.049  17040-17040/com.hellonod.targeteuler D/TargetEuler: nod-09b-Re-1222 -0.610595703125 1.2105712890625 -2.74462890625

STEP 8

That’s it! You’re ready to build something cool

More information

If you want to know more, these are some good places to continue exploring:

  • Links to SDKs, developer focussed support, IRC.
  • Engage with the larger Nod development community on a variety of topics