[AWS IoT] mosquitto clientを使ってAWS IoT Coreに接続する

# はじめに
クライアントのデバイスからAWS IoT Coreに接続する場合、通常AWSで用意されているはIoT SDKを使用することが多いと思います。今回は、SDKは使わずに、Open SourceのMQTT brokerであるmosquittoのクライアントを使ってAWS IoT Coreに接続する方法についてまとめます。MQTTにはいくつかVersionがあって、AWS IoT Coreでは現在のところ 3.1.1までしかサポートされていません。一方でmosquitto側はVersion 5とVersion 3.1.1の両方をサポートしているため、今回はVersion 3.1.1を使って接続する、ということをやってみたいと思います。

# mosquittoのインストール
何らかの環境をデバイス用に用意します。今回はAWS Cloud9の環境を用意し、そこにmosquittoをインストールします。mosquittoの[本家のページ](https://mosquitto.org/download/)からソースコードをダウンロードしてきてCloud9上に展開します。

```
 $ wget https://mosquitto.org/files/source/mosquitto-2.0.11.tar.gz
 $ tar xzvf mosquitto-2.0.11.tar.gz
 $ cd mosquitto-2.0.11.tar.gz
 $ make
```
とすると、

```
In file included from mosquitto_ctrl.c:19:0:
../../config.h:86:12: fatal error: cjson/cJSON.h: No such file or directory
 #  include <cjson/cJSON.h>
```
というようなエラーが出ます。CJSONを入れないといけないようですが、面倒なので

```
make WITH_CJSON=no
```
としてビルドします。もろもろビルドされます。mosquitto自体はMQTTのブローカーなので、ブローカー機能がビルドされますが、今回はAWS IoT Core側で提供されるMQTTブローカーにMQTT Clientとして接続するだけなので、client ディレクトリにあるクライアントのテストプログラムだけを用います。ビルドが成功すると、client以下に、mosquitto_pub, mosquitto_sub, mosquitto_rrというプログラムが生成されています。ソースコードもあり、libmosquittoを利用してMQTT接続するようなプログラムになっています。libmosquittoの使い方は[こちら](https://mosquitto.org/api/files/mosquitto-h.html)が参考になります。

#AWS IoT Coreのモノ作成
次にAWS IoT Coreからモノを作成します。AWSのマネジメントコンソールから、管理＞モノを選択し、モノを作成します。
![スクリーンショット 2021-07-04 16.01.23.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/80028/9c7a11fb-ee17-6d68-3f5d-e568d90e397f.png)
モノに名前をつけ、モノに証明書を追加します。
![スクリーンショット 2021-07-04 16.04.28.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/80028/42f9a1cb-a3f5-e853-e6ef-3b1feecde82a.png)
証明書が作成されるのでダウンロードしておきます。また忘れずに有効化します。
![スクリーンショット 2021-07-04 16.06.28.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/80028/cc43075d-bc51-5517-e167-fd5d36481b52.png)
その後、Policyをアタッチしてモノを登録します。

# mosquittoからの接続
ダウンロードした証明書を適当ななフォルダー(ここではclient/cert以下に入れました)に入れておきます。実行する前に、`sudo /sbin/ldconfig`を行い、libmosquittoを使えるようにしておきます。mosquitto_pubを以下のように実行します。-h で指定しているAWS IoT CoreのエンドポイントはIoT Coreのマネジメントコンソールの設定から確認してください。

```
./mosquitto_pub --cafile cert/AmazonRootCA1.pem --cert cert/xxxxx-certificate.pem.crt --key cert/xxxxx-private.pem.key -h "xxxxx-ats.iot.ap-northeast-1.amazonaws.com" -p 8883 -q 1 -d -t topic/test -m "Hello World"
 
```
すると、以下のようにコンソール上に表示され、Publishされたようです。

```
Client (null) sending CONNECT
Client (null) received CONNACK (0)
Client (null) sending PUBLISH (d0, q1, r0, m1, 'topic/test', ... (11 bytes))
Client (null) received PUBACK (Mid: 1, RC:0)
Client (null) sending DISCONNECT
```
AWS IoT Coreのテストから指定したTopicをサブスクライブすると、メッセージを受信することが確認できました。
![スクリーンショット 2021-07-04 16.17.09.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/80028/a4eb379c-fd39-d170-fbb9-1bd1fc117dea.png)

今度はSubscribe側を実行してみます。topic/sub/1のTopicをサブスクライブします。

```
$ ./mosquitto_sub --cafile cert/AmazonRootCA1.pem --cert cert/xxxxx-certificate.pem.crt --key cert/xxxxx-private.pem.key  -h "xxxxx-ats.iot.ap-northeast-1.amazonaws.com" -p 8883 -q 1 -d -t topic/sub/1
```

```
Client (null) sending CONNECT
Client (null) received CONNACK (0)
Client (null) sending SUBSCRIBE (Mid: 1, Topic: topic/sub/1, QoS: 1, Options: 0x00)
Client (null) received SUBACK
Subscribed (mid: 1): 1
```
このようにTopicをSubscribeし、メッセージ待ちになります。AWS IoT CoreのコンソールからこのTopicにメッセージを投げてみます。
![スクリーンショット 2021-07-04 16.23.40.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/80028/ad943d22-c520-579f-6be1-dd4e82a6099a.png)

すると、以下のようにメッセージを受信したことが確認できます。

```
Client (null) received PUBLISH (d0, q0, r0, m0, 'topic/sub/1', ... (45 bytes))
{
  "message": "Hello from AWS IoT console"
}
```

mosquitto_rr はPublishとSubscribeを交互に繰り返す実装のようです。（RRはRound Robinかな？）こちらも試してみます。
rr_client.cの内容をみると、Optionの入れ方がわかるので必要なOptionを入れて以下のように実行してみます。

```
$ ./mosquitto_rr --cafile cert/AmazonRootCA1.pem --cert cert/xxxxx-certificate.pem.crt --key cert/xxxxx-private.pem.key  -h "xxxxx-ats.iot.ap-northeast-1.amazonaws.com" -p 8883 -q 1  -t topic/test -m "test message" -e topic/sub/1  -d
```

```
Client (null) sending CONNECT
```

として止まってしまいました。この場合、MQTT Version5で通信に行ってしまってAWS側が対応できていないのでうまく通信ができないようです。以下のようにVersionで3.1.1を指定してみます。

```
$ ./mosquitto_rr --cafile cert/AmazonRootCA1.pem --cert cert/xxxxx-certificate.pem.crt --key cert/xxxxx-private.pem.key  -h "xxxxx-ats.iot.ap-northeast-1.amazonaws.com" -p 8883 -q 1  -t topic/test -m "test message" -e topic/sub/1  -d --protocol-version mqttv311
```

```
Client (null) sending CONNECT
Client (null) received CONNACK (0)
Client (null) sending SUBSCRIBE (Mid: 1, Topic: topic/sub/1, QoS: 1, Options: 0x00)
Client (null) received SUBACK
my_subscribe_callbackgranted_qos 1
Client (null) sending PUBLISH (d0, q1, r0, m2, 'topic/test', ... (12 bytes))
Client (null) received PUBACK (Mid: 2, RC:0)
```

今度は正しく、Publishし、topic/sub/1をSubscribeできました。先程と同様にAWS IoT Core上からこのTopicにメッセージを送ると、メッセージを受信することが確認できます。

```
my_message_callback
cfg.pub_mode 1
{
  "message": "Hello from AWS IoT console"
}
```

# まとめ
今回、mosquittoのクライアントを使ってAWS IoT Coreと通信してみました。特に問題なく通信ができることが確認できました。