enebularを利用し環境データ表示のウェブページの爆速プロトタイピング

####＜概要＞
普段htmlを使用しないのですが、ちょっとしたUIを作成する必要があり、そのためにhtmlで実験を行う必要が出てきました
そこでバックエンドをenebularに任せることにより、htmlのプロトタイピング環境を爆速で作成しました!

####＜目的＞
HTMLの実験環境を作成する
webページのプロトタイピングのバックエンドを爆速で作成する
（ついでに取得した環境データを表示する）

####＜開発環境＞
windows10 　[enebular](https://www.enebular.com/ja/) 　goooglchrome　　[esp32](https://www.digikey.jp/product-detail/ja/schtoeta-engineering-limited/ESP32-DEVKITC-32UE/1965-ESP32-DEVKITC-32UE-ND/12091813?utm_adgroup=RF%2FIF%20and%20RFID&utm_source=google&utm_medium=cpc&utm_campaign=Shopping_Product_IoT&utm_term=&productid=12091813&gclid=CjwKCAiAt9z-BRBCEiwA_bWv-D8OttkPGjna7ENRPXPbM-YqDO1EqH3UVBXci07042oHRmaq3SpX8xoC_AsQAvD_BwE) 　[BME280](https://store.shopping.yahoo.co.jp/stk-shop/73008235.html)


####＜流れ＞
環境データを取得してIoTデバイスでmqttプロトコルで送信する
IoTデバイスで取得した環境データをmqttで受信する
取得したデータの中から必要なデータを抜き出す
抜き出したデータをグローバル領域に保存する
httpリクエストを受信したら保存してあるデータを取り出す
作製しておいたたhtmlテンプレートに取り出したデータを挿入する
作製したhtmlをレスポンスとして返す

####＜方法＞
esp32マイコンと温湿度気圧を取得できるBME280センサーを接続します
ピンを接続するときはは電源→3.3V、GND→GND、SCL→22、SDA→21に接続します
M5stackを使用するときも同じピン番号を使用するようにするようにしてください

ブローカーサーバーは別途用意する必要があります
今回は[mqtt.uko.jp](https://mqtt.uko.jp/)の環境を使用させてもらうことにしました
個人での使用であれば、登録不要で使用することができます
[mqtt.uko.jp](https://mqtt.uko.jp/)を開くと
mqttのパスワードは毎日変わるようになっており、そのパスワードはページを開いたときに表示されたものを使用するようになっています
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/244155/701b1dc2-fcf3-d4e1-1a32-211519a180d2.png)
ユーザーネームはicecreamで固定のようです
トピック名はtest等簡単なものは避けて、ユニークになるようにした方が良いと思います

テストできるようにソースコードを張っておきます
コピペですぐに使用できるようにしています
ESP32もしくはM5stack用です

```
/*
ESP32の使用を前提にしている
M5stackでもおそらくそのまま使用可能（未確認）
使用するセンサー bme280
bme280を使用するライブラリをインストールしておく必要がある
-----------------------------
Vin (Voltage In)    ->  3.3V
Gnd (Ground)        ->  Gnd
SDA (Serial Data)   ->  21
SCL (Serial Clock)  ->  22

BME280センサーの値を取得してmqttでpublishする

@author S.Nakamura

 */

//通信ライブラリの読み込み
#include <WiFi.h>
#include <PubSubClient.h>
//センサのライブラリの読み込み
#include <BME280I2C.h>
#include <Wire.h>

//ネットワークの設定-----------------------------------------------
//SSIDの設定
const char ssid[] = " your_ssid";                     //ssidの設定（変更）
const char passwd[] = "your_passwd";                  //パスワードの設定（変更）

// Pub/Subの設定
const char* mqttHost = "mqtt.uko.jp";                 // Mqttのサーバーを指定（固定）
const int mqttPort = 1883;                            // MQTTのポートを指定(固定)
const char* mqtt_username = "icecream";               //ユーザーネーム（固定）
const char* mqtt_password = "Today's password";       //mqttのパスワード（変更）

const char* topic = "topic_name";                     // 送信するトピック名（変更）
char* payload;                                        // 送信するデータ
//--------------------------------------------------------------

#define ESP_SERIAL_BAUD 115200      //シリアルのボーレート(ESP32で適切な値にしている)
#define PAYLOAD_SIZE 100            //payloadのサイズの指定

//インスタンスの作成
WiFiClient wifiClient;
PubSubClient mqttClient(wifiClient);
BME280I2C bme;

//プロトタイプ宣言
//mqttのセットアップ
void MqttSet();
//Wi-Fiの接続
void connectWiFi();
//mqttの接続
void connectMqtt();
//mqttの送信
//引数：ペイロードの中身
void MqttPublish(char *PayloadMemory);
//周辺回路の電源オン
void I2CPin_Setup(void);
//測定してペイロードを作る関数
void MeasureSensor(char *PayLoadMemory);   
//BMEセンサーのセット
void BME_set();
//BMEセンサーでデータ測定
void MeasureBME(float *temp_display, float *hum_display, float *pres_display);


void setup() {

  //シリアルの初期化
  Serial.begin(ESP_SERIAL_BAUD);
  
  //周辺回路の電源オン
  I2CPin_Setup();
  
  Serial.println("Sensor system start!");
  
  //BMEセンサーのセット実行
  BME_set();  
  
  //MQTTのセット Wi-FiとMqttの通信を確立
  MqttSet();  
  
}

void loop() {
  
  //ペイロードの領域を確保
  char PayLoad[PAYLOAD_SIZE]; 
  
  //センサーの値の計測
  MeasureSensor(PayLoad);

  //データの送信
  MqttPublish(PayLoad);
  
  //時間を調節
  delay(5000);          //状況に応じて変更
  
}

//mqttをセットする関数
void MqttSet() {

  //Wi-Fiの接続
  connectWiFi();
  
  //mqttの接続
  connectMqtt();
  
}


//mqttを送信する関数
//引数；ペイロード
void MqttPublish(char *PayloadMemory){

  //送信データの作成
  payload = PayloadMemory;
  
  //mqttの送信
  //第一引数：トピック名
  //第二引数：送信するデータ
  mqttClient.publish(topic, payload);
  
  //コンソールに送信するデータを表示して確認
  Serial.println("published");

}

//Wi-Fiを接続する関数
void connectWiFi(){

  //Wi-Fiのアクセスポイントに接続
  //第一引数：ssid
  //第二引数：パスワード      
  WiFi.begin(ssid, passwd);
  Serial.print("WiFi connecting...");
  
  int i = 0 ;   //接続確認のリセット用
  //Wi-Fiの接続確認
  while(WiFi.status() != WL_CONNECTED) {
  i += 1;
  Serial.print("...");
  delay(5000);
  //1分間Wi-Fiが接続できないときは接続をやり直す
  if (i == 12){
    Serial.println("wi-fi reset");
    connectWiFi();
      }
  }
  //Wi-Fiの接続が確認出来たらコンソールに表示して確認する      
  Serial.print(" connected. ");
  Serial.println(WiFi.localIP());

}

//mqttに接続する関数
void connectMqtt(){

  //brokerサーバーに接続する
  //第一引数：ブローカーサーバーのドメインもしくはipアドレス
  //第二引数：接続するポート（通常は1883か8883）
  mqttClient.setServer(mqttHost, mqttPort);
  
  //clientIDを作成してサーバーに接続する
  while( ! mqttClient.connected() ) {
    
    Serial.println("Connecting to MQTT...");
    //clientIDを乱数から作成する
    String clientId = "ESP32-" + String(random(0xffff), HEX);
    //接続の確認
    if ( mqttClient.connect(clientId.c_str(),mqtt_username, mqtt_password) ) {  
      Serial.println("connected"); 
  
    }
  //clientIDを作製するために乱数を発生させる
  randomSeed(micros());
  
  }
}


//センサの測定 payloadの作成まで------------------------------------------------------------
//引数：ペイロードを作成する場所
void MeasureSensor(char *PayLoadMemory){

  //確認用にコンソールに表示
  Serial.println("");
  Serial.println("Measure");

  //測定した値を読み込むための領域の確保
   float temp, hum, pres;
   
   //BMEセンサーで測定した値の領域の確保
   //第一引数：温度を保存するアドレス
   //第二引数：湿度を保存するアドレス
   //第三引数：気圧を保存するアドレス
   MeasureBME(&temp, &hum, &pres);

  //コンソールに表示して確認する
   Serial.print("temperature is ");
   Serial.println(temp);
   Serial.print("humdity is");
   Serial.println(hum);
   Serial.print("pressure is ");
   Serial.println(pres);
  
  //ペイロードをJSONで作成する
  sprintf(PayLoadMemory,"{\"BMEtemp\":\%0.1f\,\"BMEhum\":\%0.1f\,\"BMEpres\":%0.1f}",temp,hum,pres);
  
  //コンソールに表示してペイロードの中身を確認する
  Serial.print("payload is ");
  Serial.println(PayLoadMemory);
  
}

//bme２８０をセットアップする関数
void BME_set()
{
  //i2cで接続する
  Wire.begin();
  
  //i2cの接続を確認する
  while(!bme.begin())
  {
    Serial.println("Could not find BME280 sensor!");
    delay(1000);
  }

  //接続されているセンサーのタイプを確認する
  switch(bme.chipModel())
  {
     case BME280::ChipModel_BME280:
       Serial.println("Found BME280 sensor! Success.");
       break;
     case BME280::ChipModel_BMP280:
       Serial.println("Found BMP280 sensor! No Humidity available.");
       break;
     default:
       Serial.println("Found UNKNOWN sensor! Error!");
  }
}

//bmeセンサーで測定した値を取得する関数
//第一引数：温度のポインタ
//第二引数：湿度のポインタ
//第三引数：気圧のポインタ
void MeasureBME(float *temp_display, float *hum_display, float *pres_display){

  //使用する領域の確保と初期化
  float temp(NAN); 
  float hum(NAN); 
  float pres(NAN);

  //bmeセンサーから値を読み取る
  bme.read(pres, temp, hum);

  //引数で宣言したアドレスに取得した値を入れていく
  *temp_display = temp;
  *hum_display = hum;
  *pres_display = pres/100;
   
   //delay(1000);
}

```


このソースをArduinoIDEにコピペして, passwd, topic_name, mqtt_passwd を変更すると使用できるようになっています


####ここからはenebularの設定です
[enebularの基本的な使用方法](https://docs.enebular.com/ja/GetStarted/)はわかっている前提で進めます

フロー全体の流れです
mqtt inで受信データをオブジェクトからJSONに変換する
変換したデータから必要なデータを取得してグローバル領域に保存する
httpリクエストが来たら、グローバル領域から必要なデータを取得してhtmlのテンプレートに挿入して、httpでレスポンスを返す
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/244155/9855fa25-7de3-bb26-5819-f311c060a9aa.png)
のようになっています

まずはmqttの受信用のノードを設定する必要があります
mqtt inノードをフローに配置します
mqttの設定で、トピックネーム、サーバー、ポート、パスワードを設定します
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/244155/2c102857-7077-1e1c-e272-9aaf915da716.png)![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/244155/9786f702-7be7-2a30-0b40-606328e3bc27.png)



![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/244155/49037c11-1673-bfce-1315-47c1200c1986.png)
ノードの設定から鉛筆ボタンを押して細かい設定をしていきます
サーバーとポートはESPで設定したものと合わせてください
今回はserver：mqtt.uko.jp , port：1883になります

さらにセキュリティからユーザーネームとパスワードを指定します
ユーザーネームはicecream、パスワードは先ほど取得Today's passwordなります


#####urlを取得していきます
画面の右上のiボタンを押すとurlが表示されますので
表示されたurlが今回使用するurlになります
どこかにコピーしておいてください
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/244155/841074f3-3c62-e551-a6eb-28eed008ff12.png)

