CHIRIMEN から Node-REDにMQTTでデータを投げる

# はじめに
chirimen の Node.jsからNode-REDにMQTTでデータを投げるテストです。
chirimenとNode-REDは同じRaspberry Pi上に構築してあります。
MQTTブローカーはNode-RED上に立てます。
（[Node-REDのインストールなどはこちらの記事](https://qiita.com/airpocket/items/d9b996238a14a800ecf4)）

# MQTTとは
MQTTとは端末間やプログラム間でデータを送受信するとても便利な通信方法です。送信側をPublisher、受信側をSubscriber、送受信を仲立ちする役目をBrokerと呼びます。

あるプログラムから別のプログラムへとデータを送りたい場合、送る側(Publisher)はblockerに対してデータを送信します。出たにはTopicという情報を付加して送ります。
一方で、そのデータを受信したいプログラムは(Subscriber)は、どのTopicのデータを受け取りたいかを事前にBrokerに通知しておきます。
Brokerは届いたデータのTopicを確認してそのTopicを要求しているSubscriberすべてにデータを送信します。

topicの設定次第で、1対1や1対多の通信を簡単に実行できるのがMQTTの特長の一つです。
MQTTでデータ送受信を行いたい場合の大まかな流れは次の通りです。

①Brokerを実行する
②Subscriberが要求するTopicをBrokerに通知する
③PublicherがTopicを追加したデータをBrokerに送る

# Node-RED上でMQTT Brokerを実行する

Node-REDの標準のノードにはMQTT Brokerはありませんが、必要なノードを簡単に追加できます。
画面右上の三本線のアイコンをクリックしてメニューを開き、「パレットの管理」を選択します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/496179/a8a9c80c-499f-4279-3f9c-e9f0fdcdd2d8.png)

続いて、「ノードを追加」タブを選択して検索窓に「aedes」と入力して検索。「node-red-contrib-aedes」がヒットするので「ノードを追加」をクリックして追加します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/496179/75e97fec-b6d8-400a-63e6-d2eb0ac1bcbd.png)
警告メッセージが出ますが気にせず「追加」
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/496179/e98844b9-47b9-f293-30f6-ff676fce343a.png)

しばらく待ってインストールが完了すると、パレットに「aedes broker」というノードが追加されているのでワークスペースに引き出してデプロイします。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/496179/28ecf56c-28ba-6f77-5de7-d974147a9f9a.png)
ノードの下に接続済みと出れば、ブローカーが機能しています。

# MQTTの通信テスト

Node-RED上にPublisherとSubscriberの両方を作成して通信テストをします。

## ノードの接続
「inject」と「mqtt out」、「mqtt in」と「debug」ノードをそれぞれ引き出して接続してください。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/496179/e18e91de-77c4-9380-6277-e3b5d16d07d8.png)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/496179/2b179756-69f5-a077-1ecc-5bb6dcd08a32.png)

## mqtt out ノードの設定
「mqtt out」ノードはPublisherの役割をするノードです。
ノードをダブルクリックするとプロパティが開くため、接続するBrockerと送信先のTopicを設定します。
まだBrokerが登録されていないため、新規にmqtt-brokerを追加します。ペンのアイコンをクリックします。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/496179/4841c21a-3eb5-fa7d-01e9-90bb1d167f30.png)

ここでは同じ端末上のmqtt brokerにメッセージを送る為、サーバを「localhost」として「追加」します。別の端末のmqtt brokerに送る場合はその端末のIPアドレスを入力してください。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/496179/4a22488b-9a9b-6a6b-aeb5-1835c66de007.png)

Brokerの登録が終了するとmqtt outノードの編集画面に先ほど登録した「localhost:1883」が表示されています。トピックには任意の文字列が使用できますが、今回は「from Node-RED」としました。サーバとトピックが入力できたら「完了」を押して設定終了です。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/496179/46a469fd-7dea-ef54-0bb5-d9bb97084fe2.png)

## mqtt in ノードの設定
mqtt in ノードはSubscriberとして働くノードです。ダブルクリックしてプロパティを開き、「サーバ」と「トピック」を設定します。
サーバには先ほど登録した「localhost:1883」を選択します。
トピックは、mqtt out で設定したものと同じトピックを入力します。できたら「完了」します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/496179/7d432bef-4cce-5a0f-ef20-6ad7021ea872.png)

