行き当たりばったり電子工作

昔の論理IC電子工作少年が、四苦八苦しながら現在の電子工作に挑戦する話。

ESP32-DevKitC-32E で温湿度ログ

はじめに

今年の夏は連日37℃とか38℃とかの酷暑だったので、こりゃ我が家の温度を測ってみなくてはと思って、8月にESP32-DevKitC-32E で温湿度ログを作ってみました。

使ったもの

  • ESP32-DevKitC-32E
  • DHT22 温湿度センサー

配線図

いたってシンプル。これ以上ないくらいシンプル。

ESP32温湿度ログ配線図

ちなみに、DHT22の極性を間違えると一発で死にます(当たり前)。それ以前に使っていたDHT11のセンサーモジュールと逆になっていたのに気が付かずに、以前ひとつ犠牲にしました。気を付けましょう。

プログラム(ESP32側)

ライブラリは DHT22 ライブラリを使用。Arduino IDE のライブラリマネージャーからも「DHT22」で検索できます。
github.com

Wi-Fi部分は、こちらを参考にさせていただきました。
garretlab.web.fc2.com

ただし今回は、測定後に10分間のディープスリープに入るようにしたので、すべてsetup関数内で完結しています。

#include <Arduino.h>
#include <DHT22.h>
#include <WiFi.h>

// 温湿度センサーのピン設定
#define DHT_PIN 13

// WiFi接続情報
const char* ssid     = "*****";
const char* password = "*****";

// Webサーバ
const char* host = "*****";

// 温湿度センサ
DHT22 dht(DHT_PIN);


void setup() {
  // 変数宣言
  float temperature;         // 温度測定値
  float humidity;            // 湿度測定値
  char stemp[10],shumi[10];  // 温湿度格納文字列
  char path[255];            // URLのパス部分(引数付)格納文字列
  char strRequest[1024];     // リクエスト文字列

  // 初期設定
  Serial.begin(115200);
  pinMode(32, OUTPUT); 
  delay(10);
  
  // WiFiに接続
  Serial.print("Connecting to ");
  Serial.println(ssid);
  WiFi.begin(ssid, password);

  while (WiFi.status() != WL_CONNECTED) {
    delay(500);
    Serial.print(".");
  }
  Serial.println("WiFi connected.");
  Serial.println("IP address: ");
  Serial.println(WiFi.localIP());

  // 温湿度取得
  humidity = dht.getHumidity();
  temperature = dht.getTemperature();
  
  // 文字列に変換
  dtostrf(humidity,1,2,shumi);
  dtostrf(temperature,1,2,stemp);
  
  // URLパスを作成
  sprintf(path,"/iot/logTempHumi.cgi?date=0&amp;temp=%s&amp;humi=%s", stemp, shumi);

  // デバッグ用
  Serial.print(path);

  // サーバに接続
  Serial.print("connecting to ");
  Serial.println(host);
  WiFiClient client;
  if (!client.connect(host, 80)) {
    Serial.println("connection failed");
    return;
  }
  // リクエスト送信
  sprintf (strRequest, "GET %s HTTP/1.1\r\nHost: %s\r\nConnection: close\r\n\r\n", path, host);
  client.print(strRequest);

  // 一定時間レスポンスがなければタイムアウト.
  unsigned long timeout = millis();
  while (client.available() == 0) {
    if (millis() - timeout &gt; 5000) {
      Serial.println(">>> Client Timeout !");
      client.stop();
      return;
    }
  }
  
  // レスポンスを表示.
  Serial.println("Response Begin");
  while(client.available()) {
    String line = client.readStringUntil('\r');
    Serial.print(line);
  }
  Serial.println("Response End");

  // 切断
  Serial.println();
  Serial.println("close connection");
  client.stop();

  // ディープスリープ
  esp_sleep_enable_timer_wakeup(1000000 * 60 * 10 - millis() * 1000 + 3049000);
  esp_deep_sleep_start();
}

void loop() 
{

}

最後の部分でディープスリープに入りますが、10分の間隔といっても、10分のインターバルではこのプログラムの実行時間分オーバーして、累積でどんどんずれてしまうので、その分をインターバルから引いています。millies()関数で調整している部分ですね。更にそれでも実際に動かしてみると3秒ほど早く再起動されるので、12時間=72回分測定して10分との誤差の平均を算出した補正値 3049ms(=3049000μs)を足しています。なぜずれるんだろう。内蔵時計の精度、そんなに悪いの?

プログラム(Webサーバ側)

Webサーバ側は、ちょっと都合あって Perlで記述。GETの引数でdate、temp、humiで、それぞれ日時、温度、湿度を取得してファイルに追記します。

dateが0の時にはWebサーバ側の現在日時を使用。最初はリアルタイムクロックモジュールで割り込みかけて、ESP32側から日時も送ろうかと思って、このようにしたのですが、そこまでの精度はいらないやと方針変更したため、サーバ側の時刻でも動くようにしました。

#!/usr/bin/perl
use CGI;
 
# GET パラメータを取得
my $q = new CGI; 
# 各値を取得
my $p_date = $q->param('date');  
my $p_temp = $q->param('temp');
my $p_humi = $q->param('humi'); 
# 日時が0の時には、サーバ側の現在時刻を使用
if (p_date == 0) {
  my ($sec, $min, $hour, $mday, $mon, $year, $wday, $yday, $isdst) = localtime;
  $year += 1900;
  $mon++;
  $p_date = sprintf "%04d/%02d/%02d %02d:%02d:%02d", $year, $mon, $mday, $hour, $min, $sec;
} 
#ファイルを開く
open(LOG, ">>log.txt"); 
#データ書き出し
my $outStr = sprintf "%s,%s,%s\n", $p_date, $p_temp, $p_humi;
print LOG $outStr; 
#ファイルを閉じる
close (LOG); 
print "Content-type: text/html\n\n";
print "<html>";
print "<head>";
print "</head>";
print "<body>";
print "OK";
print "</body>";
print "</html>";

これを logTempHumi.cgi とかの名前にしてWebサーバ上に置けばOK。log.txt にどんどん追記されます。最後はとってつけたようにOKだけ返しています。

これでUSB電源に繋いで動作を確認しました。