Upgrade to Pro — share decks privately, control downloads, hide ads and more …

BLE AdvertiseはRxJavaで何かが変わるのか

Yoshihiro WADA
October 25, 2017
240

BLE AdvertiseはRxJavaで何かが変わるのか

2017/10/25 に開催された potatotips #44 にて発表した資料です。

Yoshihiro WADA

October 25, 2017
Tweet

More Decks by Yoshihiro WADA

Transcript

  1. 3 BLE とは BLE (Bluetooth Low Energy) Bluetooth 4.0 の規格の一部。

    超低電力でボタン電池で最大 1 年くらい駆動可能。
  2. 4 そのなかでも BLE Advertise 未接続の BLE デバイス (ペリフェラル) が スマホ等の接続可能なデバイス

    (セントラル) に対し 自身の存在を伝えるためにパケットの発信をする機能 BLE デバイス ワタシハココヨー
  3. 5 BLE Advertise BLE Advertise 時に最大 31byte のパケットを送信できる Advertising Packet

    この中に規格に合わせて Service UUID 等の情報を埋めていく iBeacon や EddyStone は Advertising Packet の規格
  4. 6 BLE Advertise を Android で受け取る BluetoothLeScanner クラスを使う private val

    scanner: BluetoothLeScanner private var scanCallback: ScanCallback? = null init { // Kotlinならrunとか使ってこんな風に書けそう scanner = context.run { val manager = getSystemService(Context.BLUETOOTH_SERVICE) as BluetoothManager return@run manager.adapter.bluetoothLeScanner } }
  5. 7 BLE Advertise を Android で受け取る Scan に対してオプションが設定できる ScanFilter :

    スキャン対象を Advertising Packet の中身で フィルタリングする ScanSetting : スキャン間隔や検出個数、 遅延スキャンや スキャン時の callback をどうトリガするなどを設定する startScan(ScanCallack, ScanFilter, ScanSetting) で Scan 開始 stopScan(ScanCallack) で Scan 停止
  6. 7 BLE Advertise を Android で受け取る Scan に対してオプションが設定できる ScanFilter :

    スキャン対象を Advertising Packet の中身で フィルタリングする ScanSetting : スキャン間隔や検出個数、 遅延スキャンや スキャン時の callback をどうトリガするなどを設定する startScan(ScanCallack, ScanFilter, ScanSetting) で Scan 開始 stopScan(ScanCallack) で Scan 停止
  7. 8 ScanCallback onScanResult(int callbackType, ScanResult result) 一台の BLE Advertise を

    Scan できた際に呼ばれる ScanFilter を設定していればそれにあったものが呼ばれる onScanFailed(int errorCode) BLE Advertise の Scan を実行できなかった際に呼ばれる すでに Scan を開始しているとか onBatchScanResults(List<ScanResult> results) 遅延 Scan を実行している際、 その遅延の間に Scan できた デバイスのリストが通知される
  8. 9 BLE Beacon デバイスで稀によくあること iBeacon の Major/Minor で Beacon の位置を定義してみたり

    EddyStone で URL を伝えるようにしてみたり Advertising Packet で何かしら伝えようとしてくる 独自規格でセンサの状態をパケットに設定してきたり
  9. 9 BLE Beacon デバイスで稀によくあること iBeacon の Major/Minor で Beacon の位置を定義してみたり

    EddyStone で URL を伝えるようにしてみたり Advertising Packet で何かしら伝えようとしてくる 独自規格でセンサの状態をパケットに設定してきたり いい感じに受け取りたい
  10. 10 RxJava を使ってみる filter を使って重複回避や本当に必要な分だけ取得したり map を使って Advertising Packet から欲しいデータを得たり

    Scan 中は拾えた BLE デバイスがひたすら通知される Observable に扱えれば得られたデータを Web API に流したり とかできそう Observable に扱えるはず
  11. 11 実際に実装してみた 取得された Advertise から iBeacon とわかるものをフィルタ フィルタされた Advertise から

    iBeacon の Major/Minor、 DeviceName だけを取り出して通知 呼び出し元に 「Scan した iBeacon のデータのみ」 を通知したい Observable.create で Scan 開始から callback までを包む Scan 停止時に disposable を破棄する
  12. Scan 開始を Observable.create で包む // 単純にObservableを返すメソッドに変更 fun start(): Observable<ScanResult> {

    return Observable.create { subscriber -> scanCallback = object: ScanCallback() { override fun onScanFailed(errorCode: Int) { subscriber.onError(Throwable(ERROR_MESSAGE)) } override fun onScanResult(callbackType: Int, result: ScanResult?) { super.onScanResult(callbackType, result) result ?: return subscriber.onNext(result) } // 他は省略 } scanner.startScan(scanCallback) } }
  13. iBeacon とわかるものをフィルタ fun check(result: ScanResult): Boolean { // 取得したScan結果からAdvertising Packetを取得

    val packet = result.scanRecord.bytes // iBeacon固定値の4byte目の0x02、5byte目の0x15があればtrue return packet[4] == 0x02.toByte() && packet[5] == 0x15.toByte() } scanDisposable = detector.start() .filter { check(it) } .map { transform(it) } .subscribeOn(Schedulers.io()) .observeOn(AndroidSchedulers.mainThread()) .subscribe({ // onNext Log.d("MainActivity", String(it.major)) }, { // onError Log.d("MainActivity", it.message) })
  14. 必要なデータだけを取得 fun transform(result: ScanResult): IBeaconValue { val name = result.device.name

    // デバイス名を取得 val packet = result.scanRecord.bytes val major = byteArrayOf(packet[22], packet[23]) val minor = byteArrayOf(packet[24], packet[25]) return IBeaconValue(name, major, minor) } scanDisposable = detector.start() .filter { check(it) } .map { transform(it) } .subscribeOn(Schedulers.io()) .observeOn(AndroidSchedulers.mainThread()) .subscribe({ // onNext Log.d("MainActivity", String(it.major)) }, { // onError Log.d("MainActivity", it.message) })
  15. Scan 開始と終了 // Scan開始 fun startScan() { scanDisposable = scanner.start()

    .filter { check(it) } .map { transform(it) } .subscribeOn(Schedulers.io()) .observeOn(AndroidSchedulers.mainThread()) .subscribe({ // onNext Log.d("MainActivity", String(it.major)) }, { // onError Log.d("MainActivity", it.message) }) } // Scan終了 fun stopScan() { scanner.stop() // スキャン自体の終了 scanDisposable?.dispose() // 明示的にDisposeさせる }
  16. 16 まとめ Scan 自体は Callback 地獄と言う訳ではないので嬉しさは…? おそらく RxJava で書けて嬉しいのは接続した後の処理から BLE

    Advertise Scan は RxJava でそれっぽく書ける気がする BLE Advertise だけなら闇はほぼないのでお気軽に試せる ただ iBeacon の発信機は微妙に高い (製品だと 4000 円くらい) Android 5.0 以降なら Advertise の発信もできる いわゆる 「すれ違い」 アプリみたいなものはパパっとできる