## デプロイしてテストする
各ノードの設定が完了すると下図の様な状態になっています。「デプロイ」をクリックして変更を反映させてください。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/496179/2c554a5a-9493-5712-a9a7-9c13380278d9.png)

続いて画面右のサイドバーの虫のアイコンをクリックでしてデバッグタブを表示し、「タイムスタンプ」と表示されている「inject」ノードの左のタブをクリックします。
デバッグノードにメッセージが表示されれば、mqttの送受信成功です。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/496179/30240e59-9668-c741-7412-721efc203535.png)

## CHIRIMENからの受信用トピックを作る
ここまでで作ったフローは、「from Node-RED」というトピックを使って送受信しました。次項ではNode.jsからメッセージを送信するため、Node.js用のトピックの受信用フローも作っておきます。
新たに「mqtt in」ノードと「debug」ノードを引き出して接続し、「mqtt in」のプロパティで「サーバ」を「localhost:1883」、トピックを「from CHIRIMEN」としてデプロイします。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/496179/4bd5cba4-0194-a928-c931-8e35e2121daa.png)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/496179/5077ca11-5b77-6956-98d4-f97bf56b7a91.png)


# CHIRIMEN（Node.js版）からメッセージを送る
ここからは、Raspberry Pi上でJavaScriptのコードを書いて先ほど作成したmqttのトピックへメッセージを送信します。

