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

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

# 研究室にもIoTの力を取り入れよう
## その日は突然やってくる
![one_day.png](https://qiita-image-store.s3.amazonaws.com/0/212276/ada85113-6dd4-62cf-13db-af3c049b93d7.png)

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

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

![image.png](https://qiita-image-store.s3.amazonaws.com/0/212276/07f3e2a7-940a-e072-a90c-7794c0f9985e.png)

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

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

![problem.png](https://qiita-image-store.s3.amazonaws.com/0/212276/baf0f058-2eb5-f44b-a482-d2b9b0548072.png)

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

## システム概要
下の図が概要図です。
あっ、*推進機**というワードに気になりますよ？
僕は人工衛星の未来の推進機(エンジンのような感じ)の研究をしています。
ワクワクしますよね？ロマン感じますよね？
ロマン感じた方は、こちらに僕が所属している[研究室ホームページ(「東海大学 堀澤研究室」で検索！検索！)]
(http://150.7.44.1/horisawa/research.html)に訪れるとよくわかりますよ(華麗なるダブル・ステルスマーケティング)。
![image.png](https://qiita-image-store.s3.amazonaws.com/0/212276/67107426-8c91-3beb-7562-ae3956bcd626.png)

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

![image.png](https://qiita-image-store.s3.amazonaws.com/0/212276/6f066175-54b6-24c2-070c-0a6a9f77f1fc.png)

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

![image.png](https://qiita-image-store.s3.amazonaws.com/0/212276/b198c196-5caa-145b-a8c5-949e4f7d6b76.png)

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

![image.png](https://qiita-image-store.s3.amazonaws.com/0/212276/79448c02-56f2-ea22-ba64-e287a3ef8a72.png)

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

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

です。
※Nefry BTでステッピングモータを制御する方法は[こちら](https://dotstud.io/blog/stepper-motor-nefrybt-control/)です。

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

```c++

void setup() {

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

```

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

```c++

//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](https://qiita-image-store.s3.amazonaws.com/0/212276/26c41a26-1a03-410b-6930-6d4dab1eac36.png)

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

![image.png](https://qiita-image-store.s3.amazonaws.com/0/212276/9570349c-891a-9581-e247-8a179c4f9ae0.png)

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

<blockquote class="twitter-tweet" data-lang="ja"><p lang="ja" dir="ltr">xy軸ステージ×Node-RED <a href="https://t.co/CBRai1cNAW">https://t.co/CBRai1cNAW</a> <a href="https://twitter.com/YouTube?ref_src=twsrc%5Etfw">@YouTube</a>さんから</p>&mdash; たくろーどん@宇宙エンジニア (@takudooon) <a href="https://twitter.com/takudooon/status/1051829383848112129?ref_src=twsrc%5Etfw">2018年10月15日</a></blockquote>
<script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>

<blockquote class="twitter-tweet" data-lang="ja"><p lang="ja" dir="ltr">xy軸ステージ×Node-RED(本番環境) <a href="https://t.co/mjTTwzCkfz">https://t.co/mjTTwzCkfz</a> <a href="https://twitter.com/YouTube?ref_src=twsrc%5Etfw">@YouTube</a>さんから</p>&mdash; たくろーどん@宇宙エンジニア (@takudooon) <a href="https://twitter.com/takudooon/status/1051830244636684289?ref_src=twsrc%5Etfw">2018年10月15日</a></blockquote>
<script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>

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