node-red
enebular

【enebularは神ですか?】enebular×Node-REDで研究室IoTに挑戦!

この記事は健全なステマです

こんにちは、宇宙エンジニアのたくろーどんです。
みなさん、IoTを通したモノづくり楽しんでいますか?
僕は楽しんでいます!
インターネットとモノをつなげるためのマイコンやツールが最近増えてきていて、どれを組み合わせようか迷いますよね…。
僕は、よくNode-REDを使ってセンサからデータをとって可視化したりモーターを遠隔制御したりしています。
ビジュアル的でフローをつなげていくと簡単に何か作れてしまうので結構便利です。
便利なのですが…皆さん、「Node-REDの導入って大変だな…」なんて思ったことありませんか?
僕はあります。ローカル環境に入れるときコマンドをポチポチうち環境を導入しました。
いざ導入しても、よくわからないエラーが出ることもありました。
しかし、Node-REDは何もローカル環境で動かす必要はありません。
例えば、IBM Bluemixから動かすこともできます。しかし、登録が少し時間がかかり、ややこしいな…というのが使ってみた印象です。
ふぉー
そ!こ!で!
Node-REDを手軽に使うには、株式会社 ウフルが提供しているenebularというプラットフォームがとても便利です!
サクッと、新規登録ボタンから必要項目を入力するだけで使えます。
そんなenebularのNode-REDを使って、研究室でIoTしてみました。

研究室にもIoTの力を取り入れよう

その日は突然やってくる

one_day.png

はい、そうです。工学部の学生とはこういうものです。
「ねだるな。勝ち取れ。さすれば与えられん。」の精神です。
無いものは1回つくってみましょう、ということで…実験で必要になったxy軸のステージをつくることになりました。

とりあえず出来た

上に載っているのが、測定機器です。
その下にあるのが、xy軸ステージです。
これは、ネットでいろいろと調べて参考にしながら作りました。
なので、詳細は省きます。

image.png

「さぁ、これをどのようにして動かそう?」ということになりました。

研究室的問題点とレガシー

研究室的問題点とは、いろんな配線が床の上をはっていることです。
レガシーとは、過去に先輩が製作したが今となっては使い方のわからないコントロールボックスのことです。

problem.png

ということで、xy軸ステージの配線を極力減らしてスマートフォンやパソコンから操作できるようにしてみました。

システム概要

下の図が概要図です。
あっ、推進機*というワードに気になりますよ?
僕は人工衛星の未来の推進機(エンジンのような感じ)の研究をしています。
ワクワクしますよね?ロマン感じますよね?
ロマン感じた方は、こちらに僕が所属している研究室ホームページ(「東海大学 堀澤研究室」で検索!検索!)に訪れるとよくわかりますよ(華麗なるダブル・ステルスマーケティング)。
image.png

実際に真空チャンバ―にいれて…

image.png

外部にNefry BTも設置してみました…

image.png

ちなみに真空チャンバ―というものは、真空環境をつくるための装置です。
実験装置をその中にいれたら、3~4時間かけて空気を抜きます(真空チャンバ―の大きさによって時間は異なります)。ということは…実験条件を変更して、実験を続けようとすると真空チャンバ―を一度あけなければいけません。とても時間がかかります。

image.png

なので、xy軸ステージで実験条件を簡単に変えられたら時間短縮できていいですよね。
動作の流れとしては

enebularのNode-REDでつくられたxy軸ステージの操作画面をherokuでデプロイし、スマートフォンやパソコンでボタン操作
ボタン操作で入力された情報がfirebaseを介してNefry BTに送られ、Nefry BTでxy軸ステージのステッピングモーターを制御する

です。
※Nefry BTでステッピングモータを制御する方法はこちらです。

Nefry BT側のプログラム

Nefry BTのプログラムになります。
ただし、firebaseには自分で作成したもののURLを利用してください。

void setup() {

  //値が大きすぎると回転しない、許容量オーバー
  myStepper.setSpeed(15);
  myStepper2.setSpeed(15);
  Serial.begin(115200);
  firebase.begin("自分のfirebaseのURL");//インスタンスを忘れずに動かすこと

}

下が全体のプログラムです。

//firebaseのURLを変更したのでプログラムを書き直すこと
#include <Nefry.h>
#include <NefryFireBase.h>

