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();
}
controlPUMP();
displayScreens();
@ -265,24 +265,12 @@ void loop() {
* - re-organize EEPROM saved values.
* - prevent GrowStart to be in the future
* - 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
*
*
*/
/*
@ -293,13 +281,8 @@ void loop() {
* - FanExhaust (byte) Fan1 or Fan2
* -
*/
void controlPUMP() {
// UsePump true?
if (UsePump == true) {
}
}

View file

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

View file

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

View file

@ -187,7 +187,13 @@ int getSoilmoisture(byte moistureSensor, bool returnRAW = false) {
if(returnRAW == true) {
return soilmoisture;
} 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)
* 237 PumpLastOn (4 byte)
* 241 PumpIntervalVeg (1 byte)
* 242 PumpIntervalVeg (1 byte)
* 242 PumpIntervalBloom (1 byte)
* 243 ...
*
*/
@ -173,8 +173,7 @@ bool loadEEPROM() {
EEPROM.get(221, Esp32CamIP);
// size is 4 byte
EEPROM.get(237, PumpLastOn);
// size is 1 byte
EEPROM.get(242, PumpMode);
}
// TODO auth does not work atm
// EEPROM.get(160, WebUiUsername);
@ -210,7 +209,9 @@ bool loadEEPROM() {
EEPROM.get(217, SunFade);
EEPROM.get(218, SunFadeDuration);
// 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");
}
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) {
/*
* Pin assignments
@ -427,15 +452,22 @@ void controlLED() {
unsigned int secondsSunrise = (SunriseHour * 60 * 60) + (SunriseMinute * 60);
unsigned int secondsToday = (timeClient.getHours() * 60 * 60) + (timeClient.getMinutes() * 60) + timeClient.getSeconds();
if(DayOfGrow > DaysVeg ) {
lightHours = LighthoursBloom;
} else {
lightHours = LighthoursVeg;
switch(growState()) {
case 1:
lightHours = LighthoursVeg;
break;
case 2:
lightHours = LighthoursBloom;
break;
default:
lightHours = 0;
break;
}
// check if secondsToday is larger then secondsSunrise time AND if
// 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");
// when SunFade is true, fade LED light. Otherwise just turn on or off
@ -508,7 +540,8 @@ void displayScreens() {
display.println("s");
} else {
// in this switch case the single screens gets defined
switch(ScreenToDisplay) {
//switch(ScreenToDisplay) {
switch(0) {
case 0:
display.print("Humidity: ");
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
* 2 - bloom
* 3 - harvest
* - PumpIntervalVeg (byte) in days
* - PumpIntervalBloom (byte) in days
*
*/
unsigned short state;
*/
void controlPUMP() {
byte PumpInterval;
if(DayOfGrow > (DaysVeg + DaysBloom ) ) {
state = 3;
} else if(DayOfGrow > DaysVeg ) {
state = 2;
} else {
state = 1;
// UsePump true and not in harvest state?
if ( (UsePump > 0) && (growState() < 3) ) {
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 */
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()) {
case 1:
body += "&#x1F331; vegetation<br>\n";
break;
break;
case 2:
body += "&#x1F33C; bloom<br>\n";
break;
break;
case 3:
body += "&#x1F342; harvest<br>\n";
break;
body += "&#x1F342; harvest\n";
break;
}
if(UsePump == true) {
body += "<b>Pump water level:</b> ";
if(UsePump > 0) {
body += "<br><b>Pump water level:</b> ";
switch(getWaterlevel()) {
case 0:
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 += 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 += 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 += 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 += 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 += SunriseHour;
@ -515,6 +515,16 @@ void WEBgrowSettings() {
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 += "</form>\n";
body += FPSTR(JSconvertDateToEpoch);
@ -569,10 +579,10 @@ void WEBsystemSettings() {
// PumpMode byte
body += "Pump mode: <select id='UsePump' name='UsePump' required>\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, 2) + ">2</option>\n";
body += "<option value='0'" + returnStrSelected(UsePump, 3) + ">3</option>\n";
body += "<option value='2'" + returnStrSelected(UsePump, 2) + ">2</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> \
<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";
@ -593,7 +603,7 @@ void WEBsystemSettings() {
// TODO ugly. can this done be better?
// 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 += "' required> Seconds<br>\n";
@ -755,6 +765,8 @@ void POSTgrowSettings() {
SunriseMinute = webserver.arg("SunriseMinute").toInt();
SunFade = webserver.arg("SunFade").toInt();
SunFadeDuration = webserver.arg("SunFadeDuration").toInt();
PumpIntervalVeg = webserver.arg("PumpIntervalVeg").toInt();
PumpIntervalBloom = webserver.arg("PumpIntervalBloom").toInt();
// size is 32 byte
EEPROM.put(170, GrowName);
@ -778,6 +790,10 @@ void POSTgrowSettings() {
EEPROM.put(216, PinFANPWM);
EEPROM.put(217, SunFade);
EEPROM.put(218, SunFadeDuration);
// size is 1 byte
EEPROM.put(241, PumpIntervalVeg);
// size is 1 byte
EEPROM.put(242, PumpIntervalBloom);
EEPROM.commit();
@ -1054,6 +1070,10 @@ void APIgetDebug() {
objSystem["TemperatureSensor_Type"] = TemperatureSensor_Type;
objSystem["NtpOffset"] = NtpOffset;
objSystem["MaintenanceDuration"] = MaintenanceDuration;
objSystem["PumpOnTime"] = PumpOnTime;
objSystem["PumpOnTimePassed"] = PumpOnTimePassed;
objSystem["PumpOnManual"] = PumpOnManual;
objSystem["PumpLastOn"] = PumpLastOn;
// Grow
JsonObject objGrow = jsonDebug["grow"].add<JsonObject>();
@ -1071,7 +1091,8 @@ void APIgetDebug() {
objGrow["PinLEDPWM"] = PinLEDPWM;
objGrow["PinFANPWM"] = PinFANPWM;
objGrow["DayOfGrow"] = DayOfGrow;
objGrow["PumpIntervalVeg"] = PumpIntervalVeg;
objGrow["PumpIntervalBloom"] = PumpIntervalBloom;
// Sensors