Bluetooth

Bluetooth LE – Part 4

Previously in this series we got basic BLE device discovery working for our app to display ambient temperature and humidity with values obtained from a TI SensorTag over BLE. In this article we’ll look at connecting to the SensorTag now that we can find it.

bluetoothAnyone familiar with traditional Bluetooth will be aware of Bluetooth Profiles. For those new to Bluetooth, a Profile is a mechanism for standardising common behaviors. For example the Advanced Audio Distribution Profile (A2DP) allows the streaming of audio from one device to another provided both devices support A2DP.

In BLE there is a standard Profile, called the Generic ATTribute Profile (or GATT, for short) which provides a standardised mechanism for sharing small, atomic pieces of data over BLE in an extremely efficient manner. We’ll cover GATT in much more depth later on in this series, but any sensor running BLE will contain a GATT server which we need to connect to in order to begin exchanging data with the sensor.

Connecting to the GATT server is actually a two stage process. First we must create and open a connection to a local proxy instance which represents the GATT server, then we must connect this proxy to the GATT server on the sensor. In order to create the proxy instance we need to call connectGatt() on the BluetoothDevice instance that we received for each device during the discovery phase. This method takes three arguments. The first is a Context; the second is a boolean named autoConnect (more on this in a moment); and the third is a BluetoothGattCallback instance. This final argument indicates that this call is asynchronous, and it is safe to call from the UI thread because the network operation will be automatically performed on a background thread, and a callback will be made to BluetoothGattCallback once it is complete. The value returned by connectGatt() is a BluetoothGatt instance which is the local proxy object that we can now use to communicate with the GATT server on the sensor.

If we call connectGatt() with autoConnect set to FALSE it will return a BluetoothGatt object, but we will not receive a callback to indicate that the connection to the GATT server on the sensor has been made. That is to be expected because at this point we have only created the local proxy. We need to call the connect() method on the BluetoothGatt object to connect the local proxy to the remote GATT server, and only once that completes will we receive the callback. It is only the remote connection that is asynchronous because it is the only blocking network call.

It is important to understand that there are local and remote components to the GATT server, but now that we have that understanding, there is a simpler way to connect. If we set the autoConnect argument to TRUE then it will automatically connect to the GATT server on the sensor. The connectGatt() method will return quickly, and we’ll subsequently receive a callback once the remote connection has been made. The other nice thing about using autoConnect is that if the connection to the sensor is lost, the proxy will attempt to restore the connection for us without any prompting. All we need to do is monitor the current connection state, and only interact with the sensor when we’re connected.

private BluetoothGatt mGatt = null;

public void connect(String macAddress) {
	BluetoothDevice device = mDevices.get(macAddress);
	if(device != null) {
		mGatt = device.connectGatt(this, true, mGattCallback);
	}
}

We now need to add the necessary callback which allow us to respond to connection state changes. There are other callback methods in BluetoothGattCallback which will need later on, but for now we just need to manage connection / disconnection state changes:

	private BluetoothGattCallback mGattCallback = new BluetoothGattCallback() {
		@Override
		public void onConnectionStateChange(BluetoothGatt gatt, int status, int newState) {
			super.onConnectionStateChange(gatt, status, newState);
			Log.v(TAG, "Connection State Changed: " + (newState == BluetoothProfile.STATE_CONNECTED ? "Connected" : "Disconnected"));
			if(newState == BluetoothProfile.STATE_CONNECTED) {
				setState(State.CONNECTED);
			} else {
				setState(State.IDLE);
			}
		}
	};

Once again, I’m not going to cover what we’re doing in the UI as the focus here is on BLE, but this change will result in a Message being sent to the Activity following a change to the state machine in the BleService, and the Activity can update the UI accordingly.

Once we receive the callback indicating that we’re connected to the sensor we can begin communicating it, and we’ll look at that in the next article.

The source code for this article is available here.

© 2014, Mark Allison. All rights reserved.

Copyright © 2014 Styling Android. All Rights Reserved.
Information about how to reuse or republish this work may be available at http://blog.stylingandroid.com/license-information.

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.