BLE初心者へ、AndroidのBLE機能を実装する上で知っておくと良いこと

## BLE実装の全体の流れは、以下のとおりです
1. `スキャン`を開始する
1. `アドバタイズ`しているBLE機器を発見する
1. `ボンディング`または`ペアリング`を行う
1. `Gatt`を介してBLE機器に`コネクト`する
1. `Gatt`を介して通信する
1. `サービス`を`ディスカバー`する
1. `キャラスタリスティック`を取得する
1. `コマンド`を送信する
1. `通知`を有効にする
1. 受け取ったデータで何かする

BLEの実装が初めての人は、何のことがわからないですよね。
以下を流し読めば、最後には処理の流れが理解できるようになっていると思います！
それでは、BLEの実装について説明していきます。

## BLE
- 「Bluetooth Low Energy」のこと
- アプリ開発のBluetoothには、BLEの他に「Bluetooth Classic」がある

## Gatt（BluetoothGatt）
- スマートフォンと、BLE機器を接続するための、スマートフォン側がBLE機器に対して作る道のこと
- スマートフォンは、Gattを介して、BLE機器と通信をする
- 現在接続中のBLE情報を持たせることができるし、どのような通信をするかをGattを介して命令することができる

  - 例えば自動接続機能を使用して、接続が切れた後に再接続を試みることや、特定の条件下で通信を行うように設定することができる


:::note info
##### 個人的に思っていること
接続状態や通信が不安定になる原因となることが多いので、クラス間でGattを共有しない方が良いです。
また、できればクラスフィールドにgattを定義するのもやめた方がバグが起きづらいと思っています。
:::

## (Gattの）status
- Gattは接続状態を管理しており、その状態はsutatuにて判断できる
- スマートフォンとBLE機器の接続が確立したら、gattのstatusは「`2`」となる
- Bluetoothの電波が不安定で切断された場合は、gattのstatusは「`133`」となる

  - 電波不安定と上記に書いたが、原因はそれだけじゃなく、「`133`」は一般的なBluetoothエラーで、原因が多岐にわたるため詳細なエラー原因の把握が難しい （私は、BLE機器とスマートフォンの距離が離れた時に`133`をよく見たので、`133`=`電波不安定`と判断していた という感じです。）

- スマートフォンで何かイレギュラーがあってスマートフォン側から切断された時は、gattのstatusは「`22`」となる

- タイムアウトによって接続が切れたらgattのstatusは「`8`」となる

:::note info
実装する上で、このステータスは問題解決に大変役立つので、BLE接続でうまくいかないことがあればログに出して確認すると良きです。
:::

