今年のグリーンカーテンは上出来で、他にも色々野菜を育ててみました(*^^)v
1.野菜色々
ゴーヤ以外の野菜たち。
2.グリーンカーテン(ゴーヤ)
今年のグリーンカーテンは過去一の葉っぱの茂り密度となりました!(^^)!
摘芯:今年は主な芽が葉っぱ8~10枚迄成長してから摘芯→脇芽が8~10枚迄成長してから摘芯しました。結果グリーンカーテンの密度は過去一になりましたが、ゴーヤの実は過去一小さく成りました(;'∀')実る数は多いのでヨシ!
3.グリーンカーテン(ゴーヤ)_改良点
今年のグリーンカーテンの改良点は他にも色々加えてます。
4.自動散水機のプログラム
昨年までのプログラムでは、一時間に1回を超える散水設定ができず、8月の葉が成長した状態でお昼の猛暑を乗り越えられず葉がぐったりする状態を日々繰り返してました。 しかし今回の改良で以下の通り分単位で設定出来るようにし(設定上お昼は15分おきに散水)実は小さいですが元気なグリーンカーテンに育ってくれました!(^^)!2日位かけてメイン所はほぼ書き直し&デバッグ( ;∀;)
/* NOBのArduino日記! プランター自動水やり機制御プログラム(20230729更新) * rev :2.01 * 変更内容:ホース小径化に伴い吐出量減→対策として吐出回数倍増化 */ #include "PlantWatering.h" PlantWateringClass pwc; int InComingData[255]; unsigned long StartTime, loopTime = 1000; bool Skip = false, Skip2 = true; //条件判定 unsigned long On_No; //散水時間(単位:Sec) int WatTimParDay = 0; //一日当たりの散水回数合計 int OldMon = -1, OldDay = -1, OldHor = -1, OldMin = -1;//最終測定時間 void setup() { Serial.begin(115200); pwc.RTC_time_initialize(false); } void loop() { //■main if (loopTime <= millis() - StartTime) { StartTime = millis(); //■手動スイッチが押されていれば強制的に水を出す if(SkipCheck(false) && pwc.Switch_Read()){ pwc.SolenoidWrite(true); SkipWrite(true); } //■規定時刻に土が乾いていれば水を出す if(SkipCheck(false) && Time_Read()){ if(true/*Sensor_Read()*/){ pwc.SolenoidWrite(true); SkipWrite(true); } } //■最後までSkipされていなければ水を止める、Skipトグルが「true」だったら「false」に戻す if(SkipCheck(false)){ pwc.SolenoidWrite(false); }else{ SkipWrite(false); } //■information出力 #if !DEBUG String str = " WatTimParDay:" + String(WatTimParDay) + " onNo:0x"+ String(On_No, HEX) + "\r\n"; pwc.information_Print(str); #endif } #if DEBUG //■Debug[h,Yea, Mon, Day, Wee, Hor, Min, Sec,]: h2023,7,29, 06, 00, if (pwc.Receive(InComingData)) { pwc.ymd.Yea = InComingData[0]; pwc.ymd.Mon = InComingData[1]; pwc.ymd.Day = InComingData[2]; //pwc.ymd.Wee = InComingData[3]; //省略 pwc.ymd.Hor = InComingData[3]; pwc.ymd.Min = InComingData[4]; //pwc.ymd.Sec = InComingData[5]; //省略 Time_Read(); String str = " WatTimParDay:" + String(WatTimParDay) + " On_No:0x"+ String(On_No, HEX) + "\r\n"; pwc.information_Print(str); } #endif } /* * リアルタイムクロックの値を判定 */ bool Time_Read(){ //No初期化 #if !DEBUG pwc.RTC_refresh(); #endif Skip2 = true; On_No = 0; if(pwc.ymd.Hor == 0 && pwc.ymd.Min == 0 && (OldMin != pwc.ymd.Min || OldHor != pwc.ymd.Hor || OldDay != pwc.ymd.Day || OldMon != pwc.ymd.Mon )){ OldMin = pwc.ymd.Min; OldHor = pwc.ymd.Hor; OldDay = pwc.ymd.Day; OldMon = pwc.ymd.Mon; #if false//DEBUG Serial.print(", WTPD:" + String(WatTimParDay)); #endif WatTimParDay = 0; }; //規定時刻でtrueを返す処理 switch (pwc.ymd.Mon) { case pwc.ymd.June: //6月 if(0 <= pwc.ymd.Day && pwc.ymd.Day < 7){ // h2023,6,1, 06, 00, check( 6, 0, 0x060A0600); check(18, 0, 0x060A1800); } else if(7 <= pwc.ymd.Day && pwc.ymd.Day < 14){ check( 6, 0, 0x060B0600); check(12, 0, 0x120B0600); check(18, 0, 0x180B0600); } else if(14 <= pwc.ymd.Day && pwc.ymd.Day < 21){ check( 6, 0, 0x060C0600); check(10, 0, 0x060C1000); check(14, 0, 0x060C1400); check(18, 0, 0x060C1800); } else if(21 <= pwc.ymd.Day){ check( 6, 0, 0x060D0600); check( 9, 0, 0x060D0900); check(12, 0, 0x060D1200); check(15, 0, 0x060D1500); check(18, 0, 0x060D1800); } break; case pwc.ymd.July: //7月 if(0 <= pwc.ymd.Day && pwc.ymd.Day < 7){ check( 6, 0, 0x070A0600); check( 9, 0, 0x070A0900); check(12, 0, 0x070A1200); check(15, 0, 0x070A1500); check(18, 0, 0x070A1800); } else if(7 <= pwc.ymd.Day && pwc.ymd.Day < 14){ check( 6, 0, 0x070B0600); check( 8, 0, 0x070B0800); check(10, 0, 0x070B1000); check(11, 0, 0x070B1100); check(12, 0, 0x070B1200); check(13, 0, 0x070B1300); check(14, 0, 0x070B1400); check(16, 0, 0x070B1600); check(18, 0, 0x070B1800); } else if(14 <= pwc.ymd.Day && pwc.ymd.Day < 21){ check( 6, 0, 0x070C0600); check( 7, 0, 0x070C0700); check( 8, 0, 0x070C0800); check( 9, 0, 0x070C0900); check(10, 0, 0x070C1000); check(10, 30, 0x070C1030); check(11, 0, 0x070C1100); check(11, 30, 0x070C1130); check(12, 0, 0x070C1200); check(12, 30, 0x070C1230); check(13, 0, 0x070C1300); check(13, 30, 0x070C1330); check(14, 0, 0x070C1400); check(15, 0, 0x070C1500); check(16, 30, 0x070C1630); check(18, 0, 0x070C1800); } else if(21 <= pwc.ymd.Day){ check( 6, 0, 0x070D0600); check( 7, 0, 0x070D0700); check( 8, 0, 0x070D0800); check( 9, 0, 0x070D0900); check( 9, 30, 0x070D0930); check(10, 0, 0x070D1000); check(10, 30, 0x070D1030); check(11, 0, 0x070D1100); check(11, 15, 0x070D1115); check(11, 30, 0x070D1130); check(11, 45, 0x070D1145); check(12, 0, 0x070D1200); check(12, 15, 0x070D1215); check(12, 30, 0x070D1230); check(12, 45, 0x070D1245); check(13, 0, 0x070D1300); check(13, 15, 0x070D1315); check(13, 30, 0x070D1330); check(13, 45, 0x070D1345); check(14, 0, 0x070D1400); check(15, 0, 0x070D1500); check(16, 0, 0x070D1600); check(17, 0, 0x070D1700); check(18, 0, 0x070D1800); } break; case pwc.ymd.August: //8月 check( 6, 0, 0x08ff0600); check( 7, 0, 0x08ff0700); check( 8, 0, 0x08ff0800); check( 9, 0, 0x08ff0900); check( 9, 30, 0x08ff0930); check(10, 0, 0x08ff1000); check(10, 30, 0x08ff1030); check(11, 0, 0x08ff1100); check(11, 15, 0x08ff1115); check(11, 30, 0x08ff1130); check(11, 45, 0x08ff1145); check(12, 0, 0x08ff1200); check(12, 15, 0x08ff1215); check(12, 30, 0x08ff1230); check(12, 45, 0x08ff1245); check(13, 0, 0x08ff1300); check(13, 15, 0x08ff1315); check(13, 30, 0x08ff1330); check(13, 45, 0x08ff1345); check(14, 0, 0x08ff1400); check(15, 0, 0x08ff1500); check(16, 0, 0x08ff1600); check(17, 0, 0x08ff1700); check(18, 0, 0x08ff1800); break; case pwc.ymd.September: //9月 check( 6, 0, 0x09ff0600); check( 7, 0, 0x09ff0700); check( 8, 0, 0x09ff0800); check( 9, 0, 0x09ff0900); check( 9, 30, 0x09ff0930); check(10, 0, 0x09ff1000); check(10, 30, 0x09ff1030); check(11, 0, 0x09ff1100); check(11, 15, 0x09ff1115); check(11, 30, 0x09ff1130); check(11, 45, 0x09ff1145); check(12, 0, 0x09ff1200); check(12, 15, 0x09ff1215); check(12, 30, 0x09ff1230); check(12, 45, 0x09ff1245); check(13, 0, 0x09ff1300); check(13, 15, 0x09ff1315); check(13, 30, 0x09ff1330); check(13, 45, 0x09ff1345); check(14, 0, 0x09ff1400); check(15, 0, 0x09ff1500); check(16, 0, 0x09ff1600); check(17, 0, 0x09ff1700); check(18, 0, 0x09ff1800); break; default: //1~5月「Jan,Feb,Mar,Apr,May」、10~12月「Oct,Nov,Dec」) check( 6, 0, 0x09ff1500); check(18, 0, 0x09ff1600); break; } return (0 < On_No); //OnTimeが0を超えたら「true」を返す } inline void SkipWrite( bool argV ){ Skip = argV; } inline bool SkipCheck( bool argV ){ if(Skip == argV){ return true; }else{ return false; }; } /* 20230730 TBD 次回ここから修正する!!!!★★★!!! * 引数の時刻である場合はTrueを返す関数 * 例: check(6, 0, 0x060A0600) */ inline void check(int hor, int min, unsigned long on_no){ if(Skip2){ if(pwc.ymd.Hor == hor && pwc.ymd.Min == min){ On_No = on_no; #if false//DEBUG Serial.print( " now:" + (String)pwc.ymd.Mon + ":" + (String)pwc.ymd.Day + ":" + (String)pwc.ymd.Hor + ":" + (String)pwc.ymd.Min + ", Old:" + (String)OldMon + ":" + (String)OldDay + ":" + (String)OldHor + ":" + (String)OldMin + "\r\n" ); #endif if(pwc.ymd.Min != OldMin || pwc.ymd.Hor != OldHor || pwc.ymd.Day != OldDay || pwc.ymd.Mon != OldMon){ Skip2 = false; WatTimParDay++; #if false//DEBUG Serial.print( " now:" + (String)pwc.ymd.Mon + ":" + (String)pwc.ymd.Day + ":" + (String)pwc.ymd.Hor + ":" + (String)pwc.ymd.Min + ", chack:" + (String)hor + ":" + (String)min + ", Old:" + (String)OldMon + ":" + (String)OldDay + ":" + (String)OldHor + ":" + (String)OldMin + ", Skip2:" + (String)Skip2 + ", WatTimParDay:" + (String)WatTimParDay + ", On_No:0x" + String(On_No,HEX) + "\r\n" ); #endif } } OldMin = pwc.ymd.Min; OldHor = pwc.ymd.Hor; OldDay = pwc.ymd.Day; OldMon = pwc.ymd.Mon; } }
自動散水機のプログラム(PlantWatering.ino)
#ifndef Arduino #include <Arduino.h> #endif #define DEBUG false #include <Wire.h> #include "DS1307.h"//https://github.com/Seeed-Studio/RTC_DS1307 class PlantWateringClass { private: DS1307 clock; //DS1307Classのインスタンス生成 const int SensorThreshold_Morning = 410; //SensorReadの値が朝410を超えたら土に水をあげる const int SensorThreshold_Night = 462; //SensorReadの値が夜475を超えたら土に水をあげる const int Hor_Morning = 7; //朝一の時刻(閾値) int SensorPower = 2; //土壌湿度センサー電力供給制御ピン番号 int SolenoidPower = 8; //水電磁弁開閉制御ピン番号 int SwitchIn = A1; //水電磁弁の手動ONスイッチ接続ピン番号 int SensorIn = A3; //土壌湿度センサー値取得ピン番号 public: typedef struct { //変数 int Yea; int Mon; int Day; int Wee; int Hor; int Min; int Sec; //「曜日」列挙 #ifndef MON //※「DS1307.h」「#define MON 1・・・SUN 7」迄既に定義されているので以下参考1 enum WEE : byte {MON = 1, TUE, WED, THU, FRI, SAT, SUN}; #endif //「月」列挙 enum MONTH : byte { January = 1, February, March, April, May, June, July, August, September, October, November, December }; } YMD; YMD ymd; /* * 時刻調整関数 * 20190624:インターネット時間-3秒に調整済み * 通常の書き込み時は確実にDEBUG値を「false」にする! */ void RTC_time_initialize(bool argV){ if(argV){ // 年, 月, 日, 時, 分, 秒, 曜日 //RTC_Write(2019, 7, 21, 12, 5, 0, 0);//前回 //RTC_Write(2020, 6, 14, 6, 49, 55, 0);//書き込み後直ぐ出す_Test用 //RTC_Write(2021, 6, 13, 15, 39, 0, 0);//20210614 いつでも値が確認できるようにDEBUGModeでシリアル出すが日付リセットは止めておく //RTC_Write(2022, 7, 2, 16, 47, 0, 0);//20220617 いつでも値が確認できるようにDEBUGModeでシリアル出すが日付リセットは止めておく RTC_Write(2023, 6, 25, 17, 47, 0, 0);//20230625 いつでも値が確認できるようにDEBUGModeでシリアル出すが日付リセットは止めておく } } PlantWateringClass(int _SensorPower=2, int _SolenoidPower=8, int _SwitchIn=A1, int _SensorIn=A3) {//PlantWateringClassのコンストラクタ SensorPower = _SensorPower; SolenoidPower = _SolenoidPower; SwitchIn = _SwitchIn; SensorIn = _SensorIn; pinMode(SensorPower, OUTPUT); pinMode(SolenoidPower, OUTPUT); pinMode(SwitchIn, INPUT); pinMode(SensorIn, INPUT); clock.begin(); } inline void SolenoidWrite(bool argV){digitalWrite(SolenoidPower, argV);}; //TRUE:散水, FALSE:停止 /* * 土壌湿度センサーの値を取得する */ bool Sensor_Read(){ int SensorValue = 0; //SensorValueをint型の変数として宣言(毎回0にリセット) for(byte i=0;i<=10;i++){ //A3ポートの電圧を10回読み平均値を出す digitalWrite(SensorPower, HIGH); //センサー電力供給開始 delay(10); //一定時間電流を流して測定値を安定させる SensorValue = SensorValue + analogRead(SensorIn); //センサー電圧を読み合計する digitalWrite(SensorPower, LOW); //センサー電力供給停止 } SensorValue = SensorValue / 10; //センサー電圧平均値を返す int SensorThreshold_Data; //朝晩によって閾値を変える if(ymd.Hor < Hor_Morning){ SensorThreshold_Data = SensorThreshold_Morning; }else{ SensorThreshold_Data = SensorThreshold_Night; } if(SensorValue > SensorThreshold_Data){ //測定値が閾値より高い場合は1を返す return true; Serial.println("Senser_ON "); }else{ return false; } Serial.println("Senser_OFF"); } /* * 手動スイッチ入力状態の測定 */ bool Switch_Read(){ if(digitalRead(SwitchIn) == HIGH){ return true; }else{ return false; } } /* * Serial受信データ【例:h0,1,2,3CRLF】をintに変換しint配列に代入する関数 */ inline bool Receive(int* B) { int receive_bytes = Serial.available(), command_length; static String str_sum; unsigned short get_byte_count; if (receive_bytes) { str_sum = str_sum + Serial.readStringUntil(0x0A); int hed_posi = str_sum.indexOf("h"); if (hed_posi != -1){ get_byte_count = str_sum.length(); int lf_posi = get_byte_count; if (lf_posi == -1){ str_sum.remove(0, lf_posi); }else{ String str_command = str_sum.substring(0, lf_posi); str_sum.remove(0, lf_posi + 1); str_command.remove(lf_posi - 1); str_command.remove(0, 1); int k = 0; int j = 0; char text[256]; char delim = ','; for (int i = 0; i <= str_command.length(); i++) { char c = str_command.charAt(i); if (c == delim || i == str_command.length()) { text[k] = '\0'; B[j] = atoi(text); j++; k = 0; } else { text[k] = c; k++; } } return true; } } } return false; } /* * RTCの時刻を修正する関数 * 使い方 年, 月, 日, 時, 分, 秒, 曜日 * RTC_Write(2019, 6, 24, 13, 51, 00, MON); */ void RTC_Write(int Y,int M,int D,int h,int m,int s,int W){ clock.fillByYMD(Y,M,D); //年,月,日 clock.fillByHMS(h,m,s); //時:分,秒" clock.fillDayOfWeek(W); //曜日 clock.setTime(); //RTCチップへの書き込み } /* * 現在の時刻を取得する */ void RTC_refresh(){ clock.getTime(); ymd.Yea = clock.year + 2000; ymd.Mon = clock.month; ymd.Day = clock.dayOfMonth; ymd.Wee = clock.dayOfWeek; ymd.Hor = clock.hour; ymd.Min = clock.minute; ymd.Sec = clock.second; } /* * 現在のinformationを出力する */ void information_Print(String str="\r\n"){ //■Sencer_Print() Serial.flush(); int SensorValue = 0; //SensorValueをint型の変数として宣言(毎回0にリセット) for(byte i=0;i<=10;i++){ //A3ポートの電圧を10回読み平均値を出す digitalWrite(SensorPower, HIGH); //センサー電力供給開始 delay(10); //一定時間電流を流して測定値を安定させる SensorValue = SensorValue + analogRead(SensorIn); //センサー電圧を読み合計する digitalWrite(SensorPower, LOW); //センサー電力供給停止 } SensorValue = SensorValue / 10; //センサー電圧平均値を返す Serial.print("Sensor:");Serial.print(SensorValue);Serial.print(" "); //■RTC_Print() Serial.flush(); Serial.print(ymd.Yea, DEC);Serial.print("/"); Serial.print(ymd.Mon, DEC);Serial.print("/"); Serial.print(ymd.Day, DEC); switch (ymd.Wee){ case MON:Serial.print("(MON)");break; case TUE:Serial.print("(TUE)");break; case WED:Serial.print("(WED)");break; case THU:Serial.print("(THU)");break; case FRI:Serial.print("(FRI)");break; case SAT:Serial.print("(SAT)");break; case SUN:Serial.print("(SUN)");break; } char Value[2]; dtostrf(ymd.Hor,2,0,Value);Serial.print(Value);Serial.print(":"); dtostrf(ymd.Min,2,0,Value);Serial.print(Value);Serial.print(":"); dtostrf(ymd.Sec,2,0,Value);Serial.print(Value); //■出力状態も出力 Serial.print(" Solenoid:"); if(digitalRead(SolenoidPower)){Serial.print("ON");}else{Serial.print("OFF");} //■その他information Serial.print(str); } }; /* * 機能:土壌湿度センサーの値を判定 * Dry: (520 430] * Wet: (430 350] * Water: (350 260] * * 土に直挿し実測値 * 390:少し湿っている * 272:水没! * 337:水が滴っている * * センサーの周りにスポンジセットし水散布 * 一回目 * 350:水あげ5min後 * 425:水あげ10min後 * 453:水あげ60min後 * * 二回目 * 280:水あげ 0min後 13:09 * 283:水あげ 1min後 13:10 * 303:水あげ 2min後 13:11 * 339:水あげ 3min後 13:12 * 386:水あげ 4min後 13:13 * 398:水あげ 5min後 13:14 * 404:水あげ 6min後 13:15 * 406:水あげ 7min後 13:16 * 407:水あげ 8min後 13:17 * 408:水あげ 9min後 13:18 * 408:水あげ10min後 13:19 * 409:水あげ11min後 13:20 * 411:水あげ15min後 13:24 * 415:水あげ20min後 13:29 水が滴っている * 415:水あげ25min後 13:34 水が滴っている * 417:水あげ30min後 13:39 水が滴っている * 423:水あげ50min後 13:59 水が滴っていない * * 三回目(8/3 晴れ 11:08) * 443:水あげ4h後 乾燥している * * 四回目(2023/6/25 晴れ 17:35,新) * 264:水あげ4h後 乾燥している */
自動散水機のプログラム(PlantWatering.h)
5.まとめ
今年は散水ノズル・散水プログラム等々を気合を入れて大幅改善した甲斐あって、グリーンカーテンとしては過去一の出来となりました!
■おまけ
2015年から殺風景な庭の緑化を進めて来ましたが、8年越しで森感出て来たのではないでしょうか(*^^)v
励みになりますのでよければクリック下さい(^o^)/