BLEとAWS IoTでクラウドと連携するボタンをつくる

## 今日中に間に合うか！？

この記事は出張中の京都で講演を聴きながら書かれています。
BLE Advent Calenderの今日の当番なので、頑張って書きます｡ﾟ(ﾟ´Д｀ﾟ)ﾟ｡

→すいません、投稿遅れましたm(_ _)m

## IoTボタンのブループリント

https://aws.amazon.com/jp/iot/button/

![button](https://d0.awsstatic.com/IoT/Starter%20Kits/AWS_IoT_button.jpg)

これ！がつくりたいんですが、Wi-Fi使っていると思うので、それだと直接クラウドにつながっちゃってBLE使わない！ってことで、せっかくなのでBLEを使って作成します。

ちなみにざっくりイカのような構成でつくってみました。これは実はTechCrunch Tokyo 2015 CTO Nightで使われたプレゼンボタンです。

![blueprint](http://kazuph.github.io/presentation/TechCrunchTokyoCTONight2015/images/iot_button_description.png)

ボタンを押すと、BLEでスマフォに通信が行って、MQTTでAWS経由で自分の手元のPCまでPub/Subでメッセージが来て、最後にWebSocketでつながっているプレゼンツールが次のページに行ったら成功です！


## まずボタン(ハード)をつくる

ボタンの構成はこれです。

- ポップンみたいなボタンパーツ（既成品）
- 見栄えを良くするための筐体（Fusion 360で設計→3Dプリンタで製造）

ボタンを買って、

https://www.sengoku.co.jp/mod/sgk_cart/detail.php?code=EEHD-4LG4
![button](https://i.gyazo.com/75ec5fe191ebe39f73413922f039f36c.png)

3DCADで設計して、

![cad](https://dl.dropboxusercontent.com/u/6615196/images/iot_button_cad1.JPG)

3Dプリンターで出力します！

![3d](https://dl.dropboxusercontent.com/u/6615196/images/iot_button_3d_print.jpg)

これで一旦ハードウェアの完成です！

## 基板作成

EagleやQuadceptを使って作成。回路図・アートワークはまた今度。

要素的には

BLEのチップ
⇅ (ファームウェアで制御)
GPIO(Input|Interrupt)
⇅ (電気的なHIGH/LOWを受け取る)
ボタン

ができる構成。

## ファーム

ソースを紹介したいけど、NDA(´・ω・｀)
今後Nordicのチップを使うことでオープンにしていきたいと思っています。

仮想言語で書くとこんな感じ

```
void interrupt() {
    notification(characteristic_hander, 1);
}
```

割り込みピンで入力があったときに呼ばれるコールバックの中に、キャラクタリスティックの通知を送信する関数を呼ぶだけです。

## BLE

- BLEのネットワーク構造はスター型
    - IoTボタン側をペリフェラルとする
    - スマートフォンがセントラル
- Notification
    - セントラルは基本的には任意のタイミングでペリフェラルにインベントを起こせる/Write
    - ペリフェラルからもセントラルが通知を購読していると、ペリフェラルからのpushがリアルタイム受け取れる（セントラルがペリフェラルをポーリング/readしなくていい）
- GATTの設計
    - Notificationができるキャラクタリスティックが一つだけ

## iOS

CoreBluetoothを使ってゴニョゴニョします。Swiftを使ったCoreBluetoothの使い方については @shu223 のブログに詳しいのでそちらを見て下さい！

[Core Bluetooth with Swift （ObjCのおまけ付き） - Qiita](http://qiita.com/shu223/items/78614325ce25bf7f4379)

```objc:スキャン
import CoreBluetooth
...
    self.centralManager.scanForPeripheralsWithServices(services, options: nil)
...
func centralManager(central: CBCentralManager,
     didDiscoverPeripheral peripheral: CBPeripheral,
     advertisementData: [String : AnyObject],
     RSSI: NSNumber){
     self.peripheral = peripheral
}
```

```objc:接続
...
    self.centralManager.connectPeripheral(self.peripheral, options: nil)
...
func centralManager(central: CBCentralManager,
     didConnectPeripheral peripheral: CBPeripheral) {
}
```

```objc:サービスとキャラクタリスティックの探索
    let services : [CBUUID]? = [self.serviceUUID]
    self.peripheral.delegate = self
    self.peripheral.discoverServices(services)
...
func peripheral(peripheral: CBPeripheral,
     didDiscoverCharacteristicsForService service: CBService,
     error: NSError?){
    let characteristics = service.characteristics!
    self.characteristic = characteristics[0]
}
```

```objc:Notificationを受け取りたいキャラクタリスティックの登録
self.peripheral.setNotifyValue(true, forCharacteristic: self.characteristic)
```

```objc:Notificationが届いたときに受け取る
func peripheral(peripheral: CBPeripheral,
     didUpdateValueForCharacteristic characteristic: CBCharacteristic,
     error: NSError?) {
    print("Update! characteristic UUID: \(characteristic.UUID), value: \(characteristic.value)")
}
```

ほい、だいぶ端折りましたが、これでペリフェラルのキャラクタリスティックからのNotificationを受け取れるようになりました！

## iOS→AWS IoT

iOSがBLE経由で受け取った通知をそのままAWS IoTを使ってPublishします。

プロトコルはMQTTです。

```objc:iOSでMQTTにPublishする
import Moscapsule

moscapsule_init()

let mqttConfig = MQTTConfig(clientId: "iot_button", 
           host: "AAAAAAAAAAAAAA.iot.ap-northeast-1.amazonaws.com", port: 8883, keepAlive: 60)
let certFile = NSBundle.mainBundle().pathForResource("1111111111-certificate.pem", ofType: "crt")
let keyFile = NSBundle.mainBundle().pathForResource("2222222222-private.pem", ofType: "key")
let caFile = NSBundle.mainBundle().pathForResource("ca", ofType: "pem")

mqttConfig.mqttServerCert = MQTTServerCert(cafile: caFile, capath: nil)
mqttConfig.mqttClientCert = MQTTClientCert(certfile: certFile!, keyfile: keyFile!, keyfile_passwd: nil)

self.mqttClient = MQTT.newConnection(mqttConfig)
self.mqttClient!.publish(data, topic: "topic/hoge", qos: 1, retain: false)
```

## AWS IoT→Mac

Nodeで書きました！

```js:MQTTを購読してメッセージを受け取る
// aws iotでMQTTの購読
var awsIot = require('aws-iot-device-sdk');
var device = awsIot.device({
    keyPath:  '1111111111-private.pem.key',
    certPath: '2222222222-certificate.pem.crt',
    caPath:   'ca.pem',
    clientId: 'iot_button_sub',
    region: 'ap-northeast-1'
});
// 接続できたら購読する
device.on('connect', function() {
    device.subscribe('topic/hoge');
});
// メッセージを受け取る
device.on('message', function(topic, payload) {
    ...
});
```

## Mac→スライドツール

スライドツールはJS製のものなので、色々ハックできて便利です。

```js:サーバー側
// websocketでブラウザと接続
var io = require('socket.io').listen(8080)
var socket;
io.sockets.on('connection', function(_socket) {
    socket = _socket;
});

// MQTTのメッセージが届いたらsocketでメッセージを送る
socket.emit('click', 'next page');
```

```js:クライアント(ブラウザ)側
var socket = io.connect("http://localhost:8080");
socket.on('connect', function() {
});
socket.on('click', function(data) {　　
    talkie.next.push();
});
```

これだけ！簡単ですね！

## デモ

![gif](https://dl.dropboxusercontent.com/u/6615196/images/iot_button.gif)

## まとめ

今回はBLEを使ったIoTボタンを作成してみました！Wi-Fiでもやってみたいですが、電池消費を気にすると接続しっぱなしってのは辛いと思うので、リアルタイム性って意味だとBLEで実装してスマフォやIoT Hubでやり取りする方がこういう用途には向くと思いました。

BLEは触っているけどサーバー側までは…って人の参考になれば幸いです☆