firmware wip - add pump control

This commit is contained in:
Marcus 2024-06-16 20:16:40 +02:00
parent e1fcb09ae7
commit 54fec62a3e
7 changed files with 205 additions and 70 deletions

View file

@ -239,7 +239,7 @@ void loop() {
controlLED(); controlLED();
} }
controlPUMP();
displayScreens(); displayScreens();
@ -265,24 +265,12 @@ void loop() {
* - re-organize EEPROM saved values. * - re-organize EEPROM saved values.
* - prevent GrowStart to be in the future * - prevent GrowStart to be in the future
* - maybe let the user configure some screens to display. * - maybe let the user configure some screens to display.
* * - put EEPROM adresses into DEFINEs
*/ */
/*
* Pump control
*
* Vars:
* - UsePump (bool)
* - PumpOnTime (int) in sec
* - PumpInterval (int) in days
* - SoilmoistureLow (int) in %
* - PumpLastOn (long) timestamp
* - PumpMode (short) 1: Pump on every n days, 2: Pump on when Soilmoisture <= SoilmoistureLow, 3: Both
*
*
*/
/* /*
@ -294,12 +282,7 @@ void loop() {
* - * -
*/ */
void controlPUMP() {
// UsePump true?
if (UsePump == true) {
}
}

View file

@ -213,8 +213,8 @@ const char HTMLhelp[] PROGMEM = R"EOF(
<h2>&#x2753; Help</h2> <h2>&#x2753; Help</h2>
Here you will get some helpful help. Here you will get some helpful help.
<h3>API</h3> <h3>API</h3>
Sensor data: <code>GET /api/sensors</code> <a href='/api/sensors' target='_blank'>Sensor data</a>: <code>GET /api/sensors</code><br>
Debug all data: <code>GET /api/debug</code> <a href='/api/debug' target='_blank'>Debug all data:</a> <code>GET /api/debug</code>
)EOF"; )EOF";

View file