#include <Stepper.h>
#include <Nefry.h>

//インスタンス生成
NefryFireBase firebase;

const int CW = 1;
const int CCW = -1;

//ライブラリが想定している配線が異なるので2番、3番を入れ替える
//http://stupiddog.jp/note/archives/1235
#define MOTOR_1   D2   // blue
#define MOTOR_2   D3   // pink
#define MOTOR_3   D4   // yellow
#define MOTOR_4   D5   // orange
#define MOTOR_5   D0   // blue
#define MOTOR_6   D6   // pink
#define MOTOR_7   D7   // yellow
#define MOTOR_8   D8   // orange

#define MOTOR_STEPS 2048  


Stepper myStepper(MOTOR_STEPS, MOTOR_1, MOTOR_3, MOTOR_2, MOTOR_4);//2と3番を入れ替えること
Stepper myStepper2(MOTOR_STEPS, MOTOR_5, MOTOR_7, MOTOR_6, MOTOR_8);//6,7

void setup() {

  //値が大きすぎると回転しない、許容量オーバー
  myStepper.setSpeed(15);
  myStepper2.setSpeed(15);
  Serial.begin(115200);
  firebase.begin("自分のfirebaseのURL");//インスタンスを忘れずに動かすこと

}

// モーターへの電流を止める////////////////
void stopMotor() {
  digitalWrite(MOTOR_1, LOW);
  digitalWrite(MOTOR_2, LOW);
  digitalWrite(MOTOR_3, LOW);
  digitalWrite(MOTOR_4, LOW);
  digitalWrite(MOTOR_5, LOW);
  digitalWrite(MOTOR_6, LOW);
  digitalWrite(MOTOR_7, LOW);
  digitalWrite(MOTOR_8, LOW);
  }
///////////////////////////////////////////
void loop() {
  int steps;
  //常に呼ばれている、
  int motor = firebase.read("motor").toInt();
  //Serial.read()で入力されるまで止まるので回避しないと
  //byte var = Serial.read();
  byte var = var - 0x30;
  DataElement elem = DataElement();

  if(motor == 1) {
      //myStepper.step(-10000);
      Serial.println("right");
      steps = get_step_by_rev(CW, 5);
      myStepper.step(steps);
      elem.setValue("motor", 0);
      firebase.write("motor", &elem);//FireBaseのデータを書き込むFireBaseのデータを書き込む

      //delay(3000);
  }else if(motor == 2){
      //myStepper.step(10000);
      Serial.println("left");
      steps = get_step_by_rev(CCW, 5);
      myStepper.step(steps);
      elem.setValue("motor", 0);
      firebase.write("motor", &elem);//FireBaseのデータを書き込む
      //delay(3000);
  }else if(motor == 3){
      //myStepper.step(-10000);
      Serial.println("foward");
      steps = get_step_by_rev(CW, 5);
      myStepper2.step(steps);
      elem.setValue("motor", 0);
      firebase.write("motor", &elem);//FireBaseのデータを書き込む
      //delay(3000);
  }else if(motor == 4){
      //myStepper.step(10000);
      Serial.println("back");
      steps = get_step_by_rev(CCW, 5);
      myStepper2.step(steps);
      elem.setValue("motor", 0);
      firebase.write("motor", &elem);//FireBaseのデータを書き込む
      //delay(3000);
  }else if(var == 5){
      //myStepper.step(-10000);
      Serial.println("foward");
      steps = get_step_by_rev(CW, 5);
      myStepper.step(steps);
      elem.setValue("motor", 0);
      firebase.write("motor", &elem);//FireBaseのデータを書き込む
      //delay(3000);
  }else if(var == 6){
      //myStepper.step(10000);
      Serial.println("back");
      steps = get_step_by_rev(CCW, 5);
      myStepper.step(steps);
      elem.setValue("motor", 0);
      firebase.write("motor", &elem);//FireBaseのデータを書き込む
      //delay(3000);
  }else if(var == 7){
      //myStepper.step(-10000);
      Serial.println("foward");
      steps = get_step_by_rev(CW, 5);
      myStepper2.step(steps);
      elem.setValue("motor", 0);
      firebase.write("motor", &elem);//FireBaseのデータを書き込む
      //delay(3000);
  }else if(var == 8){
      //myStepper.step(10000);
      Serial.println("back");
      steps = get_step_by_rev(CCW, 5);
      myStepper2.step(steps);
      elem.setValue("motor", 0);
      firebase.write("motor", &elem);//FireBaseのデータを書き込む
      //delay(3000);
  }else if(motor == 0){
      stopMotor();
      Serial.println("stop");    
  }
    stopMotor();
    delay(200);

 }
