added timed light function

This commit is contained in:
Your Name 2021-08-29 20:45:50 +02:00
parent e927fc8c70
commit f99f72b65a
4 changed files with 102 additions and 12 deletions

View File

@ -69,8 +69,12 @@
#define SENSOR_TANK_ECHO GPIO_NUM_16 /**< GPIO 16 - echo feedback of water sensor */ #define SENSOR_TANK_ECHO GPIO_NUM_16 /**< GPIO 16 - echo feedback of water sensor */
#define SENSOR_TANK_TRG GPIO_NUM_17 /**< GPIO 17 - trigger for water sensor */ #define SENSOR_TANK_TRG GPIO_NUM_17 /**< GPIO 17 - trigger for water sensor */
#define BUTTON GPIO_NUM_0 /**< GPIO 0 - Fix button of NodeMCU */ #define BUTTON GPIO_NUM_0 /**< GPIO 0 - Fix button of NodeMCU */
#define CUSTOM1_PIN3 GPIO_NUM_2 /**< GPIO 2 - Custom GPIO controlling a MOSFET, connected to GND */
#define CUSTOM1_PIN2 GPIO_NUM_12 /**< GPIO 4 - custom GPIO directly connected to GPIO header */ #define CUSTOM1_PIN1 GPIO_NUM_34 /** direct gpio */
#define CUSTOM1_PIN3 GPIO_NUM_35 /** direct gpio */
#define CUSTOM1_PIN5 GPIO_NUM_2 /** mosfet controlled */
#define CUSTOM1_PIN7 GPIO_NUM_12 /** mosfet controlled */
#define I2C1_SDA GPIO_NUM_34 /**< GPIO 34 - I2C */ #define I2C1_SDA GPIO_NUM_34 /**< GPIO 34 - I2C */
#define I2C1_SCL GPIO_NUM_35 /**< GPIO 35 - I2C */ #define I2C1_SCL GPIO_NUM_35 /**< GPIO 35 - I2C */
/* @} */ /* @} */
@ -80,6 +84,8 @@
*/ */
#define FIRMWARE_VERSION "sw 1.3 hw 0.10" #define FIRMWARE_VERSION "sw 1.3 hw 0.10"
#define TIMED_LIGHT_PIN CUSTOM1_PIN5
#define MOIST_SENSOR_MAX_FRQ 60000 // 60kHz (500Hz margin) #define MOIST_SENSOR_MAX_FRQ 60000 // 60kHz (500Hz margin)
#define MOIST_SENSOR_MIN_FRQ 1000 // 1kHz (500Hz margin) #define MOIST_SENSOR_MIN_FRQ 1000 // 1kHz (500Hz margin)

View File