@ -19,7 +19,7 @@ const bool APhidden = false;
*/ */
// valSoilmoisture - contains the value of getSoilmoisture() // valSoilmoisture - contains the value of getSoilmoisture()
byte valSoilmoisture; unsigned short valSoilmoisture;
// valTemperature - contains the value of getTemperature() // valTemperature - contains the value of getTemperature()
float valTemperature; float valTemperature;
// valTemperature - contains the value of getHumidity() // valTemperature - contains the value of getHumidity()
@ -38,6 +38,13 @@ byte ScreenIterationPassed = 0;
bool MaintenanceMode = false; bool MaintenanceMode = false;
unsigned long MaintenanceStarted = 0; unsigned long MaintenanceStarted = 0;
// helper variable to remember how many seconds the pump was
// already on within the actual watering cycle
byte PumpOnTimePassed = 0;
bool PumpOnManual = false;
//unsigned short
/* /*
* millis timer * millis timer
* *
@ -81,6 +88,7 @@ byte MoistureSensor_Type;
// SoilmoistureLow - contains the value , when soil moisture is assumed to be low, // SoilmoistureLow - contains the value , when soil moisture is assumed to be low,
byte SoilmoistureLow = 80; byte SoilmoistureLow = 80;
// UsePump - is the pump used? bool // UsePump - is the pump used? bool
// PumpMode (short) 1: Pump on every n days, 2: Pump on when Soilmoisture <= SoilmoistureLow, 3: Both
byte UsePump; byte UsePump;
// UseFan - is the fan used? bool // UseFan - is the fan used? bool
// PumpOnTime in seconds // PumpOnTime in seconds
@ -96,8 +104,8 @@ unsigned short MaintenanceDuration = 300;
char Esp32CamIP[16]; char Esp32CamIP[16];
// PumpLastOn (long) timestamp // PumpLastOn (long) timestamp
unsigned long PumpLastOn; unsigned long PumpLastOn;
// PumpMode (short) 1: Pump on every n days, 2: Pump on when Soilmoisture <= SoilmoistureLow, 3: Both
byte PumpMode = 1;
// //
// Grow Stuff // Grow Stuff

View file

@ -187,7 +187,13 @@ int getSoilmoisture(byte moistureSensor, bool returnRAW = false) {
if(returnRAW == true) { if(returnRAW == true) {
return soilmoisture; return soilmoisture;
} else { } else {
return map(soilmoisture, wet, dry, 100, 0); short soilmoistureP = map(soilmoisture, wet, dry, 100, 0);
// dont return negative percentage values
if(soilmoistureP < 0) {
return 0;
} else {
return soilmoistureP;
}
} }
} }

View file

@ -106,7 +106,7 @@ bool loadEEPROM() {
* 221 Esp32CamIP (16 byte) * 221 Esp32CamIP (16 byte)
* 237 PumpLastOn (4 byte) * 237 PumpLastOn (4 byte)
* 241 PumpIntervalVeg (1 byte) * 241 PumpIntervalVeg (1 byte)
* 242 PumpIntervalVeg (1 byte) * 242 PumpIntervalBloom (1 byte)
* 243 ... * 243 ...
* *
*/ */
@ -173,8 +173,7 @@ bool loadEEPROM() {
EEPROM.get(221, Esp32CamIP); EEPROM.get(221, Esp32CamIP);
// size is 4 byte // size is 4 byte
EEPROM.get(237, PumpLastOn); EEPROM.get(237, PumpLastOn);
// size is 1 byte
EEPROM.get(242, PumpMode);
} }
// TODO auth does not work atm // TODO auth does not work atm
// EEPROM.get(160, WebUiUsername); // EEPROM.get(160, WebUiUsername);
@ -210,7 +209,9 @@ bool loadEEPROM() {
EEPROM.get(217, SunFade); EEPROM.get(217, SunFade);
EEPROM.get(218, SunFadeDuration); EEPROM.get(218, SunFadeDuration);
// size is 1 byte // size is 1 byte
EEPROM.get(237, PumpIntervalVeg); EEPROM.get(241, PumpIntervalVeg);
// size is 1 byte
EEPROM.get(242, PumpIntervalBloom);
} }
@ -369,6 +370,30 @@ void wifiAp() {
//Serial.println("The login credentials for the WebUI are 'cangrow' for username and password"); //Serial.println("The login credentials for the WebUI are 'cangrow' for username and password");
} }
unsigned short growState() {
/*
* growState()
*
* returns growState as short
*
* 1 - vegetation
* 2 - bloom
* 3 - harvest
*
*/
unsigned short state;
if(DayOfGrow > (DaysVeg + DaysBloom ) ) {
state = 3;
} else if(DayOfGrow > DaysVeg ) {
state = 2;
} else {
state = 1;
}
return state;
}
void setOutput(byte Output, byte OutputState) { void setOutput(byte Output, byte OutputState) {
/* /*
* Pin assignments * Pin assignments
@ -427,15 +452,22 @@ void controlLED() {
unsigned int secondsSunrise = (SunriseHour * 60 * 60) + (SunriseMinute * 60); unsigned int secondsSunrise = (SunriseHour * 60 * 60) + (SunriseMinute * 60);
unsigned int secondsToday = (timeClient.getHours() * 60 * 60) + (timeClient.getMinutes() * 60) + timeClient.getSeconds(); unsigned int secondsToday = (timeClient.getHours() * 60 * 60) + (timeClient.getMinutes() * 60) + timeClient.getSeconds();
if(DayOfGrow > DaysVeg ) { switch(growState()) {
lightHours = LighthoursBloom; case 1:
} else { lightHours = LighthoursVeg;
lightHours = LighthoursVeg; break;
case 2:
lightHours = LighthoursBloom;
break;
default:
lightHours = 0;
break;
} }
// check if secondsToday is larger then secondsSunrise time AND if // check if secondsToday is larger then secondsSunrise time AND if
// secondsToday is smaller then the sum of secondsSunrise + seconds of lightHours // secondsToday is smaller then the sum of secondsSunrise + seconds of lightHours
if( ((secondsToday >= secondsSunrise) && (secondsToday <= ( secondsSunrise + (lightHours * 60 * 60))) ) ){ if( ((secondsToday >= secondsSunrise) && (secondsToday <= ( secondsSunrise + (lightHours * 60 * 60))) ) && (growState() < 3) ){
//Serial.println("light on time"); //Serial.println("light on time");
// when SunFade is true, fade LED light. Otherwise just turn on or off // when SunFade is true, fade LED light. Otherwise just turn on or off
@ -508,7 +540,8 @@ void displayScreens() {
display.println("s"); display.println("s");
} else { } else {
// in this switch case the single screens gets defined // in this switch case the single screens gets defined
switch(ScreenToDisplay) { //switch(ScreenToDisplay) {
switch(0) {
case 0: case 0:
display.print("Humidity: "); display.print("Humidity: ");
display.print(valHumidity); display.print(valHumidity);
@ -576,26 +609,110 @@ void displayScreens() {
} }
unsigned short growState() {
/* /*
* growState() * Pump control
* *
* returns growState as short * Vars:
* - UsePump (byte)
* - PumpOnTime (byte) in sec
* - SoilmoistureLow (byte) in %
* - PumpLastOn (long) timestamp
* - PumpMode (byte) 1: Pump on every n days, 2: Pump on when Soilmoisture <= SoilmoistureLow, 3: Both
* *
* 1 - vegetation * - PumpIntervalVeg (byte) in days
* 2 - bloom * - PumpIntervalBloom (byte) in days
* 3 - harvest
* *
*/ */
unsigned short state;
if(DayOfGrow > (DaysVeg + DaysBloom ) ) { void controlPUMP() {
state = 3; byte PumpInterval;
} else if(DayOfGrow > DaysVeg ) {
state = 2; // UsePump true and not in harvest state?
} else { if ( (UsePump > 0) && (growState() < 3) ) {
state = 1; switch(growState()) {
case 1:
PumpInterval = PumpIntervalVeg;
break;
case 2:
PumpInterval = PumpIntervalBloom;
break;
default:
PumpInterval = 0;
break;
}
// when PumpOnManuel is true, turn pump on for PumpOnTime seconds
if(PumpOnManual == true) {
if(PumpOnTimePassed < PumpOnTime) {
digitalWrite(PinPUMP, HIGH);
PumpOnTimePassed++;
} else {
PumpOnManual = false;
digitalWrite(PinPUMP, LOW);
PumpOnTimePassed = 0;
}
// otherwise check which PumpMode to use
} else {
switch(UsePump) {
case 1:
// when diff of time now and time pumpLastOn is greater then PumpInterval, do some watering (Or manual watering)
if( (timeClient.getEpochTime() - PumpLastOn) >= (PumpInterval) ) { // TODO: * 24 * 60 * 60 PumpInterval
// only water as long PumpOnTime
if(PumpOnTimePassed < PumpOnTime) {
digitalWrite(PinPUMP, HIGH);
PumpOnTimePassed++;
} else {
digitalWrite(PinPUMP, LOW);
PumpLastOn = timeClient.getEpochTime();
// write the value to EEPROM for the case ESP gets restarted
EEPROM.put(237, PumpLastOn);
PumpOnTimePassed = 0;
}
} else {
digitalWrite(PinPUMP, LOW);
}
break;
case 2:
// when valSoilmoisture is lower then SoilMoisture low do some watering
if( (valSoilmoisture < SoilmoistureLow) || ( (valSoilmoisture >= SoilmoistureLow) && ( (PumpOnTimePassed > 0) && (PumpOnTimePassed <= PumpOnTime) ) ) ) {
// check if we alerady exceeded max PumpOnTime
if(PumpOnTimePassed < PumpOnTime) {
digitalWrite(PinPUMP, HIGH);
PumpOnTimePassed++;
} else {
digitalWrite(PinPUMP, LOW);
PumpLastOn = timeClient.getEpochTime();
PumpOnTimePassed = 0;
}
// when valSoilmoisture is greater then the Low value,
} else {
digitalWrite(PinPUMP, LOW);
}
break;
//
case 3:
if( ( (timeClient.getEpochTime() - PumpLastOn) >= (PumpInterval) ) && //TODO calculate PumpInterval into days as well here
( (valSoilmoisture < SoilmoistureLow) ||
( (valSoilmoisture >= SoilmoistureLow) && ( (PumpOnTimePassed > 0) && (PumpOnTimePassed <= PumpOnTime) ) )
) ) {
// check if we alerady exceeded max PumpOnTime
if(PumpOnTimePassed < PumpOnTime) {
digitalWrite(PinPUMP, HIGH);
PumpOnTimePassed++;
} else {
digitalWrite(PinPUMP, LOW);
PumpLastOn = timeClient.getEpochTime();
PumpOnTimePassed = 0;
}
// when valSoilmoisture is greater then the Low value,
} else {
digitalWrite(PinPUMP, LOW);
}
break;
}
}
} }
return state;
} }

View file

@ -1,5 +1,5 @@
/* CanGrow_Version.h gets generated from cangrow.sh */ /* CanGrow_Version.h gets generated from cangrow.sh */
const char* CanGrowVer = "0.1-dev"; const char* CanGrowVer = "0.1-dev";
const char* CanGrowBuild = "d6bd0f7"; const char* CanGrowBuild = "e1fcb09-20240616201039";

View file

@ -363,17 +363,17 @@ void WEBroot() {
switch(growState()) { switch(growState()) {
case 1: case 1:
body += "&#x1F331; vegetation<br>\n"; body += "&#x1F331; vegetation<br>\n";
break; break;
case 2: case 2:
body += "&#x1F33C; bloom<br>\n"; body += "&#x1F33C; bloom<br>\n";
break; break;
case 3: case 3:
body += "&#x1F342; harvest<br>\n"; body += "&#x1F342; harvest\n";
break; break;
} }
if(UsePump == true) { if(UsePump > 0) {
body += "<b>Pump water level:</b> "; body += "<br><b>Pump water level:</b> ";
switch(getWaterlevel()) { switch(getWaterlevel()) {
case 0: case 0:
body += "<span style='color: green;'>OK</span>"; body += "<span style='color: green;'>OK</span>";
@ -472,19 +472,19 @@ void WEBgrowSettings() {
body += "Vegetation duration: <input class='inputShort' type='number' name='DaysVeg' min='0' max='255' value='"; body += "Vegetation duration: <input class='inputShort' type='number' name='DaysVeg' min='0' max='255' value='";
body += DaysVeg; body += DaysVeg;
body+= "' required>Days<br>\n"; body+= "' required>days<br>\n";
body += "Bloom duration: <input class='inputShort' type='number' name='DaysBloom' min='0' max='255' value='"; body += "Bloom duration: <input class='inputShort' type='number' name='DaysBloom' min='0' max='255' value='";
body += DaysBloom; body += DaysBloom;
body+= "' required> Days<br>\n"; body+= "' required> days<br>\n";
body += "Time LED ON vegetation: <input class='inputShort' type='number' name='LighthoursVeg' min='0' max='255' value='"; body += "Time LED ON vegetation: <input class='inputShort' type='number' name='LighthoursVeg' min='0' max='255' value='";
body += LighthoursVeg; body += LighthoursVeg;
body+= "' required> Hours<br>\n"; body+= "' required> hours<br>\n";
body += "Time LED ON bloom: <input class='inputShort' type='number' name='LighthoursBloom' min='0' max='255' value='"; body += "Time LED ON bloom: <input class='inputShort' type='number' name='LighthoursBloom' min='0' max='255' value='";
body += LighthoursBloom; body += LighthoursBloom;
body+= "' required> Hours<br>\n"; body+= "' required> hours<br>\n";
body += "Sunrise: <input class='inputShort' type='number' name='SunriseHour' min='0' max='23' value='"; body += "Sunrise: <input class='inputShort' type='number' name='SunriseHour' min='0' max='23' value='";
body += SunriseHour; body += SunriseHour;
@ -515,6 +515,16 @@ void WEBgrowSettings() {
body += "'/> %<br>\n"; body += "'/> %<br>\n";
} }
if(UsePump > 0) {
body += "Pump interval vegetation: <input class='inputShort' type='number' name='PumpIntervalVeg' min='0' max='255' value='";
body += PumpIntervalVeg;
body+= "' required>days<br>\n";
body += "Pump interval bloom: <input class='inputShort' type='number' name='PumpIntervalBloom' min='0' max='255' value='";
body += PumpIntervalBloom;
body+= "' required>days<br>\n";
}
body += "<input type='submit' value='&#x1F4BE; Save settings'>\n"; body += "<input type='submit' value='&#x1F4BE; Save settings'>\n";
body += "</form>\n"; body += "</form>\n";
body += FPSTR(JSconvertDateToEpoch); body += FPSTR(JSconvertDateToEpoch);
@ -569,10 +579,10 @@ void WEBsystemSettings() {
// PumpMode byte // PumpMode byte
body += "Pump mode: <select id='UsePump' name='UsePump' required>\n"; body += "Pump mode: <select id='UsePump' name='UsePump' required>\n";
if(configured == false){body += "<option disabled value='' selected hidden>---</option>\n";} if(configured == false){body += "<option disabled value='' selected hidden>---</option>\n";}
body += "<option value='1'" + returnStrSelected(UsePump, 0) + ">Off</option>\n"; body += "<option value='0'" + returnStrSelected(UsePump, 0) + ">Off</option>\n";
body += "<option value='1'" + returnStrSelected(UsePump, 1) + ">1</option>\n"; body += "<option value='1'" + returnStrSelected(UsePump, 1) + ">1</option>\n";
body += "<option value='1'" + returnStrSelected(UsePump, 2) + ">2</option>\n"; body += "<option value='2'" + returnStrSelected(UsePump, 2) + ">2</option>\n";
body += "<option value='0'" + returnStrSelected(UsePump, 3) + ">3</option>\n"; body += "<option value='3'" + returnStrSelected(UsePump, 3) + ">3</option>\n";
body += "</select><br><p class='helpbox'><b>1:</b> Water every few days.<br> \ body += "</select><br><p class='helpbox'><b>1:</b> Water every few days.<br> \
<b>2:</b> Water if the soil moisture falls below <i>Soilmoisture low</i> value<br> \ <b>2:</b> Water if the soil moisture falls below <i>Soilmoisture low</i> value<br> \
<b>3:</b> Water every few days if the soil moisture falls below <i>Soilmoisture low</i> value.</p>\n"; <b>3:</b> Water every few days if the soil moisture falls below <i>Soilmoisture low</i> value.</p>\n";
@ -593,7 +603,7 @@ void WEBsystemSettings() {
// TODO ugly. can this done be better? // TODO ugly. can this done be better?
// PumpOnTime int // PumpOnTime int
body += "PUMP ON time: <input class='inputShort' type='number' name='PumpOnTime' min='0' max='255' value='"; body += "Pump ON time: <input class='inputShort' type='number' name='PumpOnTime' min='0' max='255' value='";
body += PumpOnTime; body += PumpOnTime;
body += "' required> Seconds<br>\n"; body += "' required> Seconds<br>\n";
@ -755,6 +765,8 @@ void POSTgrowSettings() {
SunriseMinute = webserver.arg("SunriseMinute").toInt(); SunriseMinute = webserver.arg("SunriseMinute").toInt();
SunFade = webserver.arg("SunFade").toInt(); SunFade = webserver.arg("SunFade").toInt();
SunFadeDuration = webserver.arg("SunFadeDuration").toInt(); SunFadeDuration = webserver.arg("SunFadeDuration").toInt();
PumpIntervalVeg = webserver.arg("PumpIntervalVeg").toInt();
PumpIntervalBloom = webserver.arg("PumpIntervalBloom").toInt();
// size is 32 byte // size is 32 byte
EEPROM.put(170, GrowName); EEPROM.put(170, GrowName);
@ -778,6 +790,10 @@ void POSTgrowSettings() {
EEPROM.put(216, PinFANPWM); EEPROM.put(216, PinFANPWM);
EEPROM.put(217, SunFade); EEPROM.put(217, SunFade);
EEPROM.put(218, SunFadeDuration); EEPROM.put(218, SunFadeDuration);
// size is 1 byte
EEPROM.put(241, PumpIntervalVeg);
// size is 1 byte
EEPROM.put(242, PumpIntervalBloom);
EEPROM.commit(); EEPROM.commit();
@ -1054,6 +1070,10 @@ void APIgetDebug() {
objSystem["TemperatureSensor_Type"] = TemperatureSensor_Type; objSystem["TemperatureSensor_Type"] = TemperatureSensor_Type;
objSystem["NtpOffset"] = NtpOffset; objSystem["NtpOffset"] = NtpOffset;
objSystem["MaintenanceDuration"] = MaintenanceDuration; objSystem["MaintenanceDuration"] = MaintenanceDuration;
objSystem["PumpOnTime"] = PumpOnTime;
objSystem["PumpOnTimePassed"] = PumpOnTimePassed;
objSystem["PumpOnManual"] = PumpOnManual;
objSystem["PumpLastOn"] = PumpLastOn;
// Grow // Grow
JsonObject objGrow = jsonDebug["grow"].add<JsonObject>(); JsonObject objGrow = jsonDebug["grow"].add<JsonObject>();
@ -1071,7 +1091,8 @@ void APIgetDebug() {
objGrow["PinLEDPWM"] = PinLEDPWM; objGrow["PinLEDPWM"] = PinLEDPWM;
objGrow["PinFANPWM"] = PinFANPWM; objGrow["PinFANPWM"] = PinFANPWM;
objGrow["DayOfGrow"] = DayOfGrow; objGrow["DayOfGrow"] = DayOfGrow;
objGrow["PumpIntervalVeg"] = PumpIntervalVeg;
objGrow["PumpIntervalBloom"] = PumpIntervalBloom;
// Sensors // Sensors