## Node.jsとは
Node.jsとは、端末（ここではRaspberry Pi）上でJavaScriptのコードを動かす環境です。CHIRIMEN環境のRaspberry Piにはすでにインストールされているためそのまま使用できます。
CHIRIMENにはhtmlをつかうWeb版と、JavaScriptのみで動かすNode.js版があるそうです。私はhtmlが書けないので、Node.js版を使用します。
Web版とNode.jsの違いは[こちらの記事](https://qiita.com/okojon/items/58726940ef51d1c4a74c)を参考にしてください。

また、Node.js版CHIRIMENを使うにはWeb-GPIO-APIとWeb-I2C-APIを使います。[こちらの記事](https://zenn.dev/kou029w/articles/node-web-gpio)を参考に実行しました。


## 温湿度センサSHT30を動かす
Raspberry Pi上ではホームディレクトリ上にWorkspaceというディレクトリを作って作業場所とします。
次のコマンドで作業用ディレクトリを作って移動します。
```shell
mkdir Workspace
cd Workspace
```

Node.jsでMQTTを使うために必要なパッケージをインストールします。
ターミナルから以下のコマンドを実行してください。

```shell
npm init -y
npm install mqtt
```
npm init -y npmの初期設定用のコマンドです。実行しなくても問題ありませんがしておかないといろいろ警告が出ますのでおまじないのためにも唱えておきましょう。詳しくはnpmについて調べてみてください。


nanoを立ち上げてNode.jsのコードを入力します

```shell
nano mqttTest.js
```

次のコードを入力して「ctrl + o」で保存、「ctrl + x」で修了します。
```JS
const mqtt = require('mqtt');                   //mqttモジュールをインポート
const _topic = 'from CHIRIMEN';                 //送信先のtopicを設定
const mqttOpt = {
    port: 1883,                                 //ブローカーのポートを設定
};

const client = mqtt.connect('mqtt://localhost', mqttOpt);       //localhostのブローカーに接続

client.on('connect', () => {
    client.publish(_topic, 'mqtt test');        //指定したトピックに「mqtt test」というメッセージを送信
    client.end();
});
```

コードが書けたらスクリプトを実行します。
```shell
node mqttTest.js
```

Node-REDのデバッグタブにこのようなメッセージが表示されれば通信成功です。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/496179/40a42428-a6f0-2e60-47f8-ceb75a4d699d.png)

# CHIRIMENでセンサーを読んでMQTTで送る
CHIRIMENのNode.js版を使用するにはWeb-I2C-APIとWeb-GPIO-APIをインストールする必要があります。
次のコマンドでインストールしてください。

```shell
npm install node-web-gpio node-web-i2c
```
続いて、I2C接続のセンサーを動かしてみます。
ここでは温湿度センサのSHT30を使用してみます。
[公式ページ](https://tutorial.chirimen.org/pizero/node-examples/sht30/sample)の通り結線します。
![image.png](https://tutorial.chirimen.org/pizero/node-examples/sht30/schematic.png)
CHIRIMEN使用のRaspberry Piにはライブラリがインストールされていますので、ライブラリの参照先のみ変更してコードを書きます。
ここではSHT30.jsとして保存しました。
```JS
const { requestI2CAccess } = require("node-web-i2c");
//SHT30のライブラリ参照先を変更（CHIRIMEN用ラズパイにはデフォルトでインストールされている）
const SHT30 = require("/home/pi/Desktop/gc/i2c/i2c-SHT30/node_modules/@chirimen/sht30");
const { promisify } = require("util");
const sleep = promisify(setTimeout);

main();

async function main() {
  const i2cAccess = await requestI2CAccess();
  const port = i2cAccess.ports.get(1);
  const sht30 = new SHT30(port, 0x44);
  await sht30.init();

  while (true) {
    const { humidity, temperature } = await sht30.readData();
    console.log(
      [
        `Humidity: ${humidity.toFixed(2)}%`,
        `Temperature: ${temperature.toFixed(2)} degree`
      ].join(", ")
    );

    await sleep(500);
  }
}
```
下のコマンドで実行すると、0.5秒ごとに温度と湿度が表示されます。
```shell
node SHT30.js
```

## 取得したデータをMQTTで送信する
続いて、先ほどのコードを手直しして、取得したデータをMQTTで送信します。送信先はサーバlocalhost:1883のトピックfrom CHIRIMENです。

ファイル名はSHT30MQTT.jsとして保存しました。
```JS
const { requestI2CAccess } = require("node-web-i2c");
const SHT30 = require("/home/pi/Desktop/gc/i2c/i2c-SHT30/node_modules/@chirimen/sht30");
const { promisify } = require("util");
const sleep = promisify(setTimeout);

//MQTTの設定...パッケージのインポート、トピック設定、ポート設定
const mqtt = require('mqtt')
const _topic = 'from CHIRIMEN';
const mqttOpt= {
        port: 1883,
};

main();

async function main() {
  const i2cAccess = await requestI2CAccess();
  const port = i2cAccess.ports.get(1);
  const sht30 = new SHT30(port, 0x44);
  await sht30.init();

  //mqttのインスタンスを作ってlocalhostのブローカーに接続
  const client = mqtt.connect('mqtt://localhost', mqttOpt);

  while (true) {
    const data = await sht30.readData();       //読み取ったデータをdataに記録
    console.log(
      [
        `Humidity: ${data.humidity.toFixed(2)}%`,               //dataから湿度情報を抽出
        `Temperature: ${data.temperature.toFixed(2)} degree`    //dataから温度情報を抽出
      ].join(", ")
    );

  //Node-RED側でデータを扱いやすい様に、JSON形式のテキストデータを作る
    const jsonText = '{"hum":"' + data.humidity.toFixed(2) + '","temp":"' + data.temperature.toFixed(2) + '"}';
  //設定したトピックにJSON形式のテキストとしてデータを送信
    client.publish(_topic, jsonText);
  //送信頻度を2秒ごとに変更
    await sleep(2000);
  }
}
```
コードが書けたら実行します。
```shell
node SHT30MQTT.js
```
Node-REDのデバッグタブに2秒ごとにデータが表示されます。JSONオブジェクトになっているので、この後の取り扱いがとても楽になっています。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/496179/65bd9cf9-c1b0-0851-7e0c-3461ae40bdda.png)



# まとめ
今回はNode-RED上でMQTTブローカーを立ち上げて、CHIIRIMENからのメッセージを受けました。これで、CHIRIMENでセンサーから数値を読み取り、Node-REDで種々の処理を実行できます。
CHIRIMEN上ですべての処理を実行してもよいのですが、JSが苦手な私は少しでもラクをしたくてこのようなフローをテストしています。
Node-REDから直接web-i2c-apiを動かせばもっとスマートになるはずですが、また別の機会に試します。