新しくなったenebularを使ってみる Vol.1　～値の見えるWebページを作る～

#最初に
１月末にリリースされたenebularを使って値の見えるWebページを作ってみたいと思います。
enebular関連の開発には携わっていましたが、正直そんなに使ったことなかったのでがっつり使ってみようと思いました。

URLは前と同じ [https://enebular.com/](https://enebular.com/)　です。そこにアクセスしてアカウント登録をするとフローが作れるようになります。ファーストステップは[マニュアル](https://docs.enebular.com/ja/)の[GetStartedページ](https://docs.enebular.com/ja/GetStarted/)に書いてあります。

フローエディタの画面（５秒おきにタイムスタンプを表示するフロー）
![image.png](https://qiita-image-store.s3.amazonaws.com/0/109992/a87023ec-ee73-0c68-06b0-5232b35bdc2f.png)


値の見えるWebページとは、例えばデバイスから送られてくる温度や湿度、人感センサなどのセンサーの値を離れた場所でも確認できるようにするということです。
出先で、家の中の温度とか、誰かいるかが確認出来たら便利だなと思いました。
それとWebページにしておくとどんな端末からでも確認ができるので、スマホに新しいアプリを入れたりする必要もないですし、最近のゲーム機等でもネットに繋がる端末であれば見ることができます。

Web系の知識があんまり無い私にとって、気軽にこのようなWebページをWebサービス上で作れるというのはかなり魅力に感じました。

#作ったものを紹介

##作ったフロー
実際に作ったフローはこんな感じです。
![image.png](https://qiita-image-store.s3.amazonaws.com/0/109992/109c5304-deef-8b3d-2dd8-24b10c326bf1.png)

HTTPノード使うとHTTPサーバとして動作します。
Milkcocoa経由でデバイスから受け取った値を保持して、ブラウザからのアクセスがあったときにデバイスからの値を表示するWebページとして返します。
わずか８個のノードを繋げるだけで作れます。


##値を表示させたWebページ
enebularのフローエディタで作ったフローはデプロイするとenebular上で動作します。URLはフローエディタ画面上の i マークから取得できます。ここらへんはenebularならではですね。AWS IoTやHerokuにデプロイして常に動かすこともできるみたいなので、別途確認したいと思います。

取得したURLをスマホのブラウザで開くとこんな感じで表示されます。今回は温度、湿度、人感センサを表示してます。
![image.png](https://qiita-image-store.s3.amazonaws.com/0/109992/4c3b6e03-946e-657b-1323-8a63d8ab3965.png)


##デバイス側
センサーの値をMilkcocoa経由でenebularに渡すデバイスは３種類実験しました。

###NUCLEO-L432KC（Mbed）+ Multi IoT Board
まずローカルでもWeb経由でも温度、湿度、気圧、照明のON/OFFを確認できるようにそれらが載ってる[Multi IoT Board](http://www.runele.com/ca1/47/p-r-s/)と[Mbed OS](https://os.mbed.com/)が動く[NUCLEO-L432KC](https://os.mbed.com/platforms/ST-Nucleo-L432KC/)を使いました。
![image.png](https://qiita-image-store.s3.amazonaws.com/0/109992/8a90501a-8780-a917-bb05-41c1a87f5162.png)


###Nefry BT
Wi-Fiがある前提では[Nefry BT](https://dotstud.io/shop/nefry-bt-single/)に温湿度センサーと人感センサを繋げました。
![image.png](https://qiita-image-store.s3.amazonaws.com/0/109992/1dd19682-a1f1-f6b1-b9c3-84f562e1d95a.png)

###Wio LTE
Wi-Fiが無い環境では[Wio LTE](https://soracom.jp/products/wio_lte/)に同じく温湿度センサーと人感センサを繋げました。
![image.png](https://qiita-image-store.s3.amazonaws.com/0/109992/8d8de946-fb81-cc7a-403b-6fb7e46d4e3b.png)

##作ってみて
enebularのフローは10分程度、トータルで半日もかかりませんでした。ちゃちゃっと作るにはenebularもWio LTEもNefryも良いですね。
ちなみにデバイスから表示情報も送ることで、センサーを変えてもデバイス側のプログラムを変えるだけで、Webページの表示が切り替わるように工夫しています。
あとデバイスから送るIDで分けることでURLの引数によって表示するデバイスを切り替えることができます。

##作ったものを使ってみて
結構便利でした。離れた場所から家の状況を確認できるのはとても便利です。

#作り方
## enebularのフロー
![image.png](https://qiita-image-store.s3.amazonaws.com/0/109992/0e8f06d6-7fab-a6cf-c29b-43299a94b8b3.png)

```json
[ { "id": "8d3932c8.e78b9", "type": "tab", "label": "Flow 1" }, { "x": 400, "y": 180, "z": "8d3932c8.e78b9", "id": "175baf78.65dbc1", "url": "/environment", "name": "HTTP GETを受けたら", "type": "http in", "wires": [ [ "7cccfea2.9a49d" ] ], "method": "get", "swaggerDoc": "" }, { "x": 889.3000869750977, "y": 224.60005855560303, "z": "8d3932c8.e78b9", "id": "38af2357.c3c6ec", "name": "レスポンスを返す", "type": "http response", "wires": [] }, { "x": 450, "y": 340, "z": "8d3932c8.e78b9", "id": "d5e7c19e.1ebff", "name": "デバイスからセンサ値を受け取ったら", "type": "milkcocoa in", "wires": [ [ "a7d3de1e.1ede1" ] ], "dataStore": "environment", "milkcocoa": "ba0e41a7.f257", "operation": "push" }, { "x": 720, "y": 340, "z": "8d3932c8.e78b9", "id": "a7d3de1e.1ede1", "func": "var id = msg.payload.value.id;\nglobal.set(id + \".temperature\",msg.payload.value.temperature);\nglobal.set(id + \".humidity\",msg.payload.value.humidity);\nreturn msg;", "name": "センサ値取得", "type": "function", "noerr": 0, "wires": [ [ "35fecfa3.6204a" ] ], "outputs": 1 }, { "x": 626.0000228881836, "y": 184.00000500679016, "z": "8d3932c8.e78b9", "id": "7cccfea2.9a49d", "func": "var id = msg.payload.id\nmsg.payload =\"<h1>環境情報</h1>\";\nmsg.payload += \"温度：\" + global.get(id + \".temperature\") + \"度<br>\";\nmsg.payload += \"湿度：\" + global.get(id + \".humidity\") + \"％<br>\";\nmsg.payload += \"Co2：212ppm<br><br>\";\nmsg.payload += \"<img src=\\\"http://jksoft.cocolog-nifty.com/img_floorplan1.png\\\">\";\nmsg.payload += \"<meta http-equiv=\\\"refresh\\\" content=\\\"10\\\" >\";\nreturn msg;\n", "name": "ID正常", "type": "function", "noerr": 0, "wires": [ [ "38af2357.c3c6ec" ] ], "outputs": 1 }, { "x": 390, "y": 260, "z": "8d3932c8.e78b9", "id": "62d22cec.bf6524", "name": "異常が起こったら", "type": "catch", "scope": null, "wires": [ [ "c785b643.4a9308" ] ] }, { "x": 630, "y": 260, "z": "8d3932c8.e78b9", "id": "c785b643.4a9308", "func": "msg.payload =\"<h1>存在しないIDです</h1>\"\nreturn msg;", "name": "ID異常", "type": "function", "noerr": 0, "wires": [ [ "38af2357.c3c6ec" ] ], "outputs": 1 }, { "x": 902.5, "y": 339.8000183105469, "z": "8d3932c8.e78b9", "id": "35fecfa3.6204a", "name": "デバッグ用", "type": "debug", "wires": [], "active": true, "console": "false", "complete": "payload" }, { "x": 409.5, "y": 544.4000177383423, "z": "8d3932c8.e78b9", "id": "7b3a1166.6d451", "name": "", "once": true, "type": "inject", "topic": "", "wires": [ [ "b5f4f99c.677e88" ] ], "repeat": "", "crontab": "", "payload": "", "payloadType": "date" }, { "x": 704.7999877929688, "y": 559, "z": "8d3932c8.e78b9", "id": "b5f4f99c.677e88", "func": "var id = 1;\nglobal.set(id + \".temperature\",25.1);\nglobal.set(id + \".humidity\",32);\nreturn msg;", "name": "センサ値取得", "type": "function", "noerr": 0, "wires": [ [] ], "outputs": 1 } ]
```
###各ノードの設定
####①httpノード
```
Method : GET
URL : /environment
```
####②funcionノード
```js
var id = msg.payload.id
msg.payload ="<h1>環境情報</h1>";
msg.payload += "温度：" + global.get(id + ".temperature") + "度<br>";
msg.payload += "湿度：" + global.get(id + ".humidity") + "％<br>";
return msg;
```
####③http responseノード
設定なし
####④milkcocoa-inノード
```
Log in as:milkcocoaのログインID
Data Store:データストア名
Operation:push
```
####⑤funcionノード
```js
var id = msg.payload.value.id;
global.set(id + ".temperature",msg.payload.value.temperature);
global.set(id + ".humidity",msg.payload.value.humidity);
return msg;
```
####⑤catchノード
```
Catch errors from:all notes
```
####⑦funcionノード
```js
var id = msg.payload.value.id;
global.set(id + ".temperature",msg.payload.value.temperature);
global.set(id + ".humidity",msg.payload.value.humidity);
return msg;
```

## NUCLEO-L432KC（Mbed）+ Multi IoT Board
### 購入先
* Multi IoT Board  [ランニングエレクトロニクス Multi IoT Board（3月末に入荷予定）](http://www.runele.com/ca1/47/p-r-s/)
* NUCLEO-L432KC　[Mouser NUCLEO-L432KC](https://www.mouser.jp/ProductDetail/STMicroelectronics/NUCLEO-L432KC?qs=%2fha2pyFaduhQJSbhHAGqiRfbHkXnLwc1tbxG8B7VhxDSYs3ACEDv%2fA%3d%3d)

### 準備
NUCLEO-L432KCはファームウェアをアップデートしないと書き込めません。アップデート方法は[ここ](https://os.mbed.com/teams/ST/wiki/Nucleo-Firmware)を参照してください。

### ソースコード
MbedのコードはSSIDやMilkcocoaのapp idを変更する必要があります。

```c++
#include "mbed.h"
#include "Milkcocoa.h"
#include "BME280.h"
#include "ESP8266Interface.h"

ESP8266Interface wifi(D1,D0);
RawSerial pc(USBTX,USBRX);
BME280 bme(I2C_SDA, I2C_SCL);

/************************* WiFi Access Point *********************************/

#define WLAN_SSID       "..ssid .."
#define WLAN_PASS       ".. password .."

/************************* Your Milkcocoa Setup *********************************/

#define MILKCOCOA_APP_ID      ".. milkcocoa app id .."
#define MILKCOCOA_DATASTORE   "environment"

/************* Milkcocoa Setup (you don't need to change this!) ******************/

#define MILKCOCOA_SERVERPORT  1883

/************ Global State (you don't need to change this!) ******************/
const char MQTT_SERVER[]  = MILKCOCOA_APP_ID ".mlkcca.com";
const char MQTT_CLIENTID[] = __TIME__ MILKCOCOA_APP_ID;

extern void onpush(MQTT::MessageData& md);

int main() {
    
    pc.baud(115200);
    pc.printf("Milkcocoa mbed os ver demo\n\r\n\r\n\r");
    bme.initialize();
    pc.printf("Connecting to %s\n\r",WLAN_SSID);
    
    int ret = wifi.connect(WLAN_SSID, WLAN_PASS, NSAPI_SECURITY_WPA_WPA2);
    if (ret != 0) {
        printf("\r\nConnection error\r\n");
        return -1;
    }
    pc.printf("\n\rWiFi connected\n\r");
    
    Milkcocoa* milkcocoa = new Milkcocoa(&wifi, MQTT_SERVER, MILKCOCOA_SERVERPORT, MILKCOCOA_APP_ID, MQTT_CLIENTID);
    
    milkcocoa->connect();
    
    int count = 0;
    while(1) {
        DataElement elem = DataElement();
        elem.setValue("temperature", bme.getTemperature());
        elem.setValue("humidity", bme.getHumidity());
        elem.setValue("id", "home");
        
        if(count >= 7 ){
            milkcocoa->push(MILKCOCOA_DATASTORE, elem);
            count = 0;
        }
        count++;

        milkcocoa->loop();
        
        Thread::wait(1*1000);
        
    }
}

```

Mbed用のワークスペース一式は下記でパブリッシュしてます。
[https://os.mbed.com/users/jksoft_mbedos5/code/Milkcocoa_multi_iot_board/](https://os.mbed.com/users/jksoft_mbedos5/code/Milkcocoa_multi_iot_board/)

##Wio LTE
### 購入先
* Wio LTE　[スイッチサイエンス　Wio LTE](https://www.switch-science.com/catalog/3600/)　or [ソラコム　https://soracom.jp/products/wio_lte/]
* GROVE - デジタル温度・湿度センサ [スイッチサイエンス GROVE - デジタル温度・湿度センサ](https://www.switch-science.com/catalog/818/)
* GROVE - PIRモーションセンサ [スイッチサイエンス GROVE - PIRモーションセンサ](https://www.switch-science.com/catalog/1049/)
* ナノ SIM　[ソラコム ナノSIM](https://soracom.jp/products/#sim_jp)

コードと詳細な作り方は、2018年3月末に出版予定の`ArduinoとJavaScriptで始めるWio LTEを使ったIoTプロトタイピング: 直接インターネットに繋がるかんたん電子工作 Groveリファレンス編（その２）`に記載予定です。
Wio LTEのファーストステップはすでに出版済みの[ArduinoとJavaScriptで始めるWio LTEを使ったIoTプロトタイピング: 直接インターネットに繋がるかんたん電子工作 ファーストステップ編](https://amzn.to/2GrLPBf)で紹介しています。