M5StackとNoodl2をMQTT通信で連携させる

## 概要
M5StackとNoodlでMQTTを使った相互通信を行います。NoodlのProjectはデプロイすればHTML・Javascriptファイルを出力するので、最終的にはM5Stackとスマホで相互操作を行います。
今回はさらに、3種類のMQTTブローカ(外部ブローカ、内部ブローカ、Noodl簡易ブローカ)で動作・設定の違いがないか検証も行いました。

<blockquote class="twitter-tweet"><p lang="ja" dir="ltr">M5StackとNoodl2をMQTT通信で連携させてみました<a href="https://t.co/US2WRoFnWy">https://t.co/US2WRoFnWy</a></p>&mdash; みよぴ (@masato_miyoshi) <a href="https://twitter.com/masato_miyoshi/status/1266863336735440896?ref_src=twsrc%5Etfw">May 30, 2020</a></blockquote> <script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>

構成
![img0080_result.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/192442/b88a0b2d-9f09-8157-be09-b5fcad449dd3.png)

- [Github](https://github.com/macole/Noodl/tree/master/Noodl2_M5StackDemo)：プロジェクトファイルNoodl、UiFlow
- [デモサイト](https://macole.github.io/Noodl2_M5StackDemo/index.html)：Noodlで作ったスマホ画面

## 使用する技術
- **M5Stack：**小型マイコンモジュール。Arduino互換のESP32に画面・ボタン・バッテリー・Wifi・SDカードスロット、入出力端子を持ち、すぐにプログラム・検証を行える。
- **UIFlow：**M5Stack用プログラミング環境。ビジュアルプログラミング言語BlocklyとMicroPythonで切り替え可能。
- **MQTT：**IoT機器をつなぐパブリッシュ/サブスクライブ(出版/購読型)のプロトコル。今回は内部ブローカ(Mosquitto）、外部ブローカ(Shiftr.io)、簡易(Noodl)の3つで検証しました。
- **Noodl：**デザイン思考のプロトタイピングツール。フローベースでUIを作成。各種IoT技術と連携が可能。

## ブラウザ上でMQTTを使う時の注意点
まずはM5StackとNoodlの間をつなぐMQTTブローカについて説明します。
ブラウザ上ではMQTT通信を直接使うことができないため、MQTT通信をWebSocket上にのせる必要があります。

>**ブラウザ上でMQTTを使う時の注意点**
― MQTTブローカは通常ではMQTT通信にポート(1883)を使います。
― ですがブラウザ・Webページ内からMQTTメッセージをPublish/Subscribeする場合はWebSocketを介してMQTTを実行する必要があります(MQTT over Websocket)。
― MQTTブローカ側はMQTT over WebScoketにも対応している必要があります
― このMQTT over Websocketには別のポート(9001など設定による) を使います。

## MQTTブローカの比較
確認した3つのMQTTブローカで動作・設定の違いを下記に示します。

|MQTTブローカ|内外|通信|難易度|まとめ|
|:--:|:--:|:--:|:--:|:--|
|Shiftr.io|外部|◎|〇|無料のアカウント作成が必要。MQTT、MQTT over Websocketsを意識しないでもサーバ側で対応し双方の通信をつないでくれる|
|Mosquitto|内部|◎|△|ブローカ構築が手間ですが、外部サービスを利用できない場合など設定次第なので自由度は高い|
|Noodl2|内部|×|◎|Noodlを立ち上げるだけでブローカの構築が不要。ただしMQTTと,MQTT over Websocket個別に通信はできましたが、相互通信ができません|

内部ブローカを使う場合はMQTTと、MQTT over Websocketどちらで行うか意識する必要がありますが、外部ブローカ(Shiftr.io)ではそのあたりをうまく処理してくれたため、ユーザは共通のMQTTブローカURLを指定するだけでOKでした。
また、Noodlの簡易MQTTサーバはピュアなMQTTとWebsociket上のMQTTで通信ができなかっためブラウザ間だけで通信する場合には使えますが、M5StackなどIoT機器との通信には使えませんでした。
結論、一番手ごろな方法は外部MQTTブローカを使う方法でした。外部MQTTブローカを利用してもよい環境であればそちらをお勧めします。
[こちらの記事](https://qiita.com/kmaepu/items/1808f63ebeb55642c345)で詳しく説明されていますので確認してみてください。

今回はMQTT over Websocketを理解するため、内部MQTTブローカmosquittoを使ってみます。

## 内部ブローカを構築する
Mosquittoサーバを立てる場合。下記手順で構築します。

- **MQTTブローカの設定手順**

1. MQTT(Mosquitto)のインストール
2. MQTT over Websocketの設定もする
3. MQTTサーバを立ち上げる

### 1. MQTTのインストール
MQTTサーバのインストール・サービスの設定については[こちら](https://qiita.com/ekzemplaro/items/ab90dd630c3ad8e819ab)を確認してください。

### 2. MQTT over Websocketの設定

mosqittoフォルダにある設定ファイル`mosqitto.conf`を書き換えます。

```sh:mosquitto.conf
#MQTT用
listener 1883

#MQTT over Websocket用
listener 9001
protocol websockets
http_dir /root
```

デフォルトの TCP/IP ポート 1883 の他に、WebSocket の指定をします。WebSocket のポート番号は`9001`を指定していますが別の値を指定してもかまいません。これでMQTTブローカでピュアなMQTT通信と、MQTT over Websocket通信ができるようになりました。

### 3. MQTTサーバを立ち上げる
では、正しく動作するか確認してみてください。

```sh:起動確認
mosquitto
```

次に送受信の確認をします。ターミナルを2つ開き一方のターミナルで、

```sh:受信側
mosquitto_sub -v -t トピック名 -h localhost -p 1883
```
を実行しておき、別のターミナルで、

```sh:送信側
mosquitto_pub -t トピック名 -h localhost -p 1883 -m "HelloWorld"
```
を実行すると、mosquitto_subを実行したターミナルにメッセージが表示されるはずです。

## UIFlowでM5Stack用プログラム
次にM5StackのMQTT通信用プログラムを実装します。

### M5Stack設定の注意点
M5Stack側のファームウェア、UIFlowの準備は[こちら](https://m5stack.github.io/UIFlow_doc/ja/)を確認ください。

**概要とポイント**
- UIFlowを使うためにM5Stackのファーウェア書き換えを行う。最新版ではWifi設定も行えます。
- M5Stack起動時にプログラムモードに行く場合は、すぐにボタンを押してUIFlowモードに切り替える必要があります
- M5Stackを起動しUIFlowのアクセスキーを表示させる
- UIFlowを立上げ上記アクセスキーを入力すると、UIFlowの開発環境に接続されます
- M5StackでMQTTサーバを起動させるにはUIFlowから実機に`ダウンロード`する必要があります。RUNだけではエラーとなります。

![img0063_result.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/192442/f60af8ef-ddf1-675a-8d31-e490ce54ce60.png)

### M5Stack側の機能
1. Wifi接続
2. MQTTサーバ接続
3. MQTTメッセージを受信すると画面に表示(topicA)
4. ボタンを押すとMQTTメッセージを送信(topicB)

これをUIFlowで作っていきます。
ビジュアルプログラミングなので下記と同じようにノードを並べてください

![img0000_result.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/192442/0a3507ef-b7da-e934-403f-3a403c4f426a.png)

- MQTT初期化のノードは横長で表示されています。ノード上で`右クリック>外部入力`とすることで縦表示に切り替えられます。
- クライアントIDは任意でOKです
- サーバアドレスにMQTTブローカのアドレスを記載します（内部なら`xxx.xxx.xxx.xxx`IPアドレス、外部なら`broker.shiftr.io`URL）
- ポートにはMQTTの通信用ポートを設定します。(通常なら`1883`など)
- ユーザ名、パスワードが不要な場合はそれぞれ空欄でOKです

## Noodlで画面の作成
次にNoodlで操作用画面とMQTT通信部を作成します。

### 機能
1. ボタンをClickされると
2. 状態を変更(トグル)
3. 状態に応じてボタンの色・表示・回転を変更
4. SendMessageノード(MQTT送信)で状態を送信(topicA)
5. RecieveMessageノード(MQTT受信)で受取ったメッセージに応じてImageを変更する(topicB)

#### ノードの設定

![img0000_result_1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/192442/256b1d6b-591c-c998-745c-d86d08a388f0.png)

String Mapperノード：MQTTで受信したメッセージに応じた画像を選択します
![img0071_result.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/192442/926eb2bc-a5cf-d5c2-7c97-2cea8737234d.png)

Statesノード：状態はONとOFFを追加。Valuesにはサイズ：size,透明度：opacity,回転：rotを追加。
![img0072_result.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/192442/7fae9b68-aff7-d94b-438c-b239c900a397.png)

Statesノード：ON/OFFの状態でサイズ、透明度、回転を制御
![img0073_result.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/192442/ef42e834-53e8-ca5a-1c5e-57397ec5e858.png)

SendMessageノード：TopicをTopicAに設定・Format=csv、sendOnChange=チェック、Payloadをmsgに設定
TopicはFormatをCSVにすると入力データをそのまま出力し、JSONだとJSON形式で送信します。
また、Send OnChangeをチェックすると、データ変更をトリガーとしてMQTTメッセージを送信します。
![img0075_result.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/192442/dc3c8992-be68-b98e-c972-2da19a091f90.png)

ReciveMessageノード：TopicをTopicBに設定、Payloadをmsgに設定
![img0076_result.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/192442/f1658a3b-594a-31c6-22ee-391909a8bdf2.png)


### メッセージブローカの設定変更
MessageBrokerメニューの一番下にある`CreateNewBroker`ボタンを押し、新しくブローカの設定をします。デフォルトではNoodlで用意された`wss://br1.noodlapp.com/mqtt/`がMQTTブローカが設定されますが、他に準備してある場合は`External Broker{にチェックを入れます。
今回は内部に立ち上げたMQTTブローカを使うので、mosquittoが立ち上がっているURLを下記のように指定します。

```sh:BrokerURL
mqtt//xxx.xxx.xxx.xxx:9001
```

```sh:BrokerURL(ユーザ認証を設定しているとき)
mqtt//userid:pass@xxx.xxx.xxx.xxx:9001
```
![img0003_result.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/192442/afd6e023-a530-5941-75ff-e0fd043b2e95.png)

```sh:補足_BrokerURL(shiftr.ioの場合)
mqtt//userid:pass@shiftr.io
```

これで完成です。
Webブラウザ上からボタンを押してM5Stackの画面表示が変わること。逆にM5Stackのボタンを押すことでWebブラウザの表示が変化することを確認してください。

## 補足：Noodlの簡易MQTTブローカについて
Noodlを立上げた時に、自動で立ち上がる簡易MQTTについて整理します。
結論、ブラウザ間のMQTT通信では利用できますが、ブラウザとIoT相互の通信では使用することができません。あくまで、Noodl内でのチェック用のようです。

- NoodlのMQTTブローカはポート1883で起動します。
- また、MQTT over Websocket用にはポート1884で起動します。
- 2つの機能があるのですが、MQTT通信とMQTT over Websocket間で通信はできません。

## まとめ
内部・外部のMQTTブローカを使うことで、WebブラウザとIoT機器の相互通信を行える環境を構築できるようになりました。

## 参考文献
- [Noodlで外部サービスとの連携方法～MQTT～](https://qiita.com/kmaepu/items/1808f63ebeb55642c345)
- [ノンコーディングでIoT！UIFlow（M5Flow）とNoodlの連携方法](https://qiita.com/kmaepu/items/74a04c79607c43f4cbff)
- [M5stack で作る ON AIR サインで手に入れる平穏な在宅勤務環境](https://blog.soracom.jp/blog/2020/04/02/on-air-sign-on-m5stack/)
- 説明用イラストはユメヒコさんがフリー素材化された清水さんと、いらすとやさんから使用させていただきました。