:::note info
##### 個人的に思っていること
close.gatt()で、gattを閉じるのを忘れないようにしたいです。
BLE機器との再接続機能を実装する時は、今まで使用していたgattを使い回すのではなく、
一度、gatt.closeをして、再度新しくgattを作り直すのがバグが少ないと思っています。
私は、この`status`をチェックして、接続が復帰しなかった場合は、一度 `gatt.close`をして、再度、`gatt.connect`から再開するような実装にしました。
(最適解は、仕様によりますが！）
:::

## ペアリング
- スマートフォンとBLE機器が初めて通信するときに、一時的にデバイス同士を認証して接続すること

## ボンディング
- ペアリング後にスマートフォンとBLE機器の間で信頼関係を作り、次回以降の接続時にもう一度ペアリングしなくても接続できるようにすること

   - ボンディングをすると、デバイス同士が覚え合うイメージ

- 「もし、近くにいるBLE機器が、ボンディング済みならコネクトをする。」というような実装をよくやる

:::note info
ボンディングは必須ではなく、BLE機器の仕様に応じて必要かどうかが決まります。
:::


## コネクト
- スマートフォンと、BLE機器が通信をすること
- コネクトすることで、スマートフォンがBLE機器に対してデータの読み書きを行ったり、コマンドを送ることができる
- コネクトすることで、BLE機器からもスマートフォンにデータを送信できる


## アドバタイズ
- BLE機器が、まだ接続相手が見つかっていない場合に、発している電波のこと
- アドバタイズしているBLE機器に対して、スマートフォンが接続しにいく
- アドバタイズの電波の中には、BLE機器が持っている識別子が含まれる


## スキャン
- スマートフォン側からBLE機器を発見するための機能のこと
   - bluetoothAdapterクラスを使って実行される

- スキャンを開始すると、周囲で「私を見つけて〜」とアドバタイズしているBLE機器を探し、その結果を取得できる
- プログラム的には、`startScan()`で、スキャンを開始し、`stopScan()`で、スキャンを停止するまで、近くにいるアドバタイズしているBLE機器を探しまくる

## ビーコン
- BLE機器が定期的に一方向で発信する信号で、近接通知などに使用される
- スマートフォンがビーコンの信号を受信するためには通常のBLEスキャン（`startScan()`）を行う必要がない場合があり、これはOSレベルやアプリの設定で自動的に検出されるからである


## RSSI
- スマートフォンがキャッチする、BLE機器の電波の強さのこと
- BLE機器と、スマートフォンがどれくらい近くにいるかが分かる
```
-30 dBm: 非常に強い信号。デバイスは非常に近くにある（ほぼ隣接している）
-50 dBm: 強い信号。デバイスは近くにある（数メートル以内）
-60 dBm: 良好な信号。デバイスは近くにある（数メートル以内）
-70 dBm: 中程度の信号。デバイスは10メートル程度以内にある
-80 dBm: 弱い信号。デバイスは遠くにある（20メートル程度）
-90 dBm以下: 非常に弱い信号。デバイスはかなり遠くにある
```

## サービス
- サービスはBLE機器の特定の機能や役割のこと
- BLE機器は複数のサービスを持つことができて、それぞれのサービスにはUUID（UUIDとは他と重複しない識別子のこと）が割り当てられている
- このUUIDを使用して、スマートフォン側から特定のサービスにアクセスできる
- BLE機器に接続した後、`discoverServices()`メソッドを使用して、サービスディスカバーをすることで、BLE機器が提供しているすべてのサービスを取得することができる
- 例えば、心拍数を測定するBLEデバイスは、心拍数の値を読み取るために特定のサービスUUIDを持っている
- 例えば、音楽を再生できるBLEデバイスは、音声再生をするために特定のサービスUUIDを持っている
 
:::note info
BLEの実装をする時、クライアント様や上司から最初に渡される情報は、多分BLE機器の設計書だと思いますが、
BLE機器の仕様書を渡されたらこのサービスのUUIDは必ずプログラムに使用するものだと認識しておくと良き。
例えば、心拍数や温度測定などの機能がサービスとして定義され、そのUUIDを通じて操作やデータの取得が行われます。
:::

## キャラスタリスティック（Characteristic）
- BLE機器の特性やデータ項目を表すもの
- 各キャラスタリスティックには一意のUUIDが割り当てられている
- キャラスタリスティックは、ある特定のサービスプロファイルに属しており、スマートフォンがBLE機器と接続後、そのサービスからキャラスタリスティックにアクセスして、データの読み書きや通知の設定などを行う
- サービスは複数のキャラスタリスティック（特性）を持っていて、そのサービスに関連するデータ項目やアクションを提供する

:::note 
##### サービスとキャラスタリスティックの違い

<dl>
  <dt>サービスとは</dt>
  <dd>「心拍数測定サービス（Heart Rate Service）」は心拍数データの送信機能全体を表す</dd>
  <dt>キャラスタリスティックとは</dt>
  <dd>サービスの中に「心拍数データキャラスタリスティック（Heart Rate Measurement Characteristic）」があり、実際の心拍数データがここから読み取れる</dd>
  <dt>つまり...</dt>
   <dd>サービスプロファイルは大枠の機能で、キャラスタリスティックはその具体的なデータ項目や動作を表すってこと</dd>
</dl>
:::

##### サービスとキャラスタリスティックらへんの、処理の流れ（kotlinです）
```kotlin
if (gattStatus == 2) { // gatt接続成功時の処理
    // サービスのUUIDを使って、接続したデバイスの特定のサービスを取得
    val service = gatt.getService(SERVICE_UUID)
    
    // サービスが存在する場合、そのサービスが持っているキャラスタリスティックを取得
    service?.getCharacteristic(CHARACTERISTIC_UUID)?.let { characteristic ->
        
        // キャラスタリスティックの通知を有効化
        gatt.setCharacteristicNotification(characteristic, true)
        
        // キャラスタリスティックに対応する通知用のディスクリプタを取得し、通知を有効化
        characteristic.getDescriptor(DESCRIPTOR_NOTIFICATION_UUID)?.let { descriptor ->
            
            // 通知を有効にするために、ディスクリプタの値を設定
            descriptor.value = BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE
            
            // 設定したディスクリプタの値を書き込み、通知設定を確定
            gatt.writeDescriptor(descriptor)
        }
    }
}

```

:::note
##### ディスクリプタの値を設定とは
上記プログラムにある、「通知を有効にする」とは、BLE機器が持っているキャラスタリスティックの値が変化した際に、その変更をスマートフォン側に自動的に通知する機能を有効にすることです。
これにより、スマートフォンは定期的に自分からデータを取りにいく必要がなく、データの変化があるたびに通知を受け取ることができます。
:::

## BLE実装の全体の流れ
まず、必要な情報が揃っているかを確認したいです。こんな感じ👇

上司に、BLE実装を頼まれる
↓
「BLEの仕様書をください」とお願いする
↓
仕様書にBLEの`ServiceのUUID`が記載されているか確認する
↓
仕様書にBLEの`キャラスタリスティックのUUID`が記載されているか確認する
↓
仕様書にBLEが受け付ける`コマンド`の詳細が記載されているか確認する

ざっくり、ここら辺を確認したら実装を開始できるかなと思います。
では、冒頭にも書きましたが、BLE実装の全体の流れをおさらいです。


1. `スキャン`を開始する
1. `アドバタイズ`しているBLE機器を発見する
1. `ボンディング`または`ペアリング`を行う
1. `Gatt`を介してBLE機器に`コネクト`する
1. `Gatt`を介して通信する
1. `サービス`を`ディスカバー`する
1. `キャラスタリスティック`を取得する
1. `コマンド`を送信する
1. `通知`を有効にする
1. 受け取ったデータで何かする

## おわり
私が、BLE機能を実装したときに覚えたことです。
最初は、BLE特有の言葉のイメージが難しく、壁にぶちあたりました。
BLE初心者のBLE機能実装の助けになれたら嬉しいです。
間違った情報があったら、すみません🙇‍♀️ ご指摘いただけると嬉しいです。