@ -51,6 +51,11 @@ HomieNode plant4("plant4", "Plant 4", "Plant"); /**< dynamic Homie information f
HomieNode plant5("plant5", "Plant 5", "Plant"); /**< dynamic Homie information for sixth plant */ HomieNode plant5("plant5", "Plant 5", "Plant"); /**< dynamic Homie information for sixth plant */
HomieNode plant6("plant6", "Plant 6", "Plant"); /**< dynamic Homie information for seventh plant */ HomieNode plant6("plant6", "Plant 6", "Plant"); /**< dynamic Homie information for seventh plant */
#if defined(TIMED_LIGHT_PIN)
HomieNode timedLightNode("timedLight", "TimedLight", "Status");
#endif // TIMED_LIGHT_PIN
HomieNode sensorLipo("lipo", "Battery Status", "Lipo"); HomieNode sensorLipo("lipo", "Battery Status", "Lipo");
HomieNode sensorSolar("solar", "Solar Status", "Solarpanel"); HomieNode sensorSolar("solar", "Solar Status", "Solarpanel");
HomieNode sensorWater("water", "WaterSensor", "Water"); HomieNode sensorWater("water", "WaterSensor", "Water");
@ -78,6 +83,14 @@ HomieSetting<const char *> lipoSensorAddr("lipoDSAddr", "1wire address for lipo
HomieSetting<const char *> waterSensorAddr("tankDSAddr", "1wire address for water temperature sensor"); HomieSetting<const char *> waterSensorAddr("tankDSAddr", "1wire address for water temperature sensor");
HomieSetting<const char *> ntpServer("ntpServer", "NTP server (pool.ntp.org as default)"); HomieSetting<const char *> ntpServer("ntpServer", "NTP server (pool.ntp.org as default)");
#if defined(TIMED_LIGHT_PIN)
HomieSetting<double> timedLightVoltageCutoff("LightVoltageCutoff", "voltage at wich to disable light");
HomieSetting<long> timedLightStart("LightStart", "hour to start light");
HomieSetting<long> timedLightEnd("LightEnd", "hour to end light");
HomieSetting<bool> timedLightOnlyWhenDark("LightOnlyDark", "only enable light, if solar is low");
#endif // TIMED_LIGHT_PIN
/** /**
* @} * @}
*/ */

View File

@ -18,7 +18,7 @@
#include "RunningMedian.h" #include "RunningMedian.h"
#include "MathUtils.h" #include "MathUtils.h"
#define MOISTURE_MEASUREMENT_DURATION 500 /** ms */ #define MOISTURE_MEASUREMENT_DURATION 400 /** ms */
class Plant class Plant

View File

@ -57,9 +57,10 @@
******************************************************************************/ ******************************************************************************/
void log(int level, String message, int code); void log(int level, String message, int code);
int determineNextPump(); int determineNextPump(bool lowLight);
void plantcontrol(); void plantcontrol();
void readPowerSwitchedSensors(); void readPowerSwitchedSensors();
bool determineTimedLightState(bool lowLight);
/****************************************************************************** /******************************************************************************
* NON VOLATILE VARIABLES in DEEP SLEEP * NON VOLATILE VARIABLES in DEEP SLEEP
@ -67,6 +68,10 @@ void readPowerSwitchedSensors();
RTC_DATA_ATTR int lastPumpRunning = -1; /**< store last successfully waterd plant */ RTC_DATA_ATTR int lastPumpRunning = -1; /**< store last successfully waterd plant */
RTC_DATA_ATTR long lastWaterValue = 0; /**< to calculate the used water per plant */ RTC_DATA_ATTR long lastWaterValue = 0; /**< to calculate the used water per plant */
#if defined(TIMED_LIGHT_PIN)
RTC_DATA_ATTR bool timedLightOn = false; /**< allow fast recovery after poweron */
#endif // TIMED_LIGHT_PIN
RTC_DATA_ATTR long rtcLastWateringPlant[MAX_PLANTS] = {0}; RTC_DATA_ATTR long rtcLastWateringPlant[MAX_PLANTS] = {0};
RTC_DATA_ATTR long consecutiveWateringPlant[MAX_PLANTS] = {0}; RTC_DATA_ATTR long consecutiveWateringPlant[MAX_PLANTS] = {0};
@ -133,19 +138,21 @@ void espDeepSleepFor(long seconds, bool activatePump)
} }
} }
//allo hold for all digital pins
gpio_deep_sleep_hold_en();
esp_sleep_pd_config(ESP_PD_DOMAIN_RTC_PERIPH, ESP_PD_OPTION_OFF); esp_sleep_pd_config(ESP_PD_DOMAIN_RTC_PERIPH, ESP_PD_OPTION_OFF);
esp_sleep_pd_config(ESP_PD_DOMAIN_RTC_SLOW_MEM, ESP_PD_OPTION_ON); esp_sleep_pd_config(ESP_PD_DOMAIN_RTC_SLOW_MEM, ESP_PD_OPTION_ON);
esp_sleep_pd_config(ESP_PD_DOMAIN_RTC_FAST_MEM, ESP_PD_OPTION_ON); esp_sleep_pd_config(ESP_PD_DOMAIN_RTC_FAST_MEM, ESP_PD_OPTION_ON);
if (activatePump) if (activatePump)
{ {
esp_sleep_pd_config(ESP_PD_DOMAIN_XTAL, ESP_PD_OPTION_ON); esp_sleep_pd_config(ESP_PD_DOMAIN_XTAL, ESP_PD_OPTION_ON);
gpio_deep_sleep_hold_en();
gpio_hold_en(OUTPUT_ENABLE_PUMP); //pump pwr gpio_hold_en(OUTPUT_ENABLE_PUMP); //pump pwr
} }
else else
{ {
gpio_hold_dis(OUTPUT_ENABLE_PUMP); //pump pwr gpio_hold_dis(OUTPUT_ENABLE_PUMP); //pump pwr
gpio_deep_sleep_hold_dis();
digitalWrite(OUTPUT_ENABLE_PUMP, LOW); digitalWrite(OUTPUT_ENABLE_PUMP, LOW);
digitalWrite(OUTPUT_ENABLE_SENSOR, LOW); digitalWrite(OUTPUT_ENABLE_SENSOR, LOW);
for (int i = 0; i < MAX_PLANTS; i++) for (int i = 0; i < MAX_PLANTS; i++)
@ -160,7 +167,10 @@ void espDeepSleepFor(long seconds, bool activatePump)
gpio_hold_en(OUTPUT_PUMP4); gpio_hold_en(OUTPUT_PUMP4);
gpio_hold_en(OUTPUT_PUMP5); gpio_hold_en(OUTPUT_PUMP5);
gpio_hold_en(OUTPUT_PUMP6); gpio_hold_en(OUTPUT_PUMP6);
//FIXME fix for outher outputs #if defined(TIMED_LIGHT_PIN)
gpio_hold_en(TIMED_LIGHT_PIN);
#endif // TIMED_LIGHT_PIN
esp_sleep_enable_timer_wakeup((seconds * 1000U * 1000U)); esp_sleep_enable_timer_wakeup((seconds * 1000U * 1000U));
if (mAliveWasRead) if (mAliveWasRead)
@ -410,9 +420,8 @@ void onHomieEvent(const HomieEvent &event)
} }
} }
int determineNextPump() int determineNextPump(bool isLowLight)
{ {
bool isLowLight = (mSolarVoltage < SOLAR_CHARGE_MIN_VOLTAGE);
int pumpToUse = -1; int pumpToUse = -1;
for (int i = 0; i < MAX_PLANTS; i++) for (int i = 0; i < MAX_PLANTS; i++)
{ {
@ -601,6 +610,13 @@ void setup()
WiFi.mode(WIFI_OFF); WiFi.mode(WIFI_OFF);
Serial.flush(); Serial.flush();
//restore state before releasing pin, to prevent flickering
#if defined(TIMED_LIGHT_PIN)
pinMode(TIMED_LIGHT_PIN, OUTPUT);
digitalWrite(TIMED_LIGHT_PIN, timedLightOn);
gpio_hold_dis(TIMED_LIGHT_PIN);
#endif // TIMED_LIGHT_PIN
gpio_hold_dis(OUTPUT_PUMP0); gpio_hold_dis(OUTPUT_PUMP0);
gpio_hold_dis(OUTPUT_PUMP1); gpio_hold_dis(OUTPUT_PUMP1);
gpio_hold_dis(OUTPUT_PUMP2); gpio_hold_dis(OUTPUT_PUMP2);
@ -683,9 +699,20 @@ void setup()
waterLevelVol.setDefaultValue(5000); /* 5l in ml */ waterLevelVol.setDefaultValue(5000); /* 5l in ml */
lipoSensorAddr.setDefaultValue(""); lipoSensorAddr.setDefaultValue("");
waterSensorAddr.setDefaultValue(""); waterSensorAddr.setDefaultValue("");
pumpIneffectiveWarning.setDefaultValue(600).setValidator([](long candidate) pumpIneffectiveWarning.setDefaultValue(5).setValidator([](long candidate)
{ return (candidate > 0) && (candidate < (20)); }); { return (candidate > 0) && (candidate < (20)); });
pumpIneffectiveWarning.setDefaultValue(5);
#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 < (22)); });
timedLightOnlyWhenDark.setDefaultValue(true);
timedLightVoltageCutoff.setDefaultValue(3.8).setValidator([](double candidate)
{ return (candidate > 3.3) && (candidate < (4.2)); });
#endif // TIMED_LIGHT_PIN
Homie.setLoopFunction(homieLoop); Homie.setLoopFunction(homieLoop);
Homie.onEvent(onHomieEvent); Homie.onEvent(onHomieEvent);
@ -891,9 +918,10 @@ void plantcontrol()
Serial.flush(); Serial.flush();
} }
bool isLowLight = (mSolarVoltage < SOLAR_CHARGE_MIN_VOLTAGE);
bool hasWater = true; //FIXMEmWaterGone > waterLevelMin.get(); bool hasWater = true; //FIXMEmWaterGone > waterLevelMin.get();
//FIXME no water warning message //FIXME no water warning message
lastPumpRunning = determineNextPump(); lastPumpRunning = determineNextPump(isLowLight);
if (lastPumpRunning != -1 && !hasWater) if (lastPumpRunning != -1 && !hasWater)
{ {
log(LOG_LEVEL_ERROR, LOG_PUMP_BUTNOTANK_MESSAGE, LOG_PUMP_BUTNOTANK_CODE); log(LOG_LEVEL_ERROR, LOG_PUMP_BUTNOTANK_MESSAGE, LOG_PUMP_BUTNOTANK_CODE);
@ -917,6 +945,13 @@ void plantcontrol()
} }
} }
#if defined(TIMED_LIGHT_PIN)
bool shouldLight = determineTimedLightState(isLowLight);
timedLightOn = shouldLight;
digitalWrite(TIMED_LIGHT_PIN, shouldLight);
#endif // TIMED_LIGHT_PIN
/* Always handle one of the deep sleep duration */ /* Always handle one of the deep sleep duration */
if (lastPumpRunning == -1 || !hasWater) if (lastPumpRunning == -1 || !hasWater)
{ {
@ -939,6 +974,42 @@ void plantcontrol()
/** @}*/ /** @}*/
bool determineTimedLightState(bool lowLight){
bool onlyAllowedWhenDark = timedLightOnlyWhenDark.get();
long hoursStart = timedLightStart.get();
long hoursEnd = timedLightEnd.get();
//ntp missing
if(getCurrentTime() < 10000){
timedLightNode.setProperty("state").send(String("Off, missing ntp"));
return false;
}
if(onlyAllowedWhenDark && !lowLight){
timedLightNode.setProperty("state").send(String("Off, not dark"));
return false;
}
if (((hoursStart > hoursEnd) &&
(getCurrentHour() >= hoursStart || getCurrentHour() <= hoursEnd)) ||
/* Handle e.g. start = 8, end = 21 */
((hoursStart < hoursEnd) &&
(getCurrentHour() >= hoursStart && getCurrentHour() <= hoursEnd)))
{
if(battery.getVoltage(BATTSENSOR_INDEX_BATTERY) >= timedLightVoltageCutoff.get() ){
timedLightNode.setProperty("state").send(String("On"));
return true;
}else {
timedLightNode.setProperty("state").send(String("Off, due to missing voltage"));
return false;
}
} else {
timedLightNode.setProperty("state").send(String("Off, outside worktime"));
return false;
}
}
void log(int level, String message, int statusCode) void log(int level, String message, int statusCode)
{ {
String buffer; String buffer;