ESP8266で部屋の湿度を最適に保つ

##はじめに
加湿器を自動的にオンオフ切り替えて部屋の湿度を適正に保ちたい！ということで湿度を適正に保つために温度湿度を30分おきに測定し、iftttを使い、一定湿度を下回っていたら加湿器をオンにし、一定湿度を上回っていたら加湿器をオフにする便利？なものを作ります。

※
・加湿器自体にオン/オフを制御する仕組みはないので加湿器のスイッチはオンにした状態でスマートプラグを通じて加湿器のオン/オフを制御します。(そのためにはスイッチが簡素なタイプの加湿器が必要)
・適正湿度は、厚生労働省令によって定められた事務所衛生基準規則第2章第5条3によると「40%以上70%以下」としていますが、ここでは湿度40%を下回っていたら加湿器をオンにし、湿度45%を上回っていたら加湿器をオフにすることにします。(湿度45%に特に根拠はないです。なんとなく)
・冗長的で見づらく、拙いコードだとは思いますが、生暖かく見守っていただければ幸いです。



##作ってみる
まずiftttでアプレットを2つ作る。(加湿器オン用/オフ用)　作り方は[こちらの記事](https://qiita.com/mono_taro/items/422789f8406cce8905ad)してください。
連携先チャンネルは、使用するスマートプラグによって違います。(自分の場合はSmart Lifeを使用しました。)
こんな感じで作ります。
![Screenshot from 2020-03-07 14-06-09.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/584794/2efbe650-7f17-aed8-77f8-66e2aea47541.png)

![Screenshot from 2020-03-07 14-06-46.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/584794/c294e9bf-3aa2-3afe-fbce-7ddd5060a7a7.png)



####コード
まずはwifiやiftttの設定
※Ambientによるグラフ作成もあったら便利なのでグラフ作成の機能もついています。詳しくは[こちらの記事](https://qiita.com/mono_taro/items/80bcf6da049d87d54eac)を参照てください。


```c++


#include <ESP8266WiFi.h>
#include <WiFiUdp.h>
#include <Ambient.h>
#include "DHT.h"

// DHT11の設定
#define DHTPIN 12        // 使用するGPIOピン
#define DHTTYPE DHT11   // DHT 11
#define channelId 15158  // Ambientのチャネル番号
#define writeKey "writeキー"  // Ambient の書き込みキーをセット
DHT dht(DHTPIN, DHTTYPE);

WiFiClient client;
Ambient ambient;


//WiFiのIDとPASS
const char *WIFI_SSID = "WiFiのID";
const char *WIFI_PASSWORD = "WiFiのパスワード";

// ITFFFの設定
const char* host = "maker.ifttt.com";
const char* secretkey = "iftttのイベントキー";

const int LED_PIN = 5;  //LEDの設定
const float Second = 1000; //1秒  //float型にして小数点にも対応

//ifttt用のURL作成,動作させたいiftttのイベント名、温度、湿度の値を入れて呼び出す
String iftttURL(String event, float t, float h){     //t=temperature,h=humidity
  String ifttt = "/trigger/"+event+"/with/key/"+secretkey+"?value1="
  +String(t)+"&value2="+String(h);
  
  return ifttt;
}

//LED点滅用,数値を入れて呼び出すと数値分点滅する
void LED(int ledInt){
  for(int i=0;i<ledInt;i++){
    digitalWrite( LED_PIN, HIGH );　//点灯
    delay(Second * 0.2);
    digitalWrite( LED_PIN, LOW );  //消灯
    delay(Second * 0.2);
  }
}

void setup() {
Serial.begin(115200);
  // WiFiに接続
  Serial.println(WIFI_SSID);
  Serial.print("Waiting for Wi-Fi connection");
  WiFi.mode(WIFI_STA);
  WiFi.begin(WIFI_SSID, WIFI_PASSWORD);
  while (WiFi.status() != WL_CONNECTED) {
    delay(Second*0.5);
    Serial.print(".");
  }
  Serial.println("");
  Serial.println("WiFi connected");
  Serial.println("IP address: ");
  Serial.println(WiFi.localIP());
  
  pinMode( LED_PIN, OUTPUT );
  ambient.begin(channelId, writeKey, &client);  //  チャネルIDとライトキーを指定してAmbientの初期化
  dht.begin();

  //LED5回点滅で接続確認
  LED(5);
}
```
LEDをつけて動作してるのか確認していますが、不必要ならLEDに係る部分はいらないです。

続いてメインの動作部分

```c++
void loop() {

  String url = "";    //url初期化
    
  // DHT11から読み込み
  float t = dht.readTemperature();
  float h = dht.readHumidity();
  delay(Second);　　//取得にラグがあるので1秒遅延
  if (isnan(t) || isnan(h)) {
    Serial.println("Failed to read from DHT sensor!");
    return;
    }
  Serial.println("Temperature: " + String(t) + "C   H  timeClient.begin();umidity: " + String(h) + "%");
     
  const int httpPort = 80; //http接続なので、ポート80を使用。https接続はしない。
  
  if (!client.connect(host, httpPort)) {
    Serial.println("connection failed");
    return;
  }

    // IFTTTに温度と湿度を送る
  if(40.0>h){
    Serial.println("on");
    url = iftttURL("加湿器onイベント名",t,h);

  }else{
    Serial.println("off");
    url = iftttURL("加湿器offイベント名",t,h);

  }
    
  // IFTTTにリクエストを送る
  Serial.print("Requesting URL: ");
  Serial.println(url);
    
  client.print(String("GET ") + url + " HTTP/1.1\r\n" +
                 "Host: " + host + "\r\n" +
                 "Connection: close\r\n\r\n"); 
                 
               
  while (client.connected()) {
    String line = client.readStringUntil('\n');
    if (line == "\r") {
    // ヘッダ部分の受取終了
    break;
    }
  }

  //Ambientに温度と湿度を送る
  ambient.set(1, t);             
  ambient.set(2, h); 
  ambient.send();   //  Ambientにデータを送信  
  //一通り動作が終了したことを知らせるため1分間LED点灯
  digitalWrite( LED_PIN, HIGH );
  delay( Second * 60 );
  digitalWrite( LED_PIN, LOW );

  delay( Second * 1739 ); //約30分停止、遅延分を除いた秒数
   
 }
```
これでとりあえず完成です。
時間の計測に関しては結構ガバガバです。NTPサーバーから時間を取得して正確にな時間通りに動作させることも出来ますが、今回はとりあえず動けばいいので省略します。

####追記
かなり冗長的ですがNTPとの時間同期をしてある程度正確に30分おきに動作するようにしました。

```c
#include <ESP8266WiFi.h>
#include <NTPClient.h>
#include <WiFiUdp.h>
#include <Ambient.h>
#include "DHT.h"

// DHT11の設定
#define DHTPIN 12        // 使用するGPIOピン
#define DHTTYPE DHT11   // DHT 11
#define channelId 15158  // Ambientのチャネル番号
#define writeKey "11ed2c071c1b8c9b"  // Ambient の書き込みキーをセット
DHT dht(DHTPIN, DHTTYPE);

WiFiUDP ntpUDP;
NTPClient timeClient(ntpUDP);
WiFiClient client;
Ambient ambient;


//WiFiのIDとPASS
const char *WIFI_SSID = "*****";
const char *WIFI_PASSWORD = "*****";

// ITFFFの設定
const char* host = "maker.ifttt.com";


const int LED_PIN = 5;
const float Second = 1000; //1秒



void setup() {
Serial.begin(115200);
  
  // WiFiに接続
  Serial.println(WIFI_SSID);
  Serial.print("Waiting for Wi-Fi connection");
  WiFi.mode(WIFI_STA);
  WiFi.begin(WIFI_SSID, WIFI_PASSWORD);
  while (WiFi.status() != WL_CONNECTED) {
    delay(Second*0.5);
    Serial.print(".");
  }
  Serial.println("");
  Serial.println("WiFi connected");
  Serial.println("IP address: ");
  Serial.println(WiFi.localIP());
  
  pinMode( LED_PIN, OUTPUT );
  ambient.begin(channelId, writeKey, &client);  //  チャネルIDとライトキーを指定してAmbientの初期化
  dht.begin();
  timeClient.begin();
  for(int i=0;i<5;i++){
    digitalWrite( LED_PIN, HIGH );
    delay(Second*0.5);
    digitalWrite( LED_PIN, LOW );
  }
}




//20秒ごとに時間確認  
void MainProcess(int TimeMinutes){
  Serial.println(TimeMinutes);
  
  //毎時30分ごとに処理
  if(TimeMinutes == 00 || TimeMinutes == 30){
  String url = "";    //url初期化
    
    // DHT11から読み込み
    float t = dht.readTemperature();
    float h = dht.readHumidity();
    delay(200);
    if (isnan(t) || isnan(h)) {
      Serial.println("Failed to read from DHT sensor!");
      return;
    }
    Serial.println("Temperature: " + String(t) + "C   Humidity: " + String(h) + "%");
     
  const int httpPort = 80; //http接続なので、ポート80を使用。https接続はしない。
  
  if (!client.connect(host, httpPort)) {
    Serial.println("connection failed");
    return;
  }

    // IFTTTに温度と湿度を送る
    if(40.0>h){
      Serial.println("on");
      url = iftttURL("test",t,h);

    }else{
      Serial.println("off");
      url = iftttURL("test2",t,h);

    }
    
    // IFTTTにリクエストを送る
    Serial.print("Requesting URL: ");
    Serial.println(url);
    
    client.print(String("GET ") + url + " HTTP/1.1\r\n" +
                 "Host: " + host + "\r\n" +
                 "Connection: close\r\n\r\n"); 
                 
               
    while (client.connected()) {
      String line = client.readStringUntil('\n');
      if (line == "\r") {
      // ヘッダ部分の受取終了
      break;
      }
    }

    //Ambientに温度と湿度を送る
    ambient.set(1, t);             
    ambient.set(2, h); 
    ambient.send();   //  Ambientにデータを送信  
    
    digitalWrite( LED_PIN, HIGH );
    delay( Second * 60 );
   
  }else{
    Serial.println("else");
    LED(4);
  }

  forDelay(20);
}


void forDelay(int count){
      for(int i=0; i<count; i++){
      Serial.println(i);
      delay(Second);
    }
    return;
}

//URL作成
String iftttURL(String event, float t, float h){
  String ifttt = "/trigger/"+event+"/with/key/*keyを入力*?value1="
  +String(t)+"&value2="+String(h);
  
  return ifttt;
}

//LED点滅
void LED(int ledInt){
  for(int i=0;i<ledInt;i++){
    digitalWrite( LED_PIN, HIGH );
    delay(Second * 0.2);
    digitalWrite( LED_PIN, LOW );
    delay(Second * 0.2);
  }
}


void loop() {
  Serial.println("hello");
  //時間取得
  timeClient.update();
  Serial.println(timeClient.getSeconds());            //秒表示
  Serial.println(timeClient.getFormattedTime());      //分表示
  
  int TimeSeconds = timeClient.getSeconds();
  int TimeMinutes = timeClient.getMinutes();
  Serial.println(TimeMinutes);

  //50分以降になったらMainProcess呼び出し それ以外は3分ごとに時間同期
  if(TimeMinutes >= 50){
    MainProcess(TimeMinutes);
  }else if(TimeMinutes == 00){
    MainProcess(TimeMinutes);
  }else if(TimeMinutes >= 20 && TimeMinutes <= 30){
    MainProcess(TimeMinutes); 
  }else{
    Serial.println("==================");
    LED(2);
    //delay(Second * 180);
    forDelay(180);
  }
   
 }
```

###最後に
普段の生活を少しでも便利にする物を作ってみたいという目標は、これでとりあえず達成！
また、何かアイディアが思いついたら作ってみたいと思います。(センサー類がもっと欲しい…)

###再追記
elchikaに記事を投稿しました
(https://elchika.com/article/af94e6a0-ae13-4793-af7f-784fe981fce8/)