はじめに
前回の記事では、水管理デバイスを構想してみました。
今回は水管理デバイスの試作に取り組んでみようかと思います。
計画のうち、下計画の赤枠部分を進めていきます。全体計画としては遅れに遅れています。

水管理デバイスの構成
前回考えたデバイス構想から若干の変更があります。
変更後の構成は下のようにしました。主な変更部は赤い部分のM5Stack用水分センサ付きユニットへ変更したことと土壌水分量センサーは廃止したことです。

M5Stack用水分測定センサ付き給水ポンプユニットは以下のものです。便利ですね。
手を抜いてるだけでは?と思う方もいるかもしれませんが、使えるものは使った方がいいのです(迫真)
デバイスの外観
今回は以下のような外観です。基本は今まで作製したものを流用しています。貯水タンクは一旦500mLペットボトルにしています。必要に応じてサイズ変更や水道直結にすることも考えています。

もう少しコンパクトにしたいと思っているのですが、ちょうど良いサイズの防水ケースがなかなかないんですよね。3Dプリンタで作ろうかとも思いましたが、防水性は流石に期待できなさそうですね。
ちなみにM5Stack basic v2.6にはPORT Bがついていないので、以下のようなものを購入して使用しました。
防水ケースはこちらを自分で穴を加工したりして使っています。
配線同士を接続する際にはこのようなコネクターを使っています。
スケッチ
今回のスケッチは以下のようにしました。いろいろ凝りすぎて時間がかかりました。そして、いつも通り詳細はコードの中にコメントで書いていますが、書きすぎてものすごく見づらいですね。
//***水分測定用センサ付き給水ポンプユニットについて***
// Port B に接続「水分センサ=GPIO36/ポンプ制御=GPIO26)
// しきい値は実環境にて調整する必要あり
#include <M5Unified.h> //M5Stack本体の機能を扱うためのライブラリ
#include <M5UnitUnified.h> //M5Stackに接続して使うユニット(ここだとセンサー類)を扱うためのライブラリ
#include <M5UnitUnifiedENV.h> //注意:M5_ENV.hだと見つからないエラーが出ました
// UnitUnified
m5::unit::UnitUnified Units;
// ENVⅢ
m5::unit::UnitENV3 env;
//以下の値でコンパイル後に変更しないものは予めconstexprで固定する
constexpr int PIN_MOIST = 36; // 水分センサ(ADC, PortB)
constexpr int PIN_PUMP = 26; // ポンプ制御(OUTPUT, PortB)
// ---- 調整箇所ここから ----
constexpr int DRY_THR = 2000; // これより大きい=乾いている状態
constexpr int WET_THR = 1800; // これより小さい=湿っている状態
//※DRY_THRとWET_THRの間に幅を持たせてチャタリング防止
constexpr uint32_t PUMP_ON_TIME = 2000; // 1回の給水時間[ms]
constexpr uint32_t SETTLE_TIME = 8000; // 給水後浸透するまでの待機時間[ms]
constexpr uint32_t LOOP_DELAYTIME = 1000; // ループ間隔[ms] 間隔が短すぎるととENVⅢがうまく機能しないので注意
// ---- 調整箇所ここまで ----
bool pumpOn = false; //ポンプのOn/Off状態、初期はfalseにしておく
uint32_t pumpChangeTime = 0; //状態を切り替えた時刻を保存する変数、intでもOKだが、メモリ削減のため(らしいです・・・)
void pumpWrite(bool on) {
pumpOn = on; //ポンプの状態を記憶
digitalWrite(PIN_PUMP, on ? HIGH : LOW);
//上記は三項演算子で書いていますが、意味としては
//if (on) digitalWrite(PIN_PUMP, HIGH);
//else
//digitalWrite(PIN_PUMP, LOW);
//と同じです
pumpChangeTime = millis(); //電源ONからの時間を記憶する変数
}
void setup() {
auto cfg = M5.config();
M5.begin(cfg);
//I2C(Wire)を準備・開始する
//自動でSDAとSCLのPIN番号を取得
//毎回確認するの面倒だなと思って生成AIに聞きました・・・
auto sda = M5.getPin(m5::pin_name_t::port_a_sda);
auto scl = M5.getPin(m5::pin_name_t::port_a_scl);
Wire.begin(sda, scl, 400000); //400000=400kHzの高速通信
//ENV3はUnitUnified経由で登録→begin(env.begin()は呼ばない)
//Units.add(Unitsにenvを追加して、通信はWireを使う)がtrueならUnitsを初期化する
//env=ENVⅢのセンサーをまとめて扱うイメージ
bool ok = Units.add(env, Wire) && Units.begin();
pinMode(PIN_MOIST, INPUT); //水分センサーの読み取り値をPIN_MOIST=36から読む=INPUTする
pinMode(PIN_PUMP, OUTPUT); //ポンプ制御のための信号をPIN_PUMPから出力=OUTPUTする
pumpWrite(false); //初期はポンプをオフ=false
//M5Stackの画面設定(なくても動作可能、あると視覚的に分かりやすい)
M5.Display.setTextSize(2);
M5.Display.setTextColor(TFT_WHITE, TFT_BLACK);
M5.Display.fillScreen(TFT_BLACK);
M5.Display.setCursor(8, 8);
M5.Display.println("If you need, adjust DRY/WET thresholds.");
//初期化失敗を表示する
if (!ok) {
M5.Display.println("ENV3 initial failed!");
}
}
void loop() {
int raw = analogRead(PIN_MOIST); //水分量を取得
//ポンプの状態と水分量に応じて、ポンプを動作させる
if (!pumpOn && raw > DRY_THR) {
pumpWrite(true);
} else if (pumpOn && raw < WET_THR) {
pumpWrite(false);
}
//ポンプがオン状態かつポンプがPUMP_ON_TIMEで指定した時間以上動作していたら強制的に止める
if (pumpOn && millis() - pumpChangeTime >= PUMP_ON_TIME) {
pumpWrite(false);
}
//給水した水が浸透するのを待つ
if (!pumpOn && (millis() - pumpChangeTime) < SETTLE_TIME) {
// 何もしないで水の浸透を待つ
}
//ENVⅢをアップデートする
Units.update(); // ENVⅢは毎ループupdateする必要があるようです
//温度・湿度・気圧(念の為、0.0で初期化しておく)
float temp = 0.0;
float humi = 0.0;
float pres = 0.0;
//ENVⅢで温度・湿度・気圧を取得する
//※updatedされたことを確認してから取得する
if (env.sht30.updated()) {
temp = env.sht30.temperature();
humi = env.sht30.humidity();
}
if (env.qmp6988.updated()) {
pres = env.qmp6988.pressure();
}
//水分量の生データを表示(調整の際に使用)する
M5.Display.setCursor(8, 100);
M5.Display.printf("RAW: %4d \n", raw);
//水分量を%表示する
int moistPct = map(raw, 4095, 0, 0, 100); //センサーの測定値0~4095を0~100に変換
moistPct = constrain(moistPct, 0, 100); //センサーの測定値が0~4095から外れてしまった場合に強制的に0~100の間にする
//水分量を表示する
M5.Display.setCursor(8, 124);
M5.Display.printf("Moisture: %3d %% \n", moistPct);
//ポンプのオン・オフを表示する
M5.Display.setCursor(8, 148);
M5.Display.printf("Pump: %s \n", pumpOn ? "ON " : "OFF");
//温度・湿度・気圧を表示する
M5.Display.setCursor(8, 172);
M5.Display.printf("Temp: %.0f 'C\n", temp);
M5.Display.setCursor(8, 196);
M5.Display.printf("Humi: %.0f %%\n", humi);
M5.Display.setCursor(8, 220);
M5.Display.printf("Pres: %.0f hPa\n", pres/100);
delay(LOOP_DELAYTIME); //間隔が短すぎるとENVⅢでうまく値が取得できす、温度・湿度・気圧がnanになってしまうので注意
}
動作結果
正常に動作すると、M5Stackの画面には以下のように表示されるはずです。ポンプは設定した閾値に従って動くはずです。今回は水分センサー部分を水に浸してみたのが、左側ポンプOFFの状態です。水分量(Moisture)が少しおかしいですね・・・今後要修正ですが、今は動いているので一旦無視しました。

最後に
今回は水管理デバイスの試作を進めました。まだまだ改善点・改良点はありますが、ひとまず形になったのではないかと思います。必要最低限の機能が整ったら、外装をおしゃれにしてみましょうか。
それでは今回はこの辺で。
おすすめの書籍
もっと詳しくM5Stackについて知りたい!という方はこちらの書籍がおすすめです。とても詳しく載っていてわかりやすいです。
とりあえず何か作ってみたい!という方にはこちらの書籍がおすすめです。書いてある通りにやるだけでお手軽にIoTデバイスを作ることができます。
Arduinoで電子工作したい方にはこちらもおすすめです!Arduinoでどんなことができるのか、一目でわかる一冊になっています。
おすすめ記事
相変わらず、本ブログでは資格系の記事が人気です。ぜひご覧ください。





コメント