2019年6月30日日曜日

ESP32で温湿度監視装置を作ってみよう


趣旨

複数の人が居る部屋の温湿度が分かると、
「この温度を超えたからエアコンを稼働させよう。」とか、
「寒い(熱い)と感じると思ったら、こんな温度になっている。設定温度を変えよう。」とか、
「寒い(熱い)と感じるけど、この温度なら服で調整しよう。」とか、
自分の感覚と測定値を照らし合わせてどうするか決められて便利なことがあります。

この記事では、ESP32というWiFiで通信できる部品で温湿度センサの値をThinkSpeakというサイトに送信して、インターネット上で温湿度情報を見られる装置の作り方を紹介します。

この記事は電子工作やプログラミング初心者の方が読むことを想定しています。
「ここが分からない」など疑問点があればコメントなどで質問してもらえると、情報共有や記事の更新しますので気軽にお知らせくださいな。

使うもの

  • WiFi情報
    ESP32からの情報送信に利用します。
  • インターネットに繋がり、USBポートがあるPC
    ESP32にプログラムを書き込みます。
  • ESP32開発モジュール 1つ
    プログラムを動かすマイコンが載った基板です。
  • microUSBケーブル 1本
    ESP32へのプログラムの書き込みと給電に利用します。
  • USB電源 1A以上 1つ
    装置の常設に利用します。
  • ブレッドボード 2枚
    はんだを使わず部品を接続するのに使います。
  • 温湿度センサ AM2320 1つ
    温度と湿度の測定に利用します。
  • ジャンパワイヤ 1セット
    はんだを使わず部品を接続するのに使います。
  • LCDモジュール 1セット
    温湿度情報の表示に利用します。
    完成品を買えば、はんだごてとはんだは不要になります。
  • はんだごて + はんだ
    LCDモジュールの組み立てに利用します。
    完成品のLCDを買った場合は不要です。
  • 両面テープ
    装置の壁などへの貼り付けに利用します。

PCにArduino IDEをインストール

Arduino IDEとは、Arduinoという電子工作に便利なプログラミングボードで動かすためのプログラムを開発するソフトウェアのことです。
今回使うESP32のプログラムもArduino IDEで開発するため、下記のリンクからダウンロードしてインストールしてください。

Arduino - Software

