Android
BLE
BLEnano

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

More than 1 year has 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