firmware wip - begin to seperate things into header files
This commit is contained in:
parent
45787b5cdc
commit
abbaedfbbc
8 changed files with 2431 additions and 2427 deletions
File diff suppressed because it is too large
Load diff
435
Arduino/CanGrow/CanGrow_HTML.h
Normal file
435
Arduino/CanGrow/CanGrow_HTML.h
Normal file
|
@ -0,0 +1,435 @@
|
||||||
|
|
||||||
|
/*
|
||||||
|
* HTML constants for header, footer, css, ...
|
||||||
|
* Note: I know of the existence of SPIFFS and ESPHtmlTemplateProcessor,
|
||||||
|
* but to keep things simple for compiling and upload for others, I decided
|
||||||
|
* to not use those.
|
||||||
|
*/
|
||||||
|
|
||||||
|
// Template: const char HTMLexamplepage[] PROGMEM = R"EOF()EOF";
|
||||||
|
|
||||||
|
// first part of HTML header stuff
|
||||||
|
const char HTMLheaderP1[] PROGMEM = R"EOF(
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<meta charset='UTF-8'>
|
||||||
|
<meta name='viewport' content='width=device-width, initial-scale=1.0'>
|
||||||
|
)EOF";
|
||||||
|
// here comes the page title in returnHTMLheader()
|
||||||
|
|
||||||
|
// second part of HTML header stuff
|
||||||
|
// Having the whole CSS here ensures it's all the time present
|
||||||
|
const char HTMLheaderP2[] PROGMEM = R"EOF(
|
||||||
|
<link rel='icon' href='data:;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABmJLR0QAAAAAAAD5Q7t/AAAACXBIWXMAAAsTAAALEwEAmpwYAAABcElEQVQ4y42TzU/bQBDFf7Nx1qGuAYVgQSuo2khBggPhyIH//9AiJAQ9tEeLqCKiUD6sxF52OMSEBCdW57aa9968fTsr3V5XWVLPO6sANNL7ZRAMNeU6Ea4T1UEI6pr55kcAwhpMrYOpk2/r/yEQmKWkIonf+TZVgex4Fw0bIEtIAALF3gbZ8U5VwKa3PJ18JT9IpiLvyflBwuhLG5veVUM0/0aoCONPa2hQjWZ8uEVeupJnXSBwO8YOH8iTeAKc2Q4Xt2C1VZL93F7MjbK/bxDnp5Zn7b+So+9pdQ+K/Q5qJlrRj5Ts6DM+rK7Ih7Mr3HaM7jYQVZqXQ6Tb6yqBYdTfomhHiFfUyMI3f+01/z7RHNzTGDyWGThP63SA2d8EEfIkrgQpzmOvH0AV+3M4zegNpUwagAYG8Yp4BS0nl4Kz5Mpf0JXJMby6w/66Aa+M+9uE53/Iexsggq4ESOYWC0jmsBfX8xdXhcJjL4cLc3kBl8uJGQ/CrpAAAAAASUVORK5CYII='>
|
||||||
|
<style>
|
||||||
|
body {
|
||||||
|
color: #cae0d0;
|
||||||
|
background-color: #1d211e;
|
||||||
|
font-family: helvetica;
|
||||||
|
}
|
||||||
|
|
||||||
|
.center {
|
||||||
|
width: 100%;
|
||||||
|
margin: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
h1, h2, h3, h4, h5 {
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
a:link, a:visited {
|
||||||
|
color: #04AA6D;
|
||||||
|
}
|
||||||
|
a:hover {
|
||||||
|
color: #64AA6D;
|
||||||
|
}
|
||||||
|
a:active {
|
||||||
|
color: #04AA6D;
|
||||||
|
}
|
||||||
|
.infomsg , .warnmsg {
|
||||||
|
color: #fff;
|
||||||
|
border-radius: 3px;
|
||||||
|
padding: 4px;
|
||||||
|
width: fit-content; min-width: 200px; max-width: 420px;
|
||||||
|
margin: auto;
|
||||||
|
margin-bottom: 5px;
|
||||||
|
font-weight: bold;
|
||||||
|
text-align: center;
|
||||||
|
text-decoration: none;
|
||||||
|
text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.5);
|
||||||
|
}
|
||||||
|
.infomsg {
|
||||||
|
background: #04AA6D;
|
||||||
|
}
|
||||||
|
.warnmsg {
|
||||||
|
background: #aa4204;
|
||||||
|
}
|
||||||
|
.inputShort {
|
||||||
|
width: 42px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.nav {
|
||||||
|
background: #333;
|
||||||
|
width: 100%;
|
||||||
|
margin: auto;
|
||||||
|
margin-bottom: 10px;
|
||||||
|
padding: 0;
|
||||||
|
position: relative;
|
||||||
|
border-radius: 3px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.nav li {
|
||||||
|
display: inline-block;
|
||||||
|
list-style: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.nav li:first-of-type {
|
||||||
|
background: #026b45;
|
||||||
|
border-top-left-radius: 3px;
|
||||||
|
border-bottom-left-radius: 3px;
|
||||||
|
}
|
||||||
|
.nav li a , .nav span {
|
||||||
|
color: #ddd;
|
||||||
|
display: block;
|
||||||
|
font-family: 'Lucida Sans Unicode', 'Lucida Grande', sans-serif;
|
||||||
|
font-size:0.8em;
|
||||||
|
padding: 10px 20px;
|
||||||
|
text-decoration: none;
|
||||||
|
text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.5);
|
||||||
|
}
|
||||||
|
.nav li a:hover , .activeMenu , .menuTime{
|
||||||
|
background: #04AA6D;
|
||||||
|
color: #fff;
|
||||||
|
border-radius: 3px;
|
||||||
|
}
|
||||||
|
.nav li a:active {
|
||||||
|
color: #cae0d0;
|
||||||
|
}
|
||||||
|
.activeMenu {
|
||||||
|
background: #444;
|
||||||
|
}
|
||||||
|
.MenuTime {
|
||||||
|
background: #292929;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media only screen and (min-width: 1280px) {
|
||||||
|
.center, .nav {
|
||||||
|
width: 60%; min-width: 420px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<ul class='nav'>)EOF";
|
||||||
|
// here comes the menu as unordered List in returnHTMLheader()
|
||||||
|
|
||||||
|
|
||||||
|
const char HTMLfooter[] PROGMEM = R"EOF(
|
||||||
|
</div>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
)EOF";
|
||||||
|
|
||||||
|
|
||||||
|
const char HTMLsuccess[] PROGMEM = R"EOF(
|
||||||
|
<div class='infomsg'>✅ Successfully saved!</div>
|
||||||
|
)EOF";
|
||||||
|
|
||||||
|
const char HTMLneedRestart[] PROGMEM = R"EOF(
|
||||||
|
<div class='warnmsg'>❗ Restart is required to apply new WiFi settings!
|
||||||
|
<form action='/system/restart'>
|
||||||
|
<input type='submit' value='Restart now' />
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
)EOF";
|
||||||
|
|
||||||
|
const char HTMLhelp[] PROGMEM = R"EOF(
|
||||||
|
<h2>❓ Help</h2>
|
||||||
|
Here you will get some helpful help.
|
||||||
|
)EOF";
|
||||||
|
|
||||||
|
|
||||||
|
const char JSconvertDateToEpoch[] PROGMEM = R"EOF(
|
||||||
|
<script>
|
||||||
|
function convertDateToEpoch(src, dst) {
|
||||||
|
var valGrowStart = document.getElementById(src).value ;
|
||||||
|
document.getElementById(dst).value = new Date(valGrowStart).getTime() / 1000;
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
)EOF";
|
||||||
|
|
||||||
|
// The gauge meter are based on sathomas' gaugemeter
|
||||||
|
// https://github.com/sathomas/material-gauge
|
||||||
|
|
||||||
|
const char CSSgauge[] PROGMEM = R"EOF(
|
||||||
|
.gauge {
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
|
||||||
|
.gaugeWrapper {
|
||||||
|
overflow: hidden;
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.gauge__container {
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
position: absolute;
|
||||||
|
left: 50%;
|
||||||
|
overflow: hidden;
|
||||||
|
text-align: center;
|
||||||
|
-webkit-transform: translateX(-50%);
|
||||||
|
-moz-transform: translateX(-50%);
|
||||||
|
-ms-transform: translateX(-50%);
|
||||||
|
-o-transform: translateX(-50%);
|
||||||
|
transform: translateX(-50%);
|
||||||
|
}
|
||||||
|
|
||||||
|
.gauge__background {
|
||||||
|
z-index: 0;
|
||||||
|
position: absolute;
|
||||||
|
background-color: #cae0d0;
|
||||||
|
top: 0;
|
||||||
|
border-radius: 300px 300px 0 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.gauge__data {
|
||||||
|
z-index: 1;
|
||||||
|
position: absolute;
|
||||||
|
background-color: #04AA6D;
|
||||||
|
margin-left: auto;
|
||||||
|
margin-right: auto;
|
||||||
|
border-radius: 300px 300px 0 0;
|
||||||
|
-webkit-transform-origin: center bottom;
|
||||||
|
-moz-transform-origin: center bottom;
|
||||||
|
-ms-transform-origin: center bottom;
|
||||||
|
-o-transform-origin: center bottom;
|
||||||
|
transform-origin: center bottom;
|
||||||
|
}
|
||||||
|
|
||||||
|
.gauge__center {
|
||||||
|
z-index: 2;
|
||||||
|
position: absolute;
|
||||||
|
background-color: #1d211e;
|
||||||
|
margin-right: auto;
|
||||||
|
border-radius: 300px 300px 0 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.gauge__marker {
|
||||||
|
z-index: 3;
|
||||||
|
background-color: #fff;
|
||||||
|
position: absolute;
|
||||||
|
width: 1px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.gauge__needle {
|
||||||
|
z-index: 4;
|
||||||
|
background-color: #E91E63;
|
||||||
|
height: 3px;
|
||||||
|
position: absolute;
|
||||||
|
-webkit-transform-origin: left center;
|
||||||
|
-moz-transform-origin: left center;
|
||||||
|
-ms-transform-origin: left center;
|
||||||
|
-o-transform-origin: left center;
|
||||||
|
transform-origin: left center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.gauge__labels {
|
||||||
|
display: table;
|
||||||
|
margin: 0 auto;
|
||||||
|
position: relative;
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
|
||||||
|
.gauge__label--low {
|
||||||
|
display: table-cell;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.gauge__label--spacer {
|
||||||
|
display: table-cell;
|
||||||
|
}
|
||||||
|
|
||||||
|
.gauge__label--high {
|
||||||
|
display: table-cell;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
.gauge { height: calc(60px + 3em); }
|
||||||
|
.gauge__container { width: 120px; height: 60px; }
|
||||||
|
.gauge__marker { height: 60px; left: 59.5px; }
|
||||||
|
.gauge__background { width: 120px; height: 60px; }
|
||||||
|
.gauge__center { width: 72px; height: 36px; top: 24px; margin-left: 24px; }
|
||||||
|
.gauge__data { width: 120px; height: 60px; }
|
||||||
|
.gauge__needle { left: 60px; top: 58px; width: 60px; }
|
||||||
|
.gauge__labels { top: 60px; width: 120px; }
|
||||||
|
.gauge__label--low { width: 24px; }
|
||||||
|
.gauge__label--spacer { width: 72px; text-align: center;}
|
||||||
|
.gauge__label--high { width: 24px; }
|
||||||
|
|
||||||
|
@media only screen and (min-width: 720px) {
|
||||||
|
.gauge { height: calc(120px + 4.2em); }
|
||||||
|
.gauge__container { width: 240px; height: 120px; }
|
||||||
|
.gauge__marker { height: 120px; left: 119.5px; }
|
||||||
|
.gauge__background { width: 240px; height: 120px; }
|
||||||
|
.gauge__center { width: 144px; height: 72px; top: 48px; margin-left: 48px; }
|
||||||
|
.gauge__data { width: 240px; height: 120px; }
|
||||||
|
.gauge__needle { left: 120px; top: 117px; width: 120px; }
|
||||||
|
.gauge__labels { top: 120px; width: 240px; }
|
||||||
|
.gauge__label--low { width: 48px; }
|
||||||
|
.gauge__label--spacer { width: 144px; text-align: center;}
|
||||||
|
.gauge__label--high { width: 48px; }
|
||||||
|
.gaugeLabel { font-size: 1.3em; }
|
||||||
|
.gauge__labels { font-size: 2em; }
|
||||||
|
}
|
||||||
|
|
||||||
|
.gauge--liveupdate .gauge__data,
|
||||||
|
.gauge--liveupdate .gauge__needle {
|
||||||
|
-webkit-transition: all 1s ease-in-out;
|
||||||
|
-moz-transition: all 1s ease-in-out;
|
||||||
|
-ms-transition: all 1s ease-in-out;
|
||||||
|
-o-transition: all 1s ease-in-out;
|
||||||
|
transition: all 1s ease-in-out;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
.gauge__data {
|
||||||
|
-webkit-transform: rotate(-.50turn);
|
||||||
|
-moz-transform: rotate(-.50turn);
|
||||||
|
-ms-transform: rotate(-.50turn);
|
||||||
|
-o-transform: rotate(-.50turn);
|
||||||
|
transform: rotate(-.50turn);
|
||||||
|
}
|
||||||
|
.gauge__needle {
|
||||||
|
-webkit-transform: rotate(-.50turn);
|
||||||
|
-moz-transform: rotate(-.50turn);
|
||||||
|
-ms-transform: rotate(-.50turn);
|
||||||
|
-o-transform: rotate(-.50turn);
|
||||||
|
transform: rotate(-.50turn);
|
||||||
|
}
|
||||||
|
|
||||||
|
)EOF";
|
||||||
|
|
||||||
|
const char JSgauge[] PROGMEM = R"EOF(
|
||||||
|
|
||||||
|
function Gauge(el) {
|
||||||
|
|
||||||
|
var element, // Containing element for the info component
|
||||||
|
data, // `.gauge__data` element
|
||||||
|
needle, // `.gauge__needle` element
|
||||||
|
value = 0.0, // Current gauge value from 0 to 1
|
||||||
|
prop, // Style for transform
|
||||||
|
valueLabel; // `.gauge__label--spacer` element
|
||||||
|
|
||||||
|
var setElement = function(el) {
|
||||||
|
// Keep a reference to the various elements and sub-elements
|
||||||
|
element = el;
|
||||||
|
data = element.querySelector('.gauge__data');
|
||||||
|
needle = element.querySelector('.gauge__needle');
|
||||||
|
valueLabel = element.querySelector('.gauge__label--spacer');
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
var setValue = function(x, max, unit) {
|
||||||
|
percentage = x * 100 / max;
|
||||||
|
value = percentage / 100;
|
||||||
|
var turns = -0.5 + (value * 0.5);
|
||||||
|
data.style[prop] = 'rotate(' + turns + 'turn)';
|
||||||
|
needle.style[prop] = 'rotate(' + turns + 'turn)';
|
||||||
|
valueLabel.textContent = x + unit;
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
function exports() { };
|
||||||
|
|
||||||
|
exports.element = function(el) {
|
||||||
|
if (!arguments.length) { return element; }
|
||||||
|
setElement(el);
|
||||||
|
return this;
|
||||||
|
};
|
||||||
|
|
||||||
|
exports.value = function(x, max=100, unit='%') {
|
||||||
|
if (!arguments.length) { return value; }
|
||||||
|
setValue(x, max, unit);
|
||||||
|
return this;
|
||||||
|
};
|
||||||
|
|
||||||
|
var body = document.getElementsByTagName('body')[0];
|
||||||
|
['webkitTransform', 'mozTransform', 'msTransform', 'oTransform', 'transform'].
|
||||||
|
forEach(function(p) {
|
||||||
|
if (typeof body.style[p] !== 'undefined') { prop = p; }
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
if (arguments.length) {
|
||||||
|
setElement(el);
|
||||||
|
}
|
||||||
|
|
||||||
|
return exports;
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
)EOF";
|
||||||
|
|
||||||
|
const char HTMLgauge[] PROGMEM = R"EOF(
|
||||||
|
|
||||||
|
<div class='gaugeWrapper'>
|
||||||
|
<div class='gauge gauge--liveupdate spacer' id='gaugeTemperature' style='float:left; margin-right: 10px;'>
|
||||||
|
<span class='gaugeLabel'>Temperature</span>
|
||||||
|
<div class='gauge__container'>
|
||||||
|
<div class='gauge__background'></div>
|
||||||
|
<div class='gauge__center'></div>
|
||||||
|
<div class='gauge__data'></div>
|
||||||
|
<div class='gauge__needle'></div>
|
||||||
|
</div>
|
||||||
|
<div class='gauge__labels mdl-typography__headline'>
|
||||||
|
<span class='gauge__label--low'></span>
|
||||||
|
<span class='gauge__label--spacer'></span></span>
|
||||||
|
<span class='gauge__label--high'></span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class='gauge gauge--liveupdate spacer' id='gaugeHumidity' style='float:left; margin-right: 10px;'>
|
||||||
|
<span class='gaugeLabel'>Humidity</span>
|
||||||
|
<div class='gauge__container'>
|
||||||
|
<div class='gauge__background'></div>
|
||||||
|
<div class='gauge__center'></div>
|
||||||
|
<div class='gauge__data'></div>
|
||||||
|
<div class='gauge__needle'></div>
|
||||||
|
</div>
|
||||||
|
<div class='gauge__labels mdl-typography__headline'>
|
||||||
|
<span class='gauge__label--low'></span>
|
||||||
|
<span class='gauge__label--spacer'></span>
|
||||||
|
<span class='gauge__label--high'></span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class='gauge gauge--liveupdate' id='gaugeSoilmoisture' style='float:left;'>
|
||||||
|
<span class='gaugeLabel'>Soilmoisture</span>
|
||||||
|
<div class='gauge__container'>
|
||||||
|
<div class='gauge__background'></div>
|
||||||
|
<div class='gauge__center'></div>
|
||||||
|
<div class='gauge__data'></div>
|
||||||
|
<div class='gauge__needle'></div>
|
||||||
|
</div>
|
||||||
|
<div class='gauge__labels mdl-typography__headline'>
|
||||||
|
<span class='gauge__label--low'></span>
|
||||||
|
<span class='gauge__label--spacer'></span>
|
||||||
|
<span class='gauge__label--high'></span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<script src='gauge.js'></script>
|
||||||
|
<script>
|
||||||
|
var gaugeTemperature = new Gauge(document.getElementById('gaugeTemperature'));
|
||||||
|
var gaugeHumidity = new Gauge(document.getElementById('gaugeHumidity'));
|
||||||
|
var gaugeSoilmoisture = new Gauge(document.getElementById('gaugeSoilmoisture'));
|
||||||
|
</script>
|
||||||
|
|
||||||
|
)EOF";
|
158
Arduino/CanGrow/CanGrow_Init.h
Normal file
158
Arduino/CanGrow/CanGrow_Init.h
Normal file
|
@ -0,0 +1,158 @@
|
||||||
|
|
||||||
|
/*
|
||||||
|
*
|
||||||
|
* Constants
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
const char* CanGrowVer = "0.1";
|
||||||
|
const char* APssid = "CanGrow-unconfigured";
|
||||||
|
/*
|
||||||
|
* TODO - does not work atm. idk why.
|
||||||
|
* const char* APpass = "CanGrow";
|
||||||
|
const int APchannel = 6;
|
||||||
|
const bool APhidden = false;
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
*
|
||||||
|
* Variables
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
// valSoilmoisture - contains the value of getSoilmoisture()
|
||||||
|
byte valSoilmoisture;
|
||||||
|
// valTemperature - contains the value of getTemperature()
|
||||||
|
float valTemperature;
|
||||||
|
// valTemperature - contains the value of getHumidity()
|
||||||
|
float valHumidity;
|
||||||
|
// valWaterlevel - contains the value of getWaterlevel()
|
||||||
|
byte valWaterlevel;
|
||||||
|
// do we need a restart? (e.g. after wifi settings change)
|
||||||
|
bool NeedRestart;
|
||||||
|
bool FirstRun;
|
||||||
|
// which screen should be actually displayed
|
||||||
|
byte ScreenToDisplay = 0;
|
||||||
|
byte DisplayScreenDuration = 3;
|
||||||
|
// how many seconds actual screen got displayed
|
||||||
|
byte ScreenIterationPassed = 0;
|
||||||
|
|
||||||
|
bool MaintenanceMode = false;
|
||||||
|
unsigned long MaintenanceStarted = 0;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* millis timer
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
unsigned long outputPrevTime = 0;
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
*
|
||||||
|
* EEPROM saved variables
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
//
|
||||||
|
// WiFi
|
||||||
|
//
|
||||||
|
|
||||||
|
// if empty, CanGrow start in AccessPoint mode
|
||||||
|
char WIFIssid[32];
|
||||||
|
char WIFIpassword[64];
|
||||||
|
// WIFIuseDHCP - if true, get IP by DHCP
|
||||||
|
bool WIFIuseDHCP;
|
||||||
|
IPAddress WIFIip(192,168,4,20);
|
||||||
|
IPAddress WIFInetmask(255,255,255,0);
|
||||||
|
IPAddress WIFIgateway(192,168,4,254);
|
||||||
|
IPAddress WIFIdns(0,0,0,0);
|
||||||
|
//char WebUiUsername[16] = "cangrow";
|
||||||
|
//char WebUiPassword[32] = "cangrow";
|
||||||
|
|
||||||
|
//
|
||||||
|
// System
|
||||||
|
//
|
||||||
|
// configured - if false, let the user configure system settings first
|
||||||
|
bool configured = false;
|
||||||
|
// NTP Offset
|
||||||
|
short NtpOffset;
|
||||||
|
// MoistureSensor_Type - contains which moisture sensor to use
|
||||||
|
// 1: analog capacitive sensor
|
||||||
|
// 2: I2C chirp sensor from catnip electronics
|
||||||
|
byte MoistureSensor_Type;
|
||||||
|
// SoilmoistureLow - contains the value , when soil moisture is assumed to be low,
|
||||||
|
byte SoilmoistureLow = 20;
|
||||||
|
// UsePump - is the pump used? bool
|
||||||
|
bool UsePump;
|
||||||
|
// UseFan - is the fan used? bool
|
||||||
|
byte PumpOnTime = 3;
|
||||||
|
bool UseFan;
|
||||||
|
// In case the user uses no 12V LED on the LED output and an relais instead
|
||||||
|
// we have to disable PWM. So we ask here for what kind of light user is going
|
||||||
|
bool UseLEDrelais;
|
||||||
|
bool UseFANrelais;
|
||||||
|
// Which temperature sensor to use?
|
||||||
|
byte TemperatureSensor_Type;
|
||||||
|
unsigned short MaintenanceDuration = 60;
|
||||||
|
|
||||||
|
//
|
||||||
|
// Grow Stuff
|
||||||
|
//
|
||||||
|
|
||||||
|
// GrowName - contains the name of the grow/plant. Up to 32 byte
|
||||||
|
// if empty, let the user setup grow settings first
|
||||||
|
char GrowName[32];
|
||||||
|
// GrowStart - contains unix timestamp from date where grow starts (00:00)
|
||||||
|
// unsigned long is 8 byte
|
||||||
|
unsigned long GrowStart;
|
||||||
|
// DayOfGrow contains on which day the grow is
|
||||||
|
byte DayOfGrow;
|
||||||
|
// DaysVeg - contains how many days to be in vegetation phase
|
||||||
|
byte DaysVeg = 35;
|
||||||
|
// DaysBloom - contains how many days to be in bloom phase
|
||||||
|
byte DaysBloom = 49;
|
||||||
|
// LighthoursVeg - contains how many hours the Growlight is on in Veg
|
||||||
|
byte LighthoursVeg = 16;
|
||||||
|
// LighthoursBloom - contains how many hours the Growlight is on in Bloom
|
||||||
|
byte LighthoursBloom = 12;
|
||||||
|
// SunriseHour - contains to which hour of day the growlight turns on
|
||||||
|
byte SunriseHour = 7;
|
||||||
|
// SunriseHour - contains to which minute of SunriseHour the growlight turns on
|
||||||
|
byte SunriseMinute = 0;
|
||||||
|
// PinLEDPWM - contains the PWM value for dimming the grow light
|
||||||
|
// default is 255 to ensure it is just on for the case UseLEDrelais is true
|
||||||
|
byte PinLEDPWM = 255;
|
||||||
|
byte PinFANPWM = 255;
|
||||||
|
|
||||||
|
// fade in and out sunrise and sunset?
|
||||||
|
bool SunFade;
|
||||||
|
byte SunFadeDuration = 30;
|
||||||
|
|
||||||
|
/*
|
||||||
|
*
|
||||||
|
* NTP
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
WiFiUDP ntpUDP;
|
||||||
|
NTPClient timeClient(ntpUDP);
|
||||||
|
|
||||||
|
/*
|
||||||
|
*
|
||||||
|
* Webserver
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
ESP8266WebServer webserver(80);
|
||||||
|
|
||||||
|
/* I2C Stuff
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
#define WIRE Wire
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Display Stuff
|
||||||
|
*/
|
||||||
|
Adafruit_SSD1306 display = Adafruit_SSD1306(128, 64, &WIRE);
|
||||||
|
// 'CanGrow_Logo', 128x32px
|
||||||
|
|
40
Arduino/CanGrow/CanGrow_Logo.h
Normal file
40
Arduino/CanGrow/CanGrow_Logo.h
Normal file
|
@ -0,0 +1,40 @@
|
||||||
|
const unsigned char bmpCanGrow_Logo [] PROGMEM = {
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x03, 0xfc, 0x00, 0x00, 0x00, 0x00, 0x03, 0xc0, 0x00, 0x0f, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x07, 0xfe, 0x00, 0x00, 0x00, 0x00, 0x03, 0xe0, 0x00, 0x1f, 0xf8, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x0e, 0x03, 0x00, 0x00, 0x00, 0x00, 0x03, 0xe0, 0x00, 0x38, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x1c, 0x03, 0x00, 0x00, 0x00, 0x00, 0x07, 0xe0, 0x00, 0x70, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x18, 0x03, 0x00, 0x00, 0x00, 0x04, 0x07, 0xe0, 0x20, 0x60, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x18, 0x03, 0x00, 0x00, 0x00, 0x06, 0x07, 0xe0, 0xe0, 0x60, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x18, 0x00, 0x00, 0x00, 0x00, 0x03, 0x87, 0xe1, 0xc0, 0x60, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x30, 0x00, 0x3f, 0xc3, 0xff, 0x03, 0xc7, 0xe3, 0xc0, 0xcf, 0xf9, 0xff, 0xe3, 0xfc, 0xc1, 0x83,
|
||||||
|
0x30, 0x00, 0x7f, 0xe3, 0xff, 0x83, 0xe7, 0xe7, 0xc0, 0xcf, 0xfb, 0xff, 0xe7, 0xfe, 0xc3, 0x87,
|
||||||
|
0x30, 0x00, 0xe0, 0x73, 0x80, 0xc1, 0xf7, 0xef, 0xc0, 0xc0, 0x1b, 0x80, 0x0e, 0x03, 0xc3, 0x86,
|
||||||
|
0x30, 0x00, 0xc0, 0x33, 0x00, 0xc1, 0xff, 0xff, 0x80, 0xc0, 0x1b, 0x00, 0x0c, 0x03, 0xc7, 0x8e,
|
||||||
|
0x30, 0x01, 0xc0, 0x37, 0x00, 0xc0, 0xff, 0xff, 0x80, 0xc0, 0x3b, 0x00, 0x1c, 0x03, 0xc7, 0x8c,
|
||||||
|
0x60, 0x01, 0xc0, 0x37, 0x00, 0xc0, 0xff, 0xff, 0x01, 0x80, 0x3f, 0x00, 0x18, 0x03, 0xcf, 0x9c,
|
||||||
|
0x60, 0x00, 0x00, 0x37, 0x00, 0xc0, 0x7f, 0xfe, 0x01, 0x80, 0x37, 0x00, 0x18, 0x03, 0xcf, 0x9c,
|
||||||
|
0x60, 0x00, 0x00, 0x76, 0x01, 0xc0, 0x1f, 0xfc, 0x01, 0x80, 0x36, 0x00, 0x18, 0x06, 0xdf, 0xb8,
|
||||||
|
0x60, 0x00, 0x7f, 0xe6, 0x01, 0x9f, 0x9f, 0xfc, 0xf9, 0x80, 0x36, 0x00, 0x18, 0x06, 0xdd, 0xb8,
|
||||||
|
0x60, 0x00, 0xff, 0xe6, 0x01, 0x87, 0xff, 0xff, 0xf1, 0x80, 0x76, 0x00, 0x18, 0x06, 0xdd, 0xb0,
|
||||||
|
0xc0, 0x01, 0xc0, 0xee, 0x01, 0x83, 0xff, 0xff, 0xc3, 0x00, 0x7e, 0x00, 0x30, 0x06, 0xf9, 0xf0,
|
||||||
|
0xc0, 0x0b, 0x80, 0x6e, 0x01, 0x81, 0xff, 0xff, 0x83, 0x00, 0x6e, 0x00, 0x30, 0x06, 0xf9, 0xe0,
|
||||||
|
0xc0, 0x1b, 0x00, 0xec, 0x01, 0x80, 0x1f, 0xf8, 0x03, 0x00, 0x6c, 0x00, 0x30, 0x0e, 0xf1, 0xe0,
|
||||||
|
0xc0, 0x3b, 0x00, 0xcc, 0x03, 0x80, 0x3f, 0xfc, 0x03, 0x00, 0xec, 0x00, 0x30, 0x0c, 0xf1, 0xc0,
|
||||||
|
0xc0, 0x7b, 0x01, 0xcc, 0x03, 0x00, 0x7f, 0xfe, 0x03, 0x01, 0xec, 0x00, 0x30, 0x1c, 0xe1, 0xc0,
|
||||||
|
0x7f, 0xf1, 0xff, 0xdc, 0x03, 0x00, 0xf0, 0x8f, 0x01, 0xff, 0xfc, 0x00, 0x1f, 0xf8, 0xe1, 0xc0,
|
||||||
|
0x3f, 0xe0, 0xff, 0xcc, 0x03, 0x00, 0x00, 0x80, 0x00, 0xff, 0xcc, 0x00, 0x0f, 0xf0, 0xc1, 0x80,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
|
||||||
|
};
|
||||||
|
|
||||||
|
// Array of all bitmaps for convenience. (Total bytes used to store images in PROGMEM = 528)
|
||||||
|
const int bmpallArray_LEN = 1;
|
||||||
|
const unsigned char* bmpallArray[1] = {
|
||||||
|
bmpCanGrow_Logo
|
||||||
|
};
|
30
Arduino/CanGrow/CanGrow_PinAssignments.h
Normal file
30
Arduino/CanGrow/CanGrow_PinAssignments.h
Normal file
|
@ -0,0 +1,30 @@
|
||||||
|
/*
|
||||||
|
*
|
||||||
|
* Pin assignments
|
||||||
|
*
|
||||||
|
* D0 - MOSFET Pump
|
||||||
|
* D1, D2 - I2C
|
||||||
|
* D3 - DHT11
|
||||||
|
* D4 - PinWIPE
|
||||||
|
* D5 - MOSFET Fan
|
||||||
|
* D6 - MOSFET Grow LED, PWM
|
||||||
|
* D7 - waterlevel (set HIGH to read value)
|
||||||
|
* D8 - analog soil moisture (set HIGH to read value)
|
||||||
|
* A0 - analog input for soil moisture and waterlevel readings
|
||||||
|
*
|
||||||
|
* D4 and D7 cannot be HIGH at the same time!
|
||||||
|
*/
|
||||||
|
|
||||||
|
// D0 is HIGH at boot, no PWM
|
||||||
|
const uint8_t PinPUMP = D0;
|
||||||
|
// If D3 is pulled to LOW, boot fails
|
||||||
|
const uint8_t PINdht = D3;
|
||||||
|
// D4 is HIGH at boot, boot fail if pulled to LOW
|
||||||
|
// During Start Screen you can pull D4 to LOW to wipe saved data in EEPROM
|
||||||
|
// DO NOT PULL D4 DOWN AT WHEN POWERING ON !!! BOOT WILL FAIL
|
||||||
|
const uint8_t PinWIPE = D4;
|
||||||
|
const uint8_t PinFAN = D5;
|
||||||
|
const uint8_t PinLED = D6; //
|
||||||
|
const uint8_t PINwaterlevel = D7;
|
||||||
|
const uint8_t PINsoilmoisture = D8;
|
||||||
|
const uint8_t PINanalog = A0;
|
160
Arduino/CanGrow/CanGrow_Sensors.h
Normal file
160
Arduino/CanGrow/CanGrow_Sensors.h
Normal file
|
@ -0,0 +1,160 @@
|
||||||
|
/*
|
||||||
|
* DHT Stuff
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
#define DHTTYPE DHT11
|
||||||
|
DHT dht(PINdht, DHTTYPE);
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Chirp functions
|
||||||
|
*/
|
||||||
|
void writeI2CRegister8bit(int addr, int value) {
|
||||||
|
Wire.beginTransmission(addr);
|
||||||
|
Wire.write(value);
|
||||||
|
Wire.endTransmission();
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned int readI2CRegister16bit(int addr, int reg) {
|
||||||
|
Wire.beginTransmission(addr);
|
||||||
|
Wire.write(reg);
|
||||||
|
Wire.endTransmission();
|
||||||
|
delay(20);
|
||||||
|
Wire.requestFrom(addr, 2);
|
||||||
|
unsigned int t = Wire.read() << 8;
|
||||||
|
t = t | Wire.read();
|
||||||
|
return t;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
*
|
||||||
|
* Sensor functions
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
int getWaterlevel() {
|
||||||
|
|
||||||
|
/*
|
||||||
|
* waterlevelRAW
|
||||||
|
* ===========
|
||||||
|
* 0 - 199 : CRITICAL
|
||||||
|
* 200 - 399 : WARNING
|
||||||
|
* >400 : OK
|
||||||
|
*
|
||||||
|
* waterlevel
|
||||||
|
* ==========
|
||||||
|
* 2 : CRITICAL
|
||||||
|
* 1 : WARNING
|
||||||
|
* 0 : OK
|
||||||
|
*/
|
||||||
|
|
||||||
|
short waterlevelWARN = 200;
|
||||||
|
short waterlevelOK = 400;
|
||||||
|
short waterlevelRAW = 0;
|
||||||
|
byte waterlevel = 0;
|
||||||
|
|
||||||
|
// enable Vcc for water level sensor
|
||||||
|
digitalWrite(PINwaterlevel, HIGH);
|
||||||
|
// wait a bit to let the circuit stabilize
|
||||||
|
// TODO: replace delay() with millis()
|
||||||
|
delay(100);
|
||||||
|
// get the value
|
||||||
|
waterlevelRAW = analogRead(PINanalog);
|
||||||
|
// disable Vcc for the sensor to prevent electrolysis effect and release analog pin
|
||||||
|
digitalWrite(PINwaterlevel, LOW);
|
||||||
|
|
||||||
|
if( waterlevelRAW >= waterlevelOK) {
|
||||||
|
waterlevel = 0;
|
||||||
|
} else if( waterlevelRAW >= waterlevelWARN) {
|
||||||
|
waterlevel = 1;
|
||||||
|
} else {
|
||||||
|
waterlevel = 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
return waterlevel;
|
||||||
|
}
|
||||||
|
|
||||||
|
float getTemperature(byte tempSensor) {
|
||||||
|
/*
|
||||||
|
* tempSensor
|
||||||
|
* ==========
|
||||||
|
* 1 : DHT11 temp sensor
|
||||||
|
* 2 : chirp I2C temp sensor
|
||||||
|
*/
|
||||||
|
|
||||||
|
float temperature = 0;
|
||||||
|
|
||||||
|
switch(tempSensor) {
|
||||||
|
case 1:
|
||||||
|
// read temperature from DHT11
|
||||||
|
temperature = dht.readTemperature();
|
||||||
|
break;
|
||||||
|
case 2:
|
||||||
|
// read temperature from chrip I2C
|
||||||
|
temperature = readI2CRegister16bit(0x20, 5) * 0.10 ;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
// if sensor type is not recognized, return 99
|
||||||
|
temperature = 99.99;
|
||||||
|
}
|
||||||
|
|
||||||
|
return temperature;
|
||||||
|
}
|
||||||
|
|
||||||
|
float getHumidity() {
|
||||||
|
float humidity = dht.readHumidity();
|
||||||
|
return humidity;
|
||||||
|
}
|
||||||
|
|
||||||
|
int getSoilmoisture(byte moistureSensor) {
|
||||||
|
/*
|
||||||
|
* moistureSensor
|
||||||
|
* ==============
|
||||||
|
* 1 : analog capacitive moisture sensor
|
||||||
|
* 2 : chirp I2C moisture sensor
|
||||||
|
*/
|
||||||
|
|
||||||
|
// value to return
|
||||||
|
int soilmoisture;
|
||||||
|
// value for wet
|
||||||
|
int wet;
|
||||||
|
// value for dry
|
||||||
|
int dry;
|
||||||
|
|
||||||
|
switch(moistureSensor) {
|
||||||
|
case 1:
|
||||||
|
// read analog value from analog moisture sensor
|
||||||
|
wet = 180;
|
||||||
|
dry= 590;
|
||||||
|
|
||||||
|
digitalWrite(PINsoilmoisture, HIGH);
|
||||||
|
// wait a bit to let the circuit stabilize
|
||||||
|
delay(100);
|
||||||
|
// get analog input value
|
||||||
|
soilmoisture = analogRead(PINanalog);
|
||||||
|
// disable Vcc for the sensor to release analog pin
|
||||||
|
digitalWrite(PINsoilmoisture, LOW);
|
||||||
|
break;
|
||||||
|
case 2:
|
||||||
|
// read soil moisture from chrip I2C
|
||||||
|
wet = 560;
|
||||||
|
dry= 250;
|
||||||
|
|
||||||
|
// get raw value from I2C chirp sensor
|
||||||
|
soilmoisture = readI2CRegister16bit(0x20, 0);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
wet = 0;
|
||||||
|
dry = 1;
|
||||||
|
soilmoisture = -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return map(soilmoisture, wet, dry, 100, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
int getLightchirp() {
|
||||||
|
// get the "light value" from I2C chirp module
|
||||||
|
writeI2CRegister8bit(0x20, 3); //request light measurement
|
||||||
|
int lightchirp = readI2CRegister16bit(0x20, 4);
|
||||||
|
return lightchirp;
|
||||||
|
}
|
566
Arduino/CanGrow/CanGrow_SysFunctions.h
Normal file
566
Arduino/CanGrow/CanGrow_SysFunctions.h
Normal file
|
@ -0,0 +1,566 @@
|
||||||
|
|
||||||
|
/*
|
||||||
|
*
|
||||||
|
*
|
||||||
|
* Functions
|
||||||
|
*
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
void wipeEEPROM() {
|
||||||
|
Serial.println(":: wipe EEPROM ::");
|
||||||
|
|
||||||
|
// wipeMsg is helper variable to know if the Serial.print Message was
|
||||||
|
// already sent
|
||||||
|
byte wipeMsg = 0;
|
||||||
|
while(digitalRead(PinWIPE) == LOW ) {
|
||||||
|
// only show the Serial message once
|
||||||
|
if(wipeMsg == 0) {
|
||||||
|
Serial.println("Please release PinWIPE to erase all data saved in EEPROM");
|
||||||
|
Serial.println("LAST CHANCE TO KEEP THE DATA BY RESETTING NOW!!");
|
||||||
|
|
||||||
|
display.clearDisplay();
|
||||||
|
display.setCursor(0,0);
|
||||||
|
display.println("!!!!!!!!!!!!!!!!!!!!!");
|
||||||
|
display.println("");
|
||||||
|
display.println("RELEASE PinWIPE");
|
||||||
|
display.println("TO WIPE EEPROM");
|
||||||
|
display.display();
|
||||||
|
|
||||||
|
// increase i to show the serial message only once
|
||||||
|
wipeMsg = 1;
|
||||||
|
}
|
||||||
|
delay(500);
|
||||||
|
}
|
||||||
|
|
||||||
|
// write a 0 to all 512 bytes of the EEPROM
|
||||||
|
Serial.print("wiping EEPROM... ");
|
||||||
|
display.println("Wiping EEPROM...");
|
||||||
|
display.println("Will restart in 3s");
|
||||||
|
display.display();
|
||||||
|
for (int i = 0; i < 512; i++) { EEPROM.write(i, 0); }
|
||||||
|
|
||||||
|
// commit everything to EEPROM and end here
|
||||||
|
EEPROM.end();
|
||||||
|
|
||||||
|
Serial.println("DONE");
|
||||||
|
|
||||||
|
// set D4 PinWIPE internal LED to Output to give feedback WIPE
|
||||||
|
// was done
|
||||||
|
pinMode(PinWIPE, OUTPUT);
|
||||||
|
|
||||||
|
Serial.println("!! Device will restart in 3 seconds !!");
|
||||||
|
|
||||||
|
// let the internal led blink fast to signalize wipe is done
|
||||||
|
for(byte i = 0; i <= 24 ; i++) {
|
||||||
|
if(i % 2) {
|
||||||
|
digitalWrite(PinWIPE, LOW);
|
||||||
|
} else {
|
||||||
|
digitalWrite(PinWIPE, HIGH);
|
||||||
|
}
|
||||||
|
delay(125);
|
||||||
|
}
|
||||||
|
ESP.restart();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
bool loadEEPROM() {
|
||||||
|
/*
|
||||||
|
* EEPROM Save table
|
||||||
|
*
|
||||||
|
* 0 WIFIssid
|
||||||
|
* 32 WIFIpassword
|
||||||
|
* 96 WIFIip
|
||||||
|
* 112 WIFInetmask
|
||||||
|
* 128 WIFIgateway
|
||||||
|
* 144 WIFIdns
|
||||||
|
* 160 WIFIuseDHCP
|
||||||
|
*
|
||||||
|
* 161 configured
|
||||||
|
* 162 UseFan
|
||||||
|
* 163 UsePump
|
||||||
|
* 164 PumpOnTime
|
||||||
|
* 165 MoistureSensor_Type
|
||||||
|
* 166 SoilmoistureLow
|
||||||
|
* 167 NtpOffset
|
||||||
|
* 169 UseLEDrelais
|
||||||
|
*
|
||||||
|
* 170 GrowName
|
||||||
|
* 202 GrowStart
|
||||||
|
* 206 DaysVeg
|
||||||
|
* 207 DaysBloom
|
||||||
|
* 208 LighthoursVet
|
||||||
|
* 209 LighthoursBloom
|
||||||
|
* 210 SunriseHour
|
||||||
|
* 211 SunriseMinute
|
||||||
|
* 212 DayOfGrow
|
||||||
|
*
|
||||||
|
* -- afterwards added, need to sort --
|
||||||
|
*
|
||||||
|
* 213 PinLEDPWM
|
||||||
|
* 214 TemperatureSensor_Type
|
||||||
|
* 215 UseFANrelais
|
||||||
|
* 216 PinFANPWM
|
||||||
|
* 217 SunFade
|
||||||
|
* 218 SunFadeDuration
|
||||||
|
* 219 MaintenanceDuration
|
||||||
|
* 221 ..
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
Serial.println(":: loading EEPROM ::");
|
||||||
|
|
||||||
|
display.setCursor(0,36);
|
||||||
|
display.fillRect(0,36,128,64-36, 0);
|
||||||
|
display.println("loading EEPROM");
|
||||||
|
display.display();
|
||||||
|
// read var WIFIssid from address 0, 32 byte long
|
||||||
|
// read this first, because we decide on the ssid length (>0?) if
|
||||||
|
// we run in unconfigured AP mode, nor not
|
||||||
|
EEPROM.get(0, WIFIssid);
|
||||||
|
|
||||||
|
// when length is > 0 then read furter EEPROM config data
|
||||||
|
if(strlen(WIFIssid)) {
|
||||||
|
/*
|
||||||
|
* WIFI settings
|
||||||
|
*/
|
||||||
|
|
||||||
|
// read var WIFIpassword from address 32, 64 byte long
|
||||||
|
EEPROM.get(32, WIFIpassword);
|
||||||
|
// read var WIFIip from address 96, 16 byte long
|
||||||
|
EEPROM.get(96, WIFIip);
|
||||||
|
// read var WIFInetmask from address 112, 16 byte long
|
||||||
|
EEPROM.get(112, WIFInetmask);
|
||||||
|
// read var WIFIgateway from address 128, 16 byte long
|
||||||
|
EEPROM.get(128, WIFIgateway);
|
||||||
|
// read var WIFIgateway from address 128, 16 byte long
|
||||||
|
EEPROM.get(144, WIFIdns);
|
||||||
|
// read var WIFIuseDHCP from Address 160, 1 byte long
|
||||||
|
EEPROM.get(160, WIFIuseDHCP);
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* System settings
|
||||||
|
*/
|
||||||
|
|
||||||
|
// size is 1 byte
|
||||||
|
EEPROM.get(161, configured);
|
||||||
|
if(configured == true) {
|
||||||
|
// size is 1 byte
|
||||||
|
EEPROM.get(162, UseFan);
|
||||||
|
// size is 1 byte
|
||||||
|
EEPROM.get(163, UsePump);
|
||||||
|
// size is 1 byte
|
||||||
|
EEPROM.get(164, PumpOnTime);
|
||||||
|
// size is 1 byte
|
||||||
|
EEPROM.get(165, MoistureSensor_Type);
|
||||||
|
// size is 1 byte
|
||||||
|
EEPROM.get(166, SoilmoistureLow);
|
||||||
|
// size is 2 byte
|
||||||
|
EEPROM.get(167, NtpOffset);
|
||||||
|
// size is 1 byte
|
||||||
|
EEPROM.get(169, UseLEDrelais);
|
||||||
|
// size is 1 byte
|
||||||
|
EEPROM.get(214, TemperatureSensor_Type);
|
||||||
|
// size is 1 byte
|
||||||
|
EEPROM.get(215, UseFANrelais);
|
||||||
|
// size is 2 byte
|
||||||
|
EEPROM.get(219, MaintenanceDuration);
|
||||||
|
}
|
||||||
|
// TODO auth does not work atm
|
||||||
|
// EEPROM.get(160, WebUiUsername);
|
||||||
|
// EEPROM.get(176, WebUiPassword);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Grow settings
|
||||||
|
*/
|
||||||
|
|
||||||
|
// size is 32 byte
|
||||||
|
EEPROM.get(170, GrowName);
|
||||||
|
if(strlen(GrowName) > 0) {
|
||||||
|
// size is 4 byte
|
||||||
|
EEPROM.get(202, GrowStart);
|
||||||
|
// size is 1 byte
|
||||||
|
EEPROM.get(206, DaysVeg);
|
||||||
|
// size is 1 byte
|
||||||
|
EEPROM.get(207, DaysBloom);
|
||||||
|
// size is 1 byte
|
||||||
|
EEPROM.get(208, LighthoursVeg);
|
||||||
|
// size is 1 byte
|
||||||
|
EEPROM.get(209, LighthoursBloom);
|
||||||
|
// size is 1 byte
|
||||||
|
EEPROM.get(210, SunriseHour);
|
||||||
|
// size is 1 byte
|
||||||
|
EEPROM.get(211, SunriseMinute);
|
||||||
|
// size is 1 byte
|
||||||
|
EEPROM.get(212, DayOfGrow);
|
||||||
|
// size is 1 byte
|
||||||
|
EEPROM.get(213, PinLEDPWM);
|
||||||
|
// size is 1 byte
|
||||||
|
EEPROM.get(216, PinFANPWM);
|
||||||
|
EEPROM.get(217, SunFade);
|
||||||
|
EEPROM.get(218, SunFadeDuration);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// print values to Serial output
|
||||||
|
Serial.println("---- WiFi values ----");
|
||||||
|
Serial.print("WIFIssid: ");
|
||||||
|
Serial.println(WIFIssid);
|
||||||
|
Serial.print("WIFIpassword: ");
|
||||||
|
Serial.println(WIFIpassword);
|
||||||
|
Serial.print("Use DHCP: ");
|
||||||
|
Serial.println(WIFIuseDHCP);
|
||||||
|
|
||||||
|
Serial.println("---- System values ----");
|
||||||
|
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("TemperatureSensor_Type: ");
|
||||||
|
Serial.println(TemperatureSensor_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("MaintenanceDuration: ");
|
||||||
|
Serial.println(MaintenanceDuration);
|
||||||
|
|
||||||
|
Serial.println("---- Grow values ----");
|
||||||
|
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("DayOfGrow: ");
|
||||||
|
Serial.println(DayOfGrow);
|
||||||
|
Serial.print("PinLEDPWM: ");
|
||||||
|
Serial.println(PinLEDPWM);
|
||||||
|
Serial.print("PinFANPWM: ");
|
||||||
|
Serial.println(PinFANPWM);
|
||||||
|
Serial.print("SunFade: ");
|
||||||
|
Serial.println(SunFade);
|
||||||
|
Serial.print("SunFadeDuration: ");
|
||||||
|
Serial.println(SunFadeDuration);
|
||||||
|
|
||||||
|
|
||||||
|
} else {
|
||||||
|
Serial.println("EEPROM value WIFIssid is empty");
|
||||||
|
}
|
||||||
|
Serial.println(":: EEPROM loaded ::");
|
||||||
|
|
||||||
|
display.setCursor(0,42);
|
||||||
|
display.println("EEPROM loaded");
|
||||||
|
display.display();
|
||||||
|
|
||||||
|
return(strlen(WIFIssid));
|
||||||
|
}
|
||||||
|
|
||||||
|
void wifiConnect() {
|
||||||
|
Serial.println(":: Connecting to WiFi ::");
|
||||||
|
FirstRun = false;
|
||||||
|
Serial.print("SSID: ");
|
||||||
|
Serial.println(WIFIssid);
|
||||||
|
|
||||||
|
display.fillRect(0,36,128,64-36, 0);
|
||||||
|
display.setCursor(0,36);
|
||||||
|
display.println("Connecting to WiFi");
|
||||||
|
display.println(WIFIssid);
|
||||||
|
display.display();
|
||||||
|
|
||||||
|
// Start WiFi connection
|
||||||
|
WiFi.begin(WIFIssid, WIFIpassword);
|
||||||
|
if(WIFIuseDHCP == false) {
|
||||||
|
WiFi.config(WIFIip, WIFIdns, WIFIgateway, WIFInetmask);
|
||||||
|
}
|
||||||
|
|
||||||
|
// wait until WiFi connection is established
|
||||||
|
while (WiFi.status() != WL_CONNECTED) {
|
||||||
|
delay(500);
|
||||||
|
Serial.print(".");
|
||||||
|
}
|
||||||
|
Serial.println(" CONNECTED!");
|
||||||
|
Serial.print("IP: ");
|
||||||
|
Serial.println(WiFi.localIP());
|
||||||
|
|
||||||
|
Serial.println(":: Getting time from NTP ::");
|
||||||
|
display.fillRect(0,36,128,64-36, 0);
|
||||||
|
display.setCursor(0,36);
|
||||||
|
display.println("Getting NTP time");
|
||||||
|
display.display();
|
||||||
|
|
||||||
|
timeClient.begin();
|
||||||
|
timeClient.setTimeOffset(NtpOffset * 60 * 60);
|
||||||
|
timeClient.update();
|
||||||
|
while ( ! timeClient.isTimeSet()) {
|
||||||
|
timeClient.update();
|
||||||
|
delay(500);
|
||||||
|
Serial.print(".");
|
||||||
|
}
|
||||||
|
|
||||||
|
Serial.println(timeClient.getFormattedTime());
|
||||||
|
Serial.println(timeClient.getEpochTime());
|
||||||
|
display.println(timeClient.getFormattedTime());
|
||||||
|
display.display();
|
||||||
|
display.print("IP: ");
|
||||||
|
display.print(WiFi.localIP());
|
||||||
|
display.display();
|
||||||
|
}
|
||||||
|
|
||||||
|
void wifiAp() {
|
||||||
|
Serial.println(":: Creating Accesspoint ::");
|
||||||
|
|
||||||
|
display.fillRect(0,36,128,64-36, 0);
|
||||||
|
display.setCursor(0,36);
|
||||||
|
display.println("Creating AccessPoint");
|
||||||
|
display.println(APssid);
|
||||||
|
display.display();
|
||||||
|
|
||||||
|
FirstRun = true;
|
||||||
|
// configure WiFi Access Point
|
||||||
|
WiFi.softAPConfig(WIFIip, WIFIgateway, WIFInetmask);
|
||||||
|
// start Access Point
|
||||||
|
// TODO make AP with password - does not work atm. idk why.
|
||||||
|
WiFi.softAP(APssid);
|
||||||
|
Serial.print("SSID: ");
|
||||||
|
Serial.println(APssid);
|
||||||
|
Serial.print("CanGrow IP address: ");
|
||||||
|
Serial.println(WiFi.softAPIP());
|
||||||
|
|
||||||
|
display.print("IP: ");
|
||||||
|
display.println(WiFi.softAPIP());
|
||||||
|
display.display();
|
||||||
|
// TODO does not work atm, idk why
|
||||||
|
//Serial.println("The login credentials for the WebUI are 'cangrow' for username and password");
|
||||||
|
}
|
||||||
|
|
||||||
|
void setOutput(byte Output, byte OutputState) {
|
||||||
|
/*
|
||||||
|
* Pin assignments
|
||||||
|
*
|
||||||
|
* 1 - LED
|
||||||
|
* 2 - FAN
|
||||||
|
* 3 - PUMP
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
bool UseRelais = true;
|
||||||
|
byte OutputPin;
|
||||||
|
|
||||||
|
switch(Output) {
|
||||||
|
case 1:
|
||||||
|
OutputPin = PinLED;
|
||||||
|
if(UseLEDrelais == true) {
|
||||||
|
UseRelais = true;
|
||||||
|
} else {
|
||||||
|
UseRelais = false;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 2:
|
||||||
|
OutputPin = PinFAN;
|
||||||
|
if(UseFANrelais == true) {
|
||||||
|
UseRelais = true;
|
||||||
|
} else {
|
||||||
|
UseRelais = false;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
// PUMP Pin (D0) does not support PWM, so we do not need to care about
|
||||||
|
case 3:
|
||||||
|
OutputPin = PinPUMP;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
//~ Serial.print("Output: ");
|
||||||
|
//~ Serial.println(Output);
|
||||||
|
//~ Serial.print("OutputPin: ");
|
||||||
|
//~ Serial.println(OutputPin);
|
||||||
|
//~ Serial.print("OutputState: ");
|
||||||
|
//~ Serial.println(OutputState);
|
||||||
|
//~ Serial.print("UseRelais: ");
|
||||||
|
//~ Serial.println(UseRelais);
|
||||||
|
|
||||||
|
if( (UseRelais == true) || (OutputPin == PinPUMP) ) {
|
||||||
|
digitalWrite(OutputPin, OutputState);
|
||||||
|
} else {
|
||||||
|
analogWrite(OutputPin, OutputState);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void controlLED() {
|
||||||
|
byte lightHours;
|
||||||
|
byte PinLEDPWM_tmp;
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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))) ) ){
|
||||||
|
//Serial.println("light on time");
|
||||||
|
|
||||||
|
// when SunFade is true, fade LED light. Otherwise just turn on or off
|
||||||
|
if( (SunFade == true) && (UseLEDrelais == false) && (secondsSunrise + SunFadeDuration * 60 >= secondsToday) ) {
|
||||||
|
// in the first n minutes of lighting (SunFadeDuration), we want
|
||||||
|
// to raise the light slowly to prevent stress from the plant
|
||||||
|
// convert progress sunrise to PWM value
|
||||||
|
PinLEDPWM_tmp = (SunFadeDuration * 60 - ((secondsSunrise + SunFadeDuration * 60) - secondsToday)) * PinLEDPWM / (SunFadeDuration * 60);
|
||||||
|
setOutput(1, PinLEDPWM_tmp);
|
||||||
|
//Serial.print("sunrise PWM; ");
|
||||||
|
//Serial.println(PinLEDPWM_tmp);
|
||||||
|
|
||||||
|
} else if( (SunFade == true) && (UseLEDrelais == false) && (secondsToday >= ((secondsSunrise + lightHours * 60 * 60) - SunFadeDuration * 60) ) ) {
|
||||||
|
// calculate progress sunset to PWM value
|
||||||
|
PinLEDPWM_tmp = (secondsSunrise + (lightHours * 60 * 60) - secondsToday) * PinLEDPWM / (SunFadeDuration * 60);
|
||||||
|
setOutput(1, PinLEDPWM_tmp);
|
||||||
|
//Serial.print("sunset PWM: ");
|
||||||
|
//Serial.println(PinLEDPWM_tmp);
|
||||||
|
|
||||||
|
} else {
|
||||||
|
//Serial.println("just turn on the light");
|
||||||
|
// no sunrise or sunset, just keep the LED turned on
|
||||||
|
setOutput(1, PinLEDPWM);
|
||||||
|
}
|
||||||
|
|
||||||
|
} else {
|
||||||
|
//Serial.println("good night time");
|
||||||
|
// turn off
|
||||||
|
setOutput(1, 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void refreshSensors() {
|
||||||
|
valSoilmoisture = getSoilmoisture(MoistureSensor_Type);
|
||||||
|
valHumidity = getHumidity();
|
||||||
|
valTemperature = getTemperature(TemperatureSensor_Type);
|
||||||
|
valWaterlevel = getWaterlevel();
|
||||||
|
}
|
||||||
|
|
||||||
|
void displayScreens() {
|
||||||
|
/*
|
||||||
|
* which screen to display
|
||||||
|
* interate through different screens
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
if(ScreenIterationPassed > DisplayScreenDuration){
|
||||||
|
ScreenIterationPassed = 0;
|
||||||
|
// helper variable, maybe i find a better way in future
|
||||||
|
byte LastScreen = 2;
|
||||||
|
// when the next screen gets displayed, clear display
|
||||||
|
display.clearDisplay();
|
||||||
|
display.display();
|
||||||
|
// when ScreenToDisplay has reach last number of screens, reset to first (0)
|
||||||
|
if(ScreenToDisplay >= LastScreen) {
|
||||||
|
ScreenToDisplay = 0;
|
||||||
|
} else {
|
||||||
|
ScreenToDisplay++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
display.setCursor(0,0);
|
||||||
|
|
||||||
|
if(MaintenanceMode == true) {
|
||||||
|
display.drawBitmap(0, 0, bmpCanGrow_Logo, 128, 32, WHITE);
|
||||||
|
display.display();
|
||||||
|
display.setCursor(0,32);
|
||||||
|
display.println("Maintenance mode active");
|
||||||
|
display.print("Time left: ");
|
||||||
|
display.print(MaintenanceDuration - ((millis() - MaintenanceStarted) / 1000));
|
||||||
|
display.println("s");
|
||||||
|
} else {
|
||||||
|
// in this switch case the single screens gets defined
|
||||||
|
switch(ScreenToDisplay) {
|
||||||
|
case 0:
|
||||||
|
display.print("Humidity: ");
|
||||||
|
display.print(valHumidity);
|
||||||
|
display.println(" %");
|
||||||
|
display.println("");
|
||||||
|
display.print("Temperature: ");
|
||||||
|
display.print(valTemperature);
|
||||||
|
display.println(" C");
|
||||||
|
display.println("");
|
||||||
|
display.print("Moisture: ");
|
||||||
|
display.print(valSoilmoisture);
|
||||||
|
display.println(" %");
|
||||||
|
display.println("");
|
||||||
|
if(UsePump == true) {
|
||||||
|
display.print("Pump Waterlvl: ");
|
||||||
|
switch(valWaterlevel) {
|
||||||
|
case 0:
|
||||||
|
display.println("OK");
|
||||||
|
break;
|
||||||
|
case 1:
|
||||||
|
display.println("Warn");
|
||||||
|
break;
|
||||||
|
case 2:
|
||||||
|
display.println("Crit");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 1:
|
||||||
|
display.print("LED: ");
|
||||||
|
display.print(PinLEDPWM * 100 / 255);
|
||||||
|
display.println(" %");
|
||||||
|
display.print("State: ");
|
||||||
|
display.println(digitalRead(PinLED));
|
||||||
|
display.println("");
|
||||||
|
display.print("FAN: ");
|
||||||
|
display.print(PinFANPWM * 100 / 255);
|
||||||
|
display.println(" %");
|
||||||
|
display.print("State: ");
|
||||||
|
display.println(digitalRead(PinFAN));
|
||||||
|
display.println("");
|
||||||
|
display.print("Pump state: ");
|
||||||
|
display.println(digitalRead(PinPUMP));
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 2:
|
||||||
|
// display Logo
|
||||||
|
display.drawBitmap(0, 0, bmpCanGrow_Logo, 128, 32, WHITE);
|
||||||
|
display.display();
|
||||||
|
display.setCursor(0,32);
|
||||||
|
display.println(GrowName);
|
||||||
|
display.print("DoG: ");
|
||||||
|
display.print(DayOfGrow);
|
||||||
|
display.print(", ");
|
||||||
|
display.println(timeClient.getFormattedTime());
|
||||||
|
display.print("IP: ");
|
||||||
|
display.println(WiFi.localIP());
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ScreenIterationPassed++;
|
||||||
|
display.display();
|
||||||
|
|
||||||
|
}
|
1034
Arduino/CanGrow/CanGrow_WebFunctions.h
Normal file
1034
Arduino/CanGrow/CanGrow_WebFunctions.h
Normal file
File diff suppressed because it is too large
Load diff
Loading…
Reference in a new issue