This tutorial describes the Android program needed to communicate with the nRF8002 breakout board and is a companion to the hardware tutorial. The complete Android source code is available on our github page.

A screen shot of the application is shown in the figure below. It consists of a list area which shows all the Bluetooth devices discovered by the phone. The status area is shown below the list of discovered devices and provides information about the current activity on the phone. At the bottom of the screen, four buttons are shown that enable interacting with the breakout board.

Android Screenshot nRF8002 BLE

The features that the buttons enable are:

Scan: Clicking on this button causes the Android device to search for the available Bluetooth devices and provide a list. Clicking on one of the items in the list will cause the phone to pair with the phone to pair with the device.

Battery: Clicking on this button shows the current battery level in percentage.

Alarm: Clicking on this button causes the buzzer to beep which is connected to the PWM pin of the nRF8002 breakout board.

Start Server: Clicking on this button enables the nRF8002 device to start the server mode of operation. The server mode is needed for the buttons on the nRF8002 breakout board to send information back to the phone.

Code Description

Working with the nRF8002 board
Connecting to the nRF8002 board via an Android device consists of the following steps

1. Initial setup

2. Scanning for devices

3. Connecting to a selected device

In this example, the Android phone is configured as both a gatt client and gatt server  simultaneously.

When the phone is configured as a  client it accepts connections from the nRF8002 board and can read or write information from nRF8002. This mode of operation is used for the battery level and alarm service. The server mode of operation is used to inform the phone of button press events on the nRF8002 device.
In the rest of this section, the code needs to connect to implement the three steps as explained.

Initial Setup
To communicate with the nRF8002 using Android, a Bluetooth manager object is first created and then used to get a 

Bluetooth adapter object.

final BluetoothManager bluetoothManager = (BluetoothManager) getSystemService(Context.BLUETOOTH_SERVICE); 
mBluetoothAdapter = bluetoothManager.getAdapter();

The next step is to determine if Bluetooth is enabled on the phone. This is done using the following code:

 if (mBluetoothAdapter == null || !mBluetoothAdapter.isEnabled()) { 
Intent enableBtIntent = new Intent( BluetoothAdapter.ACTION_REQUEST_ENABLE);
startActivityForResult(enableBtIntent, REQUEST_ENABLE_BT);
}

Once these two steps are completed, the Android program waits for user interaction to proceed.

Scanning for devices
The scanning for device is done for 2.5 seconds and the results are used to update the listview, which holds the list of returned devices. The scanning for the various devices is done using the startScan function, which in turn uses the startLeScan function provided by the Android Bluetooth library. After 2.5 seconds, this function calls the stopScan function via the mStopRunnable. The mStopRunnable calls the stopScan function and updates the listview using the mListPopulateRunnable. The stopScan function in turn calls the stopLeScan function provided by the Android Bluetooth library.

 
	private void startScan() {
		mBluetoothAdapter.startLeScan(this);
		setProgressBarIndeterminateVisibility(true);

		handler.postDelayed(mStopRunnable, 2500);
	}

	private Runnable mStopRunnable = new Runnable() {
		@Override
		public void run() {
			stopScan();
			updateText("Press Button To Scan");
			handler.post(mListPopulate);
		}
	};

	private Runnable mListPopulate = new Runnable() {
		@Override
		public void run() {
			listItems.clear();
			for (int i = 0; i < mDevices.size(); i++) {
				device = mDevices.valueAt(i);
				listItems.add(device.getName());
			}
			myarrayAdapter.notifyDataSetChanged();
		}
	};
Connecting to the device
Connecting to a device is done using the connectToDevice function. This function determines the position of the item in the list view and then connects to it using the device.connect function.
	public void connectToDevice(int i) {
		BluetoothDevice device = mDevices.valueAt(i);
		mConnectedGatt = device.connectGatt(this, true, mGattCallback);
		Log.i("BLEMainActivity:ConnectToDevice",
				"Device State " + device.getBondState());
	}

