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

(更新

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

今日はここまで。