MQTTとは？ WindowsとラズパイでMQTTを実行する

# MQTT
誤った情報もあるかと思います。
訂正やFB大歓迎ですので、よろしくお願いします。

## MQTTとは
![mqtt_pubsub.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/381381/da05c27b-b0de-85f1-2a1b-1ac13ab272b4.png)
> 画像引用元：https://amg-solution.jp/blog/15787

MQTT(Message Queueing Telemetry Transport)はPub/Sub型のシンプルで軽量なプロトコルです。
M2MやIoTに適していると言われており、私が考える理由は以下の点です。
- 多数のデバイスで短いメッセージのやりとりを想定して作られている
- 1:多での情報のやりとりができる(MQTTというよりかはPub/Subの恩恵)
- 非同期で双方向通信である
- 軽量のため消費電力が少ない

### 多数のデバイスで短いメッセージのやりとりを想定して作られている
MQTTはPub/Sub型のシンプルで軽量なメッセージングプロトコルです。
制約のあるデバイスや、低帯域幅、または信頼性の低いネットワーク向けて設計されている点からIoTやM2Mと相性が良いです。

###  1:多での情報のやりとりができる
これはMQTTというよりかは、Pub/Subの恩恵かなと思っています。(非同期通信も)
製造IoTでいえば、センサーなどIoTデバイスとのやりとりは何千台となることも考えられます。
そういった世界で1:1でのやりとりは非効率ですので、1:多もしくは多：多が可能なMQTTはうれしいですね。

### 非同期で双方向通信である
非同期通信とは送信側と受信側を空間・時間から切り離されているイメージです。
逆にHTTPのような同期通信をイメージすると、クライアントがサーバーからのレスポンスを待機します。
IoTなどはデバイスが多いため、それぞれと同期通信をするのは時間がかかりすぎて問題になります。
また、同期通信はネットワークが不安定もしくはサーバーが故障した場合はやりとりができません。

非同期通信は、ネットワークが死んだ場合や、サーバーが故障した場合に復旧と同時にデータを再送信することができます。
IoTでは、想定されるケースが多いためネットワークの信頼性が低い場合もあります。
そういった時でも非同期は役に立ちます。


### 軽量のため消費電力が少ない
ヘッダのサイズです。
 - HTTPヘッダ：50バイト～
 - MQTTヘッダ：2バイト～

ここからMQTTプロトコルは極めて軽量だということがわかるかと思います。
実際には256MBが最大メッセージサイズであり、これだけ入れることはあまり想定されていないと思います。
ネットでは256MBだったら・・・そんな変わらないんじゃという話はありますがここでは無視します。

やっぱりIoTのような環境によって条件が変わる場合にネットワークの帯域が限られている場合もあり、HTTPヘッダでは適していません。
また軽量ということは処理も軽く消費電力です。
電源が供給できないような場合はバッテリーが使われますが、消費電力が少ない方がよいので助かります。

## MQTTの資料
自分はこの辺りを見て学びました。
よければ参考にしてみてください。