Communicating with the nRF8002 device
Once the device is connected to the phone, further communication is performed using callback functions. The two callback objects implemented are of the BluetoothGattCallback class and BluetoothGattServerCallback class which are used for the client and server role of the Android program respectively. In this Android program, the alarm, battery level, and server features are implemented and are explained below.

Alarm
The Alarm feature of the nRF8002 is implemented using the immediate alert service of the Bluetooth low energy specification. The code snippet that implements the alarm feature is given below. In this function, BluetoothGattCharacterisitic object is first created. This is then associated with the immediate alert service with a mild alert level and then written to the BluetoothGatt object.

	public void soundAlarm(View view) {
		BluetoothGattCharacteristic characteristic;
		characteristic = gattOutside.getService(IMMEDIATE_ALERT_SERVICE)
				.getCharacteristic(ALERT_LEVEL_CHAR);
		characteristic.setValue(ALERT_LEVEL_MILD);

		gattOutside.writeCharacteristic(characteristic);
	}

This function triggers the onCharacteristicWrite method of the BluetoothGattCharacterisitic object. This function then updates the user interface to indicate that an alarm has been sounded.

		@Override
		public void onCharacteristicWrite(BluetoothGatt gatt,
				BluetoothGattCharacteristic characteristic, int status) {

			super.onCharacteristicWrite(gatt, characteristic, status);
			Log.i("BLEMainActivity:onCharacteristicWrite", "Status = " + status);
			updateText("Alarm Sounded!");

		}

Battery level
The battery level feature of the nRF802 is implemented using the battery service of the Bluetooth low energy specification. In this function, BluetoothGattCharacterisitic object is first created. This is then associated with the battery service and the battery level characteristic and then written to the BluetoothGatt object.

	public void getBattery(View view) {
		BluetoothGattCharacteristic characteristic = null;

		characteristic = gattOutside.getService(BATTERY_SERVICE)
				.getCharacteristic(BATTERY_LEVEL_CHAR);
		Log.i("BLEMainActivity:GetBattery", "Characteristic = "
				+ characteristic.toString());

		Boolean b = gattOutside.readCharacteristic(characteristic);
		Log.i("BLEMainActivity:GetBattery", "Reads the battery information "
				+ String.valueOf(b));
	}

This feature triggers the onCharacteristicRead method of the BluetoothGattCallback object. When the Battery button is pressed, the phone sends a read request to the device. The device sends the information back by calling the onCharacteristicRead method. In this method, we filter the data for the BATTERY_LEVEL_CHAR uuid. If the characteristic matches that UUID, then the battery level information is retrieved and the user interface is updated with the data.

		@Override
		public void onCharacteristicRead(BluetoothGatt gatt,
				BluetoothGattCharacteristic characteristic, int status) {
			super.onCharacteristicRead(gatt, characteristic, status);
			if (DEVICE_NAME_CHAR.equals(characteristic.getUuid())) {
				String DevName = characteristic.getStringValue(0);
				Log.i("BLEMainActivity:onCharacteristicRead", "Device Name is "
						+ DevName + " Status = " + status);
				updateText("Device Name is " + DevName);
			}

			if (BATTERY_LEVEL_CHAR.equals(characteristic.getUuid())) {
				int BattLevel = characteristic.getIntValue(
						BluetoothGattCharacteristic.FORMAT_UINT8, 0);
				Log.i("BLEMainActivity:onCharacteristicRead",
						"Battery Level is " + BattLevel);
				updateText("Battery Level = " + BattLevel);
			}

			
		}