//////////////////////////////////////////////

int get_step_by_rev(int dir, float rev){
  return dir * (MOTOR_STEPS * rev);  
}

Node-REDのフロー

こちらもfirebaseには自分で作成したもののURLを利用してください。

[
    {
        "id": "ab7a4560.a68cf8",
        "type": "firebase.on",
        "z": "b1df9a03.d68bf8",
        "name": "",
        "firebaseconfig": "",
        "childpath": "distance_x",
        "atStart": true,
        "eventType": "value",
        "queries": [
            {
                "name": "orderByChild",
                "value": "distance_x"
            }
        ],
        "x": 409.10000228881836,
        "y": 62,
        "wires": [
            [
                "7f3c5a29.792044",
                "b7454d13.5d424"
            ]
        ]
    },
    {
        "id": "ad870eba.c7fb9",
        "type": "inject",
        "z": "b1df9a03.d68bf8",
        "name": "",
        "topic": "",
        "payload": "{\"motor\":1}",
        "payloadType": "json",
        "repeat": "",
        "crontab": "",
        "once": false,
        "x": 152,
        "y": 291.40000343322754,
        "wires": [
            [
                "48fa529c.e6820c"
            ]
        ]
    },
    {
        "id": "24cfecd1.b2d7d4",
        "type": "inject",
        "z": "b1df9a03.d68bf8",
        "name": "",
        "topic": "",
        "payload": "{\"motor\":2}",
        "payloadType": "json",
        "repeat": "",
        "crontab": "",
        "once": false,
        "x": 151.99999618530273,
        "y": 363.3999996185303,
        "wires": [
            [
                "48fa529c.e6820c"
            ]
        ]
    },
    {
        "id": "3e6e3345.5293ec",
        "type": "inject",
        "z": "b1df9a03.d68bf8",
        "name": "",
        "topic": "",
        "payload": "{\"motor\":3}",
        "payloadType": "json",
        "repeat": "",
        "crontab": "",
        "once": false,
        "x": 151.99999618530273,
        "y": 423.3999996185303,
        "wires": [
            [
                "48fa529c.e6820c"
            ]
        ]
    },
    {
        "id": "5aaf567b.a0eaa8",
        "type": "inject",
        "z": "b1df9a03.d68bf8",
        "name": "",
        "topic": "",
        "payload": "{\"motor\":4}",
        "payloadType": "json",
        "repeat": "",
        "crontab": "",
        "once": false,
        "x": 151.99999618530273,
        "y": 483.3999996185303,
        "wires": [
            [
                "48fa529c.e6820c"
            ]
        ]
    },
    {
        "id": "48fa529c.e6820c",
        "type": "firebase modify",
        "z": "b1df9a03.d68bf8",
        "name": "",
        "firebaseconfig": "",
        "childpath": "",
        "method": "set",
        "value": "msg.payload",
        "priority": "msg.priority",
        "x": 781.1001091003418,
        "y": 548.2000551223755,
        "wires": [
            []
        ]
    },
    {
        "id": "ae045d7e.fc941",
        "type": "template",
        "z": "b1df9a03.d68bf8",
        "name": "HTML",
        "field": "payload",
        "fieldType": "msg",
        "format": "html",
        "syntax": "mustache",
        "template": "<!DOCTYPE html>\n<html lang=\"ja\">\n<head>\n<meta charset=\"utf-8\">\n<meta name=\"viewport\" content=\"width=device-width,initial-scale=1\">\n<title>加速度検出</title>\n</head>\n \n<body>\n<div id=\"datatxt\">データの表示</div> \n <btn class = 'btn'>\n  <a href=\"\">右</a>   \n </btn>\n<script>\n \n\n</script>\n</body>\n</html>",
        "x": 371.1000328063965,
        "y": 122.59999656677246,
        "wires": [
            [
                "bec29e4f.3505a"
            ]
        ]
    },
    {
        "id": "bec29e4f.3505a",
        "type": "template",
        "z": "b1df9a03.d68bf8",
        "name": "CSS",
        "field": "payload",
        "fieldType": "msg",
        "format": "css",
        "syntax": "mustache",
        "template": ".btn{\n    width:90px;\n    height:90px;\n    line-height:90px;\n}\n.btn a{\n    display:block;\n    width:100%;\n    height:100%;\n    text-decoration: none;\n    background:#FF8181;\n    text-align:center;\n    color:#FFFFFF;\n    font-size:20px;\n    font-weight:bold;\n    border-radius:50px;\n    -webkit-border-radius:50px;\n    -moz-border-radius:50px;\n    box-shadow:0px 5px 8px 0px #BBBBBB ;\n    text-shadow:0px 1px 1px #444444 ;\n}\n.btn a:hover{\n    background:#FF0303;\n    color:#FFFFFF;\n    margin-top:5px;\n    box-shadow:none;\n}",
        "x": 494.00000381469727,
        "y": 123.40000057220459,
        "wires": [
            [
                "114bbc32.3ebde4"
            ]
        ]
    },
    {
        "id": "dab74571.5af2c8",
        "type": "http in",
        "z": "b1df9a03.d68bf8",
        "name": "",
        "url": "stage",
        "method": "get",
        "swaggerDoc": "",
        "x": 184.99999618530273,
        "y": 118.39999961853027,
        "wires": [
            [
                "ae045d7e.fc941"
            ]
        ]
    },
    {
        "id": "114bbc32.3ebde4",
        "type": "http response",
        "z": "b1df9a03.d68bf8",
        "name": "",
        "x": 784.9999961853027,
        "y": 132.39999961853027,
        "wires": []
    },
    {
        "id": "6281806c.10e7b",
        "type": "inject",
        "z": "b1df9a03.d68bf8",
        "name": "",
        "topic": "",
        "payload": "{\"motor\":0}",
        "payloadType": "json",
        "repeat": "",
        "crontab": "",
        "once": false,
        "x": 156.99999618530273,
        "y": 222.39999961853027,
        "wires": [
            [
                "48fa529c.e6820c"
            ]
        ]
    },
    {
        "id": "65102861.3e4658",
        "type": "comment",
        "z": "b1df9a03.d68bf8",
        "name": "左",
        "info": "",
        "x": 115.10000419616699,
        "y": 323.2000274658203,
        "wires": []
    },
    {
        "id": "50ef3ee2.ba6e9",
        "type": "comment",
        "z": "b1df9a03.d68bf8",
        "name": "右",
        "info": "",
        "x": 113.99999618530273,
        "y": 256.4000024795532,
        "wires": []
    },
    {
        "id": "583ea2f4.26ae1c",
        "type": "ui_button",
        "z": "b1df9a03.d68bf8",
        "name": "",
        "group": "f7557849.9ddfe8",
        "order": 0,
        "width": 0,
        "height": 0,
        "passthru": false,
        "label": "right",
        "color": "",
        "bgcolor": "",
        "icon": "",
        "payload": "{\"motor\":1}",
        "payloadType": "json",
        "topic": "",
        "x": 585.0000076293945,
        "y": 268.4000053405762,
        "wires": [
            [
                "48fa529c.e6820c"
            ]
        ]
    },
    {
        "id": "d648401.e575ac",
        "type": "ui_button",
        "z": "b1df9a03.d68bf8",
        "name": "",
        "group": "f7557849.9ddfe8",
        "order": 0,
        "width": 0,
        "height": 0,
        "passthru": false,
        "label": "left",
        "color": "",
        "bgcolor": "",
        "icon": "",
        "payload": "{\"motor\":2}",
        "payloadType": "json",
        "topic": "",
        "x": 587.0000114440918,
        "y": 309.4000062942505,
        "wires": [
            [
                "48fa529c.e6820c"
            ]
        ]
    },
    {
        "id": "81c6681c.5085a8",
        "type": "ui_button",
        "z": "b1df9a03.d68bf8",
        "name": "",
        "group": "f7557849.9ddfe8",
        "order": 0,
        "width": 0,
        "height": 0,
        "passthru": false,
        "label": "forward",
        "color": "",
        "bgcolor": "",
        "icon": "",
        "payload": "{\"motor\":3}",
        "payloadType": "json",
        "topic": "",
        "x": 596.0000038146973,
        "y": 349.40000343322754,
        "wires": [
            [
                "48fa529c.e6820c"
            ]
        ]
    },
    {
        "id": "98f617c3.758038",
        "type": "ui_button",
        "z": "b1df9a03.d68bf8",
        "name": "",
        "group": "f7557849.9ddfe8",
        "order": 0,
        "width": 0,
        "height": 0,
        "passthru": false,
        "label": "back",
        "color": "",
        "bgcolor": "",
        "icon": "",
        "payload": "{\"motor\":4}",
        "payloadType": "json",
        "topic": "",
        "x": 586.0000114440918,
        "y": 387.4000072479248,
        "wires": [
            [
                "48fa529c.e6820c"
            ]
        ]
    },
    {
        "id": "2866dc2a.d0dec4",
        "type": "ui_button",
        "z": "b1df9a03.d68bf8",
        "name": "",
        "group": "f7557849.9ddfe8",
        "order": 0,
        "width": 0,
        "height": 0,
        "passthru": false,
        "label": "offset",
        "color": "",
        "bgcolor": "",
        "icon": "",
        "payload": "{\"distance_x\":0, \"distance_y\":0}",
        "payloadType": "json",
        "topic": "",
        "x": 585.0000038146973,
        "y": 227.40000247955322,
        "wires": [
            [
                "48fa529c.e6820c"
            ]
        ]
    },
    {
        "id": "7f3c5a29.792044",
        "type": "ui_text",
        "z": "b1df9a03.d68bf8",
        "group": "373ca1aa.76bade",
        "order": 0,
        "width": 0,
        "height": 0,
        "name": "",
        "label": "distance_x",
        "format": "{{msg.payload}}",
        "layout": "row-spread",
        "x": 921.1000099182129,
        "y": 65.80001258850098,
        "wires": []
    },
    {
        "id": "b7454d13.5d424",
        "type": "ui_gauge",
        "z": "b1df9a03.d68bf8",
        "name": "",
        "group": "373ca1aa.76bade",
        "order": 0,
        "width": 0,
        "height": 0,
        "gtype": "gage",
        "title": "gauge",
        "label": "units",
        "format": "{{value}}",
        "min": "-10",
        "max": 10,
        "colors": [
            "#00b500",
            "#e6e600",
            "#ca3838"
        ],
        "seg1": "",
        "seg2": "",
        "x": 973.1000099182129,
        "y": 158.80001258850098,
        "wires": []
    },
    {
        "id": "f7557849.9ddfe8",
        "type": "ui_group",
        "z": "",
        "name": "Group",
        "tab": "f58979ef.30ad38",
        "order": null,
        "disp": true,
        "width": "6",
        "collapse": false
    },
    {
        "id": "373ca1aa.76bade",
        "type": "ui_group",
        "z": 0,
        "name": "Group",
        "tab": "5d8bed2f.3397a4",
        "order": null,
        "disp": true,
        "width": 6
    },
    {
        "id": "f58979ef.30ad38",
        "type": "ui_tab",
        "z": 0,
        "name": "Tab",
        "icon": "dashboard",
        "order": 0
    },
    {
        "id": "5d8bed2f.3397a4",
        "type": "ui_tab",
        "z": 0,
        "name": "Tab",
        "icon": "dashboard",
        "order": 0
    }
]

importすると下記のようになります。
※node-red-dashboardが入っていない場合は、インストールしましょう。

image.png

herokuでデプロイしてごにょごにょすると下記のように操作画面がスマートフォンやPCから起動できるようになるます(ごにょごにょの部分は少しはまってしまったポイントなので改めて解説したいと思います)。

image.png

動作させてみる

少しわかりづらいかもしれませんが、動作させると以下のようになります。

まとめ

手軽にNode-REDが使えるのは、非常に便利です。
これなら、「難しい」や「手間がかかる」といった理由でモノづくりから疎くなるのもなくなりそうです。
enebularのNode-RED、これからも活用していきたいと思います!