milkcocoa
enebular
WioLTE

Wio LTEとmilkcocoaとenebularで環境情報をブラウザで確認できるようにする

Wio LTEに付けたセンサーの値を簡単にブラウザで確認できるようにmilkcocoaenebularを使って作ってみました。

Wio LTE側にはGroveの温湿度センサを付けて、温度と湿度をmilkcocoaにプッシュするようにしています。
image.png

ブラウザではこんな風に確認できます。
image.png
 

セキュリティ的にURLだけでは見れないようにWio LTE側で設定しているIDが一致しないと表示されないようにしました。
IDが一致しないと次のように表示されます。
image.png

Wio LTE側のスケッチ

Wio LTE側は温湿度センサから取得した温度と湿度、それとIDをmilkcocoaにプッシュしています。
基本的にmilkcocoaのスケッチ例と温湿度センサのスケッチ例を合わせこんだだけです。
milkcocoaライブラリの扱い方は以前に書いた記事を参照してください。

#include <WioLTEforArduino.h>
#include <WioLTEClient.h>
#include <stdio.h>
#include <Milkcocoa.h>

#define APN               "soracom.io"
#define USERNAME          "sora"
#define PASSWORD          "sora"

#define SENSOR_PIN        (WIOLTE_D38)

#define ID                "12345678"    // ブラウザで情報を見るためのID

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

#define MILKCOCOA_APP_ID      "...YOUR_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!) ******************/

WioLTE Wio;
WioLTEClient WioClient(&Wio);

const char MQTT_SERVER[] PROGMEM    = MILKCOCOA_APP_ID ".mlkcca.com";
const char MQTT_CLIENTID[] PROGMEM  = __TIME__ MILKCOCOA_APP_ID;

Milkcocoa milkcocoa = Milkcocoa(&WioClient, MQTT_SERVER, MILKCOCOA_SERVERPORT, MILKCOCOA_APP_ID, MQTT_CLIENTID);

void setupLTE() {
  delay(200);

  SerialUSB.println("");
  SerialUSB.println("--- START ---------------------------------------------------");

  SerialUSB.println("### I/O Initialize.");
  Wio.Init();

  SerialUSB.println("### Power supply ON.");
  Wio.PowerSupplyLTE(true);
  delay(5000);

  SerialUSB.println("### Turn on or reset.");
  if (!Wio.TurnOnOrReset()) {
    SerialUSB.println("### ERROR! ###");
    return;
  }

  SerialUSB.println("### Connecting to \""APN"\".");
  delay(5000);
  if (!Wio.Activate(APN, USERNAME, PASSWORD)) {
    SerialUSB.println("### ERROR! ###");
    return;
  }
}

void setup() {
  Serial.begin(115200);
  delay(10);

  setupLTE();

  TemperatureAndHumidityBegin(SENSOR_PIN);
}

void loop() {
  float temp;
  float humi;

  if (!TemperatureAndHumidityRead(&temp, &humi)) {
    SerialUSB.println("ERROR!");
  }
  else
  {
    SerialUSB.print("Current humidity = ");
    SerialUSB.print(humi);
    SerialUSB.print("%  ");
    SerialUSB.print("temperature = ");
    SerialUSB.print(temp);
    SerialUSB.println("C");

    milkcocoa.loop();

    DataElement elem = DataElement();

    elem.setValue("temperature", temp);
    elem.setValue("humidity", humi);
    elem.setValue("id", ID);

    milkcocoa.push(MILKCOCOA_DATASTORE, &elem);
  }

  delay(7000);
}

////////////////////////////////////////////////////////////////////////////////////////
//

int TemperatureAndHumidityPin;

void TemperatureAndHumidityBegin(int pin)
{
  TemperatureAndHumidityPin = pin;
  DHT11Init(TemperatureAndHumidityPin);
}

