Android で GATT 通信アプリを作ってみる

# 準備
## Android の BLE GATT 通信サンプル・コードの入手

Android で GATT 通信アプリをスクラッチから作るのは大変そうなので、参考になりそうなのを探しました。
まずは、[Android SDK 本家のサンプル](http://developer.android.com/guide/topics/connectivity/bluetooth-le.html)を見てみたんですが、複雑で characteristics の write がないため断念。
こちらのサイトがわかりやすかったので、こちらを参考にさせていただきました。

[AndroidのBLEでCharacteristicsのRead/Writeサンプルを作ってみた](http://greety.sakura.ne.jp/redo/2013/11/androidblecharacteristicsreadwrite.html)

## UUIDの生成

BLE の GATT 通信をするためには、サービスやキャラクタリスティックというものが必要となります。
このあたりの詳しいことは、こちらのサイトにまとまっていますので、こちらをご覧ください。

- [BLE, GATT概要](http://blue-crystal.squares.net/doc/ble/gatt.html)
- [【BLEを使う】GATT（Generic Attribute Profile）概要](http://yegang.hatenablog.com/entry/2014/08/09/195246)

BLE のサービスやキャラクタリスティックには、その種類を識別するための UUID が定義されています。
よく見かける 16bit の UUID は Bluetooth の SIG で規定されているものであり、独自のサービスを作る場合は 128bit の UUID を使うことになるようです。

作りたい BLE Nano と Android 間の通信は、独自のデータのやり取りなので、独自のサービスとして定義していきたいと思います。
そこで、[Online UUID Generator](https://www.uuidgenerator.net/)のサイトを利用して 128bit の UUID をランダム生成しました。

|種類                 |UUID|
|---------------------|----|
|Custom Service|ff1ecf59-513e-4aa6-9db0-490a63ebb1ac|
|read characteristics | e3274236-89fa-4ac6-bd1c-4fe77ecdd331|
|write characteristics |d0dbb034-75a1-4cc4-8b59-1bce1ba6adc5|

# BLE Nano 側の service / characteristics 設定

[以前の記事](http://qiita.com/maki02/items/86e4b9348755a6d5bb41)で作成したコードの UUID の指定を変更します。

変更前

```cpp:main.cpp
uint16_t customServiceUUID  = 0xA000;
uint16_t readCharUUID       = 0xA001;
uint16_t writeCharUUID      = 0xA002;
```

変更後

```cpp:main.cpp
static uint8_t customServiceUUID[16] = {0xff, 0x1e, 0xcf, 0x59, 0x51, 0x3e, 0x4a, 0xa6, 0x9d, 0xb0, 0x49, 0x0a, 0x63, 0xeb, 0xb1, 0xac};
static uint8_t readCharUUID[16] = {0xe3, 0x27, 0x42, 0x36, 0x89, 0xfa, 0x4a, 0xc6, 0xbd, 0x1c, 0x4f, 0xe7, 0x7e, 0xcd, 0xd3, 0x31};
static uint8_t writeCharUUID[16] = {0xd0, 0xdb, 0xb0, 0x34, 0x75, 0xa1, 0x4c, 0xc4, 0x8b, 0x59, 0x1b, 0xce, 0x1b, 0xa6, 0xad, 0xc5};
```

コンパイルしてバイナリを BLE Nano に書き込んだら BLE Nano 側は完了です。

# Android GATT 通信サンプル・コードの改造

[AndroidのBLEでCharacteristicsのRead/Writeサンプルを作ってみた](http://greety.sakura.ne.jp/redo/2013/11/androidblecharacteristicsreadwrite.html) で、github に公開してくださっているコードをベースに改造していきます。改造ポイントはこちらになります。

1. UUID の追加
2. データ通信ボタンを追加
3. 独自サービス発見時の処理の追加
4. GATT 通信切断時の処理の追加
5. LED ON/OFF ボタンの制御の追加
6. 受信ボタンの制御の追加

## UUID の追加

BleUuid.java で UUID の定義をしているので、BLE Nano と Android 間の通信間に生成した UUID を追加していきます。

```java:BleUuid.java
	// BLE Nano Custom Service
	public static final String SERVICE_BLENANO_CUSTOM = "ff1ecf59-513e-4aa6-9db0-490a63ebb1ac";
	public static final String CHAR_CUSTOM_SERVICE_READ = "e3274236-89fa-4ac6-bd1c-4fe77ecdd331";
	public static final String CHAR_CUSTOM_SERVICE_WRITE = "d0dbb034-75a1-4cc4-8b59-1bce1ba6adc5";
```

## データ通信ボタンの追加

BLE Nano との通信を行なうためのボタンを、app/res/layout/activity_device.xml に追加します。

```activity_device.xml
    <ToggleButton
        android:id="@+id/blenano_led_onoff_button"
        android:layout_width="match_parent"
        android:layout_height="wrap_content" />

    <Button
        android:id="@+id/blenano_read"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="@string/ble_nano_read" />
```

LED の ON/OFF を制御する ToggleButton と、BLE Nano から返ってくる characteristics を受信する Button をそれぞれ追加しています。

表示する文字列は res/values/strings.xml で定義しています。

```strings.xml
    <string name="ble_nano_read">BLE Nano Read</string>
```

さらに、追加したボタンのためのコードを DeviceActivity.java に追加しておきます。

```DeviceActivity.java
public class DeviceActivity extends Activity implements View.OnClickListener {
	private ToggleButton mWriteBleNanoLedOnOffButton;
	private Button mReadBleNanoButton;
:
	protected void onCreate(Bundle savedInstanceState) {
: 
		mWriteBleNanoLedOnOffButton = (ToggleButton) findViewById(R.id.blenano_led_onoff_button);
		mWriteBleNanoLedOnOffButton.setOnClickListener(this);
		mReadBleNanoButton = (Button) findViewById(R.id.blenano_read);
		mReadBleNanoButton.setOnClickListener(this);
	}
	private void init() {
:
		mWriteBleNanoLedOnOffButton.setEnabled(false);
		mReadBleNanoButton.setEnabled(false);
:
	}
```

追加したボタンを制御するための変数の定義と初期化コードになります。

## 独自サービス発見時の処理の追加

DeviceActivity.java の onServicesDiscovered() に、独自サービスを発見したときの処理を追加します。

```DeviceActivity.java
public void onServicesDiscovered(BluetoothGatt gatt, int status) {
	:
	if (BleUuid.SERVICE_BLENANO_CUSTOM.equalsIgnoreCase(service.getUuid().toString())) {
		runOnUiThread(new Runnable() {
			public void run() {
				mWriteBleNanoLedOnOffButton.setEnabled(true);
				mReadBleNanoButton.setEnabled(true);
			};
		});
		mWriteBleNanoLedOnOffButton.setTag(service.getCharacteristic(UUID.fromString(BleUuid.CHAR_CUSTOM_SERVICE_WRITE)));
		mReadBleNanoButton.setTag(service.getCharacteristic(UUID.fromString(BleUuid.CHAR_CUSTOM_SERVICE_READ)));
	}
	:
}
```

setEnabled(true) で追加したボタンを Enable にし、setTag を使って独自サービスの Characteristic をボタンに対応付けています。

## GATT 通信切断時の処理を追加

GATT 通信の状態が変化したときに実行する onConnectionStateChange というコールバックがサンプルの中で定義されています。
ここで、GATT 通信が切断されたときに各ボタンを Disable しているので、追加したボタンの分も追加します。

```DeviceActivity.java
private final BluetoothGattCallback mGattcallback = new BluetoothGattCallback() {
	:
	mWriteBleNanoLedOnOffButton.setEnabled(false);
	mReadBleNanoButton.setEnabled(false);
	:
}
```

## LED ON/OFF ボタンの制御の追加


ボタンが押されたときに呼ばれる onClick 関数に、LED ON/OFF の Toggle Button の処理を追加します。
Toggle Button が checked の状態の場合はLEDを点灯させるための値 `0x00` を、そうでない場合は消灯させるための値 `0x01` を data に格納します。
そして、タグ付けしておいた Characteristic を使って、data を BNE Nano に送信します。

```DeviceActivity.java
public void onClick(View v) {
	:
	} else if (v.getId() == R.id.blenano_led_onoff_button) {
		byte[] data = (mWriteBleNanoLedOnOffButton.isChecked())? new byte[] {(byte) 0x00} : new byte[] {(byte) 0x01};
		if ((v.getTag() != null) && (v.getTag() instanceof BluetoothGattCharacteristic)) {
			BluetoothGattCharacteristic ch = (BluetoothGattCharacteristic) v.getTag();
			ch.setValue(data);
			if (mConnGatt.writeCharacteristic(ch)) {
				setProgressBarIndeterminateVisibility(true);
			}
		}
	}
}
```


## 受信ボタンの制御の追加

BLE Nano に信号を送信すると、BLE Nano は送信した値をそのまま返してきます。
blenano_read ボタンで、BLE Nano から返ってきた値を取得して表示します。

```DeviceActivity.java
public void onClick(View v) {
:
	} else if (v.getId() == R.id.blenano_read) {
		if ((v.getTag() != null) && (v.getTag() instanceof BluetoothGattCharacteristic)) {
			BluetoothGattCharacteristic ch = (BluetoothGattCharacteristic) v.getTag();
			if (mConnGatt.readCharacteristic(ch)) {
				setProgressBarIndeterminateVisibility(true);
			}
		}
	}
}	
public void onCharacteristicRead(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status) {
	if (status == BluetoothGatt.GATT_SUCCESS) {
		if (BleUuid.CHAR_MANUFACTURER_NAME_STRING.equalsIgnoreCase(characteristic.getUuid().toString())) {
			 :
		} else if (BleUuid.CHAR_CUSTOM_SERVICE_READ.equalsIgnoreCase(characteristic.getUuid().toString())) {
			final byte[] val = characteristic.getValue();
			runOnUiThread(new Runnable() {
				public void run() {
				ReadBleNanoButton.setText(getString(R.string.ble_nano_read)+":"+val[0]);
				setProgressBarIndeterminateVisibility(false);
			};
		});
	}
}
```

受信ボタンのクリックを検知すると、タグ付けしておいた Characteristic を使って BLE Nano からデータを読み出します。
そして、読み出しが完了すると実行される onCharacteristicRead 関数で、読み出したデータ val を受信ボタンに表示していきます。

# 動作例

LED ON/OFF ボタンを ON にして、受信ボタンをクリックしたところ。LEDが点灯し、受信ボタンに 0 が表示されています。

![BNENanoLED_ON.PNG](https://qiita-image-store.s3.amazonaws.com/0/129898/0eae7ef5-3092-3bdf-010e-0cf07c70c9f3.png)


LED ON/OFF ボタンを OFF にして、受信ボタンをクリックしたところ。LEDが消灯し、受信ボタンに 1 が表示されています。

![BNENanoLED_OFF.PNG](https://qiita-image-store.s3.amazonaws.com/0/129898/ce71459b-4a2b-46f8-6238-872ca63975c9.png)