知的好奇心 for IoT

IoT関連の知的好奇心を探求するブログです

WIO NODEとDHT11とバッテリーでNTPとDeep Sleepを駆使しMilkcocoaを経由して外の温度と湿度をWebページに表示する

前回、Wioアプリを使って行おうとした、ベランダの温度と湿度をWebページに表示する記事のリベンジをします。

Wioアプリではバッテリーが持たないことがわかっているので、今回は素直にプログラミングで行きます。

温度・湿度センサーのDHT11はIFTTT経由でTwitterに送る記事で使ったので問題ないとして、残る問題はWebページに表示するための仲介となるサーバーをどうするかです。「なるべく手間をかけないで目的を達成する」をコンセプトにしているので、AWSとかではなくデータを中継してくれるようなサービスがないか調べてみました。

そして、今回利用することにしたのがMilkcocoaです。MQTTをベースにしているそうなんですが、Arduino SDKJavaScript APIでラッピングされているせいかMQTTの知識は要求されません。また、個人で利用するには十分な無料プランが用意されています。

 

ベランダに設置したセンサー

f:id:IntellectualCuriosity:20170330204700j:plain

底に空気穴を開けたおかず容器にWIO NODE、Grove温度・湿度センサー(DHT11)、リチウムポリマー電池を入れて、磁石を付けた蓋でピタッとエアコンの屋外機吊り下げ枠にくっつけました。

 

実際の温度と湿度の表示

ベランダの温度と湿度
時刻取得中...
温度取得中...
湿度取得中...

上の表示はMilkcocoaのJavaScript APIを使って実際のデータを表示しています。PCでご覧の方はサイドバーに同じ表示を見ることができます。

 

Milkcocoaの準備

まずはMilkcocoaに登録します。登録に必要な情報はEmailアドレスとパスワードのみです。

登録が終わってログインするとダッシュボード画面になります。

 f:id:IntellectualCuriosity:20170330224048p:plain

「新しいアプリを作る」でアプリを作ります。アプリ名はダッシュボード上の識別名でしかないのであまり気にする必要はありません。

作ったアプリ名をクリックしてアプリを設定します。

セキュリティを無視すると設定に必要な個所はメニューの「設定」です。

f:id:IntellectualCuriosity:20170330224220p:plain

許可Originに表示したいサイトのURLを登録します。図では「はてなブログの管理サイト」と「ぼくのブログサイト」を登録しています。

 

Arduino IDEの準備

以下のライブラリをArduino IDEに追加します。

Milkcocoaのページでは「ESP8266に直接プログラムを書き込む場合」の手順を行ってください。GithubからSDKをZIPでダウンロードしてライブラリマネージャーに追加します。MacLinuxRaspberry Pi場合は追加後にヘッダーファイルの修正が必要でした。

修正するファイル

[Arduinoインストールフォルダ]/libraries/Milkcocoa_ESP8266_SDK-master/Milkcocoa.h

修正内容 31行目

修正前:#include "include/aJson/aJson.h"
修正後:#include "include/aJson/aJSON.h"

DHT11とNTPのライブラリ追加はぼくの以前の記事を見てくださいね。

 

スケッチのポイント (スケッチのダウンロード)

 Milkcocoaの設定

#define MILKCOCOA_APP_ID       "作成したapp_idを記入"
#define MILKCOCOA_DATASTORE    "esp8266/dht11"

DATASTOREはその名の通りデータを保存する場所です。事前に保存場所を作成しておく必要はなく、データを保存するときに作成されます。

 

Milkcocoaへのデータ保存

WiFiClient client;                               // WiFiを使う場合
Milkcocoa milkcocoa = Milkcocoa(&client, MQTT_SERVER, MILKCOCOA_SERVERPORT, MILKCOCOA_APP_ID, MQTT_CLIENTID);
milkcocoa.loop();                                // Milkcocoaに接続
DataElement elem = DataElement();                // データ送信用のオブジェクト作成
elem.setValue("temperature", (int)round(t));     // 送信する温度データをセット
elem.setValue("humidity", (int)round(h));        // 送信する湿度データをセット
milkcocoa.push(MILKCOCOA_DATASTORE, &elem);  // Milkcocoaにデータを保存

