TL;DR
センサ値(今回は温湿度情報)をとある事情でメール通知したかったため作成しました。
(温度感覚が鈍って自分では熱中症になりそうなことを感じないご高齢の方が身内にいたら使えたりするかも)
手元にあったM5Stack Gray(普通のArduinoの方がネット上にサンプルが溢れているのでより簡単だった模様)と、GROVE - デジタル温度・湿度センサを使って作成。
Gmail送信するため、WiFi環境下でのみ動作しますのでご注意を。
ソースコードはC++に慣れていないのでヨチヨチですが公開してます。(ツッコミなど大歓迎でございます m(_ _)m )
https://github.com/LittleWat/m5stack-notify-sensors
セットアップ
開発環境構築
Arduino IDEより、VS Codeの方が慣れているのでそちらで環境構築しました。
丁寧な記事があったのでそちらを参考にしました。執筆者に感謝!
PlatformIO IDEのLibraryは platformio.ini
で指定しているので追加不要かもですが、下記のライブラリを追加しました。
- M5Stack@^0.2.9
- SimpleDHT@^1.0.12
- ezTime@^0.8.1
ちなみに、Serial Monitorの設定や、ログレベルの設定もplatformio.ini
で行なっています。
(ログレベルはソースコードからではコントロールできなかったです。)
[env:m5stack-core-esp32]
platform = espressif32
board = m5stack-core-esp32
framework = arduino
monitor_speed = 115200
build_flags = -DCORE_DEBUG_LEVEL=4
lib_deps =
M5Stack@^0.2.9
SimpleDHT@^1.0.12
ezTime@^0.8.1
デバイス
-
M5Stack Gray
- 9軸センサがついてるらしいですが、今回は使用してないです汗
-
GROVE - デジタル温度・湿度センサ(DHT11)
- M5Stackを使うのであれば、
DHT11
よりもDHT12
を使えばサンプルがそのまま動きそうでよかったのですが、たまたま手元にあったのがDHT11
だったのでそちらを使用。
- M5Stackを使うのであれば、
VCCとGNDは普通につないで、GROVE - デジタル温度・湿度センサ
の SIG
を M5Stack Gray
の5番のポートにつなぎました。
2番ポートにつなぐと、デバイスにソースコード書き込みができない下記エラーが発生します汗
`A fatal error occurred: MD5 of file does not match data in flash!
*** [upload] Error 2`
調べたところ、どうもESPの問題らしいです。
書き込み時にはケーブルを2番ポートから抜いて、書き込み後に刺せば動作します。が、面倒なので5番ポートを使用しました。
参考
- http://nekomemo2.site/?eid=1643
- https://www.mgo-tec.com/blog-entry-trouble-shooting-esp32-wroom.html/3
Gmailの設定
あまり好ましくはないのですが、簡単にメールを送るためセキュリティを緩くしました。(メール送信に使うgmailアカウントは最悪乗っ取られてもよいものを作っておくのが良さそうですが)
Googleアカウントで、安全性の低いアプリを許可する
Gmailの設定でIMAPを有効にする
以上でセットアップ終了です!
実行
下記コマンドを実行し、できたMyConfig.h
を各自の環境に合わせて編集します。
git clone https://github.com/LittleWat/m5stack-notify-sensors
cd m5stack-notify-sensors/include
cp MyConfig.h.template MyConfig.h
const char* wifi_ssid = "xxx";
const char* wifi_pass = "xxx";
const char* smtp_username = "xxx";
const char* smtp_password = "xxx";
const char* smtp_from_address = "xxx@gmail.com";
const int smtp_port = 465;
const char* smtp_hostname = "smtp.gmail.com";
const char* to_address = "xxx@gmail.com";
const char* subject = "温湿度情報";
const int temperature_upper_limit = 28;
const int temperature_lower_limit = 15;
const int minimum_email_interval_seconds = 60 * 10; // 10 minutes
const int sensing_interval_milliseconds = 5000; // should be more than 1500
下4行で、下記を設定してます。
- 通知すべき温度の上限値、下限値
- メールを送りまくらないように最低開ける時間(上記では10分に設定)
- センサ値を取得する感覚(上記では5秒に設定)
その後vscodeで開き、下のバーにある →
ボタン(コンパイルしてm5stackに書き込みするボタン)を押せば動く(ハズです)。
ソースコード作成にあたって下記資料参考にさせていただきました。
# include <M5Stack.h>
# include <Mailer.h>
# include <SimpleDHT.h>
# include <ezTime.h>
# include "MyConfig.h"
// ref: http://shikarunochi.matrix.jp/?p=2586
// 抵抗は https://www.switch-science.com/catalog/818/ を使っていたら、いらない
// for DHT11,
// VCC: 5V or 3V
// GND: GND
// DATA: 2
// int pinDHT11 = SCL;
int pinDHT11 = G5;
SimpleDHT11 dht11(pinDHT11);
Mailer mail(smtp_username, smtp_password, smtp_from_address, smtp_port,
smtp_hostname);
Timezone Tokyo;
time_t last_emailed_at;
void send_email(const String content);
void setup() {
M5.begin();
M5.Power.begin();
M5.Lcd.setTextSize(3);
log_i("Connecting to %s", wifi_ssid);
WiFi.begin(wifi_ssid, wifi_pass);
waitForSync();
log_i("WiFi connected");
log_i("IP Address: %s", WiFi.localIP().toString().c_str());
Tokyo.setLocation("Asia/Tokyo");
Serial.println("Asia/Tokyo time: " + Tokyo.dateTime());
last_emailed_at = Tokyo.now();
}
void loop() {
// start working...
Serial.println("=================================");
Serial.println("Sample DHT11...");
M5.Lcd.setCursor(0, 0);
// read without samples.
byte temperature = 0;
byte humidity = 0;
int err = SimpleDHTErrSuccess;
if ((err = dht11.read(&temperature, &humidity, NULL)) !=
SimpleDHTErrSuccess) {
Serial.print("Read DHT11 failed, err=");
Serial.println(err);
M5.Lcd.print("Read DHT11 failed, err=");
M5.Lcd.println(err);
delay(1000);
return;
}
// LCD display
M5.Lcd.print((int)temperature);
M5.Lcd.print(" *C, ");
M5.Lcd.print((int)humidity);
M5.Lcd.println(" H");
Serial.print("Sample OK: ");
Serial.print((int)temperature);
Serial.print(" *C, ");
Serial.print((int)humidity);
Serial.println(" H");
// 経過時間を計算しそれが一定以上、かつ温度が閾値を超えていたらメール送信
auto elapsed_seconds = difftime(Tokyo.now(), last_emailed_at);
String email_content = "温度: ";
email_content += (int)temperature;
email_content += "℃、湿度: ";
email_content += (int)humidity;
email_content += "%です。\n";
Serial.println(email_content);
log_d("経過時間:%.1f秒", elapsed_seconds);
if (elapsed_seconds > minimum_email_interval_seconds) {
if (temperature > temperature_upper_limit) {
send_email(email_content + "暑いので冷房を強くしてください。");
} else if (temperature < temperature_lower_limit) {
send_email(email_content + "寒いので暖房を強くしてください。");
}
}
// DHT11 sampling rate is 1HZ.
delay(sensing_interval_milliseconds);
}
void send_email(const String content) {
mail.send(to_address, subject, content);
M5.Lcd.println("");
M5.Lcd.println("Emailed at ");
M5.Lcd.println(Tokyo.dateTime());
last_emailed_at = Tokyo.now();
}
所感
ネット上の記事を漁れば手軽に色々と作れそうな印象を受けました。先人に感謝。今後も色々と作っていきたいですね。
今回、時間が一番かかった気がするのは、地味に下記の文字列結合。
長くてダサいですが一応動いたのでよしとしました。
String email_content = "温度: ";
email_content += (int)temperature;
email_content += "℃、湿度: ";
email_content += (int)humidity;
email_content += "%です。\n";
%dとか使ってやったりして色々と詰まりました汗。。
Arduinoはstdio:string
ではなく、String
を使わねばならないみたいですね。(慣れている人からしたら当然のことなのでしょうが...)
もっといい書き方などありましたら教えてくださいませ。。m(_ _)m
PythonやSwiftの手軽さは魅力的ですが、c++(普通の意味で)チョットデキルくらいにはなりたいものです。