ESP8266 ＆ BME280 ＆ Azureで始める1000円 クラウドIoT生活

#お品書き
- 何の記事？
- 材料
- サーバ作業の準備
- デバイス側の準備
- 可視化
- 参考

#何の記事？
##背景
先日NiFi＋ESP8266の記事を書いたのですが、NiFiがローカルマシンで動いているため、デスクトップを常に動かしておく必要がありました。せっかく低消費電力のデバイスでデータ収集しているのに、アホみたいに電力を食うマシン(4G CPU & GTX 1080)を起動しておくなんてナンセンス。
勉強もかねて、クラウドサービスを触ってみることにしました。

##最終的に実現したいこと
![IoT Image.png](https://qiita-image-store.s3.amazonaws.com/0/121950/2bcac290-d56e-a43f-fdcf-e05e4e291deb.png)

- デバイス側は変わらず、ESP8266&BME280で、気温、湿度、気圧を図ります
- クラウドサービスを利用して、データを送信、バッファリングします。
- 最終的な蓄積先にデータを格納後、可視化して僕が見ます


#材料
以下をFreeshipping条件を付けて、Aliexpressで検索します。
1. ESP8266ボード
　最もやすいもので3ドルで買えるようです。※技適の話があるので日本で確認できるものを買うが吉です。
2. BME280センサー
　AliexpressでBME280を入力してFreeshippingを条件に検索すると、3ドルちょっとで出てきます。
3. Pull-UP用抵抗 10KΩ×2　　　　数十円？
4. ケーブルとユニバーサル基盤　　100円程度 ※秋月

　1000円あれば余裕ですね！

#サーバ作業の準備
IoTデバイスがサーバへ接続するためのコードを準備するために、まずはサーバ側を準備します。

##IoTHubの作成

今回はAzureを使います。

https://azure.microsoft.com/ja-jp/
へアクセスしてアカウント作成などを実施し、使用できるまでの手順はすっ飛ばします。
![SnapCrab_NoName_2017-11-24_21-26-40_No-00.png](https://qiita-image-store.s3.amazonaws.com/0/121950/36a80224-aa51-a154-faa5-16d56b7291b4.png)

Azureポータルからログインして、左にあるメニューから「新規」→「モノのインターネット(IoT)」→「IoT HuB」をクリックします。

![SnapCrab_NoName_2017-11-25_16-7-33_No-00.png](https://qiita-image-store.s3.amazonaws.com/0/121950/4c0fbfd9-cca6-f583-8c88-3dff45740b80.png)


最大の注意点は、ここで、「価格とスケールティア」の部分をクリックして、「F1-Free」に変更しましょうということです。これを忘れると有料になります。

![SnapCrab_NoName_2017-11-22_20-12-7_No-00.png](https://qiita-image-store.s3.amazonaws.com/0/121950/ae24d408-910d-7049-dd30-098b773e56d0.png)

「F1-Free」でも8,000（イベント/日）を処理できるようです。

**イベント量について**
	　30秒に一度データを送るとしても、
	　　　24（時間/日）×60（分/時間）×2（イベント/分） = 2,880（イベント/日）
	　なので、デバイスが一つであれば、F1-Freeサイズで十分でしょう。


リソースグループがなければ適当な名前（たとえばIoTとかで）作ってしまいましょう。
また、場所についても、自分が住んでいる近くのモノを入力したほうがいいでしょう。

作成（デプロイ）にしばらく時間がかかります。

##デバイスの登録

AzureのIoTハブは、端末の管理もやってくれます。
デバイス側は、IoTハブから払い出される「接続文字列」を使用するだけで、接続先のサーバ、暗号化など煩わしい設定をほとんど気にしなくて済みます。

先ほど作成したIoTハブのメニューから「デバイス エクスプローラー」を選択し、「追加」をクリック、「デバイスID」を入力し、保存をクリックします。
他はデフォルトでOKです。

![SnapCrab_NoName_2017-11-22_20-21-43_No-00.png](https://qiita-image-store.s3.amazonaws.com/0/121950/713dd769-4d7e-8dc3-cc80-8926046e3e97.png)

これらの情報はデバイスツインとして、Azure IoTハブにて管理されるようです。

https://docs.microsoft.com/ja-jp/azure/iot-hub/iot-hub-devguide-device-twins

デバイス登録後に、デバイスがIoTハブに接続するための情報「接続文字列」を取得します。
先ほど登録したデバイスを選択すると、いくつかの情報が表示されますので、「接続文字列-プライマリーキー」を選択しましょう。

![SnapCrab_NoName_2017-11-25_16-49-6_No-002.png](https://qiita-image-store.s3.amazonaws.com/0/121950/dd579a07-f8fa-f363-cffd-0e4b0973d748.png)

サーバ側の情報はいったんここまでです。


#デバイスの準備

デバイスはハード、ソフトの両面を準備する必要があります。

##ハードウェアの準備

デバイスBME280ですが、急いでいたので、私はAmazonで購入しましたが本当は秋月とかから購入したほうがよいでしょう。

ＢＭＥ２８０使用　温湿度・気圧センサモジュールキット
http://akizukidenshi.com/catalog/g/gK-09421

Amazonから届いたものは何の説明もないので、どのように接続したらよいか、さっぱりわかりません。

![BME280.png](https://qiita-image-store.s3.amazonaws.com/0/121950/f08fd2eb-21bc-35b1-3e61-a608a856b33f.png)

「秋月さんで買ってないのに情報だけ見るのはちょっと反則だよなぁ。他は秋月さんでかっているから許して」と思いつつ情報を見ます。

http://akizukidenshi.com/download/ds/akizuki/AE-BME280_manu_v1.1.pdf

I2C接続をするのであれば以下の回路となります。

- SD0 ・・・ 3.3V
- CSB ・・・ グランド　I2Cのアドレス。デフォルトはグランド接続で0x76
- SDA ・・・ D1（GPIO05） ※要Pull-UP
- SCL ・・・ D2（GPIO04） ※要Pull-UP
- GND ・・・ グランド
- VCC ・・・ 3.3V

また、この後、紹介するサンプルコードが通信時にLEDを光らせるようになっています。電子工作を始めたのが2カ月ほど前からですので、練習を兼ねて、1608(1.6mm×0.8mm)の抵抗などをはんだ付けします。

![BME280LED.png](https://qiita-image-store.s3.amazonaws.com/0/121950/4f951b3c-9202-f2d1-a8e2-e9957b2a4a5c.png)

はい。出来上がりました。

![LED.png](https://qiita-image-store.s3.amazonaws.com/0/121950/3a0eb0de-199e-fc06-fdd6-d6342a21380d.png)

肉眼でも、ルーペを使ってもちゃんとはんだ付けできているかよくわからないため、一応ショートしていないことなどを確認しました。

##ソフトウェアの準備

Microsoftが提供しているサンプルが以下にありますので、取得しましょう。
https://github.com/Azure-Samples/iot-hub-feather-huzzah-client-app

展開して、Arduinoアプリからapp.inoを開くと、同時にほかのコードも開かれます。（同じフォルダだから？）

以前に試したBME280の読み込みコードがあったため、それを流用するため、DHT、Adafruit Unified Sensorは除きいています。
私の場合、以下を[スケッチ] → [ライブラリをインクルード] → [ライブラリを管理] で、以下のライブラリをインクルードしました。

 - AzureIoTHub
 - AzureIoTUtility
 - AzureIoTProtocol_MQTT
 - ArduinoJson

このサンプル 全部で4つのファイルから構成されています。

 - app.ino ・・・ setup/loopを含むメインコード
 - config.h ・・・ パラーメータなどを含む
 - credentials.ino ・・・ 無線のSSID/パスワード、接続文字列を管理する
 - iothubClient.ino ・・・ IoTハブとの通信部分
 - message.ino ・・・ 通信メッセージを作成する処理
 - serialReader.ino ・・・ Serialの初期設定とCredentials.inoで使用するSerialからの読み込み処理


コードはスイッチサイエンスさんのところにあったものですが、ライセンスが不明のため、必要な人は以下から参照してください。

https://www.switch-science.com/catalog/2236/

```ruby:message.ino
#include <ArduinoJson.h>
#include <Wire.h>

　~略~

bool readMessage(int messageId, char *payload)
{
  float temperature ;
  float humidity;
  float pressure ;

  readData();

  temperature = (double)calibration_T(temp_raw) / 100.0;
  pressure    = (double)calibration_P(pres_raw) / 100.0;
  humidity    = (double)calibration_H(hum_raw)  / 1024.0;

  StaticJsonBuffer<MESSAGE_MAX_LEN> jsonBuffer;
  JsonObject &root = jsonBuffer.createObject();
  root["deviceId"] = DEVICE_ID;
  root["messageId"] = messageId;
  bool temperatureAlert = false;

  // NAN is not the valid json, change it to NULL
  if (std::isnan(temperature))
  {
    root["temperature"] = NULL;
  }
  else
  {
    root["temperature"] = temperature;
    if (temperature > TEMPERATURE_ALERT)
    {
      temperatureAlert = true;
    }
  }

  if (std::isnan(humidity))
  {
    root["humidity"] = NULL;
  }
  else
  {
    root["humidity"] = humidity;
  }

  if (std::isnan(pressure))
  {
    root["pressure"] = NULL;
  }
  else
  {
    root["pressure"] = pressure;
  }

  root.printTo(payload, MESSAGE_MAX_LEN);
  return temperatureAlert;
}

　~略~
```

念のため、Serialの通信速度も落としています。

```ruby:serialReader.ino
　~略~
void initSerial()
{
    // Start serial and initialize stdout
    //Serial.begin(115200);
    Serial.begin(9600);
    //Serial.setDebugOutput(true);
    //Serial.println("Serial successfully inited.");
}
　~略~
```

Wifiの接続があやしかったのでちょっと書き換えたのですが、結局 statusがWL_CONNECTEDになったりならなかったりで、信用できないので接続部分はスキップする形にしました。
※ググったところ、同じような情報ありましたが詳細追いませんでした。ファームアップデートとか出ても面倒ですしね。

また、CredentialもただしくSerialからの入力を受け付けてくれなかったため、結局すべてを固定文字列としました。

時刻もJSTにしたかったので、一部コードを書き換えていますが、結局最終的な可視化ではUTCでしか表示されませんでした。
※最初の動作確認などでUTC、コード修正後はJSTと複数のタイムゾーンが混じったデータだったからかもしれません

```ruby:app.ino

void initWifi()
{
  WiFi.mode(WIFI_STA);
  WiFi.printDiag(Serial);
  WiFi.begin(MYSSID, SSIDPASSWORD);
//  while (WiFi.status() != WL_CONNECTED) {
    delay(1000);
//     Serial.print(".");
//   }

  Serial.println("");
  Serial.println("WiFi connected");

  WiFi.printDiag(Serial);
  Serial.print("IP address: ");
  Serial.println(WiFi.localIP());
}

void initTime()
{
  time_t epochTime;
  configTime(JST, 0, "ntp.nict.jp", "ntp.jst.mfeed.ad.jp");

  while (true)
  {
    epochTime = time(NULL);

    if (epochTime == 0)
    {
      Serial.println("Fetching NTP epoch time failed! Waiting 2 seconds to retry.");
      delay(2000);
    }
    else
    {
      Serial.printf("Fetched NTP epoch time is: %lu.\r\n", epochTime);
      break;
    }
  }
}


static IOTHUB_CLIENT_LL_HANDLE iotHubClientHandle;
void setup()
{
  pinMode(LED_PIN, OUTPUT);

  initSerial();
  delay(2000);
  //readCredentials(); ※Credentialsを削除

  initWifi();
  initTime();
  initSensor();

　~略~

```


#可視化

可視化部分はIoTハブ→「Stream Analytics」→「Power BI」と連携して実現します。

##IoTハブの設定

IoT ハブ　→ エンドポイント　→ Eventsの順でクリックすると、

![SnapCrab_NoName_2017-11-23_23-47-30_No-00.png](https://qiita-image-store.s3.amazonaws.com/0/121950/a2618f13-fa04-23b2-7ee9-272a7ef056e8.png)

Eventsのプロパティが表示され、消費者グループ（これ、どうなのMSさん）が表示されるので、エディットボックスにコンシューマグループ名を入力して、「保存」をクリックします。
※私はBIと入力しました。

![SnapCrab_NoName_2017-11-23_23-48-47_No-00.png](https://qiita-image-store.s3.amazonaws.com/0/121950/dd7a8e14-cae4-2e21-5393-ea62e83e3013.png)


##Stream Analyticsの設定

IoTハブの時と同じように、「新規」　→　「モノのインターネット(IoT)」 →　「Stream Analytics Job」の順にクリックします。
場所は先に作成したIoTハブと一致させましょう。
※場所（リージョン）をまたがると費用が発生すると聞いています。

![SnapCrab_NoName_2017-11-23_23-50-35_No-00.png](https://qiita-image-store.s3.amazonaws.com/0/121950/4bd2b59f-532c-4503-bfbd-4874e27a20b7.png)

次に、ジョブの入力、出力、間の処理（クエリ）を設定します。

![SnapCrab_NoName_2017-11-25_19-15-38_No-00.png](https://qiita-image-store.s3.amazonaws.com/0/121950/7384673e-8e40-2350-8108-2708c6c41146.png)

###入力の設定

Stream Analyticsの「ジョブトポロジ」→「入力」を選択し、各値を入力します。
ソースに「IoT Hub」を選択すると、選択可能が選べるようになります。
コンシューマグループには、先に設定した値を使いましょう。

![SnapCrab_NoName_2017-11-23_23-53-3_No-00.png](https://qiita-image-store.s3.amazonaws.com/0/121950/5d7ef5a2-850e-321e-4345-257b3a7d5f9a.png)


###出力の設定

出力側も同様に設定します。
PowerBIで表示するためには事前にIDを作成し、承認する必要があります。
シンクにPowerBIを選択します。

![Output.png](https://qiita-image-store.s3.amazonaws.com/0/121950/7ff081ae-7045-1a16-5353-937fca34a1e3.png)

###クエリの設定

クエリを選択し、INTOに上記の出力エイリアス名、FROMに入力エイリアス名、を設定し、保存をクリックします。

![SnapCrab_NoName_2017-11-24_0-4-59_No-00.png](https://qiita-image-store.s3.amazonaws.com/0/121950/8b58f715-6576-1c2f-df5c-5694c9f34171.png)

###ジョブの開始

あとは、画面上部の開始をクリックして、定義したジョブを開始します。

![SnapCrab_NoName_2017-11-24_0-6-22_No-00.png](https://qiita-image-store.s3.amazonaws.com/0/121950/3b633c2b-ed3f-b65d-a835-d0175d7bb4fc.png)


##PowerBIの設定

ここまでできていれば、Power BIのマイワークスペース以下にデータセットとして、先ほど設定したデータが格納されます。

![SnapCrab_NoName_2017-11-24_0-15-34_No-00.png](https://qiita-image-store.s3.amazonaws.com/0/121950/3c0c9bb2-017b-a7df-3e34-2f601372d2ac.png)

PowerBIの使い方は詳しくないうえに、本題ではないので割愛しますが、ドラッグ＆ドロップでグラフが作成できます。

以下、ここ2日くらいのデータのグラフ化になります。
スパイク部分は、ESP8266を抜き差ししたタイミングかと思います。
※時刻がUTCで表示されてどうにかならんか悩んでいます。

![SnapCrab_NoName_2017-11-25_19-36-43_No-00.png](https://qiita-image-store.s3.amazonaws.com/0/121950/58a911ca-4ee3-76c1-3239-8b8ca81bd334.png)

#まとめ
マイクロソフトが公開している情報に基づいて、以下を実施してみました。

・ESP8266とBME280を用いて気温/湿度/気圧を取得
・LANを通じて、クラウド（Azure）へ格納
・PowerBIを用いて可視化

#今後の展望
C2D（クラウドtoデバイス）を使って、エアコン、加湿器をコントロール予定。
某ディスカウントストアで700円もしたペットボトル加湿器が加湿できるほどパワーがないことが判明したため、いつになるやら...。

#参考
MicrosoftがESP8266とIoTHUBの連携情報を公開しています。

https://docs.microsoft.com/ja-jp/azure/iot-hub/iot-hub-arduino-huzzah-esp8266-get-started
https://docs.microsoft.com/ja-jp/azure/iot-hub/iot-hub-live-data-visualization-in-power-bi