データの保存はこの一連の記述で行えます。データはデータ名とデータの値のセットで保存します。

f:id:IntellectualCuriosity:20170331031939p:plain

保存したデータはダッシュボードのデータストアで確認することができます。

 

DHT11のデータ取得

delay(2000);                      // データ取得前に2秒待つ
float t = dht.readTemperature();  // 温度を取得
float h = dht.readHumidity();     // 湿度を取得
if (isnan(t) || isnan(h)) {
  return;                         // データ取得に失敗したらloop()からやり直す
}

DHT11は独自のシングルバスで温度と湿度のデータを送るため、タイミングが結構シビアです。難しいタイミングを合わせる作業はライブラリ任せですが、データ取得に失敗するケースが多々あることから、データ取得に失敗した時のリトライを行う必要があります。

 

Deep Sleepの利用

センサーで値を取得してMilkcocoaに送るとき以外の殆どの時間は、バッテリー節約のために非常に消費電力の少ないDeep Sleep状態にしています。

ESP.deepSleep()の第1引数はマイクロ秒で指定しますが、ESP8266は時計を内蔵していなく実際にはカウンターを利用しているだけのため、60分ぐらいの時間を指定すると数分のずれが生じます。

そのため、loop()の終了だけでなく開始にもDeep Sleepに入る処理を加えて意図したタイミングに処理が実行されるようにしています。

loop()の開始

int min = SLEEP_INTERVAL - minute() % SLEEP_INTERVAL;                        // 実行タイミングでないときはスリープ
if (min != SLEEP_INTERVAL) {
  ESP.deepSleep((min * 60 - second() + 10) * 1000 * 1000, WAKE_RF_DEFAULT);  // マイクロ秒で指定
}

loop()の終了

min = SLEEP_INTERVAL - minute() % SLEEP_INTERVAL;                          // スリープする端数分数を計算
ESP.deepSleep((min * 60 - second() + 10) * 1000 * 1000, WAKE_RF_DEFAULT);  // マイクロ秒で指定

 

Webページへの表示

Webページに表示するにはMilkcocoaのJavaScript APIを使用します。

<div>ベランダの温度と湿度</div>
<div id="datetime">時刻取得中...</div>     <!-- 置き換え用の場所 -->
<div id="temperature">温度取得中...</div>  <!-- 置き換え用の場所 -->
<div id="humidity">湿度取得中...</div>     <!-- 置き換え用の場所 -->
<script src="https://cdn.mlkcca.com/v0.6.0/milkcocoa.js"></script> 
<script type="text/javascript">// <![CDATA[ 
var milkcocoa = new MilkCocoa('app_idを記入.mlkcca.com');   // 「app_idを記入」を置き換えます 
var ds = milkcocoa.dataStore('esp8266/dht11');              // データ取得先のデータストア 
ds.stream().size(1).sort('desc').next(function(err, data){  // 1番新しいデータを1件取得 
  document.getElementById("datetime").innerHTML = "時刻:" + data[0].value.datetime + " "; 
  document.getElementById("temperature").innerHTML = "温度:" + data[0].value.temperature + "℃"; 
  document.getElementById("humidity").innerHTML = "湿度:" + data[0].value.humidity + "%";
}); 
// ]]></script>

時刻、温度、湿度を表示する場所をHTMLに用意しておき、データ取得後に取得したデータで置き換えるようにしています。

 

リベンジ完了!

 フルスケッチをダウンロードできるようにしました。よろしければ使ってください。

 

2017/5/11追記:

アップロードしていたファイルがBOM付きになっていたため、Windows環境のArduinoで不可解なエラーが出る状態だったのを修正しました。

また、Windows環境のArduinoではTime.hではなくTimeLib.hをインクルードしないとエラーになってしまうので修正しました。