2014年6月24日火曜日

AndroidでiBeacon(2)

前回「AndroidでiBeacon(1)」の続きで、今回は検出したBeaconから情報を取得します
取得するのは

  • UUID
  • major
  • minor
  • 電波強度

の4つです

端末を検出したらコールバックされるBluetoothAdapter.LeScanCallbackのonLeScanを変更していきます

onLeScanには3つの引数があり、UUIDなどの情報は第3引数のbyte配列に入っています

その配列は次のようになっています

データ構造


01ブロック目のバイト数
1
フラグ
2
32ブロック目のバイト数
4AD Type
5
会社ID
0x00CがAPPLE
6
7データタイプ0x02がiBeacon
8iBeaconデータのバイト数
9
UUID
16バイト
~
24
25
major
4バイト
26
27
minor
4バイト
28
29電波強度距離計算する際の基準値として利用

データを取得

onLeScanの中身を変更しています


    private BluetoothAdapter.LeScanCallback mLeScanCallback = new BluetoothAdapter.LeScanCallback() {
        @Override
        public void onLeScan(final BluetoothDevice device, int rssi, byte[] scanRecord) {
            if (scanRecord.length > 30) {
                //このif文でiBeaconかどうかを判別
                if ((scanRecord[5] == (byte) 0x4c) && (scanRecord[6] == (byte) 0x00)
                        && (scanRecord[7] == (byte) 0x02) && (scanRecord[8] == (byte) 0x15)) {
                    String uuid = getScanData(9, 24, scanRecord);
                    String major = getScanData(25, 26, scanRecord);
                    String minor = getScanData(27, 28, scanRecord);
                    String strength = String.valueOf(scanRecord[29]);
                    Log.d("BeaconSample", "-----------------------------");
                    Log.d("BeaconSample", "uuid::" + uuid);
                    Log.d("BeaconSample", "major::" + major);
                    Log.d("BeaconSample", "minor::" + minor);
                    Log.d("BeaconSample", "strength::" + strength);
                    Log.d("BeaconSample", "rssi::" + rssi);
                }
            }
        }
    };

    public String getScanData(int start, int end, byte[] scanRecord) {
        StringBuilder result = new StringBuilder(end - start);
        for (int i = start; i <= end; i++) {
            result.append(convertHex(scanRecord[i] & 0xff));
        }
        return result.toString();
    }

    public String convertHex(int i) {
        char hexArray[] = {
                Character.forDigit((i >> 4) & 0x0f, 16), Character.forDigit(i & 0x0f, 16)
        };
        return new String(hexArray).toUpperCase();
    }

データの取得は以上です

配列の中にある電波強度は基準値なので変化しません
実際の電波強度は第2引数のrssiになります



次回は、Androidで使えるライブラリを探してライブラリを利用方法などを紹介したいと思います

2014年6月23日月曜日

AndroidでiBeacon(1)

Beaconを貸して頂いたので、これら何回かに分けてAndroidでiBeaconを触ってみる記事を書いてみようと思います

iBeaconについては、いろいろと情報があると思いますので説明は省きます
iBeaconって何?という方は、まずGoogleなどで検索してみてください

必要なもの


BLEに対応したAndroid 4.3以上の端末
Beacon(今回はBeacon USBを利用)

Beaconの設定


最初にBeaconの設定をしておきます
設定する情報は、Proximity UUID(以降、UUIDと略),Major, Minorとありますが、どの情報を使って検出したいBeaconを判別するかによりますので、1台だけならUUIDだけでも良いかもしれませんし、1台でもMajorまで設定しておいても良いかもしれません

今回は、UUID,Major,Minorを全て設定しておきます

※今回利用するBeaconUSBに専用のアプリがまだ無いので、HPに記載されている設定マニュアルにあるとおり「BLE Utility」というアプリを使って設定します。このアプリはiOS用なのでAndroid端末しかない場合は、別途BLがで利用できる設定アプリを探してください



Androidアプリを作る


前提条件


BluetoothはONになっていることとする(ONにしたり、利用可否のチェックは省く)

参照


コードは、Androidの公式情報を基にして書いていきます
http://developer.android.com/guide/topics/connectivity/bluetooth-le.html



Permission


BLUETOOTHを利用するための権限を設定します。

<uses-permission android:name="android.permission.BLUETOOTH"/>

アプリの中でBluetoothのON/OFFなど操作を行う場合にはBLUETOOTH_ADMINの権限も必要となります BLUETOOTHの操作も行いたい場合は、以下の2つを設定してください
※あとで検証したら、BLUETOOTH_ADMINを指定しないと端末が検出できないことが判りました。BLUETOOTH_ADMINは必須のようです

<uses-permission android:name="android.permission.BLUETOOTH"/>
<uses-permission android:name="android.permission.BLUETOOTH_ADMIN"/>

BLEに対応した端末だけにインストールを許可したい場合には、以下のuses-featureを設定してください

<uses-feature android:name="android.hardware.bluetooth_le" android:required="true"/>

BluetoothAdapterを取得する

BluetoothAdapterはBluetoothManagerから取得します
    private BluetoothAdapter mBluetoothAdapter;
public class MainActivity extends Activity {

    private BluetoothAdapter mBluetoothAdapter;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

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

}

BLE端末を検出する

BLEを検出する処理を追加します
検出は、BluetoothAdapterのstartLeScanで実行されます
検出が終了するとstartLeScanの引数で指定したコールバックのメソッドが呼び出されます

BLEの検出は何度も繰り返し行われるため、ハードウェア的に問題となるため1秒たったらスキャンを停止する(stopLeScan)ようになっています
※コールバック用のメソッドは後述
    @Override
    protected void onResume() {
        super.onResume();
        scanLeDevice(true);
    }

    private void scanLeDevice(final boolean enable) {
        if (enable) {
            mHandler.postDelayed(new Runnable() {
                @Override
                public void run() {
                    mScanning = false;
                    mBluetoothAdapter.stopLeScan(mLeScanCallback);
                }
            }, SCAN_PERIOD);

            mScanning = true;
            mBluetoothAdapter.startLeScan(mLeScanCallback);
        } else {
            mScanning = false;
            mBluetoothAdapter.stopLeScan(mLeScanCallback);
        }
    }



BLEの検出結果を処理する


BLEの検出が終わるとstartLeScanで指定したコールバックのメソッドが呼び出されますので、その中で検出結果を解析します
今回はデバイスの名前とアドレスをログに出力してみます

    private BluetoothAdapter.LeScanCallback mLeScanCallback = new BluetoothAdapter.LeScanCallback() {
        @Override
        public void onLeScan(final BluetoothDevice device, int rssi, byte[] scanRecord) {
            Log.d("BeaconSample", "name::" + device.getName());
            Log.d("BeaconSample", "address::" + device.getAddress());
            Log.d("BeaconSample", "------------------------------");
        }
    };

スキャンの停止


 アプリが停止しているときは、スキャンを停止するようにしましょう
    @Override
    protected void onPause() {
        super.onPause();
        scanLeDevice(false);
    }


今回はここまで。
次回は、結果からUUIDなどの情報を取得して、本当に情報を取得したい機器の詳細情報を見てみたいと思います


※もう少し詳しくコードを知りたい方は、Android SDKの中にあるsamplesに参考となるコードがありますので、そちらを参照ください
samples/android-18/legacy/BluetoothLeGatt/src/com/example/bluetooth/le