golang+MQTTでAWS IoTにPubslish

[Go2 Advent Calender](https://qiita.com/advent-calendar/2017/go2 "Go2 Advent Calender") 16日目です。

以下の記事を見てからMQTTで遊ぶのがブームになっています。

http://monoist.atmarkit.co.jp/mn/series/3293/

Windows10とmosquittoでローカルで試してみて最終的にAWS IoTに
Publishしてみました。<br>


##普通にPublish
まずはpahoライブラリをgo getしてローカルでPublishしてみます。

```go
package main

import (
	"fmt"
	MQTT "github.com/eclipse/paho.mqtt.golang"
)

func main () {

	const MQTT_BROKER = "tcp://127.0.0.1:1883"
	opts := MQTT.NewClientOptions().AddBroker(MQTT_BROKER)
	opts.SetClientID("localhost")
	client := MQTT.NewClient(opts)
	if token := client.Connect(); token.Wait() && token.Error() != nil {
		fmt.Println("Error %s\n", token.Error())
	}

	token := client.Publish("test", 0, false, "{\"message\":\"hello\"}")
	token.Wait()

	client.Disconnect(250)

}
```

![2017-12-15_23h34_47.png](https://qiita-image-store.s3.amazonaws.com/0/146663/851247c6-882e-4522-5f4b-37ec0cbb7f3a.png)


実行→送信できました。  

##TLSでPublish
AWS IoTにデータを送信するにはTLS接続が必要です。
ローカルでTLS接続ができるか試してみます。
以下を参考にオレオレ証明書を作成してmosquitto.confの末尾にセットしました。

https://mosquitto.org/man/mosquitto-tls-7.html


```nginx
listener 8883
cafile certs/ca.crt
certfile certs/server.crt
keyfile certs/server.key
```

go getしたpahoのライブラリの中にSSLのサンプルがあります。
keyファイルをセットするところを自分で作成したものに置き換えました。

```go
package main

import (
	"crypto/tls"
	"crypto/x509"
	"fmt"
	"io/ioutil"

	MQTT "github.com/eclipse/paho.mqtt.golang"
)

func NewTLSConfig() *tls.Config {
	certpool := x509.NewCertPool()
	pemCerts, err := ioutil.ReadFile("ca.crt")
	if err == nil {
		certpool.AppendCertsFromPEM(pemCerts)
	}

	cert, err := tls.LoadX509KeyPair("server.crt", "server.key")
	if err != nil {
		panic(err)
	}

	cert.Leaf, err = x509.ParseCertificate(cert.Certificate[0])
	if err != nil {
		panic(err)
	}

	return &tls.Config{
		RootCAs: certpool,
		ClientAuth: tls.NoClientCert,
		ClientCAs: nil,
		InsecureSkipVerify: true,
		Certificates: []tls.Certificate{cert},
	}
}

var f MQTT.MessageHandler = func(client MQTT.Client, msg MQTT.Message) {
	fmt.Printf("TOPIC: %s\n", msg.Topic())
	fmt.Printf("MSG: %s\n", msg.Payload())
}

func main() {
	tlsconfig := NewTLSConfig()

	opts := MQTT.NewClientOptions()
	opts.AddBroker("ssl://127.0.0.1:8883")
	opts.SetClientID("ssl-sample").SetTLSConfig(tlsconfig)
	opts.SetDefaultPublishHandler(f)

	c := MQTT.NewClient(opts)
	if token := c.Connect(); token.Wait() && token.Error() != nil {
		panic(token.Error())
	}

	c.Publish("/go-mqtt/sample", 0, false, "Test Message")
	c.Disconnect(250)
}
```

![2017-12-16_00h08_46.png](https://qiita-image-store.s3.amazonaws.com/0/146663/9ff9bdc9-981a-ae00-fed4-59143edfe5f6.png)

実行→送信できました。

##AWS IoTにPublish
それではAWS IoTにPublishしてみます。<br>
モノや証明書の作成とアタッチ、ポリシーの作成などの準備は公式docや他の方の記事を参照してください。
ローカルでTLS接続したファイルから証明書をセットするところをAWSから落としてきたファイルに差し替えて実行します。<br>

```go
package main

import (
	"crypto/tls"
	"crypto/x509"
	"fmt"
	"io/ioutil"

	MQTT "github.com/eclipse/paho.mqtt.golang"
)

func NewTLSConfig() *tls.Config {
	// Import trusted certificates from CAfile.pem.
	// Alternatively, manually add CA certificates to
	// default openssl CA bundle.
	certpool := x509.NewCertPool()
	pemCerts, err := ioutil.ReadFile("xxxxxxxxx.pem")
	if err == nil {
		certpool.AppendCertsFromPEM(pemCerts)
	}

	// Import client certificate/key pair
	cert, err := tls.LoadX509KeyPair("xxxxxxxxx-certificate.pem.crt", "xxxxxxxxx-private.pem.key")
	if err != nil {
		panic(err)
	}

	// Just to print out the client certificate..
	cert.Leaf, err = x509.ParseCertificate(cert.Certificate[0])
	if err != nil {
		panic(err)
	}
	//fmt.Println(cert.Leaf)

	// Create tls.Config with desired tls properties
	return &tls.Config{
		// RootCAs = certs used to verify server cert.
		RootCAs: certpool,
		// ClientAuth = whether to request cert from server.
		// Since the server is set up for SSL, this happens
		// anyways.
		ClientAuth: tls.NoClientCert,
		// ClientCAs = certs used to validate client cert.
		ClientCAs: nil,
		// InsecureSkipVerify = verify that cert contents
		// match server. IP matches what is in cert etc.
		InsecureSkipVerify: true,
		// Certificates = list of certs client sends to server.
		Certificates: []tls.Certificate{cert},
	}
}

var f MQTT.MessageHandler = func(client MQTT.Client, msg MQTT.Message) {
	fmt.Printf("TOPIC: %s\n", msg.Topic())
	fmt.Printf("MSG: %s\n", msg.Payload())
}

func main() {
	tlsconfig := NewTLSConfig()

	opts := MQTT.NewClientOptions()
	opts.AddBroker("ssl://xxxxxxxxxxxxxxxxxxx.amazonaws.com:8883")
	opts.SetClientID("ssl-sample").SetTLSConfig(tlsconfig)
	opts.SetDefaultPublishHandler(f)

	// Start the connection
	c := MQTT.NewClient(opts)
	if token := c.Connect(); token.Wait() && token.Error() != nil {
		panic(token.Error())
	}
	fmt.Println("AWS IoT Connect")

	if token := c.Publish("$aws/things/ESP32/shadow/update", 0, false,
		`{"state":{"reported":{"welcome":"I am gopher!!!"}}}`); token.Wait() && token.Error() != nil {
				panic(token.Error())
	}
	fmt.Println("Message Publish")
	c.Disconnect(250)
}
```

![2017-12-16_20h13_56.png](https://qiita-image-store.s3.amazonaws.com/0/146663/1eae2375-c12e-ff0c-bd16-88788b3765a5.png)


![1216.PNG](https://qiita-image-store.s3.amazonaws.com/0/146663/2390d5b0-c9b1-9bce-7ff3-1bab7c71497e.png)


AWS IoTにPublishしてシャドウステータスを更新できました。
これでラズパイからgolangでAWS IoTにいろいろとデータを飛ばせますね。


飛ばすところまでやれってところですが。。。