bool TemperatureAndHumidityRead(float* temperature, float* humidity)
{
  byte data[5];

  DHT11Start(TemperatureAndHumidityPin);
  for (int i = 0; i < 5; i++) data[i] = DHT11ReadByte(TemperatureAndHumidityPin);
  DHT11Finish(TemperatureAndHumidityPin);

  if(!DHT11Check(data, sizeof (data))) return false;
  if (data[1] >= 10) return false;
  if (data[3] >= 10) return false;

  *humidity = (float)data[0] + (float)data[1] / 10.0f;
  *temperature = (float)data[2] + (float)data[3] / 10.0f;

  return true;
}

////////////////////////////////////////////////////////////////////////////////////////
//

void DHT11Init(int pin)
{
  digitalWrite(pin, HIGH);
  pinMode(pin, OUTPUT);
}

void DHT11Start(int pin)
{
  // Host the start of signal
  digitalWrite(pin, LOW);
  delay(18);

  // Pulled up to wait for
  pinMode(pin, INPUT);
  while (!digitalRead(pin)) ;

  // Response signal
  while (digitalRead(pin)) ;

  // Pulled ready to output
  while (!digitalRead(pin)) ;
}

byte DHT11ReadByte(int pin)
{
  byte data = 0;

  for (int i = 0; i < 8; i++) {
    while (digitalRead(pin)) ;

    while (!digitalRead(pin)) ;
    unsigned long start = micros();

    while (digitalRead(pin)) ;
    unsigned long finish = micros();

    if ((unsigned long)(finish - start) > 50) data |= 1 << (7 - i);
  }

  return data;
}

void DHT11Finish(int pin)
{
  // Releases the bus
  while (!digitalRead(pin)) ;
  digitalWrite(pin, HIGH);
  pinMode(pin, OUTPUT);
}

bool DHT11Check(const byte* data, int dataSize)
{
  if (dataSize != 5) return false;

  byte sum = 0;
  for (int i = 0; i < dataSize - 1; i++) {
    sum += data[i];
  }

  return data[dataSize - 1] == sum;
}

////////////////////////////////////////////////////////////////////////////////////////

enebular側のフロー

フローはこんな感じになっています。
image.png

2つのフローから構成されていて、1つはmilkcocoa経由でWio LTEから取得した温度と湿度、IDをグローバル変数に格納するフローです。もう一つのフローはHTTPのリクエストを受けて、引数のIDがWio LTEから受け取ったIDと一致すれば温度湿度を表示するためのHTMLをレスポンスで返し、一致しなければ一致しない警告を表示するためのHTMLをレスポンスで返すようにしています。

主要なノードの設定は次の通りです。

milkcocoaノード

milkcocoaのapp idとデータストア名、オペレーションを設定しています。
image.png

functionノード(センサ値取得)

「センサ値取得」と書かれているfunctionノードのプログラムはmilkcocoaノードから出力される各値をグローバル変数に代入しています。

image.png

global.set("temperature",msg.payload.value.temperature);
global.set("humidity",msg.payload.value.humidity);
global.set("id",msg.payload.value.id);
return msg;

http inノード

メソッドはGETに設定、URLのパスは「/environment」としてます。
image.png

switchノード

http inノードから出力される引数idとグローバル変数のidを比較しています。

image.png

functionノード(ID正常)

グローバル変数から温度と湿度を表示するためのHTMLを生成しています。

image.png

msg.payload ="<h1>環境情報</h1>";
msg.payload += "温度:" + global.get("temperature") + "度<br>";
msg.payload += "湿度:" + global.get("humidity") + "%<br>";
return msg;

備考

URLの調べ方

enebular上で動作させた場合のURLはenebularの画面の右上、デプロイボタンの隣のインフォメーションのアイコンで見ることができます。
今回の場合はパスと引数を含めると以下の様になります。

https://{調べたURL}/environment?id=12345678

フローはずっと動いてる?

enebular上のフローは一日ほどしか動作しません。そのため、動作させ続けるにはAWS上にデプロイしてください。

最後に

Wio LTEに接続するセンサーの種類を増やして家の環境情報をスマホで常に確認出来たらちょとと便利だと思います。
milkcocoaにプッシュした情報は同時に別なデバイスでも受け取れるので、連動して何かを動かすこともできそうですね。
紹介したIDを使ったアクセスを制限する方法は簡易的なものなので、絶対にバレたくない情報には使わないでください。