はじめに
前々回はM5Stackで取得した温度湿度データをPCで閲覧して、前回はSDカードにデータを保存してみました。
今回は今までの知識をフル活用して、IoT開発を進めていきたいと思います。
何をやるかというと、土壌センサーと温度湿度センサーを使って植木鉢の土の水分量、気温、湿度をmicroSDカードに保存してみようと思います。
ついでに前回の内容を復習も兼ねてPC上で水分量、気温、湿度を確認できるようにしてみます。それではやっていきます。
今回もネットワークに接続します。セキュリティ上で危険な行為はしていないつもりですが、読者の皆様が行う際は自己責任でお願いいたします。特にWi-Fiのパスワードの取り扱いには十分に注意してください。
システム構成
今回のIoTシステム構成は下の画像のようにしました。
温度湿度センサーは相変わらずM5Stack用温湿度気圧センサユニット Ver.3(ENV Ⅲ)を使用しました。水分量センサーはM5Stack用土壌水分センサユニットを使用しました。いつも通りスイッチサイエンス様で購入させていただきました。
ここで注意が必要なことが、M5Stack用土壌水分センサユニットはGROVE Aポートではなく、GROVE Bポートを使わなければいけないということです。M5Stack Basic V2.6ではGROVE Aポートのみで、GROVE Bポートはついていません。
そのため、GROVE Bポートをどうにかして準備しなければなりません。方法はいろいろあると思いますが、筆者はGROVE BポートがついているM5GO/FIRE バッテリーボトムを選びました。理由は、
・M5Stackのボトムを変えるだけで簡単そうだったから
・GROVE Aポートを殺さない方法が良かったから
です。デメリットがないわけではありませんが、そこはあまり考えないようにしました。
ちなみに、こちらもスイッチサイエンス様から購入させていただきました。いつもありがとうございます。
今回使用しているバッテリーボトムですが、互換性など特に深く調べずに使用しています。
不具合が絶対にないとは言い切れないので、読者の皆様が使用する場合はその点ご了承ください。2024年5月時点で、筆者が使用している範囲では今のところ大きな問題は起きていません。
各部品の接続方法
先ほど紹介した部品たちの接続方法です。
・バッテリーボトム
M5Stackにデフォルトでついているボトムを外して、バッテリーボトムに付け替えます。若干外しづらいですが、壊れないようにゆっくり外します。付け替えるとポートA側から見ると、下のような状態になります。下半分のグレーのものがバッテリーボトムです。
ポートB側から見ると下のような状態になります。今回は使いませんが、バッテリーボトムには、ポートCもついています。
・各センサーの接続
M5Stack用温湿度気圧センサユニットはポートAに、M5Stack用土壌水分センサユニットはポートBに接続します。下のように接続しました。センサーユニットはいつも通りGroveケーブルで接続しています。
スケッチ
スケッチは下のようなものになります。
#include <M5Stack.h>
#include <WebServer.h>
#include <Wire.h>
#include <WiFi.h>
#include <M5UnitENV.h>
#include "SHT3X.h"
#include <time.h>
SHT3X sht30;
File csvFile;
IPAddress ip;
WebServer server(80);
const char* ssid = "xxxxxxxxxx"; //ご自身のWi-FiのSSIDに書き換えてください
const char* pw = "yyyyyyyyyy"; //ご自身のWi-Fiのパスワードに書き換えてください
const char* ntpServerName = "ntp.nict.jp"; //NTPサーバのアドレス
const long jst = 9 * 3600; //日本時間はグリニッジ標準時から9時間=9×3600秒進んでいる
const int daylightOffset = 0; //サマータイム用の変数を設定する(今回は日本時間なので0に設定)
float tmp = 0.0; //温度を格納する変数
float hum = 0.0; //湿度を格納する変数
int samplingRate = 30; //サンプリング間隔[min]
int samplingFlag = 0; //サンプリングのタイミングを決める変数
int moisture = 0; //土壌水分量を格納する変数
//PCに各データを送信する
void sendData() {
String msg = "Temperature : ";
msg += tmp;
msg += "'C\n";
msg += "Humidity : ";
msg += hum;
msg += "%\n";
msg += "Moisture : ";
msg += moisture;
msg += "\n";
server.send(200, "text/plain", msg);
}
//サーバが見つからなかった時に404を返す
void handleNotFound() {
server.send(404, "text/plain", "File Not Found\n\n");
}
void setup() {
Serial.begin(115200);
M5.begin();
M5.Lcd.begin();
Wire.begin();
WiFi.begin(ssid, pw);
M5.Power.begin();
pinMode(26, INPUT);
dacWrite(25, 0);
sht30.begin();
while (WiFi.status() != WL_CONNECTED) {
delay(500);
Serial.print("*");
}
Serial.println("");
//M5StackにIPアドレスを表示
ip = WiFi.localIP();
M5.Lcd.setTextColor(GREEN);
M5.Lcd.setTextSize(4);
M5.Lcd.setCursor(10, 10);
M5.Lcd.println("IP : ");
M5.Lcd.println(ip);
server.on("/", sendData);
server.onNotFound(handleNotFound);
server.begin();
//microSDカードにDataファイルがなければ作成する
if (!SD.exists("/Data")) {
SD.mkdir("/Data");
}
configTime(jst, daylightOffset, ntpServerName);
}
void loop() {
struct tm timeInfo;
//時刻を取得できなかった場合にM5Stackの画面に表示
if (!getLocalTime(&timeInfo)) {
M5.Lcd.printf("Failed to obtain time(>.<)");
return;
}
char fileName[99];
//csvファイル名は何でも大丈夫です
snprintf(fileName, 99, "/Data/cherryTomato_envData.csv");
if (SD.exists(fileName) == false) {
csvFile = SD.open(fileName, FILE_APPEND);
csvFile.print("Year,Month,Date,hour,min,sec,Temperature,Humidity,Moisture\n");
csvFile.close();
delay(1000);
}
//tmp変数に温度、hum変数に湿度を格納する
if (sht30.update()) {
tmp = sht30.cTemp;
hum = sht30.humidity;
}
//moisture変数に土壌水分量を格納する
moisture = analogRead(36);
//csvファイルにデータを書き込む
//書き込むタイミングは、時刻で決めていて、"分"が偶数かつ"秒"が0の時にしている
if (timeInfo.tm_min % 2 == 0 && timeInfo.tm_sec == 0) {
csvFile = SD.open(fileName, FILE_APPEND);
csvFile.print(timeInfo.tm_year + 1900);
csvFile.print(",");
csvFile.print(timeInfo.tm_mon + 1);
csvFile.print(",");
csvFile.print(timeInfo.tm_mday);
csvFile.print(",");
csvFile.print(timeInfo.tm_hour);
csvFile.print(",");
csvFile.print(timeInfo.tm_min);
csvFile.print(",");
csvFile.print(timeInfo.tm_sec);
csvFile.print(",");
csvFile.print(tmp);
csvFile.print(",");
csvFile.print(hum);
csvFile.print(",");
csvFile.print(moisture);
csvFile.print("\n");
csvFile.close();
}
server.handleClient();
delay(1000);
}
上のスケッチではcsvファイルに保存するタイミングをスケッチ109行目のif文で決めています。意外にこれが大事で、これがないと”csvファイルにデータを書き込む動作”と”PCにデータを送る動作”を共存させることがうまくできませんでした。もっと良い方法があるかもしれませんが…
加えて、何かしらの不具合ありそうだな〜とも思っていますので、読者の方で不具合を見つけられた方はご連絡いただければ嬉しいです。
上記スケッチを温度湿度センサーを接続したM5StackにUploadしてみましょう。
PC画面上で各値を確認する
Uploadが終わったら、M5Stackの画面にIPアドレスが表示されるので、PCやスマホのブラウザにhttp://M5Stackに表示されたIPアドレス を打ち込んでみてください。すると以下のような画面が表示されるはずです。上から温度、湿度、土壌水分量の値となります。
土壌水分量センサーを土壌に刺したりしていないので、土壌水分量は4095の値を常に示します。4095がMaxの値のようですね。値が変わるか確認したい場合は、電極を土壌にさしたり、水を入れたコップにさしてみてください。
microSDカードにデータが保存されているか確認する
続いて、microSDカードに保存されているか確認します。念のため、M5Stackの電源を落としてからmicroSDカードを取り出します。
M5Stackの電源の落とし方は以下の手順です。
① PCとUSBで接続している場合は、切断する
② M5Stack本体横についている赤い電源ボタンを2回押す
ちなみに①をやらずに②をやってもすぐに電源がついてしまうのでご注意ください。
microSDカードの中に”Data”フォルダが作成されて、さらにその中に”cherryTomato_envData.csv”があると思います。それを開くと下のようにデータが書き込まれているはずです。
今回は2分ごとに取得するようにしていますが、スケッチ109行目に書かれているif文の中身を調整すれば、任意のタイミングで取得することも可能です。
データをよく見るとデータが所々欠落していますが、今は無視することにしました。スケッチ109行目において、”csvファイルに書き込むタイミングをif文で指定”して、”条件に時刻情報を使ってしまっている”ことでデータの取得タイミングがズレてしまったと考えていますが、真因は不明です。
最後に
さて、いかがでしたか?これまでの知識の集大成といった内容で、一見難しいように思えるかもしれません。しかし、よくよく分解してみると、一つ一つの技術はそこまで難しいことをしていません。M5Stack初心者の方でも以前の記事をを読み返せば、理解していただけるのではないでしょうか?
理解が難しい場合は私の説明不足ですね…
もし補足で説明が欲しい内容があれば、コメント欄やX(旧Twitter)にコメントよろしくお願いします。
それでは今回はこの辺で終わりにします。次回もお楽しみに!
おすすめの書籍
もっと詳しくM5Stackについて知りたい!という方はこちらの書籍がおすすめです。とても詳しく載っていてわかりやすいです。
とりあえず何か作ってみたい!という方にはこちらの書籍がおすすめです。書いてある通りにやるだけでお手軽にIoTデバイスを作ることができます。
※Amazonのアソシエイトとして、当メディア(Nラボ備忘録)は適格販売により収入を得ています。
他記事のご紹介
当ブログではさまざまな記事を投稿しています。特に資格取得に関する記事はとても人気です。
一読の価値はあると思いますので、ぜひご覧ください!
コメント