diff --git a/Documentation/Grow Controller EL System Diagram.vsdx b/Documentation/Grow Controller EL System Diagram.vsdx
index e30fd96..e769929 100644
Binary files a/Documentation/Grow Controller EL System Diagram.vsdx and b/Documentation/Grow Controller EL System Diagram.vsdx differ
diff --git a/Firmware/CustomHandlers.cpp b/Firmware/CustomHandlers.cpp
index e7f26a8..ebfb551 100644
--- a/Firmware/CustomHandlers.cpp
+++ b/Firmware/CustomHandlers.cpp
@@ -179,6 +179,7 @@ void HandleAPConfigPage() {
String url = wifiConfig.getURL();
String ntp = wifiConfig.getNTPServerName();
+ // Handle submissions from the client
if (server.hasArg("ssid") && server.hasArg("pass")) {
String _ssid = server.hasArg("_ssid") ? server.arg("_ssid") : "";
String _pass = server.hasArg("_pass") ? server.arg("_pass") : "";
@@ -207,11 +208,17 @@ void HandleAPConfigPage() {
}
}
+ // Send [updated] Configuration page to client
+ //
String modeText = (WiFiStateGet() == WFC_JoinedAP) ? "Station mode
" : "Access Point mode
";
if (server.hasArg("ssid") && !server.hasArg("pass")) {
ssid = server.arg("ssid");
pass = "";
Serial.println("ssid: " + ssid + ", pass: " + pass);
+ } else {
+ Serial.printf("name : %s\n", name.c_str());
+ Serial.printf(" ssid: %s\n", ssid.c_str());
+ Serial.printf(" pass: %s\n", pass.c_str());
}
if (server.hasArg("url"))
url = server.arg("url");
@@ -220,60 +227,55 @@ void HandleAPConfigPage() {
server.send(200, "text/html",
"\n"
"\n"
- "
Grow Controller - Config\n"
- "\n"
+ "Grow Controller - Config\n"
+ "\n"
+ "\n"
+ "\n"
+ "\n"
"Grow Controller - Configuration
"
" \n"
- "NAV: "
- " Home | "
- " Config | "
- " Scan | "
- " RSSI | "
- " Current | "
- " Factory Reset | "
- " About\n"
- "
\n"
+ "\n"
"\n"
"\n"
);
diff --git a/Firmware/PlantModel.h b/Firmware/PlantModel.h
index 1c2c54e..34367b0 100644
--- a/Firmware/PlantModel.h
+++ b/Firmware/PlantModel.h
@@ -1,9 +1,12 @@
//
//
//
+#ifndef PLANTMODEL_H
+#define PLANTMODEL_H
#include "ProjGlobals.h"
void HandleGetState();
void simulation();
+#endif // PLANTMODEL_H
diff --git a/Firmware/Resources/Open.png b/Firmware/Resources/Open.png
index 72bc390..feefe3a 100644
Binary files a/Firmware/Resources/Open.png and b/Firmware/Resources/Open.png differ
diff --git a/Firmware/Resources/curr.js b/Firmware/Resources/curr.js
index 458b553..5bdde88 100644
--- a/Firmware/Resources/curr.js
+++ b/Firmware/Resources/curr.js
@@ -28,12 +28,12 @@ getIt = function (aUrl, callback) {
function RefreshStatus() {
getIt(url,
- function (data) {
- obj = JSON.parse(data);
- UpdateGraph(obj.raw, obj.sense); // raw and averaged
- document.getElementById('uptime').innerHTML = 'Uptime: ' + obj.uptime;
- document.getElementById('currText').innerHTML = 'raw: ' + obj.raw + ', avg: ' + obj.sense;
- }
+ function (data) {
+ obj = JSON.parse(data);
+ UpdateGraph(obj.raw, obj.sense); // raw and averaged
+ document.getElementById('uptime').innerHTML = 'Uptime: ' + obj.uptime;
+ document.getElementById('currText').innerHTML = 'raw: ' + obj.raw + ', avg: ' + obj.sense;
+ }
);
}
diff --git a/Firmware/Resources/index.htm b/Firmware/Resources/index.htm
index 168a91e..4a737c0 100644
--- a/Firmware/Resources/index.htm
+++ b/Firmware/Resources/index.htm
@@ -39,7 +39,7 @@
--.- °F
-- %
-
+
O
C
Motor
@@ -57,7 +57,7 @@
8 s
10 s
- 
+ 
diff --git a/Firmware/Resources/index.js b/Firmware/Resources/index.js
index ec3d477..ee8c703 100644
--- a/Firmware/Resources/index.js
+++ b/Firmware/Resources/index.js
@@ -1,9 +1,13 @@
//
// Main Page period status update
//
+// @todo Draw the 'drum' as an arc instead of trying to rotate a picture of an arc.
+// which will work better to reflect the opening.
+//
+//
// var mySite = 'http://192.168.1.23'; // from myip.js
var url = mySite + '/state';
-setInterval(RefreshStatus, 2500);
+setInterval(RefreshStatus, 1000); // @todo speed this up to 500 msec or even 250
getIt = function (aUrl, callback) {
var xhr = new XMLHttpRequest();
@@ -19,28 +23,24 @@ getIt = function (aUrl, callback) {
function ConvertTempToString(tempC, showF) {
var tempF = tempC * 9 / 5 + 32;
- if (showF)
- return tempF.toFixed(1) + "°F";
+ if (showF == 0)
+ return tempF.toFixed(0) + "°F";
else
- return tempC.toFixed(1) + "°C";
+ return tempC.toFixed(0) + "°C";
+}
+
+function ConvertSecToMMSS(sec) {
+ mm = parseInt(sec/60).toString().padStart(4,'0');
+ ss = (sec%60).toString().padStart(2,'0');
+ return mm + ":" + ss;
}
function RefreshStatus() {
getIt(url,
function(data) {
obj = JSON.parse(data);
- if (obj.state === 1)
- state = "On";
- else
- state = "Off";
document.getElementById('version').innerHTML = obj.version;
document.getElementById('name').innerHTML = obj.name;
- //document.getElementById('state').innerHTML = 'Output: ' + state;
- //document.getElementById('raw').innerHTML = 'raw: ' + obj.raw;
- //document.getElementById('sense').innerHTML = 'Sense: ' + obj.sense;
- //document.getElementById('toggle').innerHTML = 'Toggle: ' + obj.toggle;
- //document.getElementById('reset').innerHTML = 'Reset: ' + obj.reset;
- //document.getElementById('countdown').innerHTML = 'Countdown: ' + obj.countdown;
document.getElementById('uptime').innerHTML = 'Uptime: ' + obj.uptime;
document.getElementById('wifimode').innerHTML = 'WiFi Mode: ' + obj.wifimode;
// IP Address
@@ -67,7 +67,7 @@ function RefreshStatus() {
}
if (typeof obj.SoilHeater != "undefined") {
- document.getElementById('SoilHeater').innerHTML = obj.SoilHeater;
+ document.getElementById('SoilHeater').innerHTML = (obj.SoilHeater) ? "Heat On" : "Heat Off";
} else {
document.getElementById('SoilHeater').innerHTML = "--";
}
@@ -83,24 +83,34 @@ function RefreshStatus() {
document.getElementById('SoilMoisture_X').innerHTML = "--";
}
- if (obj.DrumDoorIcon == 1) {
- document.getElementById('DrumDoorIcon').style.display === "none";
+ if (obj.DrumState == 1) { // closed, hide the "open" image
+ document.getElementById('DrumState').style.display === "none";
} else {
- document.getElementById('DrumDoorIcon').style.display === "block";
+ document.getElementById('DrumState').style.display === "block";
}
- document.getElementById('DrumOpenSensor').innerHTML = obj.DrumOpenSensor;
- document.getElementById('DrumClosedSensor').innerHTML = obj.DrumClosedSensor;
- document.getElementById('DrumMotorStatus').innerHTML = obj.DrumMotorStatus;
+ document.getElementById('DrumOpenSensor').innerHTML = (obj.DrumOpenSensor) ? "O" : "!O";
+ document.getElementById('DrumClosedSensor').innerHTML = (obj.DrumClosedSensor) ? "C" : "!C";
+ document.getElementById('DrumMotorStatus').innerHTML = (obj.DrumMotorStatus == 0) ? "stopped" : (obj.DrumMotorStatus == 1) ? "CW" : "CCW";
document.getElementById('DrumMotorOn_sec').innerHTML = obj.DrumMotorOn_sec + "s";
- document.getElementById('DrumMotor_V').innerHTML = parseFloat(obj.DrumMotor_V).toFixed(1) + "V";
- document.getElementById('DrumMotor_I').innerHTML = parseFloat(obj.DrumMotor_I).toFixed(1) + "A";
+ document.getElementById('DrumMotor_V').innerHTML = parseFloat(obj.DrumMotor_V).toFixed(1) + " V";
+ document.getElementById('DrumMotor_I').innerHTML = parseFloat(obj.DrumMotor_I).toFixed(1) + " A";
document.getElementById('WaterInTank').innerHTML = obj.WaterInTank;
- document.getElementById('WaterPumpStatus').innerHTML = obj.WaterPumpStatus;
- document.getElementById('WaterMotor_V').innerHTML = parseFloat(obj.WaterMotor_V).toFixed(1) + "V";
- document.getElementById('WaterCyclePeriod_sec').innerHTML = obj.WaterCyclePeriod_sec + "s";
- document.getElementById('WaterCycleOn_sec').innerHTML = obj.WaterCycleOn_sec + "s";
- document.getElementById('WaterCycle_sec').innerHTML = obj.WaterCycle_sec + "s";
+ document.getElementById('WaterPumpStatus').innerHTML = (obj.WaterPumpStatus) ? "On" : "Off";
+ document.getElementById('WaterMotor_V').innerHTML = parseFloat(obj.WaterMotor_V).toFixed(1) + " V";
+ document.getElementById('WaterCyclePeriod_sec').innerHTML = ConvertSecToMMSS(obj.WaterCyclePeriod_sec);
+ document.getElementById('WaterCycleOn_sec').innerHTML = ConvertSecToMMSS(obj.WaterCycleOn_sec);
+ document.getElementById('WaterCycle_sec').innerHTML = ConvertSecToMMSS(obj.WaterCycle_sec);
+
+ // The full-scale pump timer box ranges from 22 to 234px
+ // The WaterCycleOnBox.width is proportional to the WaterCyclePeriod_sec and the (234 - 22)px width
+ var boxW = (234 - 22);
+ // The WaterCyclePointer_sec.width (24) is positioned from 22 to 234 - 1/2 width.
+ var onW = (obj.WaterCycleOn_sec/obj.WaterCyclePeriod_sec) * boxW;
+ var ptrX = 22 + (obj.WaterCycle_sec/obj.WaterCyclePeriod_sec) * boxW - 12;
+ console.log("onW: " + onW + ", ptrX: " + ptrX);
+ document.getElementById('WaterCycleOnBox').width = onW;
+ document.getElementById('WaterCyclePointer_sec').left = ptrX;
}
);
}
diff --git a/Firmware/Resources/plantmodel.css b/Firmware/Resources/plantmodel.css
index 79ce1bc..c94938b 100644
--- a/Firmware/Resources/plantmodel.css
+++ b/Firmware/Resources/plantmodel.css
@@ -53,7 +53,7 @@ div.ChamberHumi {
}
div.SoilMoisture_X {
position: absolute;
- left: 130px;
+ left: 135px;
top: 135px;
color: white;
}
@@ -64,12 +64,12 @@ div.SoilHumiIcon {
}
div.SoilThermoIcon {
position: absolute;
- left: 170px;
+ left: 175px;
top: 100px;
}
div.SoilTemp_C {
position: absolute;
- left: 165px;
+ left: 170px;
top: 135px;
color: white;
}
@@ -90,7 +90,7 @@ div.SoilHeaterOffIcon {
top: 181px;
}
-div.DrumDoorIcon {
+div.DrumState {
position: absolute;
transform: rotate(180deg);
left: 14px;
@@ -109,7 +109,7 @@ div.DrumClosedSensor {
div.DrumMotorLabel {
position: absolute;
left: 95px;
- top: 270px;
+ top: 260px;
width: 50px;
text-align: center;
color: white;
@@ -117,7 +117,7 @@ div.DrumMotorLabel {
div.DrumMotorStatus {
position: absolute;
left: 95px;
- top: 290px;
+ top: 280px;
width: 50px;
text-align: center;
color: white;
@@ -231,19 +231,19 @@ div.WaterCycleOnBox {
height: 20px;
background-color: #BDD7EE;
z-index: -35;
- //border-color: red;
+ border-color: red;
border-style: solid;
border-width: 1px;
}
-div.WaterCycleStatusIcon {
+div.WaterCyclePointer_sec {
position: absolute;
left: 80px;
top: 469px;
- width: 25px;
+ width: 24px;
text-align: center;
z-index: -25;
border-color: green;
- //border-style: solid;
+ border-style: solid;
border-width: 1px;
}
\ No newline at end of file
diff --git a/Firmware/Web_Resources.h b/Firmware/Web_Resources.h
index fbc3d4f..2ec7677 100644
--- a/Firmware/Web_Resources.h
+++ b/Firmware/Web_Resources.h
@@ -1,8 +1,8 @@
//
// Command: MakeByteArray -ESP -DIR -ROOT *.* -o=..\Web_Resources.h
// version: 1.0
-// From cwd: D:/Projects/GrowController/gcfw/Resources
-// Generated: Mon Oct 25 17:32:22 2021
+// From cwd: C:/Projects/GrowController/Firmware/Resources
+// Generated: Tue Oct 26 13:26:40 2021
// NOTE: Run script in command shell, not PowerShell
//
// PROGMEM is not recognized by Win32 tools...
@@ -1208,7 +1208,7 @@ const char index_htm[] PROGMEM = {
" --.- °F \n"
" -- % \n"
" \n"
- " \n"
+ " \n"
" O \n"
" C \n"
" Motor \n"
@@ -1226,7 +1226,7 @@ const char index_htm[] PROGMEM = {
" 8 s \n"
" 10 s \n"
" \n"
- " \n"
+ " \n"
" \n"
" | \n"
" \n"
@@ -1343,7 +1343,7 @@ const char index_js[] PROGMEM = {
" } \n"
"\n"
" if (typeof obj.SoilHeater != \"undefined\") {\n"
- " document.getElementById('SoilHeater').innerHTML = obj.SoilHeater;\n"
+ " document.getElementById('SoilHeater').innerHTML = (obj.SoilHeater) ? \"Heat On\" : \"Heat Off\";\n"
" } else {\n"
" document.getElementById('SoilHeater').innerHTML = \"--\";\n"
" }\n"
@@ -1359,14 +1359,14 @@ const char index_js[] PROGMEM = {
" document.getElementById('SoilMoisture_X').innerHTML = \"--\";\n"
" }\n"
" \n"
- " if (obj.DrumDoorIcon == 1) {\n"
- " document.getElementById('DrumDoorIcon').style.display === \"none\";\n"
+ " if (obj.DrumState == 1) { // closed, hide the \"open\" image\n"
+ " document.getElementById('DrumState').style.display === \"none\";\n"
" } else {\n"
- " document.getElementById('DrumDoorIcon').style.display === \"block\";\n"
+ " document.getElementById('DrumState').style.display === \"block\";\n"
" }\n"
- " document.getElementById('DrumOpenSensor').innerHTML = obj.DrumOpenSensor;\n"
- " document.getElementById('DrumClosedSensor').innerHTML = obj.DrumClosedSensor;\n"
- " document.getElementById('DrumMotorStatus').innerHTML = obj.DrumMotorStatus;\n"
+ " document.getElementById('DrumOpenSensor').innerHTML = (obj.DrumOpenSensor) ? \"O\" : \"!O\";\n"
+ " document.getElementById('DrumClosedSensor').innerHTML = (obj.DrumClosedSensor) ? \"C\" : \"!C\";\n"
+ " document.getElementById('DrumMotorStatus').innerHTML = (obj.DrumMotorStatus == 0) ? \"stopped\" : (obj.DrumMotorStatus == 1) ? \"CW\" : \"CCW\";\n"
" document.getElementById('DrumMotorOn_sec').innerHTML = obj.DrumMotorOn_sec + \"s\";\n"
" document.getElementById('DrumMotor_V').innerHTML = parseFloat(obj.DrumMotor_V).toFixed(1) + \"V\";\n"
" document.getElementById('DrumMotor_I').innerHTML = parseFloat(obj.DrumMotor_I).toFixed(1) + \"A\";\n"
@@ -1380,7 +1380,7 @@ const char index_js[] PROGMEM = {
" }\n"
" );\n"
"}\n"
-}; // index_js, 4528 bytes
+}; // index_js, 4702 bytes
// File: myip.js
//
@@ -2475,7 +2475,7 @@ const char plantmodel_css[] PROGMEM = {
" top: 181px;\n"
"}\n"
"\n"
- "div.DrumDoorIcon {\n"
+ "div.DrumState {\n"
" position: absolute;\n"
" transform: rotate(180deg);\n"
" left: 14px;\n"
@@ -2494,7 +2494,7 @@ const char plantmodel_css[] PROGMEM = {
"div.DrumMotorLabel {\n"
" position: absolute;\n"
" left: 95px;\n"
- " top: 270px;\n"
+ " top: 260px;\n"
" width: 50px;\n"
" text-align: center;\n"
" color: white;\n"
@@ -2502,7 +2502,7 @@ const char plantmodel_css[] PROGMEM = {
"div.DrumMotorStatus {\n"
" position: absolute;\n"
" left: 95px;\n"
- " top: 290px;\n"
+ " top: 280px;\n"
" width: 50px;\n"
" text-align: center;\n"
" color: white;\n"
@@ -2620,7 +2620,7 @@ const char plantmodel_css[] PROGMEM = {
" border-style: solid;\n"
" border-width: 1px;\n"
"}\n"
- "div.WaterCycleStatusIcon {\n"
+ "div.WaterCyclePointer_sec {\n"
" position: absolute;\n"
" left: 80px;\n"
" top: 469px;\n"
@@ -4634,7 +4634,7 @@ const DirEntry Directory[] PROGMEM = {
{ "/green1x1.png" , green1x1_png , "image/png", 119 },
{ "/icon.png" , icon_png , "image/png", 8721 },
{ "/index.htm" , index_htm , "text/html", 5346 },
- { "/index.js" , index_js , "text/javascript", 4528 },
+ { "/index.js" , index_js , "text/javascript", 4702 },
{ "/myip.js" , myip_js , "text/javascript", 167 },
{ "/nav.js" , nav_js , "text/javascript", 684 },
{ "/navigation.htm" , navigation_htm , "text/html", 557 },
diff --git a/Firmware/WiFiStateHandler.cpp b/Firmware/WiFiStateHandler.cpp
index c385dc7..fd470ea 100644
--- a/Firmware/WiFiStateHandler.cpp
+++ b/Firmware/WiFiStateHandler.cpp
@@ -5,6 +5,10 @@
#include "ProjGlobals.h"
#include "WiFiStateHandler.h"
+#include ///< Required for Captive Portal behavior.
+const byte DNS_PORT = 53; ///< Capture DNS requests on port 53
+//DNSServer dnsServer; ///< Used for the captive portal AP mode
+
WiFiConnectState wifiConState; ///< Track the current WiFi state
@@ -73,7 +77,7 @@ WiFiConnectState WiFiStateHandler() {
WiFi.softAP(AP_NAME.c_str());
myIP = WiFi.softAPIP();
Serial.printf("IP Address: %s\n", myIP.toString().c_str());
- // @todo dnsServer.start(DNS_PORT, "*", apIP);
+ //dnsServer.start(DNS_PORT, "growcontroller.info", myIP);
timeStateChange = millis();
//StartWebServer();
wifiConState = WFC_HostingAP;
@@ -93,7 +97,7 @@ WiFiConnectState WiFiStateHandler() {
// @todo SetLED(LED_WIFI, 0);
// @todo }
// @todo server.handleClient();
- // @todo dnsServer.processNextRequest();
+// dnsServer.processNextRequest();
break;
default:
case WFC_Idle:
|