BLEを使った宝さがしアプリ
BLE(Bluetooth Low Energy)のRSSI(受信信号強度)を使った簡単な宝さがしアプリを作りました。
作るもの
宝さがしを開始すると、BLEのRSSIからスマホとBLE端末の間の近さを推定して画面上に表示します。
単にそれだけです。
材料
- Android端末
- SimpleLink SensorTag (TI)
SimpleLink SensorTag
BLE端末として、TIのSimpleLink SensorTagを使用します。
CC2650STK SimpleLink SensorTag | TIJ.co.jp
SensorTagには温度、湿度、加速度など10種類ほどのセンサーが搭載されておりIoTっぽいことを試すには非常に便利です。 また、ファームウェアが公開されているので、別途、有償のデバッガを購入すれば改造したファームを書き込むことも可能です。
今回は電波強度を使った宝さがしなので、センサー類は一切使いません。
Androidアプリ
Android 6.0用です。
ソースコードはこちら。
権限
AndroidでBLEを使用するには権限が必要です。AndroidManifest.xmlに以下を追加します。
<uses-permission android:name="android.permission.BLUETOOTH"/> <uses-permission android:name="android.permission.BLUETOOTH_ADMIN"/> <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION"/> <uses-feature android:name="android.hardware.bluetooth_le" android:required="true"/>
Android 6.0以降はACCESS_COARSE_LOCATION
も必要になったようです。
初回アプリ起動時にアクセス権を求めるダイアログを表示するためにrequestPermissions
を使います。
public class MainActivity extends AppCompatActivity { private static final int REQUEST_CODE = 1; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); // 位置情報へのアクセス許可を求めるダイアログ表示 requestPermissions(new String[]{Manifest.permission.ACCESS_COARSE_LOCATION}, REQUEST_CODE); Button startSearch = (Button)findViewById(R.id.start_search); startSearch.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { Intent intent = new Intent(getApplicationContext(), SearchActivity.class); startActivity(intent); } }); } }
スキャンと接続
BLE端末がアドバタイズし続けてくれれば、アプリとしてはスキャンでRSSIを取得するだけです。しかし、SensorTagは未接続状態で120sec経過するとアドバタイズをやめてしまいます。
http://processors.wiki.ti.com/index.php/CC2650_SensorTag_User’s_Guide#When_disconnected
そこで、スキャンでSensorTagを検出したら、即時接続して、BluetoothGatt
のreadRemoteRssi()
メソッドでRSSI値を取得します。
public class SearchActivity extends AppCompatActivity { private static final long READ_RSSI_PERIOD = 1000; // msec private BluetoothAdapter mAdapter; private BluetoothGatt mBluetoothGatt; private TextView mCloseness; private TextView mRssiText; private Handler mHandler = new Handler(); private BluetoothGattCallback mBluetoothGattCallback = new BluetoothGattCallback() { @Override public void onConnectionStateChange(BluetoothGatt gatt, int status, int newState) { ... } @Override public void onReadRemoteRssi(BluetoothGatt gatt, int rssi, int status) { if (status == BluetoothGatt.GATT_SUCCESS) { setClosenessText(rssi); } } }; private BluetoothAdapter.LeScanCallback mScanCallback = new BluetoothAdapter.LeScanCallback() { @Override public void onLeScan(BluetoothDevice device, int rssi, byte[] scanRecord) { // SensorTag以外のBLE端末は無視 if (device.getName() == null || !device.getName().contains("SensorTag")) { return; } if (mBluetoothGatt == null) { // SensorTagは120sec経過するとアドバタイズをやめてしまうので接続しておく mBluetoothGatt = device.connectGatt(getApplicationContext(), false, mBluetoothGattCallback); // READ_RSSI_PERIOD(=1sec)間隔でRSSIを取得する mHandler.postDelayed(new Runnable() { @Override public void run() { if (mBluetoothGatt == null) { return; } mBluetoothGatt.readRemoteRssi(); mHandler.postDelayed(this, READ_RSSI_PERIOD); } }, READ_RSSI_PERIOD); } } }; @Override protected void onResume() { // 画面表示時のみスキャン実行 super.onResume(); mAdapter.startLeScan(mScanCallback); } // 省略 }
近さの判定
計算でBLE端末との推定距離を求めることも可能なようですが、今回はどれくらい近いかがわかればよいだけなので、単純にRSSIの値から近さを4段階に分けます。
RSSIのレンジは実際に動作させてみてそれっぽく決定しました。
おわりに
非常に単純な宝さがしアプリを作ってみました。
今は、スキャンして最初に見つかったSensorTagの近さしか表示できませんが、
- 複数のSensorTagを同時に扱えるようにする
- SensorTagのセンサー値(温度とか光とか)を宝さがしのヒントとしてつかう
とかできるようになると、もっと面白いかもしれません。