#####各ノードの中身を説明していきます
jsonノード葉オブジェクトをjsonに変換するノードになる
global保存ノードの中身

```
//環境データを変数として抽出する
var temp = msg.payload.BMEtemp;
var humi = msg.payload.BMEhum;
var pres = msg.payload.BMEpres;

//抽出したデータをグローバル変数として保存
global.set("global_temp",temp);
global.set("global_humi",humi);
global.set("global_pres",pres);

//デバッグ用に出力する
msg.payload = {
    
    temp,
    humi,
    pres
    
}
return msg;
```


http inノードは
メソッドをGETにして、urlに/testを入れてください
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/244155/8bd9a94e-db04-d89e-3502-5c632adf2311.png)
global取り出しノードの中身です

```
//グローバル領域に保存したデータを取り出す
var temp = global.get("global_temp");
var humi = global.get("global_humi");
var pres = global.get("global_pres");

//取り出したデータをhtmlテンプレートに送る
msg.payload = {
    temp,
    humi,
    pres
}


return msg;
```

テンプレートノードの中にはhtmlを書いていきます

```
<!DOCTYPE html>
<html lang="ja">
 <head>
  <meta charset="utf-8">
  <title>環境データ取得</title> 
  <meta name="description" content="環境データを取得して表示">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <script src="main.js"></script>　　 //おそらく消去する必要がある

  <style type="text/css">
  .box1 {
    padding: 0.5em 1em;
    margin: 2em 0;
    width: 30%;
    height: 25px;
    border: double 5px #4ec4d3;
  }
  .box1 p {
    margin: 0; 
    padding: 0;
  }

  .box2{
    padding: 0.5em 1em;
    margin: 2em 0;
    width: 30%;
    height: 100px;
    color: #5989cf;
    background: #c6e4ff;
    border-bottom: solid 6px #aac5de;
    border-radius: 9px;
}
.box2 p {
    margin: 0; 
    padding: 0;
}
.box3{
    padding: 0.5em 1em;
    margin: 2em 0;
    width: 30%;
    height: 100px;
    color: #ffffff;
    background: #000077;
    border-bottom: solid 6px #aac5de;
    border-radius: 9px;
}
.box3 p {
    margin: 0; 
    padding: 0;
}
  </style>
 </head>
 <body>
  <article>
  <h1>環境データの表示</h1>
  <section>
    <h2>温度</h2>
    <div class="box1">
      <p>現在の温度は{{payload.temp}}℃です</p>
    </div>    
  </section>
  <section>
    <div class="box2">
      <h2>湿度</h2>
      <p>現在の湿度は{{payload.humi}}%です</p>
    </div>
  </section>
  <section>
    <div class="box3">
      <h2>気圧</h2>
      <p>現在の気圧は{{payload.pres}}hpaです</p>
    </div>
  </section>
  </article>
  <footer><small>(c)S.Nakamura</small></footer>  
 </body>
</html>
```

