はじめに
土壌湿度計を作成し、19日間計測してみましたが、期待していたほど単純にはいきませんでした。
観察して分かったことは次の通りです。
- 鉢植えと露地栽培では土壌環境が異なり、湿度の変化も大きく違う
- 同じナスビでも株によって欲しがる水分量が異なる
- 1箇所だけの測定では菜園全体の状態を把握できない
- 土壌湿度は水やりの目安にはなるが、最終的には野菜の状態も確認する必要がある
そこで、もう少し長期間観察するために測定器の改良を行いました。
1. リポバッテリーの容量アップ
これまではAliExpressで購入した800mAhのリポバッテリーを使用していました。
今回はAmazonで購入した1800mAh品へ交換し、連続運用時間の延長を目指します。
また、今回はコネクター接続としましたが、極性が異なっていたため、ピン配置を入れ替えて対応しました。
2. 温湿度センサーを追加
毎年、ピーマンやパキラなどを廊下で越冬させています。
そこで、土壌湿度だけでなく温度も記録できるように、DHT22温湿度センサーを追加しました。
回路を確認すると4.7kΩでプルアップされている構成でしたが、実測では約2.3kΩでした。
調べてみると1kΩ~10kΩ程度であれば問題なく動作するようです。
今回の接続は次のようにしました。
- 電源(VCC):ESP32-C3のデジタルピンD4
- データ線:D1
省電力化のため、測定時のみD4から電源を供給する構成としています。
マイコン側
温湿度センサーの読み取りには、
「DHT sensor library by Adafruit Ver.1.4.7」
を利用しました。
#include <WiFi.h> #include <HTTPClient.h> #include <DHT.h> #include <Preferences.h> Preferences preferences; // ------------温湿度------------------------------------ #define TEMP_SW_PIN D4 //温湿度センサー 電源SW #define DHTPIN D1 // DHT22のDATAピン #define DHTTYPE DHT22 // 使用するセンサーの種類をDHT22に設定 DHT dht(DHTPIN, DHTTYPE); // ------------WiFi------------------------------------ const char* ssid = "XXXXXXXX"; const char* password = "XXXXXXXX"; //デブロイのURL const String url = "https://script.google.com/macros/s/XXXXXXXXXexec"; // ---------土壌センサー------------------------------------ float set_sleep_sec = 1 * 60 * 60; #define MIN_MIST 50 //土壌センサー最小値 #define MAX_MIST 1400 //土壌センサー最大値 #define HUMI_PIN A2 //土壌センサー Pin #define HUMI_SW_PIN D3 //土壌センサー 電源SW Pin // ---------電圧読み取り------------------------------------ #define VOLT_PIN A0 //電圧読み取りPin void setup() { Serial.begin(115200); pinMode( VOLT_PIN, INPUT); //電圧センサー pinMode( HUMI_PIN, INPUT); //土壌センサー pinMode( HUMI_SW_PIN, OUTPUT); //土壌センサー電源SW pinMode( TEMP_SW_PIN, OUTPUT); //温湿度センサー 電源SW // ---------WiFi-------------------------------------------- WiFi.mode(WIFI_STA); WiFi.disconnect(); if ( WiFi.begin(ssid, password) != WL_DISCONNECTED){ ESP.restart(); } Serial.println(" "); while( WiFi.status() != WL_CONNECTED){ delay(1000); Serial.print("."); } Serial.print("Connected to the WiFi network!"); // 温湿度 dht.begin(); // 測定 moist_send(); //タイマー スリープ esp_sleep_enable_timer_wakeup( set_sleep_sec * 1000000LL * 1.006); esp_deep_sleep_start(); } void loop() { } void moist_send(){ // ---------フラッシュメモリ(不揮発性メモリ)----------- preferences.begin("my-app", false); // "my-app"という名前空間で開始 // ---------バッテリの電圧------------------------------ char voltStr[8]; uint32_t Vbatt = 0; for(int i = 0; i < 2; i++) { Vbatt = Vbatt + analogReadMilliVolts(A0); // ADC with correction } float Vbattf = Vbatt / 1000.0 ; // attenuation ratio 1/2, mV --> V sprintf( voltStr, "%5.2f", Vbattf ); Serial.print(" GetVolt"); // ---------土壌センサー値------------------------------ analogSetAttenuation(ADC_11db); digitalWrite(HUMI_SW_PIN, HIGH); //土壌センサーon delay(2000); int valu = analogRead(HUMI_PIN); for ( int lopCun = 0; lopCun < 3 ; lopCun ++ ){ delay(500); valu = valu + analogRead(HUMI_PIN); } valu = valu / 4; int valud = constrain(valu, MIN_MIST, MAX_MIST ); int humidity = map( valud, MIN_MIST, MAX_MIST, 0, 100); digitalWrite(HUMI_SW_PIN, LOW); //土壌センサーoff Serial.print(" GetMIST"); // ---------温湿度センサー------------------------------- digitalWrite(TEMP_SW_PIN, HIGH); //温湿度センサーon delay(2000); // Sensor readings may also be up to 2 seconds 'old' (its a very slow sensor) float h = dht.readHumidity(); // Read temperature as Celsius (the default) float t = dht.readTemperature(); digitalWrite(TEMP_SW_PIN, LOW); //温湿度センサーoff // Check if any reads failed and exit early (to try again). if (isnan(h) || isnan(t) ) { Serial.println(F("Failed to read from DHT sensor!")); delay(2000); // return; } else{ Serial.print(" GetTemp"); } // ---------フラッシュメモリ(不揮発性メモリ)-------------- // データの読み込み(存在しない場合はデフォルト値として0を返す) int counter = preferences.getInt("counter", 0); float humidity2 = preferences.getFloat("humidity", 0); int valu2 = preferences.getInt("valu", 0); if (humidity2 != 0.0 ) humidity = (humidity + humidity2) * 0.5 ; else Serial.print("preferences getErr"); if (valu2 != 0 ) valu = (valu + valu2) * 0.5 ; String urlFinal = url + "?humidity=" + String(humidity)+ "&humi=" + String(valu)+ "&volt=" + String(Vbattf)+ "&temp=" + String(t) ; // ---------デバック用------------------------------------ Serial.printf( "counter: %d humi:%s volt:%s temp:%s ", counter, String(valu),String(Vbattf), String(t) ); // ---------隔回データ送信を構想------------------------------- if ( counter == 1 ){ counter = 0; //送信するとき } else { counter = 1; //送信しないとき } // ---------WiFi出力---------------------------------------- HTTPClient http; http.begin(urlFinal.c_str()); http.setFollowRedirects(HTTPC_STRICT_FOLLOW_REDIRECTS); int httpCode = http.GET(); if (httpCode == HTTP_CODE_OK) { String body = http.getString(); Serial.print("HTTP_CODE_OK: "); Serial.println(body); } else if (httpCode <= 0) { Serial.println(http.errorToString(httpCode)); } http.end(); // ---------フラッシュメモリ(不揮発性メモリ)------------------- // データの保存 preferences.putInt("counter", counter); preferences.putFloat("humidity", humidity); preferences.putInt("valu", counter); preferences.end(); delay(3000); }
土壌湿度データと合わせてGoogleスプレッドシートへ送信し、外出先からも確認できるようにしています。
function doGet(e) {
const url = "https://docs.google.com/spreadsheets/d/1h2h98NdJ-HfWDlRHsKu6osUmajUAgOoGHKgm8bZempY/edit?gid=0#gid=0";
const ss = SpreadsheetApp.openByUrl(url);
const sheet = ss.getSheets()[0];
const params = {
"timestamp": new Date(),
"humidity": e.parameter.humidity,
"humi": e.parameter.humi,
"volt": e.parameter.volt,
"temp": e.parameter.temp
};
sheet.appendRow(Object.values(params));
return ContentService.createTextOutput('sccess');
}
気になる点
まだ改善したい点があります。
- 温湿度センサーが読めなかった場合のエラー処理が未実装
- 消費電力削減のための送信間引き
現在妄想しているのは、
- 毎回計測する
- 毎回送信はしない
- 2回分のデータを平均化して送信する
という方法です。
送信回数を減らせれば、バッテリー寿命の延長が期待できます。
また、屋外で長期間使用するため、防水やサビ対策も今後の課題です。
おわりに
土壌湿度計だけで自動的に水やりを判断するのは難しいことが分かりましたが、菜園管理の目安としては十分役立っています。
今後は温度データも活用しながら、越冬管理や自動水やりの改善にも取り組んでいこうと思います。
以上、ここまで読んでいただき、ありがとうございました。


