From 49664ba6f7451d3c630ebd02286ecd04c0e8afc8 Mon Sep 17 00:00:00 2001 From: Empire Date: Fri, 22 Oct 2021 17:39:42 +0000 Subject: [PATCH] added initial support for hydroponic systems, changed cooldown to use minutes --- esp32/host/config-example.json | 55 ------------------------ esp32/host/settings.json.example | 14 +++---- esp32/include/HomieConfiguration.h | 4 +- esp32/include/HomieTypes.h | 6 ++- esp32/include/PlantCtrl.h | 14 ++++++- esp32/src/PlantCtrl.cpp | 6 +-- esp32/src/main.cpp | 67 ++++++++++++++++++------------ 7 files changed, 70 insertions(+), 96 deletions(-) delete mode 100644 esp32/host/config-example.json diff --git a/esp32/host/config-example.json b/esp32/host/config-example.json deleted file mode 100644 index 433ae42..0000000 --- a/esp32/host/config-example.json +++ /dev/null @@ -1,55 +0,0 @@ -{ - "name": "PlantControl", - "device_id": "PlantCtrl1", - "device_stats_interval": 60, - "wifi": { - "ssid": "SSID", - "bssid" : "BSSID", - "password": "mysecretPassword", - "channel": 1 - }, - "mqtt": { - "host": "[0-255].[0-255].[0-255].[0-255]", - "port": 1883, - "base_topic": "mqtt/topic/", - "auth": false - }, - "ota": { - "enabled": true - }, - "settings": { - "deepsleep" : 60000, - "nightsleep" : 60000, - "watermaxlevel": 50, - "watermin" : 5, - "plants" : 3, - "moist0" : 2000, - "moist1" : 2000, - "moist2" : 2000, - "moist3" : 2000, - "moist4" : 2000, - "moist5" : 2000, - "moist6" : 2000, - "plant0MaxPumpTime": 1000, - "plant1MaxPumpTime": 1000, - "plant2MaxPumpTime": 1000, - "plant3MaxPumpTime": 1000, - "plant4MaxPumpTime": 1000, - "plant5MaxPumpTime": 1000, - "plant6MaxPumpTime": 1000, - "plant0MinPumpIdle": 10000, - "plant1MinPumpIdle": 10000, - "plant2MinPumpIdle": 10000, - "plant3MinPumpIdle": 10000, - "plant4MinPumpIdle": 10000, - "plant5MinPumpIdle": 10000, - "plant6MinPumpIdle": 10000, - "plant0PumpDuration": 5, - "plant1PumpDuration": 5, - "plant2PumpDuration": 5, - "plant3PumpDuration": 5, - "plant4PumpDuration": 5, - "plant5PumpDuration": 5, - "plant6PumpDuration": 5 - } -} diff --git a/esp32/host/settings.json.example b/esp32/host/settings.json.example index b23763b..e3b7734 100644 --- a/esp32/host/settings.json.example +++ b/esp32/host/settings.json.example @@ -14,36 +14,36 @@ "hourstart0":6, "hourend0":20, "lowLight0": false, - "delay0": 10, + "delay0": 30, "dry1":-1, "hourstart1":6, "hourend1":20, "lowLight1": false, - "delay1": 10, + "delay1": 30, "dry2":-1, "hourstart2":6, "hourend2":20, "lowLight2": false, - "delay2": 10, + "delay2": 30, "dry3":-1, "hourstart3":6, "hourend3":20, "lowLight3": false, - "delay3": 10, + "delay3": 30, "dry4":-1, "hourstart4":6, "hourend4":20, "lowLight4": false, - "delay4": 10, + "delay4": 30, "dry5":-1, "hourstart5":6, "hourend5":20, "lowLight5": false, - "delay5": 10, + "delay5": 30, "dry6":-1, "hourstart6":6, "hourend6":20, "lowLight6": false, - "delay6": 10 + "delay6": 30 } } diff --git a/esp32/include/HomieConfiguration.h b/esp32/include/HomieConfiguration.h index d271324..70002c9 100644 --- a/esp32/include/HomieConfiguration.h +++ b/esp32/include/HomieConfiguration.h @@ -104,9 +104,9 @@ HomieSetting ntpServer("ntpServer", "NTP server (pool.ntp.org as d HomieSetting mPumpAllowedHourRangeStart##plant = HomieSetting("hourstart" strplant, "Plant" strplant " - Range pump allowed hour start (0-23)"); \ HomieSetting mPumpAllowedHourRangeEnd##plant = HomieSetting("hourend" strplant, "Plant" strplant " - Range pump allowed hour end (0-23)"); \ HomieSetting mPumpOnlyWhenLowLight##plant = HomieSetting("lowLight" strplant, "Plant" strplant " - Enable the Pump only, when there is no sunlight"); \ - HomieSetting mPumpCooldownInHours##plant = HomieSetting("delay" strplant, "Plant" strplant " - How long to wait until the pump is activated again (minutes)"); \ + HomieSetting mPumpCooldownInMinutes##plant = HomieSetting("delay" strplant, "Plant" strplant " - How long to wait until the pump is activated again (minutes)"); \ HomieSetting pPumpDuration##plant = HomieSetting("pumpDuration" strplant, "Plant" strplant " - time seconds or ml (if using flowmeter) to water when pump is active"); \ - PlantSettings_t mSetting##plant = {&mSensorDry##plant, &mPumpAllowedHourRangeStart##plant, &mPumpAllowedHourRangeEnd##plant, &mPumpOnlyWhenLowLight##plant, &mPumpCooldownInHours##plant, &pPumpDuration##plant}; \ + PlantSettings_t mSetting##plant = {&mSensorDry##plant, &mPumpAllowedHourRangeStart##plant, &mPumpAllowedHourRangeEnd##plant, &mPumpOnlyWhenLowLight##plant, &mPumpCooldownInMinutes##plant, &pPumpDuration##plant}; \ /**< Generate all settings for one plant \ * \ * Feature to start pumping only at morning: @link{SOLAR_CHARGE_MIN_VOLTAGE} and @link{SOLAR_CHARGE_MAX_VOLTAGE} \ diff --git a/esp32/include/HomieTypes.h b/esp32/include/HomieTypes.h index 9f08fbb..7a87888 100644 --- a/esp32/include/HomieTypes.h +++ b/esp32/include/HomieTypes.h @@ -13,8 +13,12 @@ #include +//plant pump is deactivated, but sensor values are still recorded and published #define DEACTIVATED_PLANT -1 +//special value to indicate a missing sensor when the plant is not deactivated but no valid sensor value was read #define MISSING_SENSOR -2 +//plant uses only cooldown and duration, moisture is measured but ignored, allowedHours is ignored (eg. make a 30min on 30min off cycle) +#define HYDROPONIC_MODE -3 typedef struct PlantSettings_t { @@ -22,7 +26,7 @@ typedef struct PlantSettings_t HomieSetting *pPumpAllowedHourRangeStart; HomieSetting *pPumpAllowedHourRangeEnd; HomieSetting *pPumpOnlyWhenLowLight; - HomieSetting *pPumpCooldownInHours; + HomieSetting *pPumpCooldownInMinutes; HomieSetting *pPumpDuration; } PlantSettings_t; diff --git a/esp32/include/PlantCtrl.h b/esp32/include/PlantCtrl.h index 9f0fa58..9ec969d 100644 --- a/esp32/include/PlantCtrl.h +++ b/esp32/include/PlantCtrl.h @@ -61,6 +61,12 @@ public: void activatePump(void); + + bool isHydroponic(){ + long current = this->mSetting->pSensorDry->get(); + return !equalish(current,HYDROPONIC_MODE); + } + /** * @brief Check if a plant is too dry and needs some water. * @@ -69,11 +75,17 @@ public: */ bool isPumpRequired() { + if(isHydroponic()){ + //hydroponic only uses timer based controll + return true; + } bool isDry = getCurrentMoisture() > getSetting2Moisture(); bool isActive = isPumpTriggerActive(); return isDry && isActive; } + + bool isPumpTriggerActive() { long current = this->mSetting->pSensorDry->get(); @@ -110,7 +122,7 @@ public: void init(void); long getCooldownInSeconds() { - return this->mSetting->pPumpCooldownInHours->get()*60*60; + return this->mSetting->pPumpCooldownInMinutes->get()*60; } /** diff --git a/esp32/src/PlantCtrl.cpp b/esp32/src/PlantCtrl.cpp index ea45eb5..ba418a1 100644 --- a/esp32/src/PlantCtrl.cpp +++ b/esp32/src/PlantCtrl.cpp @@ -46,9 +46,9 @@ void Plant::init(void) return ((candidate >= 0) && (candidate <= 23)); }); this->mSetting->pPumpOnlyWhenLowLight->setDefaultValue(true); - this->mSetting->pPumpCooldownInHours->setDefaultValue(20); // minutes - this->mSetting->pPumpCooldownInHours->setValidator([](long candidate) { - return ((candidate >= 0) && (candidate <= 1024)); + this->mSetting->pPumpCooldownInMinutes->setDefaultValue(20); // minutes + this->mSetting->pPumpCooldownInMinutes->setValidator([](long candidate) { + return ((candidate >= 0) && (candidate <= 60*24*7)); }); this->mSetting->pPumpDuration->setDefaultValue(5); diff --git a/esp32/src/main.cpp b/esp32/src/main.cpp index 0733a71..84c83d1 100644 --- a/esp32/src/main.cpp +++ b/esp32/src/main.cpp @@ -42,7 +42,6 @@ #define AMOUNT_SENOR_QUERYS 8 #define MAX_TANK_DEPTH 2000 - /****************************************************************************** * FUNCTION PROTOTYPES ******************************************************************************/ @@ -69,7 +68,7 @@ RTC_DATA_ATTR long consecutiveWateringPlant[MAX_PLANTS] = {0}; ******************************************************************************/ bool volatile mDownloadMode = false; /**< Controller must not sleep */ bool volatile mSensorsRead = false; /**< Sensors are read without Wifi or MQTT */ -int volatile pumpToRun = -1; /** pump to run at the end of the cycle */ +int volatile pumpToRun = -1; /** pump to run at the end of the cycle */ bool mConfigured = false; long nextBlink = 0; /**< Time needed in main loop to support expected blink code */ @@ -216,16 +215,16 @@ void readOneWireSensors() Serial << "DS18S20 Temperatur " << String(buf) << " : " << temp << " °C " << endl; if (strcmp(lipoSensorAddr.get(), buf) == 0) { - mqttWrite(&sensorTemp,TEMPERATUR_SENSOR_LIPO,String(temp)); + mqttWrite(&sensorTemp, TEMPERATUR_SENSOR_LIPO, String(temp)); Serial << "Lipo Temperatur " << temp << " °C " << endl; } if (strcmp(waterSensorAddr.get(), buf) == 0) { - mqttWrite(&sensorTemp,TEMPERATUR_SENSOR_WATER,String(temp)); + mqttWrite(&sensorTemp, TEMPERATUR_SENSOR_WATER, String(temp)); Serial << "Water Temperatur " << temp << " °C " << endl; } /* Always send the sensor address with the temperatur value */ - mqttWrite(&sensorTemp,String(buf),String(temp)); + mqttWrite(&sensorTemp, String(buf), String(temp)); } else { @@ -397,22 +396,26 @@ int determineNextPump(bool isLowLight) log(LOG_LEVEL_DEBUG, String(String(i) + " No pump required: due to light"), LOG_DEBUG_CODE); continue; } - if (equalish(plant.getCurrentMoisture(), MISSING_SENSOR)) + if (!plant.isHydroponic()) { - plant.publishState("nosensor"); - log(LOG_LEVEL_ERROR, String(String(i) + " No pump possible: missing sensor"), LOG_MISSING_PUMP); - continue; + if (equalish(plant.getCurrentMoisture(), MISSING_SENSOR)) + { + plant.publishState("nosensor"); + log(LOG_LEVEL_ERROR, String(String(i) + " No pump possible: missing sensor"), LOG_MISSING_PUMP); + continue; + } } + if (plant.isPumpRequired()) { /* Handle e.g. start = 21, end = 8 */ - if (((plant.getHoursStart() > plant.getHoursEnd()) && + if ( plant.isHydroponic() || (((plant.getHoursStart() > plant.getHoursEnd()) && (getCurrentHour() >= plant.getHoursStart() || getCurrentHour() <= plant.getHoursEnd())) || /* Handle e.g. start = 8, end = 21 */ ((plant.getHoursStart() < plant.getHoursEnd()) && (getCurrentHour() >= plant.getHoursStart() && getCurrentHour() <= plant.getHoursEnd())) || /* no time from NTP received */ - (getCurrentTime() < 10000)) + (getCurrentTime() < 10000))) { if (wateralarm) { @@ -423,7 +426,10 @@ int determineNextPump(bool isLowLight) plant.publishState("active"); } - consecutiveWateringPlant[i]++; + if(!plant.isHydroponic()){ + consecutiveWateringPlant[i]++; + } + log(LOG_LEVEL_DEBUG, String(String(i) + " Requested pumping"), LOG_DEBUG_CODE); pumpToUse = i; } @@ -573,21 +579,24 @@ void pumpActiveLoop() #ifdef FLOWMETER_PIN int16_t pulses; - pcnt_unit_t unit = (pcnt_unit_t) (PCNT_UNIT_7); + pcnt_unit_t unit = (pcnt_unit_t)(PCNT_UNIT_7); esp_err_t result = pcnt_get_counter_value(unit, &pulses); - if(result != ESP_OK){ + if (result != ESP_OK) + { log(LOG_LEVEL_ERROR, LOG_HARDWARECOUNTER_ERROR_MESSAGE, LOG_HARDWARECOUNTER_ERROR_CODE); targetReached = true; - } else { + } + else + { /**FLOWMETER_FLOWFACTOR * (L/Min) = F; given 1L/min -> FLOWMETER_FLOWFACTOR*60 pulses per liter -> 1000/result -> ml pro pulse; -> result * pulses ->*/ long pumped = (FLOWMETER_FLOWFACTOR * 60) * pulses / 1000; - if(pumped >= pumpTargetMl) + if (pumped >= pumpTargetMl) { targetReached = true; - pcnt_counter_pause(unit); + pcnt_counter_pause(unit); } mPlants[pumpToRun].setProperty("waterusage").send(String(pumped)); } @@ -696,7 +705,8 @@ void setup() // Set default values //in seconds - deepSleepTime.setDefaultValue(600).setValidator([](long candidate) { return (candidate > 0) && (candidate < (60 * 60 * 2) /** 2h max sleep */); }); + deepSleepTime.setDefaultValue(600).setValidator([](long candidate) + { return (candidate > 0) && (candidate < (60 * 60 * 2) /** 2h max sleep */); }); deepSleepNightTime.setDefaultValue(600); ntpServer.setDefaultValue("pool.ntp.org"); @@ -706,13 +716,17 @@ void setup() waterLevelVol.setDefaultValue(5000); /* 5l in ml */ lipoSensorAddr.setDefaultValue(""); waterSensorAddr.setDefaultValue(""); - pumpIneffectiveWarning.setDefaultValue(5).setValidator([](long candidate) { return (candidate > 0) && (candidate < (20)); }); + pumpIneffectiveWarning.setDefaultValue(5).setValidator([](long candidate) + { return (candidate > 0) && (candidate < (20)); }); #if defined(TIMED_LIGHT_PIN) - timedLightStart.setDefaultValue(18).setValidator([](long candidate) { return (candidate > 0) && (candidate < (25)); }); - timedLightEnd.setDefaultValue(23).setValidator([](long candidate) { return (candidate > 0) && (candidate < (24)); }); + timedLightStart.setDefaultValue(18).setValidator([](long candidate) + { return (candidate > 0) && (candidate < (25)); }); + timedLightEnd.setDefaultValue(23).setValidator([](long candidate) + { return (candidate > 0) && (candidate < (24)); }); timedLightOnlyWhenDark.setDefaultValue(true); - timedLightVoltageCutoff.setDefaultValue(3.8).setValidator([](double candidate) { return (candidate > 3.3) && (candidate < (4.2)); }); + timedLightVoltageCutoff.setDefaultValue(3.8).setValidator([](double candidate) + { return (candidate > 3.3) && (candidate < (4.2)); }); #endif // TIMED_LIGHT_PIN Homie.setLoopFunction(homieLoop); @@ -888,7 +902,7 @@ void plantcontrol() readOneWireSensors(); - Serial << "W : " << waterRawSensor.getMedian() << " cm (" << String(waterLevelMax.get() - waterRawSensor.getMedian ()) << "%)" << endl; + Serial << "W : " << waterRawSensor.getAverage() << " cm (" << String(waterLevelMax.get() - waterRawSensor.getAverage()) << "%)" << endl; float batteryVoltage = battery.getVoltage(BATTSENSOR_INDEX_BATTERY); float chipTemp = battery.getTemperature(); @@ -896,14 +910,14 @@ void plantcontrol() if (aliveWasRead()) { - float remaining = waterLevelMax.get() - waterRawSensor.getMedian(); + float remaining = waterLevelMax.get() - waterRawSensor.getAverage(); if (!isnan(remaining)) { sensorWater.setProperty("remaining").send(String(remaining)); } - if (!isnan(waterRawSensor.getMedian())) + if (!isnan(waterRawSensor.getAverage())) { - sensorWater.setProperty("distance").send(String(waterRawSensor.getMedian())); + sensorWater.setProperty("distance").send(String(waterRawSensor.getAverage())); } sensorLipo.setProperty("percent").send(String(100 * batteryVoltage / VOLT_MAX_BATT)); sensorLipo.setProperty("volt").send(String(batteryVoltage)); @@ -1004,4 +1018,3 @@ bool determineTimedLightState(bool lowLight) } #endif -