やっぱり Node-RED に戻ってしまった

投稿者: | 2018/10/06

Anyway, wrote a Node-RED flow to capture sensor data from ESP8266 via MQTT, finally storing to MongoDB.  Only we need is a few lines JavaScript code.

先日、myDevices Cayenne に「浮気」したばかりですが、結局 Node-RED に戻ってきてしまいました。ある程度のプログラミング(JavaScript で数行程度)は必要ですが、柔軟性ではやはり、Node-RED に分がありそうです。

今日のお題は

ESP8266 マイコンから Wi-Fi 経由で MQTT ブローカーに送られたセンサーデータを、Node-RED を使って、これまた流行りのデータベース MongoDB に書き込む。

というものです。

今回は申し訳ありませんが、MQTT ブローカーのインストールや設定、同じく Node-RED や MongoDB のセットアップについては、関連ウェブサイトを参照お願いします。ネットを探すと、たくさん入門資料(日本語や英語)が見つかると思います。

また、ESP8266 マイコンと Node-RED を繋ぐ方法については、以下のサイトが非常に詳しいです。これまた、ただの翻訳になってしまうので、ここでは詳細には触れません。説明は英文ですが、画像が多いので再現は難しくないと思います。(以下、文章中で「上述の説明サイト」と呼ばせて頂きます。)

今回の技術背景

「じゃあ、お前さんは一体何を説明するのだ!?」というと、ESP8266 マイコンから JSON フォーマットで気温と湿度のデータを吐き出して、それにタイムスタンプを付けて MongoDB にたたき込む、というところだけです、すみません。ちなみに、上記サイトのサンプルで、出力ノードに MongoDB を付ければ、だいたい同じようなことはできるのですが、次のような問題にぶち当たることでしょう。

  • センサデータには、タイムスタンプを付けたい
  • 温度と湿度は、まとめて同じ MongoDB「ドキュメント」(SQL 系データベースで言うところの「レコード」)に書き込みたい

そんな訳で、Node-RED の入力ノードと出力ノードの間には、ある程度の変換処理(function)が必要になってくる訳です。JavaScript と MongoDB の専門家であれば朝飯前でしょうが、おそらく、(私と同様に)組込系技術者は JavaScript も MongoDB も御専門でない方が多いでしょうから、「なんだ、簡単そうじゃん」とお気づき頂ければ光栄です。

フローの設計

さて。フローの設計はだいたいこんな感じになります。

ちなみに、Atsushi というのは私のファーストネームです(汗)。センサを私の部屋に設置したので、こんな名前になっています。いずれは、居間とか、ベランダにもセンサを置きたいと思っています。

また、下の 2行(Split なんとかと、ダッシュボードのグラフ関係)は、リアルタイムデータを Node-RED ダッシュボードで確認するためのものですので、MongoDB にデータを書き出すだけであれば不要です。また、debug mqtt receive ノードは、ご想像の通りデバッグ用のノードです。

フローの説明する前に、MQTT に publish されるフォーマットから説明しましょう。上述の説明サイトでは、ESP8266 Arduino のスケッチコードで

dtostrf(t, 6, 2, temperatureTemp);
dtostrf(h, 6, 2, humidityTemp);

// Publishes Temperature and Humidity values
client.publish("room/temperature", temperatureTemp);
client.publish("room/humidity", humidityTemp);

みたいなコードになっています。つまり、温度と湿度を別のトピックに publish している訳ですが、これだと MQTT ブローカーへの到達が時間差になってしまい、後々の処理が厄介です。そこで、温度と湿度を JSON にまとめて publish するようにします。具体的には、

{
    "temp": 23.45,
    "humid": 65.78
}

みたいな感じですね。私は Arduino スケッチを系統的に勉強している訳ではないので、エレガントではないかも知れませんが、こんなコードをでっち上げました。

void loop() {
    String strTemp;
    String strHumid;
    char buf[80];

    // 略

    float t = sht31.readTemperature();
    float h = sht31.readHumidity();

    if (! isnan(t)) {
        strTemp = String(t, 2);
    } else {
        strTemp = "null";
    }

    if (! isnan(h)) {
        strHumid = String(h, 2);
    } else {
        strHumid = "null";
    }

    String s = "{\"temp\": " + strTemp + ", " + "\"humid\": " + strHumid + "}";

    Serial.println(s);  // シリアルデバッグ用
    s.toCharArray(buf, sizeof(buf));
    client.publish("roomtemp/atsushi", buf);
}