htmlレスポンスノードは特に設定をする必要はありません

ここまで出来たら、フローをデプロイします

ブラウザを開いて
先ほどのiボタンででてきたuriをブラウザに張り付けて、最後に/testを付け足して検索を行うと、作成したページが表示されます
(例)https://exampe.herokuapp.com/test
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/244155/4984d6e1-57a1-6859-cc7f-73bb826fc603.png)
無事にMQTTで取得したデータをhtmlに反映させることに成功
テンプレートの中のhtmlとcssを書き換えることでデザインの変更ができます

http inノードのurlをうまく使用することで、下層ページの表現等もできる（はず）

これで、htmlを利用したUIのプロトタイピングを行う準備ができました

＜まとめ＞
enebularを使用することによってwebページのプロトタイピングが爆速でできました
自分でサーバーを用意する必要がないので、ネットワークさえ使用できれば、簡単にwebページを作成することができるはず
enebularのurlは開くたびに変わるので気をつける必要があります
mqttのパスワードも毎日変更されますので気を付けてください

＜参考情報＞
[node-redでwebページを作ろう](https://tibbo-pi.co-works.co.jp/blog/node-red%E3%81%A7web%E3%83%9A%E3%83%BC%E3%82%B8%E3%82%92%E4%BD%9C%E3%82%8D%E3%81%86/)
[おしゃれなボックスデザイン（囲み枠）のサンプル30](https://saruwakakun.com/html-css/reference/box)
[HTML文書にCSSを適用する](http://www.htmq.com/csskihon/004.shtml)
[mqtt.uko.jp](https://mqtt.uko.jp/)