【IoT】ESP8266で温湿度取得してスマホでモニタリング 第1回

#概要
①ESP8266で温湿度センサー(AM2320)を使い温湿度を収集、MQTTでデータを飛ばす。
②Xamarin.Androidで作ったAndroidアプリでモニタリング。

#はじめに
EPS8266で簡単にIoTを作れることを知り、早速挑戦してみました。

せっかくなので、収集したデータをスマホでモニタリングするアプリも作製しました。
投稿のネタになるように、Javaではなく、少しでもサンプルソースが増えればと思いXamarin.Android C#で制作してみました。
概要①～②を完成するまでに４０～５０時間くらいかかってます。
（結構手探りな部分が多かったため時間がかかってしまいました。）
また、初めての投稿のため、投稿にあたり説明不足やルールが守れていないところなどあればご指摘ください。

#全体構成と環境
図1では、センサーから取得したデータをスマホでモニタリングする構成となっています。
Arduino側もVisual Studioで開発できることを知り、全てVisual Studio 2017 Communityで開発しています。
![zentai1.jpg](https://qiita-image-store.s3.amazonaws.com/0/189598/a44714d0-4a72-d4f9-c86a-b2292c1ce8ac.jpeg)
図1.モニタリング

またMQTTは双方向でデータの送受信が可能なので、Androidアプリ側からESP8266へ収集周期の設定を行えるようにしてみました。(図2参照)
![zentai2.jpg](https://qiita-image-store.s3.amazonaws.com/0/189598/dfcfcfd7-a860-74c8-5403-469947b1b377.jpeg)
図2.周期変更

#環境のセットアップ
ここでは環境のセットアップ手順やMQTTの詳しい説明は行いません。参考までに下記URLをご確認ください。
##Visuial Studio 2017 Communityのインストール
https://jyn.jp/visualstudio-2017/

##Arduino IDE for Visual Studioのインストール
https://marketplace.visualstudio.com/items?itemName=VisualMicro.ArduinoIDEforVisualStudio
##mosquittoのインストール
http://www.1ft-seabass.jp/memo/2015/07/13/windows-7-64bit-install-mosquitto/
##WireSharkのインストール
WireSharkは必須ではありませんが、MQTTができているか確認したい方はインストールしてください。
ver2以降でMQTT対応しているそうです。
http://forest.watch.impress.co.jp/library/software/wireshark/

#Arduino実装
図3は制作した回路です。
左側の黒い直方体の物体が温湿度センサーです。
Arduinoでプログラム書き込みするときは、右側の黒い線をジャンパーピンなどでつなげてください。
![DSC_0146_compressed.jpg](https://qiita-image-store.s3.amazonaws.com/0/189598/d87b812f-4864-bb09-9338-2b495f1d1a9f.jpeg)
図3.回路

#スケッチ
ssidとpassword、各IPアドレスは各々の環境に合わせて設定してください。
getDateTime()で現在日時を、getCycle関数で、収集周期を取得します。
”if (tm->tm_sec % cycle == 0) {”の部分で毎正時のときのみ動作するようチェックしています。
getData関数でデータを取得し、client.publishでデータを送信しています。
callback関数は、スマホ側からMQTTで送信されたデータを受信したときに呼ばれる関数で、受信した周期をメモリに書き込んでいます。

```mqtt_esp8266.ino
#include <ESP8266WiFi.h>		// WiFiモジュールのライブラリ
#include <DHT.h>				// DHTセンサのライブラリ
#include <Adafruit_Sensor.h>
#include <Arduino.h>
#include <time.h>
#include <ArduinoJson.h>		// JSONライブラリ
#include <EEPROM.h>
#include "PubSubClient.h"		// MQTTのライブラリ

const char* ssid = "XXXXXXXX";				// WiMAXのSSID
const char* password = "XXXXXXX";			// WiMAXのパスワード
//const char* mqtt_server = "broker.mqtt-dashboard.com";
//const char* mqtt_server = "iot.eclipse.org";
const char* mqtt_server = "192.168.179.3";		// ローカルPC
const char* topic  = "thermo";					// topic
const char* topic2 = "setting";					// 収集周期変更用のtopic
char message_buff[100];
StaticJsonBuffer<200> jsonBuffer;

#define DHTPIN 4								// 温湿度計PIN番号
#define DHTTYPE DHT22
#define JST     3600*9


WiFiClient espClient;
PubSubClient client(espClient);
DHT dht(DHTPIN, DHTTYPE);
long lastMsg = 0;
char msg[50];
float humidity = 0.0;
float tempC = 0.0;
time_t t;
struct tm *tm;
char *datetime = "2017/01/01 00:00:00";
int cycle = 10;					// 収集周期(秒)


void setup() {
	//pinMode(BUILTIN_LED, OUTPUT);     // Initialize the BUILTIN_LED pin as an output
	Serial.begin(115200);
	WiFi.config(IPAddress(192, 168, 179, 200), IPAddress(192, 168, 179, 1), IPAddress(255, 255, 255, 0));
	setup_wifi();
	client.setServer(mqtt_server, 1883);
	client.setCallback(callback);
	// DHT起動
	setup_DHT();
	delay(100);
	configTime(JST, 0, "ntp.nict.jp", "ntp.jst.mfeed.ad.jp");
}

/// <summary>
/// WiFiセットアップ
/// </summary>
void setup_wifi() {

	delay(1000);
	// We start by connecting to a WiFi network
	Serial.println();
	Serial.print("Connecting to ");
	Serial.println(ssid);

	// WiFi起動
	WiFi.begin(ssid, password);
	while (WiFi.status() != WL_CONNECTED) {
		delay(500);
		Serial.print(".");
	}
	Serial.println("");
	Serial.println("WiFi connected");
	Serial.println("IP address: ");
	Serial.println(WiFi.localIP());
	client.subscribe(topic2);
	Serial.print("subscribe:");
	Serial.println(topic2);
}

/// <summary>
/// DHTセットアップ
/// </summary>
void setup_DHT() {
	delay(1000);
	dht.begin();
	delay(2000);
	while (dht.read() == false) {
		dht.begin();
		Serial.println("dht読み込みできません。");
		dht.begin();
		delay(2000);
	}
}

void callback(char* topic, byte* payload, unsigned int length) {
	Serial.print("Message arrived [");
	Serial.print(topic);
	Serial.print("] ");
	// 初期化
	memset(message_buff, '\0', sizeof(message_buff));
	for (int i = 0; i < length; i++) {
		Serial.print((char)payload[i]);
		message_buff[i] = payload[i];
	}
	Serial.println();

	String msgString = String(message_buff);
	JsonObject& root = jsonBuffer.parseObject(msgString);
	if (root.success() == true) {
		cycle = root["cycle"];
		// 収集周期をメモリに書き込み
		byte b_cycle = (cycle & 0xFF);
		Serial.print("b_cycle=");
		Serial.println(b_cycle);
		EEPROM.write(0, b_cycle);
	}
}

void reconnect() {
	// Loop until we're reconnected
	while (!client.connected()) {
		Serial.print("Attempting MQTT connection...");
		// Attempt to connect
		if (client.connect("ESP8266Client")) {
			Serial.println("connected");
			client.subscribe(topic2);
			Serial.print("subscribe:");
			Serial.println(topic2);
		}
		else {
			Serial.print("failed, rc=");
			Serial.print(client.state());
			Serial.println(" try again in 5 seconds");
			// Wait 5 seconds before retrying
			delay(5000);
		}
	}
}


int getCycle() {
	int temp;
	temp = EEPROM.read(0);
	if (temp != 0) {
		cycle = temp;
	}
	Serial.print("収集周期:");
	Serial.println(cycle);
	return cycle;
}


void loop() {

	if (!client.connected()) {
		reconnect();
	}
	client.loop();

	// 時刻取得
	getDateTime();
	// 収集周期取得
	getCycle();

	long now = millis();
	// 毎正時に取得
	if (tm->tm_sec % cycle == 0) {
		// 温度・湿度取得
		getData();
		int retrycount = 0;
		while (isnan(humidity) || isnan(tempC)) {
			getData();
			retrycount++;
			if (retrycount > 2) {
				setup_DHT();
				return;		// loop()の始めに戻る
			}
		}

		String json = buildJson();
		Serial.print("json=");
		Serial.println(json);
		char jsonStr[200];
		json.toCharArray(jsonStr, 200);
		client.publish(topic, jsonStr);
		delay(1000);		// 連続パブリッシュ防止
	}
	else {
		Serial.print("遅延時間=");
		Serial.println(cycle - (tm->tm_sec % cycle));
		delay((cycle - (tm->tm_sec % cycle)) * 1000);
	}
}

String buildJson() {
	String json = "{";
	json += "\"devices\": \"freeboard\"";
	json += ",";
	json += "\"datetime\":\"";
	json += datetime;
	json += "\",";
	json += "\"payload\":";
	json += "{";
	json += "\"humidity\":";
	json += humidity;
	json += ",";
	json += "\"temperature\":";
	json += tempC;
	json += "}";
	json += "}";

	return json;
}

/// <summary>
/// 温度・湿度取得
/// </summary>
void getData() {
	humidity = dht.readHumidity();
	Serial.print("湿度:");
	Serial.println(humidity);
	tempC = dht.readTemperature();
	Serial.print("温度:");
	Serial.println(tempC);
}

/// <summary>
/// 日時取得
/// </summary>
void getDateTime() {
	while (true) {
		t = time(NULL);
		Serial.print("t=");
		Serial.println(t);
		tm = localtime(&t);
		sprintf(datetime, "%04d/%02d/%02d %02d:%02d:%02d",
			tm->tm_year + 1900, tm->tm_mon + 1, tm->tm_mday,
			tm->tm_hour, tm->tm_min, tm->tm_sec);
		Serial.println(datetime);
		if (t == 0) {
			delay(1000);
		} else {
			break;
		}
	}
}
```

#トラブルシューティング
困ったことに、この回路ではセンサーから温湿度を取得できたりできなかったりします。
未だに解決していません。原因として考えられるのは、
１．回路が正しくない。
２．中古で温湿度センサーを購入したせいかどこか破損している。
３．プログラムが正しくない。
ということが考えられます。
データを取得できているときがあるので、壊れていることはないと思いますがどうなんでしょうか。何かトラブルがあったときに原因を特定するのに切り分けが難しいですね。

#おわりに
第1回では、環境構築とArduinoの説明をしました。
Androidアプリ側は第2回に掲載しました。
こちらをご覧ください。
http://qiita.com/pocota5260/items/b2721ea2cf69f0ed2cda
githubのリンクも後程貼ります。