CanGrow/Arduino/CanGrow/CanGrow_WebFunctions.h

1296 lines
42 KiB
C

/*
*
*
* Web related stuff
*
*
*/
/*
*
* return functions, they return things for the web stuff
*/
String returnHTMLheader(String MenuEntry = "") {
String header;
String activeMenu = "class='activeMenu'";
// add first part of the header
header += FPSTR(HTMLheaderP1);
// add title tag
header += "<title>";
// check if GrowName was set. if yes, its part of the page title.
if(strlen(GrowName) > 0) {
header += "CanGrow - ";
header += GrowName;
} else {
header += "CanGrow";
}
// close title tag
header += "</title>\n";
// add additional header stuff, like loading guage files
if(MenuEntry == "root") {
header += "<link rel='stylesheet' type='text/css' href='gauge.css'>";
}
header += FPSTR(HTMLheaderP2);
// first menu entry
header += "<li><a href='/'>&#x1F331; ";
if(strlen(GrowName) > 0) {
header += GrowName;
} else {
header += "CanGrow";
}
header += "</a></li>\n";
// second menu entry
header += "<li><a href='/growSettings' ";
if(MenuEntry == "growSettings") {
header += activeMenu;
}
header += ">&#128262; Grow settings</a></li>\n";
// third menu entry
header += "<li><a href='/systemSettings' ";
if(MenuEntry == "systemSettings") {
header += activeMenu;
}
header += ">&#9881; System settings</a></li>\n";
// fourth menu entry
header += "<li><a href='/wifiSettings' ";
if(MenuEntry == "wifiSettings") {
header += activeMenu;
}
header += ">&#128225; WiFi settings</a></li>\n";
// fifth menu entry
header += "<li><a href='/help' ";
if(MenuEntry == "help") {
header += activeMenu;
}
header += ">&#x2753; Help</a></li>\n";
// sixth menu entry - time and status icons / info
header += "<li><span class='MenuTime'>";
header += timeClient.getFormattedTime();
// status icons and info
if(MaintenanceMode == true) {
// status icons
header += " | &#9208;&#65039; ";
header += MaintenanceDuration - ((millis() - MaintenanceStarted) / 1000);
header += "s";
}
header += "</span></li>\n";
// CanGrow Version
header += "<li><a href='https://git.la10cy.net/DeltaLima/CanGrow' target='_blank'>CanGrow v";
header += CANGROW_VER;
header += "</a></li>\n";
// close <ul> and start <div>
header += "</ul><div class='center'>";
if(NeedRestart == true) {
header += FPSTR(HTMLneedRestart);
}
return header;
}
/*
* returnSelected(bool)
* returns char[] "selected" if bool is true
* useful for html forms, to represet a saved value as selected
*/
String returnStrSelected(byte savedValue, byte selectId) {
String returnStr;
if(configured == true) {
if(savedValue == selectId) {
returnStr = " selected ";
} else {
returnStr = "";
}
}
return returnStr;
}
String returnStrDateFromEpoch(unsigned long epochTime) {
String dateStr;
byte Day = day(epochTime);
byte Month = month(epochTime);
unsigned int Year = year(epochTime);
dateStr = Year;
dateStr += "-";
if(Month < 10) {
dateStr += "0";
dateStr += Month;
} else {
dateStr += Month;
}
dateStr += "-";
if(Day < 10) {
dateStr += "0";
dateStr += Day;
} else {
dateStr += Day;
}
return dateStr;
}
/*
*
* System pages like infos, errors
*
*/
void SysRestart() {
String body = returnHTMLheader();
// TODO only debug and development solution, remove this later
if( (webserver.hasArg("confirmed")) || (NeedRestart == true) ) {
body += "<h1>&#10071; Restarting</h1>";
body += "<div class='infomsg'>After restart CanGrow will be connected to WiFi SSID<br><b>";
body += WIFIssid;
body += "</b><br>You get its IP-Address from the display or serial console.</div>";
body += FPSTR(HTMLfooter);
webserver.send(200, "text/html", body);
Serial.println("Restarting... see you soon space cowboy!");
delay(1000);
ESP.restart();
} else {
body += "<h1>&#10071; Restart CanGrow</h1>";
body += "<div class='infomsg'>Do you want to restart CanGrow?";
body += "<br>Please confirm.";
body += "<form action='/system/restart'><input type='hidden' name='confirmed' value='true' /><input type='submit' value='Confirm restart' /></form>";
body += "</div>";
body += FPSTR(HTMLfooter);
webserver.send(200, "text/html", body);
}
}
void SysWipe() {
String body = returnHTMLheader();
body += "<h1>&#10071;&#10071; Wiping EEPROM</h1>";
body += "<div class='warnmsg'>All settings will be removed!!<br>";
body += "<br>Please confirm wiping the EEPROM";
body += "<form action='/system/wipeConfirm' method='post'><br>Please confirm: <input type='checkbox' id='confirm' name='confirm' required /><br><input type='submit' value='Confirm wiping' /></form>";
body += "</div>";
body += FPSTR(HTMLfooter);
webserver.send(200, "text/html", body);
}
void Sys404() {
String body = returnHTMLheader();
body += "<div class='warnmsg'><h1>&#10071; &#65039; 404 - not found</h1></div>";
body += FPSTR(HTMLfooter);
webserver.send(404, "text/html", body);
}
void Syslogout() {
String body = returnHTMLheader();
body += "<h1>you are logged out.</h1>";
body += FPSTR(HTMLfooter);
// TODO does not work atm
webserver.send(401, "text/html", body);
}
void SysMaintenance() {
String body = returnHTMLheader();
if( (webserver.hasArg("on")) ) {
MaintenanceMode = true;
MaintenanceStarted = millis();
body += "<div class='infomsg'>&#9208;&#65039; On for ";
body += MaintenanceDuration;
body += "s</div>";
} else if (webserver.hasArg("off")){
MaintenanceMode = false;
body += "<div class='infomsg'>&#9208;&#65039; Off</div>";
}
body += "<h2>&#9208;&#65039; Maintenance Mode</h2>";
body += "<a class='button' href='/system/maintenance?on=1'>On</a>&nbsp;&nbsp;<a class='button' href='/system/maintenance?off=1'>Off</a>";
body += FPSTR(HTMLfooter);
webserver.send(200, "text/html", body);
}
void SysUpdate() {
String body = returnHTMLheader();
body += "<h2>&#x1F504; Firmware update</h2>";
body += "<b>Version:</b> ";
body += CANGROW_VER;
body += "<br><b>Build:</b> ";
body += CANGROW_BUILD;
body += FPSTR(HTMLupdate);
body += FPSTR(HTMLfooter);
webserver.send(200, "text/html", body);
}
/*
* TODO
* DOES NOT WORK WHEN CONNECTED TO EXISTING WIFI
* IDK WHY
*
* void WebAuth() {
*
* char webAuthRealm[] = "CanGrowRealm";
* if(!webserver.authenticate(WebUiUsername, WebUiPassword)) {
* String body = FPSTR(HTMLheader);
* body += "<h1>Login failed.</h1>";
* body += FPSTR(HTMLfooter);
* webserver.requestAuthentication(DIGEST_AUTH, webAuthRealm, body);
* }
* }
*
* void WebAuthApi() {
*
* TODO
* DOES NOT WORK WHEN CONNECTED TO EXISTING WIFI
* IDK WHY
*
*
* char webAuthRealm[] = "CanGrowRealm";
* if(!webserver.authenticate(WebUiUsername, WebUiPassword)) {
* webserver.requestAuthentication(DIGEST_AUTH, webAuthRealm);
* }
* }
*/
/*
*
* Main UI pages
*
*/
/*
* Gauge meter files
*/
void WEBgaugeCss() {
//String css = CSSgauge;
webserver.send(200, "text/css", FPSTR(CSSgauge));
}
void WEBgaugeJs() {
//String javascript = JSgauge;
webserver.send(200, "text/javascript", FPSTR(JSgauge));
}
/*
* Root page
*/
void WEBroot() {
if(FirstRun == true) {
webserver.sendHeader("Location", String("/wifiSettings"), true);
webserver.send(302, "text/plain", "please configure wifiSettings first");
} else if(configured == false){
webserver.sendHeader("Location", String("/systemSettings"), true);
webserver.send(302, "text/plain", "please configure systemSettings first");
} else if(strlen(GrowName) < 1){
webserver.sendHeader("Location", String("/growSettings"), true);
webserver.send(302, "text/plain", "please configure growSettings first");
} else {
String body = returnHTMLheader("root");
body += "<h2>&#x1F331; ";
body += GrowName;
body += "</h2>";
// add gauge meter
body += FPSTR(HTMLgauge);
// and give javascript the values
// todo: auto refresh by api call
body += "<script>";
body += "gaugeTemperature.value('";
body += valTemperature;
body += "', 42, ' °C'); ";
body += "gaugeHumidity.value('";
body += valHumidity;
body += "'); ";
body += "gaugeSoilmoisture.value('";
body += valSoilmoisture;
body += "'); ";
body += "</script><br>\n";
// when an ESP32-Cam IP is given, display picture from it
if(strlen(Esp32CamIP) > 0) {
body += "<a href='http://";
body += Esp32CamIP;
body += "' target='_blank'><img class='centered' src='http://";
body += Esp32CamIP;
body += "/capture' alt='Image capture from ESP32CAM at ";
body += Esp32CamIP;
body += "'></a>\n<br>\n";
}
body += "<b>Grow started:</b> ";
body += returnStrDateFromEpoch(GrowStart);
body += "<br>\n";
body += "<b>Day of Grow:</b> ";
body += DayOfGrow;
body += "<br>\n";
body += "<b>Grow status:</b> ";
switch(growState()) {
case 1:
body += "&#x1F331; vegetation<br>\n";
break;
case 2:
body += "&#x1F33C; bloom<br>\n";
break;
case 3:
body += "&#x1F342; harvest\n";
break;
}
if(UsePump > 0) {
body += "<b>Pump water level:</b> ";
switch(getWaterlevel()) {
case 0:
body += "<span style='color: green;'>OK</span>";
break;
case 1:
body += "<span style='color: yellow;'>Warning</span>";
break;
case 2:
body += "<span style='color: red;'>Critical</span>";
break;
}
}
body += "<br>\n";
body += "<b>Growlight brightness:</b> ";
body += ((PinLEDPWM * 100) / 255);
body += " %<br>\n";
//~ body += "<form method='post' action='/switch'>\n";
//~ body += "<b>MOSFET:</b> <select id='output' name='output' >\n";
//~ body += "<option disabled value='' selected hidden>---</option>\n";
//~ body += "<option value='1'>LED</option>\n";
//~ body += "<option value='2'>FAN</option>\n";
//~ body += "<option value='3'>PUMP</option>\n";
//~ body += "</select><br>";
//~ body += "<b>On/Off:</b> <select id='state' name='state' >\n";
//~ body += "<option disabled value='' selected hidden>---</option>\n";
//~ body += "<option value='1'>On</option>\n";
//~ body += "<option value='0'>Off</option>\n";
//~ body += "</select><br>\n";
//~ body += "<b>Intensity:</b> <input type='range' id='OutputPWM' name='OutputPWM' min='1' max='255' value='255'/><br>\n";
//~ body += "<input type='submit' value='Save'>\n";
//~ body += "</form><br>\n";
body += "<a class='button' href='/system/maintenance'>Maintenance Mode</a>";
body += FPSTR(HTMLfooter);
webserver.send(200, "text/html", body);
}
}
/*
*
* settings pages
*
*
*/
/*
* Grow page
*/
void WEBgrowSettings() {
// if system settings are unconfigured, we cannot proceed with growSettings
if(configured == false) {
webserver.sendHeader("Location", String("/systemSettings"), true);
webserver.send(302, "text/plain", "please configure systemSettings first");
} else {
String body = returnHTMLheader("growSettings");
if(strlen(GrowName) < 1) {
body += "<h1>Final step: Grow settings</h1>";
body += "<p>Please configure all settings<br>";
body += "</p>";
GrowStart = timeClient.getEpochTime();
}
body += "<h2>&#128262; Grow settings</h2>";
if(webserver.hasArg("success")) {
body += FPSTR(HTMLsuccess);
}
body += "<p>Here you can set everything grow related, like light hours, how much water, LED brightness<br>";
body += "</p>";
body += "<form method='post' action='/growSettings/save'>\n";
body += "Grow name: <input type='text' name='GrowName' maxlength='31' value='";
body += GrowName;
body+= "' required><br>\n";
// the input field, which calls javascript convertDateToEpoch() to write data to transmit to id GrowStart
body += "Grow start date: <input type='date' id='GrowStart_sel' onChange='convertDateToEpoch(\"GrowStart_sel\", \"GrowStart\");' value='";
body += returnStrDateFromEpoch(GrowStart);
body += "' required><br>\n";
body += "<input type='hidden' id='GrowStart' name='GrowStart' value='";
body += GrowStart;
body+= "' required>\n";
body += "Vegetation duration: <input class='inputShort' type='number' name='DaysVeg' min='0' max='255' value='";
body += DaysVeg;
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 += "Time LED ON vegetation: <input class='inputShort' type='number' name='LighthoursVeg' min='0' max='255' value='";
body += LighthoursVeg;
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 += "Sunrise: <input class='inputShort' type='number' name='SunriseHour' min='0' max='23' value='";
body += SunriseHour;
body+= "' required>\n";
body += " <b>:</b> <input class='inputShort' type='number' name='SunriseMinute' min='0' max='59' value='";
body += SunriseMinute;
body+= "' required><br>\n";
// SunFade bool
body += "Fade in/out sunrise/sunset?: <select id='SunFade' name='SunFade' required>\n";
body += "<option value='1'" + returnStrSelected(SunFade, 1) + ">Yes</option>\n";
body += "<option value='0'" + returnStrSelected(SunFade, 0) + ">No</option>\n";
body += "</select><br>\n";
body += "Fade duration: <input class='inputShort' type='number' name='SunFadeDuration' min='1' max='255' value='";
body += SunFadeDuration;
body+= "' required> Minutes<br>\n";
if(UseLEDrelais == false) {
body += "LED brightness: <input type='range' id='PinLEDPWM' name='PinLEDPWM' min='0' max='255' value='";
body += PinLEDPWM;
body += "'/> %<br>\n";
} else {
body += "LED on/off: <select id='PinLEDPWM' name='PinLEDPWM' required>\n";
body += "<option value='1'" + returnStrSelected(PinLEDPWM, 1) + ">On</option>\n";
body += "<option value='0'" + returnStrSelected(PinLEDPWM, 0) + ">Off</option>\n";
body += "</select><br>\n";
}
if(UseFANrelais == false) {
body += "FAN1 speed: <input type='range' id='PinFANPWM' name='PinFANPWM' min='0' max='255' value='";
body += PinFANPWM;
body += "'/> %<br>\n";
} else {
body += "FAN1 on/off: <select id='PinFANPWM' name='PinFANPWM' required>\n";
body += "<option value='1'" + returnStrSelected(PinFANPWM, 1) + ">On</option>\n";
body += "<option value='0'" + returnStrSelected(PinFANPWM, 0) + ">Off</option>\n";
body += "</select><br>\n";
}
body += "FAN2 speed: <input type='range' id='PinFAN2PWM' name='PinFAN2PWM' min='0' max='255' value='";
body += PinFAN2PWM;
body += "'/> %<br>\n";
body += "Pump interval vegetation: every <input class='inputShort' type='number' name='PumpIntervalVeg' min='0' max='255' value='";
body += PumpIntervalVeg;
body += "' required ";
if( (UsePump != 1) && UsePump != 3) { body += "readonly"; }
body += "> days<br>\n";
body += "Pump interval bloom: every <input class='inputShort' type='number' name='PumpIntervalBloom' min='0' max='255' value='";
body += PumpIntervalBloom;
body += "' required ";
if((UsePump != 1) && (UsePump != 3)) { body += "readonly"; }
body += "> days<br>\n";
body += "<input type='submit' value='&#x1F4BE; Save settings'>\n";
body += "</form>\n";
body += FPSTR(JSconvertDateToEpoch);
body += FPSTR(HTMLfooter);
webserver.send(200, "text/html", body);
}
}
void WEBsystemSettings() {
// if wifi settings are unconfigured, we cannot proceed with systemSettings
if(FirstRun == true) {
webserver.sendHeader("Location", String("/wifiSettings"), true);
webserver.send(302, "text/plain", "please configure wifiSettings first");
} else {
String body = returnHTMLheader("systemSettings");
if(configured == false) {
body += "<h1>Step 2: System settings</h1>";
body += "<p>Please configure all settings<br>";
body += "</p>";
}
body += "<h2>&#9881; System settings</h2>";
body += FPSTR(HTMLsystemSubNav);
if(webserver.hasArg("success")) {
body += FPSTR(HTMLsuccess);
}
body += "<p>here you can set which features and sensors you use<br>";
body += "</p>";
// form starts
body += "<form method='post' action='/systemSettings/save'>\n";
// UseFan bool
//~ body += "Fan mode: <select id='UseFan' name='UseFan' required>\n";
//~ if(configured == false){body += "<option disabled value='' selected hidden>---</option>\n";}
//~ body += "<option value='1'" + returnStrSelected(UseFan, 0) + ">Off</option>\n";
//~ body += "<option value='1'" + returnStrSelected(UseFan, 1) + ">Use</option>\n";
//~ body += "<option value='0'" + returnStrSelected(UseFan, 2) + ">Do not use</option>\n";
//~ body += "</select><br>\n";
/*
// UsePump bool
body += "Use PUMP: <select id='UsePump' name='UsePump' required>\n";
if(configured == false){body += "<option disabled value='' selected hidden>---</option>\n";}
body += "<option value='1'" + returnStrSelected(UsePump, 1) + ">Yes</option>\n";
body += "<option value='0'" + returnStrSelected(UsePump, 0) + ">No</option>\n";
body += "</select><br>\n";
*/
// OutputInvert bool
body += "Invert Outputs: <select id='OutputInvert' name='OutputInvert' required>\n";
if(configured == false){body += "<option disabled value='' selected hidden>---</option>\n";}
body += "<option value='1'" + returnStrSelected(OutputInvert, 1) + ">Yes</option>\n";
body += "<option value='0'" + returnStrSelected(OutputInvert, 0) + ">No</option>\n";
body += "</select><br>\n";
// 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='0'" + returnStrSelected(UsePump, 0) + ">Off</option>\n";
body += "<option value='1'" + returnStrSelected(UsePump, 1) + ">1</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";
// UseLEDrelais bool
body += "Use relais for LED (disable PWM): <select id='UseLEDrelais' name='UseLEDrelais' required>\n";
if(configured == false){body += "<option disabled value='' selected hidden>---</option>\n";}
body += "<option value='1'" + returnStrSelected(UseLEDrelais, 1) + ">Yes</option>\n";
body += "<option value='0'" + returnStrSelected(UseLEDrelais, 0) + ">No</option>\n";
body += "</select><br>\n";
// UseFANrelais bool
body += "Use relais for FAN (disable PWM): <select id='UseFANrelais' name='UseFANrelais' required>\n";
if(configured == false){body += "<option disabled value='' selected hidden>---</option>\n";}
body += "<option value='1'" + returnStrSelected(UseFANrelais, 1) + ">Yes</option>\n";
body += "<option value='0'" + returnStrSelected(UseFANrelais, 0) + ">No</option>\n";
body += "</select><br>\n";
// 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 += PumpOnTime;
body += "' required> Seconds<br>\n";
// MoistureSensor_Type byte
body += "Soilmoisture sensor: <select id='SelMoistureSensor_Type' name='MoistureSensor_Type' onchange='MoistureSensorType();' required>\n";
if(configured == false) {
body += "<option disabled value='' selected hidden>---</option>\n";
}
body += "<option value='1'" + returnStrSelected(MoistureSensor_Type, 1) + ">Analog capacitive</option>\n";
body += "<option value='2'" + returnStrSelected(MoistureSensor_Type, 2) + ">I2C Chirp</option>\n";
body += "</select><br>\n";
// SoilmoistureLow byte
body += "Soilmoisture low: <input class='inputShort' type='number' name='SoilmoistureLow' min='0' value='";
body += SoilmoistureLow;
body += "' required> %<br>\n";
// SoilmoistureWet byte
body += "Soilmoisture wet: <input type='number' id='iSoilmoistureWet' name='SoilmoistureWet' min='0' value='";
body += SoilmoistureWet;
body += "' required>\n";
body += "<p class='helpbox'><b>Analog capacitive:</b> <i>160</i><br> \
<b>I2C Chirp:</b> <i>485</i></p>";
// SoilmoistureDry byte
body += "Soilmoisture dry: <input type='number' id='iSoilmoistureDry' name='SoilmoistureDry' min='0' value='";
body += SoilmoistureDry;
body += "' required>\n";
body += "<p class='helpbox'><b>Analog capacitive:</b> <i>360</i><br> \
<b>I2C Chirp:</b> <i>250</i></p>";
// MoistureSensor_Type Javascript
body += FPSTR(JSsoilmoistureTypeSelect);
// TemperatureSensor_Type byte
body += "Temperature sensor: <select id='TemperatureSensor_Type' name='TemperatureSensor_Type' required>\n";
if(configured == false) {
body += "<option disabled value='' selected hidden>---</option>\n";
}
body += "<option value='1'" + returnStrSelected(TemperatureSensor_Type, 1) + ">I2C BME280</option>\n";
body += "<option value='2'" + returnStrSelected(TemperatureSensor_Type, 2) + ">I2C Chirp</option>\n";
body += "<option value='2'" + returnStrSelected(TemperatureSensor_Type, 3) + ">I2C SHT31</option>\n";
body += "</select><br>\n";
// HumiditySensor_Type byte
body += "Humidity sensor: <select id='HumiditySensor_Type' name='HumiditySensor_Type' required>\n";
if(configured == false) {
body += "<option disabled value='' selected hidden>---</option>\n";
}
body += "<option value='1'" + returnStrSelected(HumiditySensor_Type, 1) + ">I2C BME280</option>\n";
body += "<option value='2'" + returnStrSelected(HumiditySensor_Type, 2) + ">I2C SHT31</option>\n";
body += "</select><br>\n";
// NtpOffset int
body += "NTP offset: <input class='inputShort' type='number' name='NtpOffset' min='-12' max='14' value='";
body += NtpOffset;
body+= "' required> Hours<br>\n";
body += "Maintenance Duration: <input class='inputShort' type='number' name='MaintenanceDuration' min='0' max='900' value='";
body += MaintenanceDuration;
body += "' required> Seconds<br>\n";
// PWMFrequency short
body += "PWM Frequency: <input type='number' name='PWMFrequency' min='0' max='20000' value='";
body += PWMFrequency;
body += "' required> Hz<br>\n";
// DisplayScreenDuration byte
body += "Display rotation interval: <input class='inputShort' type='number' name='DisplayScreenDuration' min='0' max='255' value='";
body += DisplayScreenDuration;
body += "' required> s<br>\n";
body += "<p class='helpbox'><b>0</b> will always show sensor value screen</p>";
body += "ESP32-Cam IP (optional): <input type='text' name='Esp32CamIP' maxlength='16' value='";
body += Esp32CamIP;
body += "' ><br>\n";
body += "<input type='submit' value='&#x1F4BE; Save settings'>\n";
body += "</form>\n";
body += FPSTR(HTMLfooter);
webserver.send(200, "text/html", body);
}
}
void WEBwifiSettings() {
byte ssidsAvail = WiFi.scanNetworks();
String body = returnHTMLheader("wifiSettings");
if(FirstRun == true) {
body += "<h1>Welcome!</h1>";
body += "<p>CanGrow is actually unconfigured. You need to Setup your WiFi first down below.<br>";
body += "<br>After you entered your WiFi connection details, you need to restart and are step closer to your grow &#129382;";
body += "<br>";
body += "</p>";
}
body += "<h2>&#128225; WiFi settings</h2>\n";
if(webserver.hasArg("success")) {
body += FPSTR(HTMLsuccess);
}
if(FirstRun == false) {
body += "<u>Current Settings:</u><br>";
body += "WiFi SSID: <b>";
body += WIFIssid;
body += "</b><br>\n";
body += "Use DHCP: <b>";
body += WIFIuseDHCP;
body += "</b><br>\n";
body += "IP address: <b>";
body += WiFi.localIP().toString();
body += "</b><br>\n";
body += "Subnet mask: <b>";
body += WiFi.subnetMask().toString();
body += "</b><br>\n";
body += "Gateway: <b>";
body += WiFi.gatewayIP().toString();
body += "</b><br>\n";
body += "DNS: <b>";
body += WiFi.dnsIP().toString();
body += "</b><br><br>\n";
}
body += "<p>Select your wifi network from the SSID list.<br>To use DHCP leave IP, Subnet, Gateway and DNS fields blank!</p>";
body += "<form method='post' action='/wifiSettings/save'>\n";
body += "SSID: <select id='WIFIssid' name='WIFIssid' required>\n";
body += "<option disabled value='' selected hidden>-Select your network-</option>";
// build option list for selecting wifi
Serial.println("Available Wifis: ");
for(int i = 0 ; i < ssidsAvail; i++) {
String wifiName = WiFi.SSID(i);
Serial.println(wifiName);
body += "<option value='" + wifiName + "'>";
body += wifiName + "</option>\n";
}
body += "</select><br>\n";
body += "Password: <input type='password' name='WIFIpassword'><br>\n";
body += "IP: <input type='text' name='WIFIip'><br>\n";
body += "Subnet mask: <input type='text' name='WIFInetmask'><br>\n";
body += "Gateway: <input type='text' name='WIFIgateway'><br>\n";
body += "DNS: <input type='text' name='WIFIdns'><br>\n";
body += "<input type='submit' value='&#x1F4BE; Save settings'>\n";
body += "</form>\n";
body += FPSTR(HTMLfooter);
webserver.send(200, "text/html", body);
}
void WEBhelp() {
String body = returnHTMLheader("help");
body += FPSTR(HTMLhelp);
body += FPSTR(HTMLfooter);
webserver.send(200, "text/html", body);
}
/*
*
* POSTs
*
*/
void POSTgrowSettings() {
PinLEDPWM = webserver.arg("PinLEDPWM").toInt();
PinFANPWM = webserver.arg("PinFANPWM").toInt();
PinFAN2PWM = webserver.arg("PinFAN2PWM").toInt();
String GrowName_tmp = webserver.arg("GrowName");
GrowName_tmp.toCharArray(GrowName, 32);
GrowStart = webserver.arg("GrowStart").toInt();
DaysVeg = webserver.arg("DaysVeg").toInt();
DaysBloom = webserver.arg("DaysBloom").toInt();
LighthoursVeg = webserver.arg("LighthoursVeg").toInt();
LighthoursBloom = webserver.arg("LighthoursBloom").toInt();
SunriseHour = webserver.arg("SunriseHour").toInt();
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);
// size is 4 byte
EEPROM.put(202, GrowStart);
// size is 1 byte
EEPROM.put(206, DaysVeg);
// size is 1 byte
EEPROM.put(207, DaysBloom);
// size is 1 byte
EEPROM.put(208, LighthoursVeg);
// size is 1 byte
EEPROM.put(209, LighthoursBloom);
// size is 1 byte
EEPROM.put(210, SunriseHour);
// size is 1 byte
EEPROM.put(211, SunriseMinute);
// size is 1 byte
EEPROM.put(213, PinLEDPWM);
// size is 1 byte
EEPROM.put(216, PinFANPWM);
// size is 1 byte
EEPROM.put(248, PinFAN2PWM);
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();
//analogWrite(PinLED, PinLEDPWM);
Serial.println(":: POSTgrowSettings ::");
Serial.print("GrowName: ");
Serial.println(GrowName);
Serial.print("GrowStart: ");
Serial.println(GrowStart);
Serial.print("DaysVeg: ");
Serial.println(DaysVeg);
Serial.print("DaysBloom: ");
Serial.println(DaysBloom);
Serial.print("LighthoursVeg: ");
Serial.println(LighthoursVeg);
Serial.print("LighthoursBloom: ");
Serial.println(LighthoursBloom);
Serial.print("SunriseHour: ");
Serial.println(SunriseHour);
Serial.print("SunriseMinute: ");
Serial.println(SunriseMinute);
Serial.print("PinLEDPWM: ");
Serial.println(PinLEDPWM);
Serial.print("PinFANPWM: ");
Serial.println(PinFANPWM);
Serial.print("PinFAN2PWM: ");
Serial.println(PinFAN2PWM);
webserver.sendHeader("Location", String("/growSettings?success"), true);
webserver.send(302, "text/plain", "growSettings/save: success!\n");
}
void POSTsystemSettings() {
unsigned short UseLEDrelais_old = UseLEDrelais;
unsigned short UseFANrelais_old = UseFANrelais;
unsigned short PWMFrequency_old = PWMFrequency;
NtpOffset = webserver.arg("NtpOffset").toInt();
MoistureSensor_Type = webserver.arg("MoistureSensor_Type").toInt();
SoilmoistureLow = webserver.arg("SoilmoistureLow").toInt();
UsePump = webserver.arg("UsePump").toInt();
PumpOnTime = webserver.arg("PumpOnTime").toInt();
UseFan = webserver.arg("UseFan").toInt();
UseLEDrelais = webserver.arg("UseLEDrelais").toInt();
UseFANrelais = webserver.arg("UseFANrelais").toInt();
TemperatureSensor_Type = webserver.arg("TemperatureSensor_Type").toInt();
MaintenanceDuration = webserver.arg("MaintenanceDuration").toInt();
String Esp32CamIP_tmp = webserver.arg("Esp32CamIP");
Esp32CamIP_tmp.toCharArray(Esp32CamIP, 221);
OutputInvert = webserver.arg("OutputInvert").toInt();
SoilmoistureWet = webserver.arg("SoilmoistureWet").toInt();
SoilmoistureDry = webserver.arg("SoilmoistureDry").toInt();
HumiditySensor_Type = webserver.arg("HumiditySensor_Type").toInt();
PWMFrequency = webserver.arg("PWMFrequency").toInt();
DisplayScreenDuration = webserver.arg("DisplayScreenDuration").toInt();
// when configured is false, set it to true and ensure outputs are set
if(configured == false) {
configured = true;
pinMode(PinLED, OUTPUT);
pinMode(PinPUMP, OUTPUT);
pinMode(PinFAN, OUTPUT);
}
// size is 1 byte
EEPROM.put(161, configured);
// size is 1 byte
EEPROM.put(162, UseFan);
// size is 1 byte
EEPROM.put(163, UsePump);
// size is 1 byte
EEPROM.put(164, PumpOnTime);
// size is 1 byte
EEPROM.put(165, MoistureSensor_Type);
// size is 1 byte
EEPROM.put(166, SoilmoistureLow);
// size is 2 byte
EEPROM.put(167, NtpOffset);
// size is 1 byte
EEPROM.put(169, UseLEDrelais);
// size is 1 byte
EEPROM.put(214, TemperatureSensor_Type);
// size is 1 byte
EEPROM.put(215, UseFANrelais);
EEPROM.put(219, MaintenanceDuration);
EEPROM.put(221, Esp32CamIP);
EEPROM.put(243, OutputInvert);
EEPROM.put(244, SoilmoistureWet);
EEPROM.put(246, SoilmoistureDry);
EEPROM.put(249, HumiditySensor_Type);
EEPROM.put(250, PWMFrequency);
EEPROM.put(252, DisplayScreenDuration);
// write data to EEPROM
EEPROM.commit();
// update time with new offset
timeClient.setTimeOffset(NtpOffset * 60 * 60);
timeClient.update();
Serial.println(":: POSTsystemSettings ::");
// when user uses an relais for LED control, we force here PinLEDPWM to 255
// to ensure nothing bad happens and its turned on
if( (UseLEDrelais == false) && (UseLEDrelais != UseLEDrelais_old) ) {
PinLEDPWM = 255;
EEPROM.put(213, PinLEDPWM);
EEPROM.commit();
Serial.println("UseLEDrelais is 0, forcing PinLEDPWM to max to prevent relais damage and ensure its turned on");
}
if( (UseFANrelais == false) && (UseFANrelais != UseFANrelais_old) ) {
PinFANPWM = 255;
EEPROM.put(215, PinFANPWM);
EEPROM.commit();
Serial.println("UseFANrelais is 0, forcing PinFANPWM to max to prevent relais damage and ensure its turned on");
}
if(PWMFrequency != PWMFrequency_old) {
// if PWM freq changed, apply new settings
analogWriteFreq(PWMFrequency);
}
Serial.print("configured: ");
Serial.println(configured);
Serial.print("UseFan: ");
Serial.println(UseFan);
Serial.print("UsePump: ");
Serial.println(UsePump);
Serial.print("PumpOnTime: ");
Serial.println(PumpOnTime);
Serial.print("MoistureSensor_Type: ");
Serial.println(MoistureSensor_Type);
Serial.print("SoilmoistureLow: ");
Serial.println(SoilmoistureLow);
Serial.print("NtpOffset: ");
Serial.println(NtpOffset);
Serial.print("UseLEDrelais: ");
Serial.println(UseLEDrelais);
Serial.print("UseFANrelais: ");
Serial.println(UseFANrelais);
Serial.print("TemperatureSensor_Type: ");
Serial.println(TemperatureSensor_Type);
Serial.print("MaintenanceDuration: ");
Serial.println(MaintenanceDuration);
if(strlen(GrowName) < 1) {
webserver.sendHeader("Location", String("/growSettings?success"), true);
} else {
webserver.sendHeader("Location", String("/systemSettings?success"), true);
}
webserver.send(302, "text/plain", "systemSettings/save: success!\n");
}
void POSTwifiSettings() {
String WIFIssid_new = webserver.arg("WIFIssid");
String WIFIpassword_new = webserver.arg("WIFIpassword");
String WIFIip_new = webserver.arg("WIFIip");
String WIFInetmask_new = webserver.arg("WIFInetmask");
String WIFIgateway_new = webserver.arg("WIFIgateway");
String WIFIdns_new = webserver.arg("WIFIdns");
// convert String we got from webserver.arg to EEPROM friendly char[]
WIFIssid_new.toCharArray(WIFIssid, 32);
WIFIpassword_new.toCharArray(WIFIpassword, 64);
// if WIFIip_new was not set, we assume DHCP should be used
if(WIFIip_new.length() > 0) {
WIFIip.fromString(WIFIip_new);
WIFInetmask.fromString(WIFInetmask_new);
WIFIgateway.fromString(WIFIgateway_new);
WIFIdns.fromString(WIFIdns_new);
//
WIFIuseDHCP = false;
} else {
WIFIuseDHCP = true;
}
// restart is needed to load the new settings
NeedRestart = true;
EEPROM.put(0, WIFIssid);
EEPROM.put(32, WIFIpassword);
EEPROM.put(96, WIFIip);
EEPROM.put(112, WIFInetmask);
EEPROM.put(128, WIFIgateway);
EEPROM.put(144, WIFIdns);
EEPROM.put(160, WIFIuseDHCP);
EEPROM.commit();
Serial.println(":: POSTwifiSettings ::");
Serial.print("WIFIssid: ");
Serial.println(WIFIssid_new);
Serial.println(WIFIssid);
Serial.print("WIFIpassword: ");
Serial.println(WIFIpassword_new);
Serial.println(WIFIpassword);
Serial.print("WIFIip: ");
Serial.println(WIFIip_new);
Serial.print("WIFInetmask: ");
Serial.println(WIFInetmask_new);
Serial.print("WIFIgateway: ");
Serial.println(WIFIgateway_new);
Serial.print("WIFIdns: ");
Serial.println(WIFIdns_new);
Serial.print("WIFIuseDHCP: ");
Serial.println(WIFIuseDHCP);
webserver.sendHeader("Location", String("/wifiSettings?success"), true);
webserver.send(302, "text/plain", "wifiSettings/save: success!\n");
}
void POSTsetOutput() {
byte OutputState = webserver.arg("state").toInt();
byte OutputNr = webserver.arg("output").toInt();
//PinLEDPWM = webserver.arg("PinLEDPWM").toInt();
byte OutputPWM = webserver.arg("OutputPWM").toInt();
Serial.println(":: POSTsetOutput ::");
Serial.print("OutputState: ");
Serial.println(OutputState);
Serial.print("OutputNr: ");
Serial.println(OutputNr);
if((OutputNr > 3) || (OutputNr < 1) || (OutputState > 255) || (OutputState < 0)) {
webserver.send(400, "text/plain", "not valid\n");
} else {
if(OutputState > 0){
setOutput(OutputNr, OutputPWM);
} else {
setOutput(OutputNr, 0);
}
webserver.sendHeader("Location", String("/?success"), true);
webserver.send(302, "text/plain", "switch: success!\n");
}
}
void POSTsetPumpManual() {
PumpOnManual = webserver.arg("PumpOnManual").toInt();
webserver.sendHeader("Location", String("/?success"), true);
webserver.send(302, "text/plain", "switch: success!\n");
}
void POSTwipeConfirm() {
String body = returnHTMLheader();
// TODO only debug and development solution, remove this later
String confirm = webserver.arg("confirm");
if(confirm == "on") {
body += "<div class='warnmsg'><h2>!! Wiping CanGrow's EEPROM !!</h2><br>Device will restart in a few seconds.<br>After restart a new WiFi 'CanGrow-unconfigured' will be created. To access the WebUI visit <a href='http://192.168.4.20'>http://192.168.4.20</a></div>";
body += FPSTR(HTMLfooter);
webserver.send(200, "text/html", body);
wipeEEPROM();
} else {
webserver.send(400, "text/html", String("Error, 'confirm' missing nor wrong"));
}
}
/*
* API section
*
*/
// return as json all sensor readings
void APIgetSensors() {
JsonDocument jsonSensors;
jsonSensors["soilmoisture"] = valSoilmoisture;
jsonSensors["soilmoistureAvg"] = valSoilmoistureAvg;
jsonSensors["temperature"] = valTemperature;
jsonSensors["humidity"] = valHumidity;
jsonSensors["waterlevel"] = valWaterlevel;
String body;
serializeJsonPretty(jsonSensors, body);
webserver.send(200, "text/json", body);
}
void APIgetDebug() {
JsonDocument jsonDebug;
// Runtime vars
JsonObject objRuntime = jsonDebug["runtime"].add<JsonObject>();
objRuntime["PumpOnTimePassed"] = PumpOnTimePassed;
objRuntime["PumpOnManual"] = PumpOnManual;
objRuntime["valSoilmoistureAvg"] = valSoilmoistureAvg;
objRuntime["valSoilmoistureAvg_tmp"] = valSoilmoistureAvg_tmp;
objRuntime["valSoilmoistureAvg_count"] = valSoilmoistureAvg_count;
// WiFi
JsonObject objWiFi = jsonDebug["wifi"].add<JsonObject>();
objWiFi["ssid"] = WIFIssid;
objWiFi["dhcp"] = WIFIuseDHCP;
objWiFi["ip"] = WiFi.localIP().toString();
objWiFi["netmask"] = WiFi.subnetMask().toString();
objWiFi["gateway"] = WiFi.gatewayIP().toString();
objWiFi["dns"] = WiFi.dnsIP().toString();
// System
JsonObject objSystem = jsonDebug["system"].add<JsonObject>();
objSystem["UseFan"] = UseFan;
objSystem["UsePump"] = UsePump;
objSystem["UseLEDrelais"] = UseLEDrelais;
objSystem["UseFANrelais"] = UseFANrelais;
objSystem["PumpOnTime"] = PumpOnTime;
objSystem["MoistureSensor_Type"] = MoistureSensor_Type;
objSystem["SoilmoistureLow"] = SoilmoistureLow;
objSystem["TemperatureSensor_Type"] = TemperatureSensor_Type;
objSystem["NtpOffset"] = NtpOffset;
objSystem["MaintenanceDuration"] = MaintenanceDuration;
objSystem["PumpOnTime"] = PumpOnTime;
objSystem["PumpLastOn"] = PumpLastOn;
// Grow
JsonObject objGrow = jsonDebug["grow"].add<JsonObject>();
objGrow["GrowName"] = GrowName;
objGrow["GrowStart"] = GrowStart;
objGrow["GrowStartDate"] = returnStrDateFromEpoch(GrowStart);
objGrow["DaysVeg"] = DaysVeg;
objGrow["DaysBloom"] = DaysBloom;
objGrow["LighthoursVeg"] = LighthoursVeg;
objGrow["LighthoursBloom"] = LighthoursBloom;
objGrow["SunriseHour"] = SunriseHour;
objGrow["SunriseMinute"] = SunriseMinute;
objGrow["SunFade"] = SunFade;
objGrow["SunFadeDuration"] = SunFadeDuration;
objGrow["PinLEDPWM"] = PinLEDPWM;
objGrow["PinFANPWM"] = PinFANPWM;
objGrow["DayOfGrow"] = DayOfGrow;
objGrow["PumpIntervalVeg"] = PumpIntervalVeg;
objGrow["PumpIntervalBloom"] = PumpIntervalBloom;
// Sensors
JsonObject objSensors = jsonDebug["sensors"].add<JsonObject>();
// Chirp
objSensors["chirp"]["temperature"] = getTemperature(2);
objSensors["chirp"]["soilmoisture"] = getSoilmoisture(2);
objSensors["chirp"]["soilmoistureRAW"] = getSoilmoisture(2, true);
objSensors["chirp"]["light"] = getLightchirp();
// BME280
objSensors["bme280"]["temperature"] = getTemperature(1);
objSensors["bme280"]["humidity"] = getHumidity(1);
objSensors["bme280"]["preassure"] = bme.readPressure() / 100.0F;
objSensors["bme280"]["appAltitude"] = bme.readAltitude(SEALEVELPRESSURE_HPA);
// Analog
objSensors["analog"]["soilmoisture"] = getSoilmoisture(1);
objSensors["analog"]["soilmoistureRAW"] = getSoilmoisture(1, true);
objSensors["analog"]["waterlevel"] = getWaterlevel();
String body;
serializeJsonPretty(jsonDebug, body);
webserver.send(200, "text/json", body);
}
/*
* Web Handler
*/
void WebHandler() {
/*
* Webserver handlers
* here are the generic webserver handlers like 404 not found
* wifiSettings, ...
*
* if you are looking for the single webpages handler, have a look to
*
* WebHandler_unconfigured() and WebHandler_configured()
*/
// style.css
//webserver.on("/style.css", HTTP_GET, WEBstyleCSS);
// Web root
webserver.on("/", HTTP_GET, WEBroot);
// WiFi Stuff
webserver.on("/wifiSettings", HTTP_GET, WEBwifiSettings);
webserver.on("/wifiSettings/save", HTTP_POST, POSTwifiSettings);
// System stuff
webserver.on("/systemSettings", HTTP_GET, WEBsystemSettings);
webserver.on("/systemSettings/save", HTTP_POST, POSTsystemSettings);
// Grow stuff
webserver.on("/growSettings", HTTP_GET, WEBgrowSettings);
webserver.on("/growSettings/save", HTTP_POST, POSTgrowSettings);
// help
webserver.on("/help", HTTP_GET, WEBhelp);
// restart when NeedRestart is true
webserver.on("/system/restart", HTTP_GET, SysRestart);
// wipe eeprom triggered from WebGui
webserver.on("/system/wipe", HTTP_GET, SysWipe);
// confirm wiping the device
webserver.on("/system/wipeConfirm", HTTP_POST, POSTwipeConfirm);
// Maintenance mode
webserver.on("/system/maintenance", HTTP_GET, SysMaintenance);
// system update with binary
// update form
webserver.on("/system/update", HTTP_GET, SysUpdate);
// update itself
webUpdater.setup(&webserver, "/system/applyUpdate");
// does not work atm TODO
//webserver.on("/logout", [](){ webserver.send(401, "text/html", "logged out!"); });
// 404 handling
// favicon.ico is a special one, because its requested everytime and i dont wont to deliver the
// failed whole page every call. we can save up this 0,5kb traffic :o)
webserver.on("/favicon.ico", [](){ webserver.send(404, "text/html", "404 - not found"); });
webserver.onNotFound(Sys404);
// switching MOSFETs
webserver.on("/switch", HTTP_POST, POSTsetOutput);
webserver.on("/pumpManual", HTTP_POST, POSTsetPumpManual);
// api stuff
webserver.on("/api/sensors", HTTP_GET, APIgetSensors);
webserver.on("/api/debug", HTTP_GET, APIgetDebug);
// gauge meter stuff
webserver.on("/gauge.css", HTTP_GET, WEBgaugeCss);
webserver.on("/gauge.js", HTTP_GET, WEBgaugeJs);
}