Posted at

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

More than 3 years have passed since last update.


準備


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

Android で GATT 通信アプリをスクラッチから作るのは大変そうなので、参考になりそうなのを探しました。

まずは、Android SDK 本家のサンプルを見てみたんですが、複雑で characteristics の write がないため断念。

こちらのサイトがわかりやすかったので、こちらを参考にさせていただきました。

AndroidのBLEでCharacteristicsのRead/Writeサンプルを作ってみた


UUIDの生成

BLE の GATT 通信をするためには、サービスやキャラクタリスティックというものが必要となります。

このあたりの詳しいことは、こちらのサイトにまとまっていますので、こちらをご覧ください。

BLE のサービスやキャラクタリスティックには、その種類を識別するための UUID が定義されています。

よく見かける 16bit の UUID は Bluetooth の SIG で規定されているものであり、独自のサービスを作る場合は 128bit の UUID を使うことになるようです。

作りたい BLE Nano と Android 間の通信は、独自のデータのやり取りなので、独自のサービスとして定義していきたいと思います。

そこで、Online UUID Generatorのサイトを利用して 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 設定

以前の記事で作成したコードの UUID の指定を変更します。

変更前


main.cpp

uint16_t customServiceUUID  = 0xA000;

uint16_t readCharUUID = 0xA001;
uint16_t writeCharUUID = 0xA002;

変更後


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サンプルを作ってみた で、github に公開してくださっているコードをベースに改造していきます。改造ポイントはこちらになります。


  1. UUID の追加

  2. データ通信ボタンを追加

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

  4. GATT 通信切断時の処理の追加

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

  6. 受信ボタンの制御の追加


UUID の追加

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


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

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

BNENanoLED_OFF.PNG