GoでBLEセンサーデータをBeamする

# はじめに

SORACOM Advent Calendar 2015 の 15 日目の記事です。

# やりたいこと
BLE センサーデータを AWS IoT に Publish します。
これを Go 言語でやってみます。
Publish するときは、SORACOM Beam を使って、暗号化処理をオフロードします。
Go で実装するのは最近 Go を触り始めて、勉強がてら何か書いてみようと思ったからです。
Mac で動作確認をしています。
最近は、IoT 関連の技術情報はネットに溢れているので、調べてすぐ分かるところは本記事では適宜省略します。
いろいろごった煮にしてしまったので、重要な前提が欠けていたりしたらすみません。

# AWS IoT のセットアップ
（手順だけ整理します！）

1. Thing を作成します。
2. 証明書の作成・ダウンロードします。(忘れずに！)
3. Policy を作成します。
4. 証明書に Thing と Polich をアタッチします。

# SORACOM Beam のセットアップ
（Beam の設定は User Console だけでできます！）

1. AWS IoT のエンドポイントを取得します。
2. [CA 証明書](https://www.symantec.com/content/en/us/enterprise/verisign/roots/VeriSign-Class%203-Public-Primary-Certification-Authority-G5.pem) をダウンロードします。
3. SORACOM User Console でグループを追加し、そのグループに対して SORACOM Beam 設定を追加します。
4. 使用する SIM を 2. で設定したグループに所属させます。

# Go で BLE センサーデータを取得する

今回はセンサーに　[SensorTag CC2650STK](http://www.tij.co.jp/tool/jp/CC2650STK?DCMP=edn0915&HQS=ep-wcs-lprf-edn0915-bs-text-evm-cc2650stk-jp) を使用します。

ちょっと時間が足りなかったので、今回はボタンを押したらその都度通知させる、というところまでしかできませんでした。


Go でセンサーデータを取得するために、[paypal/gatt](https://github.com/paypal/gatt) を使用します。

```
% go get github.com/paypal/gatt
```

examples/explorer.go 参考に、SensorTag からデータを取得する処理を書いてみます。

BLEセンサーからデータを取得するには、以下の情報が必要になります。

* Peripheral ID
* Service の UUID
* Charactaristic の UUID

Peripheral ID は examples/discover.go を実行することで、確認することができます。

Service の UUID と Charactaristic の UUID は examples/explorer.go を実行したりドキュメントを確認すれば分かるはずなんですが、少し楽をするために、[sandeepmistry/node-sensortag](https://github.com/sandeepmistry/node-sensortag) を参考にしました。

node-sensortag の lib/common.js に SIMPLE_KEY_UUID と、SIMPLE_KEY_DATA_UUID という定数が定義されていて、この値が Sensortag のボタンを押した時に通知してもらうために必要な、UUID になります。

というわけで、センサーからデータを取得する部分のコードはこんな感じになりました。

``` app.go
package main

import (
	"fmt"
	"strings"

	"github.com/paypal/gatt"
)

var message = make(chan string)
var done = make(chan struct{})

func main() {
	d, err := gatt.NewDevice()
	if err != nil {
		fmt.Println(err)
		return
	}
	
	go publish(message)

	d.Handle(
		gatt.PeripheralDiscovered(onPeriphDiscovered),
		gatt.PeripheralConnected(onPeriphConnected),
		gatt.PeripheralDisconnected(onPeriphDisconnected),
	)

	d.Init(onStateChanged)
	<-done
	fmt.Println("Exit")
}



func onStateChanged(d gatt.Device, s gatt.State) {
	switch s {
	case gatt.StatePoweredOn:
		fmt.Println("Scanning...")
		d.Scan([]gatt.UUID{}, false)
		return
	default:
		d.StopScanning()
	}
}

func onPeriphDiscovered(p gatt.Peripheral, a *gatt.Advertisement, rssi int) {
	id := strings.ToUpper(<Peripheral ID>)
	if strings.ToUpper(p.ID()) != id {
		return
	}

	p.Device().StopScanning()

	p.Device().Connect(p)
}

func onPeriphConnected(p gatt.Peripheral, err error) {
	fmt.Println("connected")
	sUUID := gatt.MustParseUUID("ffe0")
	ss, err := p.DiscoverServices([]gatt.UUID{sUUID})
	if err != nil {
		return
	}
	
	cUUID := gatt.MustParseUUID("ffe1")
	cc, err := p.DiscoverCharacteristics([]gatt.UUID{cUUID}, ss[0])
	if err != nil {
		return
	}
	
	p.SetNotifyValue(cc[0], func(c *gatt.Characteristic, b []byte, err error) {
		switch b[0] {
		case 0x1:
			message<-"left"
		case 0x2:
			message<-"right"
		case 0x3:
			message<-"gobeam"
		}
	})
}

func onPeriphDisconnected(p gatt.Peripheral, err error) {
	fmt.Println("Disconnected")
	close(done)
}
```

Scan を開始するといろんな Peripheral が見つかりますので、onPeriphDiscovered 関数では、見つかった Peripheral の中から Sensortag に一致するものだけに接続しています。
この辺、コマンドライン引数で渡せるようにしたり、アドレスや名前で検索できるようにした方が便利ですね。
Peripheral に繋がったら、onPeriphConnected 関数で Service と Characteristic を取得し、Peripheral に対して SetNotifyValue しています。
ボタンが押されると、右のボタンか、左のボタンか、あるいは両方かで取得できる値が変わります。

次に、AWS IoT に Publish する部分を書いてみます。

``` publisher.go
package main

import (
	"fmt"
	
	MQTT "git.eclipse.org/gitroot/paho/org.eclipse.paho.mqtt.golang.git"
)

const uri = "tcp://beam.soracom.io:1883"

func publish(message chan string) {
	opts := MQTT.NewClientOptions().AddBroker(uri)
	opts.SetClientID("clientid2")
	
	c := MQTT.NewClient(opts)
	if token := c.Connect(); token.Wait() && token.Error() != nil {
    	panic(token.Error())
    }
	defer c.Disconnect(250)
	
	for ;; {
		select {
		case text := <-message:
			token := c.Publish("gobeam", 0, false, text)
			token.Wait()
			fmt.Printf("published: %s\n", text)
		}
	}
}

```

Paho MQTT client library 使用しました。
肝は、ホストに SORACOM Beam を指定することと、メインの処理からチャネルを経由してメッセージを受け取るところです。

# 実行してみる

私は gobeam というパッケージを作ったので、ビルドすると gobeam というコマンドができあがりました。
ちゃんと Mac が SORACOM Air を使ってインターネットに接続できていることを確認してから

```
% ./gobeam
```

とやって実行すると、"Scanning..." と表示されて Sensortag を探し始めます。
見つかると、"connected" と表示されるので、ボタンを押しまくります。
がその前にちゃんと Publish されているか確認するために、mosuquitto_sub コマンドを実行しておきます。

```
% mosquitto_sub -h beam.soracom.io -p 1883 -q 1 -d -t gobeam -i clientid1
```

そうすると、ボタンの右か左か両方かで Subscribe される内容が変わるかと思います。

# このあとやりたいこと

時間切れになってしまったので今回はできませんでしたが、

* SensorTag 以外のセンサーからデータを取得する。
* IoT ゲートウェイに配置してみて、Beam を使う/使わないで比較
* 多言語で実装してみて比較

などやってみたいなと思います。