Server
The server feature of the Android program enables the nRF8002 device to send information to the phone. The gattServer object is created with mBluetoothGattServerCallback object as one of the parameters. Then a service is created with the IMMEDIATE_ALERT_SERVICE and a characteristic with PROPERTY_WRITE_NO_RESPONSE. This is the characteristic needed for the button press to be sent from the device to the phone. The values needed for such a characteristic is set using the setValue method. Finally, the service is then added to the server using the addService method.

 
	public void setupGattServer(View view) {

		final BluetoothManager bluetoothManager = (BluetoothManager) getSystemService(Context.BLUETOOTH_SERVICE);
		BluetoothGattServerCallback mBluetoothGattServerCallback = new BluetoothGattServerCallback() {
			@Override
			public void onConnectionStateChange(BluetoothDevice device,
					int status, int newState) {
				Log.i("BLEMainActivity:BluetoothGattServerCallback:onConnectionStateChange",
						"device : " + device + " status : " + status
								+ " new state : " + newState);
			}

			@Override
			public void onServiceAdded(int status, BluetoothGattService service) {
				Log.i("BLEMainActivity:BluetoothGattServerCallback:onServiceAdded",
						"service : " + service.getUuid() + " status = "
								+ status);
			}

			@Override
			public void onCharacteristicReadRequest(BluetoothDevice device,
					int requestId, int offset,
					BluetoothGattCharacteristic characteristic) {
				Log.i("BLEMainActivity:BluetoothGattServerCallback:onCharacteristicReadRequest",
						"device : " + device.getAddress() + " request = "
								+ requestId + " offset = " + offset
								+ " characteristic = "
								+ characteristic.getUuid());
		
			}

			@Override
			public void onCharacteristicWriteRequest(BluetoothDevice device,
					int requestId, BluetoothGattCharacteristic characteristic,
					boolean preparedWrite, boolean responseNeeded, int offset,
					byte[] value) {
				super.onCharacteristicWriteRequest(device, requestId,
						characteristic, preparedWrite, responseNeeded, offset,
						value);
				Log.i("BLEMainActivity:BluetoothGattServerCallback:onCharacteristicWriteRequest",
						"device : " + device.getAddress()
								+ " characteristic : "
								+ characteristic.getUuid() + "Value = "
								+ value.toString());
				updateText("Received button press event");

			}

			@Override
			public void onDescriptorReadRequest(BluetoothDevice device,
					int requestId, int offset,
					BluetoothGattDescriptor descriptor) {
				Log.i("BLEMainActivity:BluetoothGattServerCallback:onDescriptorReadRequest",
						"device : " + device.getAddress() + " request = "
								+ requestId + " offset = " + offset
								+ " descriptor = " + descriptor.getUuid());
			}

			@Override
			public void onDescriptorWriteRequest(BluetoothDevice device,
					int requestId, BluetoothGattDescriptor descriptor,
					boolean preparedWrite, boolean responseNeeded, int offset,
					byte[] value) {
				Log.i("BLEMainActivity:BluetoothGattServerCallback:onDescriptorWriteRequest",
						"device : " + device.getAddress() + " \n descriptor : "
								+ descriptor.getUuid());
			}

			@Override
			public void onExecuteWrite(BluetoothDevice device, int requestId,
					boolean execute) {
				Log.i("BLEMainActivity:BluetoothGattServerCallback:onExecuteWrite",
						"device : " + device.getAddress() + " request = "
								+ requestId + " execute = " + true);
			}
		};
		BluetoothGattServer gattServer = bluetoothManager.openGattServer(
				getApplicationContext(), mBluetoothGattServerCallback);
		BluetoothGattService service = new BluetoothGattService(
				IMMEDIATE_ALERT_SERVICE,
				BluetoothGattService.SERVICE_TYPE_PRIMARY);
		BluetoothGattCharacteristic characteristic = new BluetoothGattCharacteristic(
				ALERT_LEVEL_CHAR,
				BluetoothGattCharacteristic.PROPERTY_WRITE_NO_RESPONSE,
				BluetoothGattCharacteristic.PERMISSION_WRITE);
		characteristic.setValue(ALERT_LEVEL_CHARACTERISTIC_VALUE,
				ALERT_LEVEL_CHARACTERISTIC_FORMATTYPE,
				ALERT_LEVEL_CHARACTERISTIC_OFFSET);
		service.addCharacteristic(characteristic);
		gattServer.addService(service);
		Log.i("BLEMainActivity:setupGattServer",
				"Gatt server setup complete : " + gattServer.toString());

	}
In this tutorial we introduced the basic steps needed to interact with the nRF8002 breakout board using an Android device. The detailed tutorial of the hardware is available here.