- [MQTT入門ガイド](https://www.advanet.co.jp/2020/10/14/mqtt-introduction/ "MQTT入門ガイド")
- [MQTTとは](https://kfep.jp/solution/iot-mqtt/mqtt"MQTTとは")
- [【第１６回】 IoT向けのプロトコルMQTTについて
](https://isabou.net/knowhow/colum-it/colum16.asp#:~:text=%E3%81%9D%E3%81%AE%E5%90%8D%E5%89%8D%E3%81%8C%E7%A4%BA%E5%94%86%E3%81%99%E3%82%8B,%E6%80%A7%E8%B3%AA%E3%82%92%E7%99%BA%E6%8F%AE%E3%81%97%E3%81%BE%E3%81%99%E3%80%82 "【第１６回】 IoT向けのプロトコルMQTTについて
")

# MQTTのPub/Subとは何か？
> MQTT(Message Queueing Telemetry Transport)はPub/Sub型のシンプルで軽量なプロトコルです。

Pub/Sub型とは一体なんでしょうか？
簡単に言ってしまえば、Publisher(送信側)からブローカーを通して、Subscriber(受信側)へメッセージを送ってあげています。
ブローカーを通すことで、PublisherとSubscriberが密になることはありません。
要するにpublisherはメッセージをsubscriberが受け取ったか気にせず送ります。
そもそもsuscriberがいるかも気にしていません。
逆にsubscriber側もメッセージを受け取った際にpublisherのことは知りません。

![pub_sub.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/381381/51f710ab-7900-106b-ac62-409f50c648dd.png)

## Topicでメッセージを振り分ける
これだと興味のないメッセージもpublisherから送られたら、subscliberは受け取ってしまいます。
ですので、すべてのメッセージにはtopicというラベルを付けます。
subscliberはブローカーに対して、トピックフィルタを作成してsubscribeをします。(定期購読)
topicはファイルシステム内のフォルダやファイルのような階層構造になっていて、スラッシュ（/）を区切り文字としてしようします。

![サブスクライブ.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/381381/6fafe20d-78e5-a0d5-f8e6-0f575b9637ea.png)

![流れ.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/381381/4c199ad6-1b77-ab36-7f3f-4311cf8702a7.png)

※黄色は[/tsuda/hoge]というトピックスです。

## QoS(Quality of Service)
メッセージをやりとりするうえでQoS(Quality of Service)サービスの品質が大切になります。
実際には、メッセージが届いたかどのレベルで保証するか設定するのがQoSです。
そのため送信側と受信側で品質レベルを0,1,2で決めます。
ここで送信側と受信側とあえて言っているのは、図のようにbrokerが送信でもあり受信でもあるためです。

![送受信.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/381381/2828f358-6fb4-79de-3449-fa2a39952794.png)

### QoS0
QoS0はとりあえず一回データを送信します。
が、それが受信側に届いたかどうかは確認しません。
![qos0.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/381381/65631383-2d44-4015-af3e-398bc144f50a.png)


### QoS1
最低でも一回は届けることを保証しています。
メッセージを送信して、受信側からPUBACK(受取)が来ることで届いたことを確認します。
![qos1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/381381/fb6b4ce2-2886-7c4d-a32f-e4d94c24ad6f.png)

しかしQoS1は重複をすることがあります。
メッセージを送信して受信側からなんかしらの影響でPUBACK(受取)が来なかった場合、送信側が再送信するためです。
なぜ重複するのか・・・それは、
受信側は、このメッセージが再送or新規を把握できないから受け取ることしかできません。
重複しないためにはQoS2を使います。

![qos1_重複.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/381381/f13dd502-2748-2b5e-3c44-6e021f115254.png)


### QoS2
必ず届けることを保証し、重複もありません。
しかし、通信回数も増えるため一番負荷がかかります。
QoSのレベルを上げるほど、MQTTプロトコルのやりとりが多くなるのでオーバヘッドが大きくなります。

QoS2が重複しないのは、受信側がメッセージを受け取りPUBRECを送った時何かしらの理由で、送信側に届かなかった場合、
想定する次はPUBRELです。
ですが、メッセージが来た場合は、あれメッセージが再送信されたぞ？と把握できるためですね。

![qos_2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/381381/401c4895-27b4-1c8d-3f90-0cb925459ee6.png)


### QoSの補足
ちなみに送信側と受信側でのQoSレベルはメッセージ発行者以上のQoSレベルを受信側は設定することはできません。
つまりメッセージ発行でQoS0を設定した場合は、QoS1,2は設定できないということです。
逆にメッセージ発行者がQoS2を設定した場合は、受信側でQoS0,1も設定できます。
![qos-sample-3.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/381381/51f222f4-5b0b-5afd-2e3a-a918877cef85.png)
> 画像引用元：https://sakiot.com/what-is-qos-of-mqtt/

そして、MQTTはそもそもTCPネットワークプロトコル上で実装されます。
TCPを使っているなら、データは届けられるよね？という疑問はありましたが、メッセージが届いたことは保証できません。
なぜなら、ネットワークトラブルでコネクションが切断したり、相手の電源が落ちた場合はメッセージが届いたかわからないためです。
TCPを使っていればつながっていることは保証していますが、メッセージが届いたかは保証できないのでQoSを利用します。


# MQTTプロトコルの補足
MQTT(Message Queueing Telemetry Transport)は、Message Queueingと書かれているようにキューを使用されているかと思いきや、使われておりません。
キューが使われていないので、メッセージの順序を保証してくれません。
例えば、メッセージ送信でQoS2で発行しトランザクション中に、QoS0で発行した場合は逆になる可能性もあります。
順序を保証したい場合は、基本的には同じQoSレベルを使うもしくは順序保障している？QoS2を使うとよいかと思います。


# デバイスの環境
| 仕様機器 | OS |
|:-:|:-:|
| Windows  | Windows Home 11  |
| ラズパイ | ラズパイOS |

# 準備
## Mosquittoをインストール
[Mosquitto](https://mosquitto.org/download/ "Mosquitto")をインストールします。
私の場合は64ビットですので、こちらをインストールしました。
`mosquitto-2.0.15-install-windows-x64.exe`

OpenSSLが必要なためそれもインストールします。

## OpenSSL
[OpenSSL](http://slproweb.com/products/Win32OpenSSL.html"OpenSSL")をインストールします。
2022/08/25時点では`Win64 OpenSSL v3.0.5 Light`が最新なのでこちらもインストールしてます。
OpenSSLを使ってアプリやサービスをしない限り軽量版でいいと思います。
※今回くらいであれば十分でした。

最後に寄付をするかと聞かれますので、私のようにケチな人はすべて外して`Finish`してください。

![スクリーンショット 2022-08-25 225449.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/381381/a5832eaf-efff-df9c-9a95-2887071c9bd4.png)


完了したらMosquittoのパスを通します。
パスの通し方がわからない方はググれば簡単に出てくるので調べてください。
Mosquittoをダウンロードしたパスを追加します。(C:\Program Files\mosquitto)←あくまでも私の場合です
これで準備完了！！

# 実行
## ブローカーの立ち上げ
コマンドプロントを立ち上げて以下のコードを打ちます
とくに何も起きませんが、これでBrokerが立ち上がりました。
ちなみにコマンドのオプションについては書きません。
[公式マニュアル](https://mosquitto.org/man/mosquitto_sub-1.html "マニュアル")をご覧ください。

```terminal
C:\Users\****> mosquitto
```

![howto_1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/381381/3b8b2b8d-d484-1ae6-9edc-f59f39f776f5.png)


## Subscribe側のクライアントを立ち上げる
立ち上げには以下のコードを新しいコマンドプロントで打ち込みます。
```terminal
C:\Users\****> mosquitto_sub -d -t topicA
Client null sending CONNECT
Client null received CONNACK (0)
Client null sending SUBSCRIBE (Mid: 1, Topic: topicA, QoS: 0, Options: 0x00)
Client null received SUBACK
```

![howto_1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/381381/c1b938dc-808f-bbe3-199a-99c60780b4b8.png)


このコマンドは、クライアントを立ち上げて`topicA`というトピックフィルタを作成してサブスクライブしました。
`Client null sending SUBSCRIBE (Mid: 1, Topic: topicA, QoS: 0, Options: 0x00)`
TopicはTopicAでQoSは0です。
QoSとは通信の品質で、0,1,2とありますが、0はただ送るだけです。
サブスクライブ側は受信ですので、送信側にちゃんと届いたといった応答はしないということです。


## Publiah側のクライアントを立ち上げる
新しくコマンドプロントを開いて以下のコマンドを打つ
```terminal
C:\Users\****>mosquitto_pub -d -t topicA -m "test"
Client null sending CONNECT
Client null received CONNACK (0)
Client null sending PUBLISH (d0, q0, r0, m1, 'topicA', ... (4 bytes))
Client null sending DISCONNECT
```

![howto_1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/381381/eebff3d5-60df-e1b3-aa52-cc4e767cca0a.png)


`Client null sending PUBLISH (d0, q0, r0, m1, 'topicA', ... (4 bytes))`が表示されていればOK
sub側のターミナルを確認すると・・・
```terminal
Client null received PUBLISH (d0, q0, r0, m0, 'topicA', ... (4 bytes))
test
```
ちゃんとsub側でメッセージを取得できています。

# ほかの機器と通信してみる

今回はラズベリーパイ zero Wを使ってみます。
ラズパイを使ってやってみたい方は、[こちら](https://hagane-karakuriya.com/2022/07/22/%E3%80%90%E5%82%99%E5%BF%98%E9%8C%B2%E3%80%91raspberry-pi-%E3%81%AB-mosquitto-mqtt-broker%E3%82%92%E3%82%A4%E3%83%B3%E3%82%B9%E3%83%88%E3%83%BC%E3%83%AB/ "【備忘録】Raspberry Pi に Mosquitto MQTT Brokerをインストール")を参考にMosquitto MQTT Brokerをインストールしてみてください。

## ラズパイをBrokerにしてみる。
ラズパイにBrokerを立ち上げるので、IPアドレスを取得します。
```terminal
pi@raspberrypi:~ $ hostname -I
192.168.*.*
```
以下のコマンドでBrokerを立ち上げます。
止めるときは**start**を**stop**に変えてください。
```terminal
pi@raspberrypi:~ $ sudo systemctl start mosquitto
```

特に何も起きませんが、これで立ち上がっています。

## windowsでpub/subを作る
### サブスクライブする
windowsでコマンドプロントを開いてラズパイに対してtopicAをサブスクライブします。
ポートは1883がデフォルトです。
```terminal
C:\Users\****>mosquitto_sub -d -h 192.168.*.* -p 1883 -t topicA
Client null sending CONNECT
Client null received CONNACK (0)
Client null sending SUBSCRIBE (Mid: 1, Topic: topicA, QoS: 0, Options: 0x00)
Client null received SUBACK
Subscribed (mid: 1): 0
```

### メッセージを送る
メッセージ送信もサブスクライブと同様にラズパイに向けて送ります。
```terminal
C:\Users\****>mosquitto_pub -d -h 192.168.*.* -p 1883 -t topicA -m "test2"
Client null sending CONNECT
Client null received CONNACK (0)
Client null sending PUBLISH (d0, q0, r0, m1, 'topicA', ... (5 bytes))
Client null sending DISCONNECT
```

### メッセージを受信
ちゃんと受け取ることができました。
今回は1:1でしたがもちろん、1:多もできますし、メッセージのtopicをtopicBとかにすれば受信しません。
色々試してみると面白いかと思います。
```terminal
Client null received PUBLISH (d0, q0, r0, m0, 'topicA', ... (5 bytes))
test2
```