Linuxをお使いの場合は、シリアルポートを使えるdialoutグループに自身を追加しておくと、書き込み時の権限エラーを回避できます。(参考: UbuntuのArduinoIDEでアップロードやシリアル通信で出るpermissionエラーの対応方法

Arduino IDEに関連ライブラリをインストール

これらのライブラリをArduino IDEにインストールします。
ライブラリ名役割
AM2320_asukiaaa温湿度センサ制御
ST7032_asukiaaaLCD制御
ThingSpeak_asukiaaaThingSpeakへの情報送信

スケッチ -> ライブラリをインクルード -> ライブラリを管理(Sketch -> Include library -> Manage libraries)を選んでライブラリマネージャを開きます。


ライブラリの名前に含まれる文字列を検索欄に入力します。


出てきたライブラリの行をクリックするとインストールボタンが表示されるので、それをクリックしてインストールします。
冒頭で紹介したライブラリを全てインストールしてください。


インストールすると、プログラムからライブラリが利用可能になります。
また、ライブラリのサンプルプログラムを選べるようになります。


ライブラリの使い方を知りたいときは、サンプルプログラムを見るのが1つの方法です。

Arduino IDEにESP32用のボード情報をインストール

Arduino IDEでESP32のプログラムを開発するには、ESP32のボード情報をArduino IDEにインストールする必要があります。

ESP32用ボード情報のArduino IDE用の説明を参考に、ESP32のリンクを設定に追加し、ボードマネージャーからESP32の環境をインストールします。

ファイル -> 環境設定 (File -> Preferences) を選んで設定画面を開きます。


追加のボードマネージャーのURL(Additional Board Manager URLs)にesp32のurl「https://dl.espressif.com/dl/package_esp32_index.json」を追加します。


追加したら、OKを選択して設定画面を閉じます。

次に、ツール -> ボード -> ボードマネージャ(Tools -> Board -> Boards Manager)を選んでボードマネージャを開きます。


検索欄にesp32と入力して絞り込まれた中から、Espressif Systemsの提供するesp32をインストールします。
インストールボタンは、ボード情報の行をクリックすると出てきます。


インストールできるとボードの選択肢からesp32が選べるようになります。


macの場合: シリアル通信のためのドライバをインストール

macでESP32モジュールで使われているCP210x系のチップを利用するには、ドライバをインストールが必要です。
下記のサイトからインストーラをダウンロードしてインストールを行ってください。

CP210x USB - UART ブリッジ VCP ドライバ

WindowsはインターネットにPCが繋がっていればドライバが自動でインストールされるため、手動でインストールしなくて良いことが多いです。
Ubuntuは元から対応しているので、ドライバのインストールは不要です。(Linuxのディストリビューションによっては、ドライバのインストールが必要かもしれません。)

回路の組み立て

ESP32、温湿度センサ、LCDを接続します。

温湿度センサとLCDはI2Cという通信規格でESP32と接続するので、同じ信号線を利用します。
I2Cはpull up抵抗を必要とするため、LCDモジュールの裏側のPUのはんだを盛ってpull up抵抗を使う回路にします。



温湿度センサAM2320の信号線の役割はデータシートに書いてあるように下記図の配置です。
通信線と電源線が交互になっていることに注意してください。


下記表の各行の信号線をそれぞれ接続します。
ESP32 温湿度センサ LCD
3V3 VDD VDD
21 SDA SDA
22 SCL SCL
GND GND GND

組み立てるとこうなりました。


部品を設置していない、ジャンパワイヤとブレッドボードだけの状態はこちらです。


温湿度センサは、ESP32開発ボードの方が表面(網目がある方)になるように差し込んでください。


ESP32にプログラムを書き込む準備

書き込みたいプログラムが開けたら、今度は書き込みの準備をします。
ESP32モジュールをUSBでPCに接続した状態で、Arduinoのツールのボード設定をこのようにします。

ボード: ESP32 Dev Module
シリアルポート: ESP32モジュールであろうポートを選んでください
Flash Size: 4MB (32Mb) 何もして無ければ、4MBになっていると思います。


これでESP32にプログラムを書き込める状態になりました。

サンプルプログラムを書き込んで、回路が期待通りに出来ていることを確認

温湿度センサ

温湿度センサのサンプルプログラムをESP32に書き込み、認識されることを確認します。

ファイル -> スケッチ例 -> AM2320_asukiaaa -> GetData を選んで、温湿度センサのサンプルプロジェクトを開きます。
このプログラムは温湿度センサの値を読み取ってシリアルポートに書き出す処理を行っています。


「マイコンボードに書き込む」ボタンをクリックすると、プログラムのビルドと書き込みが始まります。


書き込みに成功すると「ボードへの書き込みが完了しました」と表示されます。


失敗すると、エラーが表示されます。


IDEのエラー表示画面は小さいので、メモ帳などの別の文章編集プログラムにエラーをコピーペーストしてすると見やすいのでお勧めです。

シリアル通信する記述があるプログラムは、シリアルモニタを開くとArduinoから送られる情報を確認できます。
左上のボタンを押すとシリアルモニタを表示できます。


温湿度センサのGetDataやこの記事で作成するプログラムは115200bpsで通信する設定にしているので、右下の通信速度を115200bpsにしてください。


温湿度センサがESP32に期待通り接続されていれば、GetDataのプログラムを書き込んでシリアルポートを見ると、温度と湿度を確認できます。


シリアルモニタを使うとプログラムを動かした結果どのような状態になっているか確認できるので、エラーにならずにプログラムを書き込めるけど動きががおかしい場合の原因調査や、正しく動いていることの確認などに利用できます。

LCD

温湿度センサのサンプルプログラムをESP32に書き込み、認識されることを確認します。

ファイル -> スケッチ例 -> ST7032_asukiaaa -> HelloWorld を選んで、LCDのサンプルプロジェクトを開きます。
このプログラムはLCDに「HelloWord」と「起動からの秒数」を表示する処理を行っています。


プログラムを開けたら、先程と同じようにプログラム書き込みボタンを押してプログラムを書き込んでください。

期待通りに接続されていれば、このように表示されます。


温湿度センサの値をLCDに表示するプログラムを作成

ThingSpeakへ情報を送信する前に、温湿度情報をLCDに表示するプログラムを作ります。

#include <AM2320_asukiaaa.h>
#include <ST7032_asukiaaa.h>

AM2320_asukiaaa sensor;
ST7032_asukiaaa lcd;

void setup() {
  Serial.begin(115200);
  lcd.begin(8, 2);
  lcd.setContrast(30);
}

void loop() {
  lcd.clear();
  if (sensor.update() != 0) {
    Serial.println("Cannot read value from sensor.");
    lcd.setCursor(0, 0);
    lcd.print("error");
  } else {
    Serial.println("TemperatureC: " + String(sensor.temperatureC));
    Serial.println("Humidity: " + String(sensor.humidity));
    lcd.setCursor(0, 0);
    lcd.print(sensor.temperatureC);
    lcd.print("C");
    lcd.setCursor(0, 1);
    lcd.print(sensor.humidity);
    lcd.print("%");
  }
  delay(1000);
}

動くことを優先するなら、とりあえずコピーペーストでも構いません。
余裕があれば写経しつつ、タイプミスによるエラーでエラーの読み方に慣れるのも良いと思います。

取り組んだ内容が消えるのを防ぐために、保存ボタンを押すか「Ctl + s」を入力して、プログラムをこまめに保存することをお勧めします。
プロジェクト名は好きな名前を付けてください。
例として表示するスクリーンショットは「TemperatureHumidityMonitor」というプロジェクト名にしてみました。


プロジェクトができたら、プログラムを書き込んでください。
期待通りに回路とプログラムを作れていれば、書き込み後に温度情報がLCDに表示されます。


プログラムの概要を説明します。

この記述で温湿度センサとLCDをプログラムで使えるようにしています。
#include <AM2320_asukiaaa.h>
#include <ST7032_asukiaaa.h>

AM2320_asukiaaa sensor;
ST7032_asukiaaa lcd;

Arduinoのプログラムはsetupとloopの最低2つの関数で記述され、setupはプログラムの開始時1回だけ実行され、loopは繰り返し実行されます。
void setup() {
}

void loop() {
}

ということで、setupで利用する機能の初期設定を行います。
後で使い方を紹介するシリアルモニタでArduinoの情報を見れるようにするために、シリアル通信を115200bpsで開始します。
LCDが8x2の文字数であることを設定し、コントラスト(色の濃さ)を30にします。
温湿度センサはWire.beginを呼び出す必要がありますが、その処理はlcd.beginで行われているため、温湿度センサに関するsetupでの処理は不要です。
void setup() {
  Serial.begin(115200);
  lcd.begin(8, 2);
  lcd.setContrast(30);
}

loopでは、センサからの情報取得、lcdの更新、それらに応じてシリアルで情報を送信、それぞれを1秒ごとに行います。
delayは与える数字分のミリ秒待機する関数なので、delay(1000)は1秒待つことを意味します。
void loop() {
  lcd.clear();
  if (sensor.update() != 0) {
    Serial.println("Cannot read value from sensor.");
    lcd.setCursor(0, 0);
    lcd.print("error");
  } else {
    Serial.println("TemperatureC: " + String(sensor.temperatureC));
    Serial.println("Humidity: " + String(sensor.humidity));
    lcd.setCursor(0, 0);
    lcd.print(sensor.temperatureC);
    lcd.print("C");
    lcd.setCursor(0, 1);
    lcd.print(sensor.humidity);
    lcd.print("%");
  }
  delay(1000);
}

センサは.update()で値の取得を行い、戻り値が0だったら成功、0出なかったら取得失敗を意味します。
シリアル通信では、センサの値を取得できたらセンサ値を、取得できなかったらその旨を送信しています。
void loop() {
  if (sensor.update() != 0) {
    Serial.println("Cannot read value from sensor.");
  } else {
    Serial.println("TemperatureC: " + String(sensor.temperatureC));
    Serial.println("Humidity: " + String(sensor.humidity));
  }
}

センサ値の取得に成功したらLCDにセンサ値を表示し、失敗したら「error」と表示しています。
setCursorはLCDに文字を表示し始める場所を定める関数で、setCursor(0, 1)なら、左から0番目、上から1番目(0始まりなので2行目)から文字を表示し始めることを意味します。
void loop() {
  lcd.clear();
  if (sensor.update() != 0) {
    lcd.setCursor(0, 0);
    lcd.print("error");
  } else {
    lcd.setCursor(0, 0);
    lcd.print(sensor.temperatureC);
    lcd.print("C");
    lcd.setCursor(0, 1);
    lcd.print(sensor.humidity);
    lcd.print("%");
  }
}

インターネットで情報を扱うために、ThinkSpeakに会員登録

ThingSpeakのページはこちらです。

https://thingspeak.com/

signupから会員登録して、会員登録後にチャンネルを作ってください。

温度と湿度を送信するので、Channel SettingsでField1とField2を有効にしてください。
「温度」と「湿度」などの名前を付けると、グラフの値が何を意味するか分かりやすくなって良いです。


Sharingタブでチャンネルの設定を「Share channel view with everyone」にしておくと、urlを知っていれば誰でもグラフを見れるようになります。



チャンネルのAPI KeysタブでWRITE_API_KEYを確認してください。
後でコピーペーストして使います。


温湿度センサの値をThingSpeakに送信するプログラムを作成

ThinkSpeakのWRITE_API_KEYを利用して、温湿度情報をWiFi経由でThingSpeakに送信します。
THINGSPEAK_API_KEY、WIFI_SSID、WIFI_PASSの****は、利用されるチャンネルのキーとWiFiのIDとパスワードに書き換えてください。

#include <AM2320_asukiaaa.h>
#include <ST7032_asukiaaa.h>
#include <WiFi.h>
#include <ThingSpeakWriter_asukiaaa.h>

#define WIFI_SSID "****"
#define WIFI_PASS "****"
#define WIFI_CONNECT_WAIT_SEC 10

#define THINGSPEAK_WRITE_API_KEY "****"
#define THINGSPEAK_TEMPERATURE_FIELD 1
#define THINGSPEAK_HUMIDITY_FIELD    2
#define THINGSPEAK_SEND_INTERVAL_MS  (10 * 60 * 1000)

AM2320_asukiaaa sensor;
ST7032_asukiaaa lcd;
ThingSpeakWriter_asukiaaa channelWriter(THINGSPEAK_WRITE_API_KEY);
unsigned long wroteAt = 0;

void connectToWifi() {
  if (WiFi.status() == WL_CONNECTED) return;
  WiFi.begin(WIFI_SSID, WIFI_PASS);
  unsigned long startAt = millis();
  Serial.println("Connecting to WiFi..");
  while (WiFi.status() != WL_CONNECTED) {
    delay(1000);
    Serial.println(String(WiFi.status()));
    if (WiFi.status() == WL_CONNECTED) {
      Serial.println("Connected to WiFi");
      break;
    }
    if (millis() - startAt > 1000UL * WIFI_CONNECT_WAIT_SEC) {
      Serial.println("Cannot connect to WiFi");
      break;
    }
  }
}

void setup() {
  Serial.begin(115200);
  lcd.begin(8, 2);
  sensor.setWire(&Wire);
  lcd.setContrast(30);
}

void loop() {
  connectToWifi();
  lcd.clear();
  if (sensor.update() != 0) {
    Serial.println("Cannot read value from sensor.");
    lcd.setCursor(0, 0);
    lcd.print("error");
  } else {
    Serial.println("TemperatureC: " + String(sensor.temperatureC));
    Serial.println("Humidity: " + String(sensor.humidity));
    lcd.setCursor(0, 0);
    lcd.print(sensor.temperatureC);
    lcd.print("C");
    lcd.setCursor(0, 1);
    lcd.print(sensor.humidity);
    lcd.print("%");

    if (wroteAt == 0 || millis() - wroteAt > THINGSPEAK_SEND_INTERVAL_MS) {
      channelWriter.setField(THINGSPEAK_TEMPERATURE_FIELD, String(sensor.temperatureC));
      channelWriter.setField(THINGSPEAK_HUMIDITY_FIELD, String(sensor.humidity));
      int writeResult = channelWriter.writeFields();
      if (writeResult == 200) {
        Serial.println("Succeeded in writing values.");
        wroteAt = millis();
      } else {
        Serial.println("Failed to write. Error: " + String(writeResult));
      }
    }
  }
  delay(1000);
}

プログラムを書き込んで情報の送信に成功すれば、10分に1回のペースでThingSpeakに情報を送信します。


情報を送信できない場合は、シリアルモニタでログを確認して、何が失敗しているのか確認して、不具合箇所周辺を見直してみてください。
送信に成功する場合は、Connected to WiFi や Succeeded in writing values.と表示されます。


プログラムの概要を説明します。

ThingSpeakに値を表示するためのライブラリとWiFiのライブラリの読み込みと設定値の定義をしています。
****はご自身の環境に合わせて書き換えてください。
#include <WiFi.h>
#include <ThingSpeakWriter_asukiaaa.h>

#define WIFI_SSID "****"
#define WIFI_PASS "****"
#define WIFI_CONNECT_WAIT_SEC 10

#define THINGSPEAK_WRITE_API_KEY "****"
#define THINGSPEAK_TEMPERATURE_FIELD 1
#define THINGSPEAK_HUMIDITY_FIELD    2
#define THINGSPEAK_SEND_INTERVAL_MS  (10 * 60 * 1000)

ThingSpeakWriter_asukiaaa channelWriter(THINGSPEAK_WRITE_API_KEY);
unsigned long wroteAt = 0;

WiFiが繋がっていなければ接続を試みる関数を作りました。
これをloopで呼び出すことで、WiFiが途切れても再接続を試みる装置になっています。
void connectToWifi() {
  if (WiFi.status() == WL_CONNECTED) return;
  WiFi.begin(WIFI_SSID, WIFI_PASS);
  unsigned long startAt = millis();
  Serial.println("Connecting to WiFi..");
  while (WiFi.status() != WL_CONNECTED) {
    delay(1000);
    Serial.println(String(WiFi.status()));
    if (WiFi.status() == WL_CONNECTED) {
      Serial.println("Connected to WiFi");
      break;
    }
    if (millis() - startAt > 1000UL * WIFI_CONNECT_WAIT_SEC) {
      Serial.println("Cannot connect to WiFi");
      break;
    }
  }
}

void loop() {
  connectToWifi();
}

センサ値の取得に成功して、プログラムを起動してからまだ一度も送信していない(wroteAt == 0)か前回の書き込みから特定の時間(THINGSPEAK_SEND_INTERVAL_MS(ミリ秒))経過した場合にThingSpeakへ値を送信します。
このプログラムではTHINGSPEAK_SEND_INTERVAL_MSを10x60x1000として、10分ごとに送信する処理にしています。
void loop() {
  if (sensor.update() != 0) {
  } else {
    if (wroteAt == 0 || millis() - wroteAt > THINGSPEAK_SEND_INTERVAL_MS) {
    }
  }
}

フィールドに値を設定して、送信します。
writeFieldsの戻り値は、200なら成功で、それ以外だと失敗を意味します。
void loop() {
  if (sensor.update() != 0) {
  } else {
    if (wroteAt == 0 || millis() - wroteAt > THINGSPEAK_SEND_INTERVAL_MS) {
      channelWriter.setField(THINGSPEAK_TEMPERATURE_FIELD, String(sensor.temperatureC));
      channelWriter.setField(THINGSPEAK_HUMIDITY_FIELD, String(sensor.humidity));
      int writeResult = channelWriter.writeFields();
      if (writeResult == 200) {
        Serial.println("Succeeded in writing values.");
        wroteAt = millis();
      } else {
        Serial.println("Failed to write. Error: " + String(writeResult));
      }
    }
  }
}

電源が入っていることを意味する赤いLEDが要らないなら

ハンダごてでLEDか抵抗を取り外せば、赤いLEDが光らなくなります。
ニッパーやペンチで取り外すのは、基板上の銅箔も一緒に剥がしてしまう可能性があるので、ハンダごてで加熱して取り外すことをお勧めします。

グラフの見た目を変えてみる

ThingSpeakのグラフは、色や表示するデータの数や期間を設定できます。
ログインした状態でグラフ右上の鉛筆マークを選択すると、設定画面を開けます。


1週間のデータを表示したいなら、Days(日数)を7、Results(結果の数)を5000位にするのが良いと思います。
Color(色)の値を変更すると、線と点の色が変わります。


Daysを7、Resultsを5000、Colorを#FF9933にするとこのようになります。


データが少ないと横軸が7日分ありませんが、1週間以上稼働させるとこのように7日分表示されます。


装置を設置

USBの電源から給電しつつ、温度と湿度が気になるところに設置します。
両面テープを使えば壁にも設置できます。

まとめ

温湿度情報をThingSpeakのグラフで確認する仕組みができました。
快適な環境づくりの指標の1つにしてくださいな。

参考

HaLakeの温度が気になる
asukiaaa/esp32-env-monitor

変更履歴

2019.07.13
プログラムの書き込み方法紹介前にプログラムの内容を紹介すると都合が悪かったため、書き込み方法の紹介位置をプログラムを紹介する前に移動しました。
2019.11.10
RSコンポーネンツの紹介広告を追加しました。
2019.12.02
完成品のLCD情報を追加しました。
2019.12.07
温湿度センサの向きが分かる写真を追加しました。
LCDのサンプルプログラムを書き込んで、LCDの動作確認を行う手順を追加しました。
カラーコードが8桁になっていたので、6桁に修正しました。
2020.12.01
RSコンポーネンツの紹介広告を削除しました。
2023.07.22
温度センサのピンの役割が分かりづらかったので図を追加しました。

0 件のコメント :