実際には、センサデータを 5秒に一回 publish する、みたいな工夫を入れてください。

それでは、Node-RED フローの説明に戻ります。

最初のノード RoomTemp Atsushi は、いいですね。上述の説明サイトと変わりません。

JSON をデコードして JavaScript オブジェクトに変換する

次です。json ノード DecodeJson は、JSON 形式の MQTT メッセージを JavaScript オブジェクトに変換するためのものです。プロパティはこんな感じです。

MongoDB 書込み用のフォーマットに変換する

次に、これを MongoDB にたたき込みます。そのままですとスキーマがカッコ悪くなりますし、タイムスタンプも入れられないので、function ノード ConvertToDbFormat を用意しました。プロパティはこんな感じです。(必要な JavaScript コードは、たったの 7行で、ほとんどは MongoDB に渡すデータの組立て作業です。)

…ところが、私は JavaScript にも MongoDB にも詳しくないので、datetime のところで少しハマりました。まず最初に、2行目を

datetime: Date().now()

とか書いてしまいましたが、それだと、UNIX エポック時からのミリ秒が MongoDB に渡されてしまい、スキーマ上の方が整数値になってしまいます。これだと、MongoDB で扱いが面倒です。

次に、いろいろ調べて

datetime: new Date().toISOString()

と書いたのですが、今度は文字列になってしまいます。。。冷静に考えれば当たり前なのですが、

違うんだ、私のやりたいのは MongoDB の Date 型で書き込むことなんだ!!

結論としては実は簡単で、上述のように new Date() と書けば OK です。これで無事に、MongoDB にタイムスタンプ datetime が Date 型で格納できるようになりました。

MongoDB に書き出す

この mqtt ノード StoreToMqttAtsushi、上述の説明サイトと同様かと思います。特に難しいところはありません。

Server の設定では、MongoDB が動いているサーバー名(IP アドレス)や、MongoDB のデータベース名を記述します。

これでおしまいです。簡単ですね。ちゃんと MongoDB に書き込めているか、MongoDB の GUI ツール「MongoDB Compass」で覗いてみました。

大丈夫そうですね。

おまけ: ダッシュボードのグラフに出力

これは、上述の説明サイトと同じ…、と言いたいところなのですが、今回はセンサからのデータが JSON なので、そのままではグラフに書き出せません。Node-RED の split ノードとか使えば簡単にできるのかも知れませんが、つい、JavaScript で function を書く方法を覚えてしまったので、プログラマの悲しいさが、これも function ノードで実現してみました。本当は DecodeJson ノードの出力を加工すべきかも知れませんが、勢い余って MongoDB 書き出し用のフォーマットを変換するようにしてしまいました。皆さんは、いろいろ実験してみてください。私はオッサンなので面倒。

ノード SplitTemp は、こんな感じです。(SplitHumid も同様ですので、そちらは略。)

これも簡単ですね。これで無事に、ダッシュボードにグラフが出力されるようになりました。(Node-RED のフロー設計をいじってしまっても、オリジナルデータは MongoDB に残っているので、いろいろ安心です。)

まとめ

既に多くの人が、ESP8266 から Wi-Fi と MQTT 経由で Node-RED を活用していらっしゃるかと思いますが、私も JavaScript と MongoDB の勉強を兼ねて(というほど大袈裟なことではない)、ちょっとした Node-RED フローを設計してみました。

新しい技術(ここでは MongoDB や、Node-RED の function 設計)を覚えようとすると、特に私のようなオッサン技術者にとっては非常に敷居が高いのですが、実際に書いてみると、それほどでもありません。一方で、このような技術を習得しておくと、特に私のような自営業な技術者にとっては、新しい仕事に取り組む上での有用なツールとして活用できそうです。同じような立場の技術者様に、少しでも御参考頂ければ幸いです。

今日はここまで。