Raspberry Pi zero whのBLEとAndroidアプリ(react-native)で通信する

Raspberry Pi zero whのBLEモジュールとAndroidアプリ(react-native)を繋げるのに少し苦労したので備忘録として書きます。

# 環境
ここでは、Raspberry Pi zero wh と Android(react-native) の間でBLEを使った通信をすることを想定しています。

なお、Raspberry Pi側のプログラムはgolangで書きます。

# 準備
## BLE開発に必要な概念

これだけはまず知っておいた方がいい、という基礎知識です。

BLE概論系の記事には色々と難しい言葉が並んでいますが、まず覚える必要があるのは次の2つだけです。


- サーバー(ペリフェラル)
- クライアント(セントラル)

### サーバー
サーバー(ペリフェラル)は、デバイス側を表します。つまりここではRaspberry Pi側です。

### クライアント
クライアント(セントラル)はここではAndroid側です。

サーバーはRaspberry Pi、クライアントはAndroid。これだけ覚えておきましょう。

## BLEのアプリケーションを識別するのに必要な値

BLEモジュールと通信するために、そのBLEモジュールを使っているアプリケーションを識別する値が必要になります。これが以下の二つです。


- MACアドレス
- UUID (service UUIDとcharacteristic UUIDの2種)

ここで、MACアドレスはBLEモジュールに固有の値で、 `hciconfig` などで確認することができます。
2種のUUIDはアプリケーション側で設定できる値で、任意の値を使うことができます。


# MACアドレスの確認

ではまずBLEモジュールのMACアドレスを確認します。Raspberry Piで以下のコマンドを実行してください。

```
pi@raspberrypi:~ $ sudo hciconfig
hci0:        Type: Primary  Bus: UART
        BD Address: B8:26:EB:BA:3C:11  ACL MTU: 1021:8  SCO MTU: 64:1
        UP RUNNING PSCAN
        RX bytes:15221 acl:0 sco:0 events:530 errors:0
        TX bytes:17899 acl:18 sco:0 commands:722 errors:0

pi@raspberrypi:~ $
```

ここでのMACアドレスは`B8:26:EB:BA:3C:11`ですね。メモしておきましょう。


# golangでBLEサーバーの作成
まずはただ動いて欲しいので、github.com/paypal/gattというライブラリのサンプルをそのまま使います。 https://github.com/paypal/gatt

https://github.com/paypal/gatt/blob/master/examples/server_lnx.go

こいつをビルドしてRaspberry Pi上で実行しましょう。(`sudo` を忘れないように)

```
pi@raspberrypi:~ $ sudo ./gatt_server_lnx
2018/04/07 09:48:34 dev: hci0 up
2018/04/07 09:48:34 dev: hci0 reset
2018/04/07 09:48:34 dev: hci0 down
2018/04/07 09:48:34 dev: hci0 opened
State: PoweredOn
2018/04/07 09:48:34 BD Addr: B8:26:EB:BA:3C:11
2018/04/07 09:48:34 Generating attribute table:
2018/04/07 09:48:34 handle	type	props	secure	pvt	value
2018/04/07 09:48:34 0x0001	0x2800	0x02	0x00	*gatt.Service	[ 00 18 ]
2018/04/07 09:48:34 0x0002	0x2803	0x02	0x00	*gatt.Characteristic	[ 02 03 00 00 2A ]
2018/04/07 09:48:34 0x0003	0x2a00	0x02	0x00	*gatt.Characteristic	[ 47 6F 70 68 65 72 ]
2018/04/07 09:48:34 0x0004	0x2803	0x02	0x00	*gatt.Characteristic	[ 02 05 00 01 2A ]
2018/04/07 09:48:34 0x0005	0x2a01	0x02	0x00	*gatt.Characteristic	[ 00 80 ]
2018/04/07 09:48:34 0x0006	0x2803	0x02	0x00	*gatt.Characteristic	[ 02 07 00 02 2A ]
2018/04/07 09:48:34 0x0007	0x2a02	0x02	0x00	*gatt.Characteristic	[ 00 ]
2018/04/07 09:48:34 0x0008	0x2803	0x02	0x00	*gatt.Characteristic	[ 02 09 00 03 2A ]
2018/04/07 09:48:34 0x0009	0x2a03	0x02	0x00	*gatt.Characteristic	[ 00 00 00 00 00 00 ]
2018/04/07 09:48:34 0x000A	0x2803	0x02	0x00	*gatt.Characteristic	[ 02 0B 00 04 2A ]
2018/04/07 09:48:34 0x000B	0x2a04	0x02	0x00	*gatt.Characteristic	[ 06 00 06 00 00 00 D0 07 ]
2018/04/07 09:48:34 Generating attribute table:
2018/04/07 09:48:34 handle	type	props	secure	pvt	value
2018/04/07 09:48:34 0x0001	0x2800	0x02	0x00	*gatt.Service	[ 00 18 ]
2018/04/07 09:48:34 0x0002	0x2803	0x02	0x00	*gatt.Characteristic	[ 02 03 00 00 2A ]
...略
```

# react-native側
react-native側は、 https://github.com/innoveit/react-native-ble-manager というライブラリを使って通信します。

コード片で申し訳ないのですが、こんな感じのコードを書きます。

```
  startScan() {
    if (!this.state.scanning) {
      this.setState({peripherals: new Map()});
      BleManager.scan([], 3, true).then((results) => {
        console.log('Scanning...');
        this.setState({scanning: true});
        console.log(results)
      });
    }

    BleManager.getConnectedPeripherals([]).then((results) => {
      console.log(results);
      var peripherals = this.state.peripherals;
      for (var i = 0; i < results.length; i++) {
        var peripheral = results[i];
        peripheral.connected = true;
        peripherals.set(peripheral.id, peripheral);
        console.log(peripheral)
        this.setState({ peripherals });
      }
    });

    BleManager.connect("B8:26:EB:BA:3C:11").then(() => {
      console.log('Connected');
    }).catch((error) => {
      // Failure code
      console.log(error);
    })

    BleManager.retrieveServices("B8:26:EB:BA:3C:11").then((peripheralInfo) => {
      // Success code
      console.log('Peripheral info:', peripheralInfo);
    });

    const data = stringToBytes("piyo");
    console.log(data)
    BleManager.write("B8:26:EB:BA:3C:11", "09fc95c0-c111-11e3-9904-0002a5d5c51b", "16fe0d80-c111-11e3-b8c8-0002a5d5c51b", data).then(() => {
      // Success code
      console.log('Write: ' + data);
    }).catch((error) => {
      // Failure code
      console.log(error);
    });
  }
```

ここで大事な点が2点あります。まず1つ目は、 **read, write, notifyの前には必ずBleManager.retrieveServices()を実行しなくてはいけない** ことです。 
普通にドキュメントに書いてあるのですが、忘れがちみたいです。
https://github.com/innoveit/react-native-ble-manager/issues/259

次に、2つのUUIDについてです。これらはpaypal/gattのexampleに書いてあります。

まず、serviceUUIDは https://github.com/paypal/gatt/blob/4ae819d591cfc94c496c45deb55928469542beec/examples/service/count.go#L13 です。

そして、characteristicUUIDは https://github.com/paypal/gatt/blob/4ae819d591cfc94c496c45deb55928469542beec/examples/service/count.go#L20 です。


雑に書きましたがこんな感じです。