みなさん、こんばんわ。 トドお父さんです。
家庭菜園の自動水やりシステム、EasyIoTとの連携の説明が今回の5回目でようやく終わりです。
連携の説明が終わったら、実際の家庭菜園の自動水やりの制御をしているところをレポート
したいのですが、こちらはまだ出来上がってませんので今回でいったん終了です。
GWに始めたので。ここまで3か月強。 ながながとお付きあい、有難うございます。
【閑話休題】
ESP8266のプログラムコードの説明、前回の続きを始めますね。
プログラム:ESP8266_mqtt_irrigator_Aug09a.ino
前のブログから、説明が続いてますので併せて見てくださいねー。
前回のブログ
[電子工作]Part5 家庭菜園の自動水やりシステムの作成再開!(EasyIoTとの連携 Ⅳ)
前々回のブログ、
[電子工作]Part4 家庭菜園の自動水やりシステムの作成再開!(EasyIoTとの連携 Ⅲ)
前々々回のブログ、
[電子工作]Part3 家庭菜園の自動水やりシステムの作成再開!(EasyIoTとの連携 Ⅱ)
前々々々回のブログ、
[電子工作]Part2 家庭菜園の自動水やりシステムの作成再開!(EasyIoTとの連携)
また、もとネタのインストラクタブルの下記の記事も併せて参照してくださいね。
ESP8266 Smart Plant Irrigation System
この記事にある、Githubに登録されたコードをベースに私のシステムに合わせて修正しています。
【プログラムの説明の続き】
今回作ったプログラムは本家と同様に、GitHubに登録しています。
My ESP8266 Vegitable gardem iIrigation System
Githubの内容は前回に説明していますので、今回は割愛させて頂きますね。
前回は初期設定 setuo() の途中まで説明したので、今回はその続き、それとプログラムの
本体部 loop()の説明です。
6、プログラム 初期化処理3 (moduleIdの生成と新規moduleの登録(初回のみ))
//create module if necessary if (storage.moduleId == 0) { //create module Serial.println("create module: /NewModule"); storage.moduleId = myMqtt.NewModule(); if (storage.moduleId == 0) { Serial.println("Module NOT created. Check module limit"); while(1) delay(100); } // set module type Serial.println("Set module type"); myMqtt.SetModuleType(storage.moduleId, "ZMT_IRRIGATOR");
ESP8266の初回起動時は、EEPROM(512)の領域は全て、"00h"でクリアされています。
EEPROMの初期化のときに、 loadConfig(); でEEPROMに書き込まれたパラメータをを読みこ
みますので、初回は全てParametaが"0" になります。 ModuleID を読みこんで判定します。
その下の文は、moduleIDが"0"の初回のみ処理して、ここでmoduleIdを生成します。
この新しいmoduleIdでEasyIOT Cloudサーバーにアクセスして新しいModuleを登録し、
Module Type (ここでは"ZMT_IRRIGATOR"を登録します。
7、プログラム 初期化処理4 (4つのパラメータのEasyIoTサーバー登録)
// create Sensor.Parameter1 - humidity treshold value Serial.println("new parameter: /"+String(storage.moduleId)+ PARAM_HUMIDITY_TRESHOLD); myMqtt.NewModuleParameter(storage.moduleId, PARAM_HUMIDITY_TRESHOLD); // set IsCommand Serial.println("set isCommand: /"+String(storage.moduleId)+ PARAM_HUMIDITY_TRESHOLD); myMqtt.SetParameter(storage.moduleId, PARAM_HUMIDITY_TRESHOLD, true); // create Sensor.Parameter2 // Sensor.Parameter2 - manual/auto mode 0 - manual, 1 - auto mode Serial.println("new parameter: /"+String(storage.moduleId)+ PARAM_MANUAL_AUTO_MODE); myMqtt.NewModuleParameter(storage.moduleId, PARAM_MANUAL_AUTO_MODE); // set IsCommand Serial.println("set isCommand: /"+String(storage.moduleId)+ PARAM_MANUAL_AUTO_MODE); myMqtt.SetParameterIsCommand(storage.moduleId, PARAM_MANUAL_AUTO_MODE, true); // create Sensor.Parameter3 // Sensor.Parameter3 - pump on/ pump off Serial.println("new parameter: /"+String(storage.moduleId)+ PARAM_PUMP_ON); myMqtt.NewModuleParameter(storage.moduleId, PARAM_PUMP_ON); // set IsCommand Serial.println("set isCommand: /"+String(storage.moduleId)+ PARAM_PUMP_ON); myMqtt.SetParameterIsCommand(storage.moduleId, PARAM_PUMP_ON, true); // create Sensor.Parameter4 // Sensor.Parameter4 - current soil humidity Serial.println("new parameter: /"+String(storage.moduleId)+ PARAM_HUMIDITY); myMqtt.NewModuleParameter(storage.moduleId, PARAM_HUMIDITY); // set Description Serial.println("set description: /"+String(storage.moduleId)+ PARAM_HUMIDITY); myMqtt.SetParameterDescription(storage.moduleId, PARAM_HUMIDITY, "Soil moist."); // set Unit Serial.println("set Unit: /"+String(storage.moduleId)+ PARAM_HUMIDITY); myMqtt.SetParameterUnit(storage.moduleId, PARAM_HUMIDITY, "%"); // set dbLogging Serial.println("set Unit: /"+String(storage.moduleId)+ PARAM_HUMIDITY); myMqtt.SetParameterDBLogging(storage.moduleId, PARAM_HUMIDITY, true); // save new module id saveConfig(); } subscribe(); lastAnalogReading = analogRead(PIN_HUM_ANALOG); autoModeOld = !autoMode; }
折り返し処理がないので見にくくなっておりすみません。 (CSSで横の折り返しのみ付きました!)
長くなっていますが、Parameter1/~Parameter4/までのパラメータ設定を自動で行う内容です。
最後に、EEPROMにmoduleIdを書き込んで、サーバーの初期のパラメータを一旦subscribe()で
読み込みます。
あとは、土壌センサの値を読み込んで初期設定です。
ここまでが、setup()の内容になります。
一旦、EEPROMに書き込まれると、もういちど書き込もうとしても無効になりますので注意してください。
ESP8266のEEPROMを初期化するには、ツールでファームを書き込むのが一番早いと思います。
ESP8266_Flasher でググってくださいね。
8、プログラム 実行プログラム (前半分です)
void loop() { while (WiFi.status() != WL_CONNECTED) { delay(500); #ifdef DEBUG Serial.print("."); #endif } int in = digitalRead(PIN_BUTTON); //Serial.println(in); if (in == 0) { while(digitalRead(PIN_BUTTON) == 0) delay(100); if (state == s_idle || state == s_irrigation_start) state = s_irrigation_start; else state = s_irrigation_stop; } // post auto mode changes if (autoModeOld != autoMode) { autoModeOld = autoMode; if (autoMode) valueStr = String("1"); else valueStr = String("0"); topic = "/"+String(storage.moduleId)+ PARAM_MANUAL_AUTO_MODE; result = myMqtt.publish(topic, valueStr, 0, 1); Serial.print("Publish topic: "); Serial.print(topic); Serial.print(" value: "); Serial.println(valueStr); } // post treshold changes if (soilHumidityThreshold != soilHumidityThresholdOld) { soilHumidityThresholdOld = soilHumidityThreshold; valueStr = String(soilHumidityThreshold); topic = "/"+String(storage.moduleId)+ PARAM_HUMIDITY_TRESHOLD; result = myMqtt.publish(topic, valueStr, 0, 1); Serial.print("Publish topic: "); Serial.print(topic); Serial.print(" value: "); Serial.println(valueStr); } if (IsTimeout()) { startTime = millis(); // process every second int aireading = analogRead(PIN_HUM_ANALOG); Serial.print("Analog value: "); Serial.print(aireading); Serial.print(" "); // filter s lastAnalogReading += (aireading - lastAnalogReading) / 10; Serial.print(lastAnalogReading); // calculate soil humidity in % int newSoilHum = map(lastAnalogReading, MIN_ANALOG_VAL, MAX_ANALOG_VAL, 100, 0); Serial.print(", Soil hum %:"); Serial.println(newSoilHum); // limit to 0-100% if (newSoilHum < 0) newSoilHum = 0; if (newSoilHum > 100) newSoilHum = 100; // report soil humidity if changed if (soilHum != newSoilHum) { soilHum = newSoilHum; //esp.send(msgHum.set(soilHum)); valueStr = String(soilHum); topic = "/"+String(storage.moduleId)+ PARAM_HUMIDITY; result = myMqtt.publish(topic, valueStr, 0, 1); Serial.print("Publish topic: "); Serial.print(topic); Serial.print(" value: "); Serial.println(valueStr); }
最初はWifii接続です。 Disconnectされたときの再接続の対応だと思います。
次がスイッチ入力の処理です。
① 乾燥センサの値で自動ON、 ②PC/スマホのUI上からON/OFF、③ 基板のスイッチによるON/OFF
と3種類の方法があります。 ここでは、スイッチの入力をセンシングして、OFF状態なら、ON。
ON状態ならOFFします。
次はオートモードの切り替えです。
サーバーからsubscribe()でゲトしたParameter2のデータで、automode (自動モード)のON/OFFを
制御します。ON/OFFの変化があったら、サーバーにPublishします。
次はスライダーバーで表示されるHUMIDITY_THRESHOLD (湿度閾値)の変動をチェックします。
オートモード同様に、subscribe()でゲトしたParameter1のデータで閾値の変化をチェックし、
前の値と変化があったら、結果をサーバーにPublishします。
土壌湿度(乾燥度)の測定は1000mS毎に行います。(IsTimeout())
lastAnalogReadingには10回の測定値(Airreading) の移動平均値を入れています。
湿度%の計算はmap()を使って、MIN_ANALOG_VALを0%、MAX_ANALOG_VALを100%に
正規化処理を行っています。上の値は実験的に決めて、自分のシステムやセンサーに
あった値にしてくださいねー。(Analog入力、1V超えても壊れはしないようです。1024のままですが)
計算値がオーバー/アンダーした場合は、シリアルプリントでは105%とか、-5%とか
表示されますが、サーバーへPublishする値は 0%と100%で頭切りしていますね。
9、プログラム 実行プログラム (後半分です)
// irrigator state machine switch(state) { case s_idle: if (irrigatorCounter <= IRRIGATION_PAUSE_TIME) irrigatorCounter++; if (irrigatorCounter >= IRRIGATION_PAUSE_TIME && autoMode) { if (soilHum <= soilHumidityThreshold) state = s_irrigation_start; } break; case s_irrigation_start: irrigatorCounter = 0; digitalWrite(PIN_PUMP, HIGH); //esp.send(msgMotorPump.set((uint8_t)1)); valueStr = String(1); topic = "/"+String(storage.moduleId)+ PARAM_PUMP_ON; result = myMqtt.publish(topic, valueStr, 0, 1); Serial.print("Publish topic: "); Serial.print(topic); Serial.print(" value: "); Serial.println(valueStr); state = s_irrigation; break; case s_irrigation: if (irrigatorCounter++ > IRRIGATION_TIME) state = s_irrigation_stop; break; case s_irrigation_stop: irrigatorCounter = 0; state = s_idle; //esp.send(msgMotorPump.set((uint8_t)0)); valueStr = String(0); topic = "/"+String(storage.moduleId)+ PARAM_PUMP_ON; result = myMqtt.publish(topic, valueStr, 0, 1); Serial.print("Publish topic: "); // add for debug 2017. Aug.9th Serial.print(topic); Serial.print(" value: "); Serial.println(valueStr); digitalWrite(PIN_PUMP, LOW); break; } } }
後は、enum{ 列挙型変数で定義した4つのステートを使ったステートマシンで制御しています。
Case文で分かりやすく記述していますね。(Breakを忘れないこと)
1、s_idleステート:Autoモードの場合はIRRIGATION_PAUSE_TIMEに設定された値(msec)
の間は、次の灌水をしないように制御しています。
30000設定 の場合、自動灌水は50分間隔になります。灌水時間は10秒で設定されています。
灌水開始の条件が満たされると、2のステートに移行します。
2、」s_irrigation_startステート:irrigatorCounterを0リセットして、ポンプ(水バルブ)をONします。
同時に、サーバーにポンプがON状態になったことをPublishして知らせます。
3のステートに移行します。
3、s_irrigationtステート:IRRIGATION_TIMEの設定値(ここでは10(秒))が経過するとポンプ
(水バルブ)をOFFし、4のステートに移行します。
4、s_irrigationt_stopステート:irrigatorCounterを0リセットして、1のステートに移行します。
同時にサーバーにポンプがOFF状態になったことをPublishして知らせます。
ここまでが、プログラムの説明になります。
内容は分かっていただけたでしょうか?
自分でいじらないといけない部分が分かると、いいんですけどね。
メインの後に関数がいくつかついてますが、説明は割愛させて頂きますね。
自分自身がプログラムを始めたばかりで、良く理解できない部分もありますので、分かりにくい
ところはご容赦くださいねー。
10、プログラムを起動して確認
Part2でEasyIoT Cloudサーバーにアクセスして、手動で作った内容を覚えていますでしょうか?
このプログラム起動して新しく登録されたModuleのパラメータは下記の4つになります。
ModuleのModue Type と設定パラメータ
Sensor.Parameter1の右側⇒をクリックすると、下記のように設定されています。
パラメータの設定内容
追加されたモジュール(起動直後)
これまでの、プログラムの内容、実は理解するのに2カ月かかってしまったんですよ。
とくにEEPROMが一回目しか書き込まれていないことを理解できなくて試行錯誤しました。
IoTのサーバー、他にも色々あって試しましたが、このサーバーが自由度が高くて個人的には
気にいっています。
あとは、カスタムのModule_Type のHTMLとJavaScriptの記述が理解できるともっと
面白くなりそうな気がします。
(実はWhether Stationのアイコンはカスタムで追加しました)
そんなところで、プロトタイプはうまく動作するようになりました。
ホンちゃんのシステムを作るのに、もう少し時間が必要なようです。
このプロジェクト、一旦終わりますが、実際の運用が始まりましたら、また報告しますね。
あ”-ここまで疲れました!
それでは、おやすみなさい。