Raspberry Pi 3とGenuino (Arduino) 101でBLE通信する

Raspberry Pi 3とGenuino 101を手に入れたのでガーッとBLE通信します。
下記については詳しい説明は書きません。

* Raspberry Pi 3について
* Node.jsについて
* nobleについて
* Genuino 101について
* BLE通信について

また、Raspberry Pi 3からGenuino 101に対して接続するときに、アドバタイズパケットに含まれる情報ではなく、MACアドレスを直接記述して接続します。
一応、伏せ字にしましたので、xxxxxxxxxxxxという記述があったら、お手元のGenuino 101のMACアドレスと差し替えて試してください。

# Genuino 101側

Genuino 101について、下記を参考にさせていただきました。ありがとうございました。

* [Genuino (Arduino) 101 で BLE 経由でLチカさせる - Qiita](http://qiita.com/hotchpotch/items/79bca9ec00e969a0c44a "Genuino (Arduino) 101 で BLE 経由でLチカさせる - Qiita")
* [さっそくGenuino101を触ってみた。 セットアップからNode.jsでLチカまで。 - Qiita](http://qiita.com/n0bisuke/items/a6a024cb8dad6b209d04 "さっそくGenuino101を触ってみた。 セットアップからNode.jsでLチカまで。 - Qiita")

キャラクタリスティックなどの設計からしなければ！と思っていたけど、スケッチの例が充実していたので、`スケッチの例 > CurieBLE > ButtonLED`をそのまま使用することにしました。

![display201603292104.png](https://qiita-image-store.s3.amazonaws.com/0/118889/cddf1c7f-86e8-388f-2329-15ab6524cbf3.png)

スケッチが想定しているであろう下記のような回路を作成しました。

![写真 2016-03-29 22 04 43 のコピー.jpg](https://qiita-image-store.s3.amazonaws.com/0/118889/3f20ccc3-f921-8ed8-de86-7db432f9d3be.jpeg)

セントラルからの接続がない状態の動作としては、スイッチが押されている間はLEDが点灯し、押されていない間は点灯しません。
スイッチにはプルダウン抵抗を加え、スイッチが押されている間はHIGH、押されていない間はLOWとなるようにします。

# Raspberry Pi 3側

Node.jsのnobleライブラリを使用して、Genuino 101からの通知を受け取ります。

* [sandeepmistry/noble: A Node.js BLE (Bluetooth Low Energy) central module](https://github.com/sandeepmistry/noble "sandeepmistry/noble: A Node.js BLE (Bluetooth Low Energy) central module")

## アドバタイズパケットの確認

nobleのリポジトリに含まれている[サンプルコード advertisement-discovery.js](https://github.com/sandeepmistry/noble/blob/master/examples/advertisement-discovery.js)を使用して、Genuino 101が出してるアドバタイズパケットを確認します。

```
peripheral discovered (xxxxxxxxxxxx with address <xx:xx:xx:xx:xx:xx, public>, connectable true, RSSI -68:
	hello my local name is:
		ButtonLE
	can I interest you in any of the following advertised services:
		["19b10010e8f2537e4f6cd104768a1214"]
```

上記の出力の中に、サンプルスケッチの中の`blePeripheral.setLocalName("ButtonLED");`で指定されているローカルネームと、`BLEService ledService("19B10010-E8F2-537E-4F6C-D104768A1214");`で指定されているUUIDがハイフンなしで含まれています。

## サービスの構成を確認

nobleのリポジトリに含まれている[サンプルコード peripheral-explorer.js](https://github.com/sandeepmistry/noble/blob/master/examples/peripheral-explorer.js)を使用して、サンプルスケッチが定義しているledServiceの構成を確認します。

実行時に下記のように前述のアドバタイズパケットの受信時の出力に含まれているMACアドレスをコロンなしで指定します。

```
$ sudo node peripheral-explorer.js xxxxxxxxxxxx
```

すると、サンプルスケッチで定義されているCharacteristicのプロパティや現在の値などが出力されます。

```
peripheral with ID xxxxxxxxxxxx found
  Local Name        = ButtonLE
  Service Data      = 
  Service UUIDs     = 19b10010e8f2537e4f6cd104768a1214

services and characteristics:
1800 (Generic Access)
  2a00 (Device Name)
    properties  read
    value       47454e55494e4f203130312d38323841 | 'GENUINO 101-828A'
  2a01 (Appearance)
    properties  read
    value       0000 | ''
  2a04 (Peripheral Preferred Connection Parameters)
    properties  read
    value       4000780000005802 | '@xX'
1801 (Generic Attribute)
19b10010e8f2537e4f6cd104768a1214
  19b10011e8f2537e4f6cd104768a1214
    properties  read, write
    value       00 | ''
  19b10012e8f2537e4f6cd104768a1214
    properties  read, notify
    value       00 | ''
```

## スイッチの状態を受信

確認したServiceの構成に合わせて、ボタンの状態をRaspberry Pi 3で受信します。

```js
var noble = require('noble');

var ledServiceUuid = '19b10010e8f2537e4f6cd104768a1214';
var ledCharacteristicUuid = '19b10011e8f2537e4f6cd104768a1214';
var buttonCharacteristicUuid = '19b10012e8f2537e4f6cd104768a1214';

var peripheralIdOrAddress = process.argv[2].toLowerCase();

// Bluetoothデバイスの状態変更時の処理
noble.on('stateChange', function(state) {
  if (state === 'poweredOn') {
    // ペリフェラルのスキャンを開始
    noble.startScanning();
  } else {
    noble.stopScanning();
  }
});

// ペリフェラル発見時の処理
noble.on('discover', function(peripheral) {
  if (peripheral.id === peripheralIdOrAddress || peripheral.address === peripheralIdOrAddress) {
    // ペリフェラルのスキャンを停止
    noble.stopScanning();
    console.log('peripheral with ID ' + peripheral.id + ' found');

    peripheral.once('disconnect', function() {
      console.log('disconnected');
      process.exit(0);
    });

    // ペリフェラルとの接続を開始
    peripheral.connect(function(error) {
      console.log('connected');
      // サービス探索を開始
      peripheral.discoverServices([], onServicesDiscovered);
    });
  }
});

// サービス発見時の処理
function onServicesDiscovered(error, services) {
  console.log('services discovered');

  services.forEach(function(service) {
    if (service.uuid == ledServiceUuid) {
      // キャラクタリスティック探索を開始
      service.discoverCharacteristics([], onCharacteristicDiscovered);
    }
  });
}

// キャラクタリスティック発見時の処理
function onCharacteristicDiscovered(error, characteristics) {
  console.log('characteristics discovered');

  characteristics.forEach(function(characteristic) {
    if (characteristic.uuid == ledCharacteristicUuid) {
      characteristic.on('read', onLedCharacteristicRead);
    } else if (characteristic.uuid == buttonCharacteristicUuid) {
      characteristic.on('read', onButtonCharacteristicRead);

      // データ更新通知の受け取りを開始
      characteristic.notify(true, function(error) {
        console.log('buttonCharacteristic notification on');
      });
    }
  });
}

// ledCharacteristicのデータ読み出し時の処理
// (現在のこのコードでは呼ばれることがない想定です)
function onLedCharacteristicRead(data, isNotification) {
  console.log('ledCharacteristic read response value: ', data.readInt8(0));
}

// buttonCharacteristicのデータ読み出し時、もしくは更新時の処理
function onButtonCharacteristicRead(data, isNotification) {
  if (isNotification) {
    // データ更新通知による呼び出し
    console.log('buttonCharacteristic notification value: ', data.readInt8(0));
  } else {
    // データ読み出し時の呼び出し
    console.log('buttonCharacteristic read response value: ', data.readInt8(0));
  }
}
```

上記コードを`button_led_central.js"として保存して、下記のように実行します。

```
sudo node button_led_central.js xxxxxxxxxxxx
```

接続後、ボタンの押下に合わせて値が出力されます。

```
peripheral with ID xxxxxxxxxxxx found
connected
services discovered
characteristics discovered
buttonCharacteristic notification on
buttonCharacteristic notification value:  1
buttonCharacteristic notification value:  0
buttonCharacteristic notification value:  1
buttonCharacteristic notification value:  0
buttonCharacteristic notification value:  1
buttonCharacteristic notification value:  0
buttonCharacteristic notification value:  1
buttonCharacteristic notification value:  0
buttonCharacteristic notification value:  1
buttonCharacteristic notification value:  0
```

# 最後に

Raspberry Pi 3側でもLEDを光らせたりしたかったけど夜も深まってきたのであきらめました…。
Genuino 101のスケッチ書き込みのときの具合が微妙なこと以外は、安定して動作しているようなので、しばらく遊べそうです。