Compare commits

..

27 commits

Author SHA1 Message Date
DeltaLima
86c21ab864 firmware - v0.1.4 2024-12-13 21:55:58 +01:00
DeltaLima
0fe2b80b5c Add ArduinoJson as single file
From https://github.com/bblanchon/ArduinoJson/releases/download/v7.2.1/ArduinoJson-v7.2.1.h
2024-12-13 21:46:21 +01:00
DeltaLima
32cd179a69 cangrow.sh fix typo, remove ArduinoJson because broke submodule 2024-12-13 21:45:28 +01:00
DeltaLima
5b56c8bc86 firmware wip - try to add external libs as git submodule, does not work atm 2024-12-13 21:41:31 +01:00
DeltaLima
593220d6cf firmware wip - include libs as git submodules 2024-12-13 21:03:15 +01:00
DeltaLima
abb79ebecc firmware - change to set version in CanGrow_Version.h, build by cangrow.sh 2024-12-13 20:50:29 +01:00
DeltaLima
c80ac2e6f6 firmware - fix set PumpLastOn when activating manually, add stop button for pump in maint 2024-12-12 03:37:05 +01:00
DeltaLima
ce7f2b6ec4 renew Screenshot 2024-12-12 02:56:21 +01:00
DeltaLima
483632fbdf renew Screenshot, set firmware version to v0.1.4-dev 2024-12-12 02:52:14 +01:00
DeltaLima
fb2a896da7 firmware - v0.1.3 2024-12-12 02:39:50 +01:00
DeltaLima
d1d0fc494b firmware - cosmetic changes 2024-12-12 02:39:26 +01:00
DeltaLima
33231851fe firmware - add refreshSensors to systemconfig when configured 2024-12-12 02:29:04 +01:00
DeltaLima
85d785f3cd firmware - add soilmoisture calibration, add grow end date to root page
query json without jquery: https://gist.github.com/laurenancona/bd560948d71054e3d1477e43c4d48cb6
2024-12-12 02:19:04 +01:00
DeltaLima
f5ac68375d firmware - new emoji when maintenance dimm is off 2024-12-12 00:14:34 +01:00
DeltaLima
89b449aee6 firmware - new emoji when maintenance dimm is off 2024-12-12 00:12:39 +01:00
DeltaLima
d4e5656656 firmware - new emoji when maintenance dimm is on 2024-12-11 23:38:25 +01:00
DeltaLima
6ff7af0fb0 firmware - wrong bool 2024-12-11 23:36:54 +01:00
DeltaLima
35ed7ba616 firmware - improve output init, fix typos 2024-12-11 23:31:30 +01:00
DeltaLima
d0ea1588b5 firmware - webui changes, move pump settings to grow settings, add debug json 2024-12-11 22:59:25 +01:00
DeltaLima
a4c0d060e1 firmware - cosmetic changes 2024-12-11 02:31:26 +01:00
DeltaLima
c81e68b3e4 firmware - v0.1.2 2024-12-11 00:33:12 +01:00
DeltaLima
3ce946b380 firmware - add VPD, thx Mr. Paolo Pinkel!
https://www.grower.ch/forum/threads/diy-grow-controller-cangrow-projektvorstellung.163654/page-4#post-4294197
2024-12-11 00:30:06 +01:00
DeltaLima
ebfd33af0c firmware - some html cosmetics 2024-12-10 23:50:23 +01:00
DeltaLima
ffaf4e7b82 firmware - add some emojis to gauge label 2024-12-10 23:13:33 +01:00
Marcus
0a2c56cd7b firmware - v0.1.1, offer different I2C addresses for BME280, SHT31.
its done quick and dirty
and some Typos got fixed
2024-12-10 15:20:06 +01:00
2098445202 firmware - fix typo , defines should be at the right place :) 2024-12-09 15:54:15 +01:00
4fe57dff56 firmware - define CANGROW_VER and CANGROW_BUILD defaults, when not defined by compiler flag
With the recent change of defining CANGROW_VER and CANGROW_BUILD as compiler flag and not within CanGrow_Version.h
building from Arduino IDE broke. So I define defaults to them, when not defined by the compiler
2024-12-09 15:40:59 +01:00
19 changed files with 8851 additions and 256 deletions

24
.gitmodules vendored Normal file
View file

@ -0,0 +1,24 @@
[submodule "Arduino/CanGrow/Adafruit-GFX-Library"]
path = Arduino/CanGrow/Adafruit-GFX-Library
url = https://github.com/adafruit/Adafruit-GFX-Library
[submodule "Arduino/CanGrow/lib/Adafruit-GFX-Library"]
path = Arduino/CanGrow/lib/Adafruit-GFX-Library
url = https://github.com/adafruit/Adafruit-GFX-Library
[submodule "Arduino/CanGrow/lib/Adafruit_SSD1306"]
path = Arduino/CanGrow/lib/Adafruit_SSD1306
url = https://github.com/adafruit/Adafruit_SSD1306
[submodule "Arduino/CanGrow/lib/Adafruit_BME280_Library"]
path = Arduino/CanGrow/lib/Adafruit_BME280_Library
url = https://github.com/adafruit/Adafruit_BME280_Library/
[submodule "Arduino/CanGrow/lib/NTPClient"]
path = Arduino/CanGrow/lib/NTPClient
url = https://github.com/arduino-libraries/NTPClient
[submodule "Arduino/CanGrow/lib/Time"]
path = Arduino/CanGrow/lib/Time
url = https://github.com/PaulStoffregen/Time
[submodule "Arduino/CanGrow/lib/Adafruit_SHT31"]
path = Arduino/CanGrow/lib/Adafruit_SHT31
url = https://github.com/adafruit/Adafruit_SHT31/
[submodule "Arduino/CanGrow/lib/Adafruit_Sensor"]
path = Arduino/CanGrow/lib/Adafruit_Sensor
url = https://github.com/adafruit/Adafruit_Sensor

View file

@ -3,12 +3,13 @@
* *
*/ */
/* /*
* Includes * Includes
* *
*/ */
// Libraries // Libraries internal (Arduino Core / ESP)
// https://github.com/arduino/ArduinoCore-avr/tree/master/libraries/SPI // https://github.com/arduino/ArduinoCore-avr/tree/master/libraries/SPI
#include <SPI.h> #include <SPI.h>
// https://github.com/arduino/ArduinoCore-avr/tree/master/libraries/Wire // https://github.com/arduino/ArduinoCore-avr/tree/master/libraries/Wire
@ -22,32 +23,40 @@
#include <ESP8266WebServer.h> #include <ESP8266WebServer.h>
// OTA update // OTA update
#include <ESP8266HTTPUpdateServer.h> #include <ESP8266HTTPUpdateServer.h>
// Libraries external
// https://github.com/adafruit/Adafruit-GFX-Library // https://github.com/adafruit/Adafruit-GFX-Library
//include "lib/Adafruit-GFX-Library/Adafruit_GFX.h"
#include <Adafruit_GFX.h> #include <Adafruit_GFX.h>
// https://github.com/adafruit/Adafruit_SSD1306 // https://github.com/adafruit/Adafruit_SSD1306
//include "lib/Adafruit_SSD1306/Adafruit_SSD1306.h"
#include <Adafruit_SSD1306.h> #include <Adafruit_SSD1306.h>
// https://github.com/adafruit/Adafruit_BME280_Library/ // https://github.com/adafruit/Adafruit_Sensor
// include "lib/Adafruit_Sensor/Adafruit_Sensor.h"
#include <Adafruit_Sensor.h> #include <Adafruit_Sensor.h>
// https://github.com/adafruit/Adafruit_BME280_Library
//include "lib/Adafruit_BME280_Library/Adafruit_BME280.h"
#include <Adafruit_BME280.h> #include <Adafruit_BME280.h>
// https://github.com/adafruit/Adafruit_SHT31/
//#include "lib/Adafruit_SHT31/Adafruit_SHT31.h"
#include <Adafruit_SHT31.h>
// https://github.com/bblanchon/ArduinoJson // https://github.com/bblanchon/ArduinoJson
#include <ArduinoJson.h> #include "lib/ArduinoJson/ArduinoJson-v7.2.1.h"
// https://github.com/arduino-libraries/NTPClient // https://github.com/arduino-libraries/NTPClient
//#include "lib/NTPClient/NTPClient.h"
#include <NTPClient.h> #include <NTPClient.h>
// https://github.com/PaulStoffregen/Time // https://github.com/PaulStoffregen/Time
//#include "lib/Time/TimeLib.h"
#include <TimeLib.h> #include <TimeLib.h>
// DHT support dropped // DHT support dropped
// https://github.com/adafruit/DHT-sensor-library // https://github.com/adafruit/DHT-sensor-library
// #include "DHT.h" // #include "DHT.h"
// SHT30/31
// https://github.com/adafruit/Adafruit_SHT31/
#include "Adafruit_SHT31.h"
/* /*
* CanGrow header files * CanGrow header files
*/ */
#include "CanGrow_Version.h"
#include "CanGrow_PinAssignments.h" #include "CanGrow_PinAssignments.h"
#include "CanGrow_Init.h" #include "CanGrow_Init.h"
#include "CanGrow_Logo.h" #include "CanGrow_Logo.h"
@ -57,6 +66,7 @@
#include "CanGrow_WebFunctions.h" #include "CanGrow_WebFunctions.h"
/* /*
* Setup * Setup
* *
@ -126,25 +136,39 @@ void setup() {
// dht.begin(); //TODO: Do only, when configured // dht.begin(); //TODO: Do only, when configured
// initialise BME280 // initialise BME280
Serial.println(":: initialise BME280 sensor ::"); // dirty way of supporting multiple addresses, DRY? :p
Serial.println(":: initialise BME280 sensor, address 0x76 ::");
// ToDo: let the user configure somewhere the ID of the BME280 sensor // ToDo: let the user configure somewhere the ID of the BME280 sensor
if(!bme.begin(0x76)) { if(!bme_0x76.begin(0x76)) {
Serial.println("!! Cannot find BME280 on I2C bus. Please check connection or ID"); Serial.println("!! Cannot find BME280 on I2C bus at address 0x76. Please check connection or ID");
}
Serial.println(":: initialise BME280 sensor, address 0x77 ::");
// ToDo: let the user configure somewhere the ID of the BME280 sensor
if(!bme_0x77.begin(0x77)) {
Serial.println("!! Cannot find BME280 on I2C bus at address 0x77. Please check connection or ID");
} }
// initialise SHT31 // initialise SHT31
Serial.println(":: initialise SHT31 sensor ::"); Serial.println(":: initialise SHT31 sensor, address 0x44 ::");
if (! sht31.begin(0x44)) { // Set to 0x45 for alternate i2c addr if (! sht31_0x44.begin(0x44)) { // Set to 0x45 for alternate i2c addr
Serial.println("Couldn't find SHT31"); Serial.println("!! Cannot find SHT31 on I2C bus at address 0x45. Please check connection or ID");
}
Serial.println(":: initialise SHT31 sensor, address 0x45 ::");
if (! sht31_0x45.begin(0x45)) { // Set to 0x45 for alternate i2c addr
Serial.println("!! Cannot find SHT31 on I2C bus at address 0x45. Please check connection or ID");
} }
Serial.print("SHT31 Heater Enabled State: "); Serial.print(":: SHT31 (0x44) heater enable state ::");
if (sht31.isHeaterEnabled()) if (sht31_0x44.isHeaterEnabled())
Serial.println("ENABLED"); Serial.println("ENABLED");
else else
Serial.println("DISABLED"); Serial.println("DISABLED");
Serial.print(":: SHT31 (0x45) heater enable state ::");
if (sht31_0x45.isHeaterEnabled())
Serial.println("ENABLED");
else
Serial.println("DISABLED");
@ -218,12 +242,9 @@ void setup() {
// we do this here because otherwise on inverted // we do this here because otherwise on inverted
// boards like CanGrow PCB v0.6 it would be turned on // boards like CanGrow PCB v0.6 it would be turned on
if(configured == true) { if(configured == true) {
pinMode(PinLED, OUTPUT); initOutputs();
pinMode(PinPUMP, OUTPUT);
pinMode(PinFAN, OUTPUT);
// set PWM frequency
analogWriteFreq(PWMFrequency);
} }
} }
@ -249,6 +270,9 @@ void loop() {
// refresh all sensor values // refresh all sensor values
refreshSensors(); refreshSensors();
// calculate VPD - https://www.grower.ch/forum/threads/diy-grow-controller-cangrow-projektvorstellung.163654/page-4#post-4294197
valVPD = (((100 - valHumidity) / 100) * (610.7 * (pow(10, (7.5 * valTemperature / (237.3 + valTemperature))))))/1000;
// calculate acutal DayOfGrow // calculate acutal DayOfGrow
DayOfGrow = int(ceil(float((timeClient.getEpochTime() - GrowStart) / 60 / 60 / 24))); DayOfGrow = int(ceil(float((timeClient.getEpochTime() - GrowStart) / 60 / 60 / 24)));
// decide if we are in Veg or Bloom phase of grow // decide if we are in Veg or Bloom phase of grow

View file

@ -186,6 +186,24 @@ input[type=text], input[type=date], input[type=number], input[type=password], se
margin-bottom: 3px; margin-bottom: 3px;
} }
} }
/* VPD colors */
.vpd_danger1 {
color: #1a6c9c;
}
.vpd_earlyveg {
color: #22ab9c;
}
.vpd_lateveg {
color: #9cc55b;
}
.vpd_latebloom {
color: #9cc55b;
}
.vpd_danger2 {
color: #1a6c9c;
}
</style> </style>
</head> </head>
<body> <body>
@ -452,7 +470,7 @@ const char HTMLgauge[] PROGMEM = R"EOF(
<div class='gaugeWrapper'> <div class='gaugeWrapper'>
<div class='gauge gauge--liveupdate spacer' id='gaugeTemperature' style='float:left; margin-right: 10px;'> <div class='gauge gauge--liveupdate spacer' id='gaugeTemperature' style='float:left; margin-right: 10px;'>
<div class='gaugeLabel'>Temperature</div> <div class='gaugeLabel'>&#x1F321;&#xFE0F; Temperature</div>
<div class='gauge__container'> <div class='gauge__container'>
<div class='gauge__background'></div> <div class='gauge__background'></div>
<div class='gauge__center'></div> <div class='gauge__center'></div>
@ -467,7 +485,7 @@ const char HTMLgauge[] PROGMEM = R"EOF(
</div> </div>
<div class='gauge gauge--liveupdate spacer' id='gaugeHumidity' style='float:left; margin-right: 10px;'> <div class='gauge gauge--liveupdate spacer' id='gaugeHumidity' style='float:left; margin-right: 10px;'>
<div class='gaugeLabel'>Humidity</div> <div class='gaugeLabel'>&#x2601;&#xFE0F; Humidity</div>
<div class='gauge__container'> <div class='gauge__container'>
<div class='gauge__background'></div> <div class='gauge__background'></div>
<div class='gauge__center'></div> <div class='gauge__center'></div>
@ -482,7 +500,7 @@ const char HTMLgauge[] PROGMEM = R"EOF(
</div> </div>
<div class='gauge gauge--liveupdate' id='gaugeSoilmoisture' style='float:left;'> <div class='gauge gauge--liveupdate' id='gaugeSoilmoisture' style='float:left;'>
<div class='gaugeLabel'>Soilmoisture</div> <div class='gaugeLabel'>&#x1FAB4; Soilmoisture</div>
<div class='gauge__container'> <div class='gauge__container'>
<div class='gauge__background'></div> <div class='gauge__background'></div>
<div class='gauge__center'></div> <div class='gauge__center'></div>
@ -527,7 +545,7 @@ const char HTMLsystemSubNav[] PROGMEM = R"EOF(
</ul> </ul>
)EOF"; )EOF";
const char JSsoilmoistureTypeSelect[] PROGMEM = R"EOF( const char JSsoilmoisture[] PROGMEM = R"EOF(
<script> <script>
function MoistureSensorType() { function MoistureSensorType() {
let selVal = document.getElementById('SelMoistureSensor_Type').value; let selVal = document.getElementById('SelMoistureSensor_Type').value;
@ -553,5 +571,30 @@ const char JSsoilmoistureTypeSelect[] PROGMEM = R"EOF(
break; break;
} }
} }
function loadJSON(callback) {
var xobj = new XMLHttpRequest();
xobj.overrideMimeType("application/json");
xobj.open('GET', '/api/sensors', true);
xobj.onreadystatechange = function() {
if (xobj.readyState == 4 && xobj.status == "200") {
callback(xobj.responseText);
}
}
xobj.send(null);
}
function SoilmoistureRefresh() {
loadJSON(function(response) {
json = JSON.parse(response);
document.getElementById('iSoilmoistureRaw').textContent = json.soilmoistureRaw;
console.log(json.soilmoistureRaw);
});
}
</script> </script>
)EOF"; )EOF";
const char HTMLsoilmoistureCalibrateText[] PROGMEM = R"EOF(<p class='helpbox'> <b>Calibration</b><br>
Put your soilmoisture sensor into dry soil and hit Refresh.<br>
Adjust the value of '<i>Soilmoisture dry</i>' if needed according to the reading.<br>
Repeat this with wet soil for '<i>Soilmoisture wet</i>'.</p>)EOF";

View file

@ -20,12 +20,20 @@ const bool APhidden = false;
// valSoilmoisture - contains the value of getSoilmoisture() // valSoilmoisture - contains the value of getSoilmoisture()
unsigned short valSoilmoisture; unsigned short valSoilmoisture;
// helper variable for pump control with soilmoisture
// average of last readings
unsigned short valSoilmoistureAvg = 0;
unsigned short valSoilmoistureAvg_tmp = 0;
byte valSoilmoistureAvg_count = 0;
short valSoilmoistureRaw;
// valTemperature - contains the value of getTemperature() // valTemperature - contains the value of getTemperature()
float valTemperature; float valTemperature;
// valTemperature - contains the value of getHumidity() // valTemperature - contains the value of getHumidity()
float valHumidity; float valHumidity;
// valWaterlevel - contains the value of getWaterlevel() // valWaterlevel - contains the value of getWaterlevel()
byte valWaterlevel; byte valWaterlevel;
// do we need a restart? (e.g. after wifi settings change) // do we need a restart? (e.g. after wifi settings change)
bool NeedRestart; bool NeedRestart;
bool FirstRun; bool FirstRun;
@ -34,6 +42,13 @@ byte ScreenToDisplay = 0;
// how many seconds actual screen got displayed // how many seconds actual screen got displayed
byte ScreenIterationPassed = 0; byte ScreenIterationPassed = 0;
// VPD value - https://www.grower.ch/forum/threads/diy-grow-controller-cangrow-projektvorstellung.163654/page-4#post-4294197
float valVPD;
// DayNight -
// true Day , false night
bool DayNight;
bool MaintenanceMode = false; bool MaintenanceMode = false;
unsigned long MaintenanceStarted = 0; unsigned long MaintenanceStarted = 0;
@ -42,12 +57,7 @@ unsigned long MaintenanceStarted = 0;
byte PumpOnTimePassed = 0; byte PumpOnTimePassed = 0;
bool PumpOnManual = false; bool PumpOnManual = false;
// helper variable for pump control with soilmoisture
// average of last readings
unsigned short valSoilmoistureAvg = 0;
unsigned short valSoilmoistureAvg_tmp = 0;
byte valSoilmoistureAvg_count = 0;
//unsigned short
/* /*
* millis timer * millis timer

View file

@ -12,16 +12,17 @@
*/ */
#define SEALEVELPRESSURE_HPA (1013.25) #define SEALEVELPRESSURE_HPA (1013.25)
Adafruit_BME280 bme; // dirty way of having multiple addresses configurable
Adafruit_BME280 bme_0x76;
Adafruit_BME280 bme_0x77;
/* /*
* SHT30/31 Stuff * SHT30/31 Stuff
* *
*/ */
bool enableHeater = false; Adafruit_SHT31 sht31_0x44 = Adafruit_SHT31();
Adafruit_SHT31 sht31 = Adafruit_SHT31(); Adafruit_SHT31 sht31_0x45 = Adafruit_SHT31();
/* /*
* Chirp functions * Chirp functions
@ -103,9 +104,11 @@ float getTemperature(byte tempSensor) {
/* /*
* tempSensor * tempSensor
* ========== * ==========
* 1 : DHT11 temp sensor * 1 : BME280 0x76 temp sensor
* 2 : chirp I2C temp sensor * 2 : BME280 0x77 temp sensor
* 3 : SHT31 * 3 : SHT31 0x44 temp sensor
* 4 : SHT31 0x45 temp sensor
* 5 : Chirp I2C 0x20 temp sensor
*/ */
float temperature = 0; float temperature = 0;
@ -113,18 +116,23 @@ float getTemperature(byte tempSensor) {
switch(tempSensor) { switch(tempSensor) {
case 1: case 1:
// read temperature from BME280 // read temperature from BME280
temperature = bme.readTemperature(); temperature = bme_0x76.readTemperature();
// read temperature from DHT11
// dht support dropped
// temperature = dht.readTemperature();
break; break;
case 2: case 2:
// read temperature from chrip I2C // read temperature from BME280
temperature = readI2CRegister16bit(0x20, 5) * 0.10 ; temperature = bme_0x77.readTemperature();
break; break;
case 3: case 3:
// read temp from SHT31 // read temp from SHT31
temperature = sht31.readTemperature(); temperature = sht31_0x44.readTemperature();
break;
case 4:
// read temp from SHT31
temperature = sht31_0x45.readTemperature();
break;
case 5:
// read temperature from chrip I2C
temperature = readI2CRegister16bit(0x20, 5) * 0.10 ;
break; break;
default: default:
// if sensor type is not recognized, return 99 // if sensor type is not recognized, return 99
@ -138,18 +146,26 @@ float getHumidity(byte HumSensor) {
/* /*
* sensors: * sensors:
* 1: BME280 * 1 : BME280 0x76 humidity sensor
* 2: SHT31 * 2 : BME280 0x77 humidity sensor
* 3 : SHT31 0x44 humidity sensor
* 4 : SHT31 0x45 humidity sensor
* *
*/ */
float humidity; float humidity;
switch(HumSensor) { switch(HumSensor) {
case 1: case 1:
humidity = bme.readHumidity(); humidity = bme_0x76.readHumidity();
break; break;
case 2: case 2:
humidity = sht31.readHumidity(); humidity = bme_0x77.readHumidity();
break;
case 3:
humidity = sht31_0x44.readHumidity();
break;
case 4:
humidity = sht31_0x45.readHumidity();
break; break;
default: default:
humidity = 0.0; humidity = 0.0;
@ -187,7 +203,7 @@ int getSoilmoisture(byte moistureSensor, bool returnRAW = false) {
digitalWrite(PINsoilmoisture, HIGH); digitalWrite(PINsoilmoisture, HIGH);
// wait a bit to let the circuit stabilize // wait a bit to let the circuit stabilize
delay(50); //delay(50);
// get analog input value // get analog input value
// get values 10 times and get the middle for more precise data // get values 10 times and get the middle for more precise data

View file

@ -114,7 +114,7 @@ bool loadEEPROM() {
* 249 HumiditySensor_Type (1 byte) * 249 HumiditySensor_Type (1 byte)
* 250 PWMFrequency (2 byte) * 250 PWMFrequency (2 byte)
* 252 DisplayScreenDuration (1 byte) * 252 DisplayScreenDuration (1 byte)
* 253 * 253 ...
* *
*/ */
@ -551,10 +551,15 @@ void controlLED() {
setOutput(1, PinLEDPWM); setOutput(1, PinLEDPWM);
} }
// its daytime
DayNight = true;
} else { } else {
//Serial.println("good night time"); //Serial.println("good night time");
// turn off // turn off
setOutput(1, 0); setOutput(1, 0);
// nighttime
DayNight = false;
} }
} }
@ -562,6 +567,7 @@ void refreshSensors() {
byte soilmoistureAvgSampleCount = 5; byte soilmoistureAvgSampleCount = 5;
valSoilmoisture = getSoilmoisture(MoistureSensor_Type); valSoilmoisture = getSoilmoisture(MoistureSensor_Type);
valSoilmoistureRaw = getSoilmoisture(MoistureSensor_Type, true);
valHumidity = getHumidity(HumiditySensor_Type); valHumidity = getHumidity(HumiditySensor_Type);
valTemperature = getTemperature(TemperatureSensor_Type); valTemperature = getTemperature(TemperatureSensor_Type);
valWaterlevel = getWaterlevel(); valWaterlevel = getWaterlevel();
@ -730,6 +736,7 @@ void controlPUMP() {
PumpOnManual = false; PumpOnManual = false;
setOutput(3, 0); setOutput(3, 0);
//digitalWrite(PinPUMP, LOW); //digitalWrite(PinPUMP, LOW);
PumpLastOn = timeClient.getEpochTime();
EEPROM.put(237, PumpLastOn); EEPROM.put(237, PumpLastOn);
EEPROM.commit(); //write to EEPROM EEPROM.commit(); //write to EEPROM
PumpOnTimePassed = 0; PumpOnTimePassed = 0;
@ -824,3 +831,13 @@ void controlFAN() {
setOutput(4, PinFAN2PWM); setOutput(4, PinFAN2PWM);
} }
void initOutputs() {
pinMode(PinLED, OUTPUT);
pinMode(PinPUMP, OUTPUT);
pinMode(PinFAN, OUTPUT);
// set PWM frequency
analogWriteFreq(PWMFrequency);
for(byte i = 1; i <= 4; i++) {
setOutput(i, 0);
}
}

View file

@ -0,0 +1,13 @@
/*
*
*
* Version
*
*
*/
#define CANGROW_VER "0.1.4"
// CANGROW_BUILD default dummy value if not set as Compiler Flag
#ifndef CANGROW_BUILD
#define CANGROW_BUILD "1a2b3c4-0000000000000"
#endif

View file

@ -78,7 +78,7 @@ String returnHTMLheader(String MenuEntry = "") {
// status icons and info // status icons and info
if(MaintenanceMode == true) { if(MaintenanceMode == true) {
// status icons // status icons
header += " | &#9208;&#65039; "; header += " | &#x26C5; ";
header += MaintenanceDuration - ((millis() - MaintenanceStarted) / 1000); header += MaintenanceDuration - ((millis() - MaintenanceStarted) / 1000);
header += "s"; header += "s";
} }
@ -210,31 +210,51 @@ void Syslogout() {
void SysMaintenance() { void SysMaintenance() {
String body = returnHTMLheader(); String body = returnHTMLheader();
if( (webserver.hasArg("DimmOn")) ) { // when requesting to handle Dimming
MaintenanceMode = true; if( (webserver.hasArg("DimmOn")) || (webserver.hasArg("DimmOff")) ) {
MaintenanceStarted = millis(); // check first if PWM is disabled / relais is used
body += "<div class='infomsg'>&#9208;&#65039; Dimm LED On for "; if(UseLEDrelais == false) {
body += MaintenanceDuration; // if not, do it
body += "s</div>"; if( (webserver.hasArg("DimmOn")) ) {
} else if (webserver.hasArg("DimmOff")){ MaintenanceMode = true;
MaintenanceMode = false; MaintenanceStarted = millis();
body += "<div class='infomsg'>&#9208;&#65039; Dimm LED Off</div>"; body += "<div class='infomsg'>&#x26C5; Dimm LED ON for ";
body += MaintenanceDuration;
body += "s</div>";
} else if( (webserver.hasArg("DimmOff")) ) {
MaintenanceMode = false;
body += "<div class='infomsg'>&#x23F9;&#xFE0F; Dimm LED OFF</div>";
}
} else {
// otherwise nice error
body += "<div class='warnmsg'>&#x26C5; Cannot dimm LED, <i>Use relais for LED (disable PWM)</i> is set to <i>Yes</i> in &#128262; Grow settings</div>";
}
} else if(webserver.hasArg("PumpOnManual")) { } else if(webserver.hasArg("PumpOnManual")) {
PumpOnManual = true; if(UsePump > 0) {
body += "<div class='infomsg'>&#x1F4A7; Pump manual activated for "; PumpOnManual = true;
body += PumpOnTime; body += "<div class='infomsg'>&#x1F4A7; Pump manual activated for ";
body += "s</div>"; body += PumpOnTime;
body += "s</div>";
} else {
body += "<div class='warnmsg'>&#x1F4A7; Cannot activate, <i>Pump mode</i> is set to <i>Off</i> in &#128262; Grow settings</div>";
}
} else if(webserver.hasArg("PumpOffManual")) {
// stooop
setOutput(3, 0);
PumpOnManual = false;
PumpOnTimePassed = 0;
body += "<div class='infomsg'>&#x1F4A7; Pump manual stopped</div>";
} }
body += "<h2>&#x1f9f0; Maintenance</h2>"; body += "<h2>&#x1f9f0; Maintenance</h2>";
body += "Dimm LED <a class='button' href='/system/maintenance?DimmOn=1'>On</a>&nbsp;&nbsp;<a class='button' href='/system/maintenance?DimmOff=1'>Off</a><br><br><br>"; body += "Dimm LED <a class='button' href='/system/maintenance?DimmOn=1'>&#x26C5; On</a>&nbsp;&nbsp;<a class='button' href='/system/maintenance?DimmOff=1'>&#x23F9;&#xFE0F; Off</a><br><br><br>";
body += "Pump manual <a class='button' href='/system/maintenance?PumpOnManual=1'>Activate for "; body += "Pump manual <a class='button' href='/system/maintenance?PumpOnManual=1'>&#x1F4A7; Activate for ";
body += PumpOnTime; body += PumpOnTime;
body += "s</a><br>"; body += "s</a>&nbsp;&nbsp;";
body += "<a class='button' href='/system/maintenance?PumpOffManual=1'>&#x1F6D1; Stop manual</a><br>";
body += FPSTR(HTMLfooter); body += FPSTR(HTMLfooter);
@ -359,25 +379,68 @@ void WEBroot() {
body += "'></a>\n<br>\n"; body += "'></a>\n<br>\n";
} }
body += "<b>Grow started:</b> "; body += "<b>Grow started: &#x1F5D3;&#xFE0F;</b> ";
body += returnStrDateFromEpoch(GrowStart); body += returnStrDateFromEpoch(GrowStart);
body += "<br>\n"; body += "<br>\n";
body += "<b>Day of Grow:</b> "; body += "<b>Harvest date (est.): &#x1F342; </b> ";
body += returnStrDateFromEpoch(GrowStart + (60 * 60 * 24 * (DaysVeg + DaysBloom) ) );
body += "<br>\n";
body += "<b>Day of Grow: </b> ";
if(DayNight == true) {
body += " &#x1F31E; ";
} else {
body += " &#x1F31A; ";
}
body += DayOfGrow; body += DayOfGrow;
body += "<br>\n"; body += "<br>\n";
body += "<b>Grow status:</b> "; body += "<b>Grow status:</b> ";
switch(growState()) { switch(growState()) {
case 1: case 1:
body += "&#x1F331; vegetation<br>\n"; body += "&#x1F331; Vegetation<br>\n";
break; break;
case 2: case 2:
body += "&#x1F33C; bloom<br>\n"; body += "&#x1F33C; Bloom (";
body += DayOfGrow - DaysVeg;
body += ")<br>\n";
break; break;
case 3: case 3:
body += "&#x1F342; harvest\n"; body += "&#x1F342; Harvest (";
body += DayOfGrow - (DaysVeg + DaysBloom);
body += ")<br>\n";
break; break;
} }
// VPD
body += "<b>VPD (est.): ";
// apply text color to the value according to this chart
if(valVPD < 0) {
body += "&#x26A0;&#xFE0F; <span class=''>";
body += valVPD;
body += "</span></b> (Danger! Check for disease!)";
} else if(valVPD < 0.4 ) {
body += "&#x26A0;&#xFE0F; <span class='vpd_danger1'>";
body += valVPD;
body += "</span></b> (Danger! Under transpiration!)";
} else if(valVPD < 0.8 ) {
body += "&#x1F343; <span class='vpd_earlyveg'>";
body += valVPD;
body += "</span></b> (Early vegetation)";
} else if(valVPD < 1.2 ) {
body += "&#x1F343; <span class='vpd_lateveg'>";
body += valVPD;
body += "</span></b> (Late vegetation)";
} else if(valVPD < 1.6 ) {
body += "&#x1F343; <span class='vpd_latebloom'>";
body += valVPD;
body += "</span></b> (Late bloom)";
} else if(valVPD > 1.6 ) {
body += "&#x26A0;&#xFE0F; <span class='vpd_danger2'>";
body += valVPD;
body += "</span></b> (Danger - over transpiration!)";
}
body += "<br>\n";
if(UsePump > 0) { if(UsePump > 0) {
body += "<b>Pump water level:</b> "; body += "<b>Pump water level:</b> ";
switch(getWaterlevel()) { switch(getWaterlevel()) {
@ -397,7 +460,8 @@ void WEBroot() {
body += "<b>Growlight brightness:</b> "; body += "<b>Growlight brightness:</b> ";
body += ((PinLEDPWM * 100) / 255); body += ((PinLEDPWM * 100) / 255);
body += " %<br>\n"; body += " %<br>\n";
body += "<br>\n";
//~ body += "<form method='post' action='/switch'>\n"; //~ body += "<form method='post' action='/switch'>\n";
//~ body += "<b>MOSFET:</b> <select id='output' name='output' >\n"; //~ body += "<b>MOSFET:</b> <select id='output' name='output' >\n";
//~ body += "<option disabled value='' selected hidden>---</option>\n"; //~ body += "<option disabled value='' selected hidden>---</option>\n";
@ -416,7 +480,7 @@ void WEBroot() {
//~ body += "<input type='submit' value='Save'>\n"; //~ body += "<input type='submit' value='Save'>\n";
//~ body += "</form><br>\n"; //~ body += "</form><br>\n";
body += "<a class='button' href='/system/maintenance'>&#x1f9f0; Maintenance</a>"; body += "<br><a class='button' href='/system/maintenance'>&#x1f9f0; Maintenance</a>";
body += FPSTR(HTMLfooter); body += FPSTR(HTMLfooter);
@ -467,32 +531,35 @@ void WEBgrowSettings() {
body+= "' required><br>\n"; 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 += "<input type='hidden' id='GrowStart' name='GrowStart' value='";
body += GrowStart; body += GrowStart;
body+= "' required>\n"; body+= "' required>\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><br>\n";
body += "<b>Grow duration</b><br>";
body += "Vegetation duration: <input class='inputShort' type='number' name='DaysVeg' min='0' max='255' value='"; body += "Vegetation duration: <input class='inputShort' type='number' name='DaysVeg' min='0' max='255' value='";
body += DaysVeg; body += DaysVeg;
body+= "' required>days<br>\n"; body+= "' required> Days<br>\n";
body += "Bloom duration: <input class='inputShort' type='number' name='DaysBloom' min='0' max='255' value='"; body += "Bloom duration: <input class='inputShort' type='number' name='DaysBloom' min='0' max='255' value='";
body += DaysBloom; body += DaysBloom;
body+= "' required> days<br>\n"; body+= "' required> Days<br><br>\n";
body += "Time LED ON vegetation: <input class='inputShort' type='number' name='LighthoursVeg' min='0' max='255' value='";
body += "<b>Light configuration</b><br>";
body += "LED ON vegetation: <input class='inputShort' type='number' name='LighthoursVeg' min='0' max='255' value='";
body += LighthoursVeg; body += LighthoursVeg;
body+= "' required> hours<br>\n"; body+= "' required> Hours<br>\n";
body += "Time LED ON bloom: <input class='inputShort' type='number' name='LighthoursBloom' min='0' max='255' value='"; body += "LED ON bloom: <input class='inputShort' type='number' name='LighthoursBloom' min='0' max='255' value='";
body += LighthoursBloom; body += LighthoursBloom;
body+= "' required> hours<br>\n"; body+= "' required> Hours<br>\n";
body += "Sunrise: <input class='inputShort' type='number' name='SunriseHour' min='0' max='23' value='"; body += "Sunrise: <input class='inputShort' type='number' name='SunriseHour' min='0' max='23' value='";
body += SunriseHour; body += SunriseHour;
@ -521,7 +588,9 @@ void WEBgrowSettings() {
body += "<option value='0'" + returnStrSelected(PinLEDPWM, 0) + ">Off</option>\n"; body += "<option value='0'" + returnStrSelected(PinLEDPWM, 0) + ">Off</option>\n";
body += "</select><br>\n"; body += "</select><br>\n";
} }
body += "<br>";
body += "<b>Fan configuration</b><br>";
if(UseFANrelais == false) { if(UseFANrelais == false) {
body += "FAN1 speed: <input type='range' id='PinFANPWM' name='PinFANPWM' min='0' max='255' value='"; body += "FAN1 speed: <input type='range' id='PinFANPWM' name='PinFANPWM' min='0' max='255' value='";
body += PinFANPWM; body += PinFANPWM;
@ -535,20 +604,42 @@ void WEBgrowSettings() {
body += "FAN2 speed: <input type='range' id='PinFAN2PWM' name='PinFAN2PWM' min='0' max='255' value='"; body += "FAN2 speed: <input type='range' id='PinFAN2PWM' name='PinFAN2PWM' min='0' max='255' value='";
body += PinFAN2PWM; body += PinFAN2PWM;
body += "'/> %<br>\n"; body += "'/> %<br><br>\n";
body += "<b>Pump configuration</b><br>";
// 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";
// 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";
// SoilmoistureLow byte
body += "Soilmoisture low: <input class='inputShort' type='number' name='SoilmoistureLow' min='0' value='";
body += SoilmoistureLow;
body += "' required> %<br>\n";
body += "Pump interval vegetation: every <input class='inputShort' type='number' name='PumpIntervalVeg' min='0' max='255' value='"; body += "Pump interval vegetation: every <input class='inputShort' type='number' name='PumpIntervalVeg' min='0' max='255' value='";
body += PumpIntervalVeg; body += PumpIntervalVeg;
body += "' required "; body += "' required> Days<br>\n";
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 += "Pump interval bloom: every <input class='inputShort' type='number' name='PumpIntervalBloom' min='0' max='255' value='";
body += PumpIntervalBloom; body += PumpIntervalBloom;
body += "' required "; body += "' required> Days<br><br>\n";
if((UsePump != 1) && (UsePump != 3)) { body += "readonly"; }
body += "> days<br>\n";
body += "<input type='submit' value='&#x1F4BE; Save settings'>\n"; body += "<input type='submit' value='&#x1F4BE; Save settings'>\n";
@ -586,60 +677,31 @@ void WEBsystemSettings() {
// form starts // form starts
body += "<form method='post' action='/systemSettings/save'>\n"; body += "<form method='post' action='/systemSettings/save'>\n";
// UseFan bool body += "<b>Output configuration</b><br>";
//~ 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 // OutputInvert bool
body += "Invert Outputs: <select id='OutputInvert' name='OutputInvert' required>\n"; body += "Invert Outputs: <select id='OutputInvert' name='OutputInvert' required>\n";
if(configured == false){body += "<option disabled value='' selected hidden>---</option>\n";} if(configured == false){body += "<option disabled value='' selected hidden>---</option>\n";}
body += "<option value='1'" + returnStrSelected(OutputInvert, 1) + ">Yes</option>\n";
body += "<option value='0'" + returnStrSelected(OutputInvert, 0) + ">No</option>\n"; body += "<option value='0'" + returnStrSelected(OutputInvert, 0) + ">No</option>\n";
body += "<option value='1'" + returnStrSelected(OutputInvert, 1) + ">Yes</option>\n";
body += "</select><br>\n"; body += "</select><br>\n";
body += "<p class='helpbox'>When using CanGrow PCB v0.6, set to <b>Yes</b></p>\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 // UseLEDrelais bool
body += "Use relais for LED (disable PWM): <select id='UseLEDrelais' name='UseLEDrelais' required>\n"; 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";} 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 += "<option value='0'" + returnStrSelected(UseLEDrelais, 0) + ">No</option>\n";
body += "<option value='1'" + returnStrSelected(UseLEDrelais, 1) + ">Yes</option>\n";
body += "</select><br>\n"; body += "</select><br>\n";
// UseFANrelais bool // UseFANrelais bool
body += "Use relais for FAN (disable PWM): <select id='UseFANrelais' name='UseFANrelais' required>\n"; body += "Use relais for FAN1 (disable PWM): <select id='UseFANrelais' name='UseFANrelais' required>\n";
if(configured == false){body += "<option disabled value='' selected hidden>---</option>\n";} if(configured == false){body += "<option disabled value='' selected hidden>---</option>\n";}
body += "<option value='1'" + returnStrSelected(UseFANrelais, 1) + ">Yes</option>\n";
body += "<option value='0'" + returnStrSelected(UseFANrelais, 0) + ">No</option>\n"; body += "<option value='0'" + returnStrSelected(UseFANrelais, 0) + ">No</option>\n";
body += "</select><br>\n"; body += "<option value='1'" + returnStrSelected(UseFANrelais, 1) + ">Yes</option>\n";
body += "</select><br><br>\n";
// TODO ugly. can this done be better?
// PumpOnTime int body += "<b>Sensor configuration</b><br>";
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 // MoistureSensor_Type byte
body += "Soilmoisture sensor: <select id='SelMoistureSensor_Type' name='MoistureSensor_Type' onchange='MoistureSensorType();' required>\n"; body += "Soilmoisture sensor: <select id='SelMoistureSensor_Type' name='MoistureSensor_Type' onchange='MoistureSensorType();' required>\n";
@ -647,31 +709,33 @@ void WEBsystemSettings() {
body += "<option disabled value='' selected hidden>---</option>\n"; body += "<option disabled value='' selected hidden>---</option>\n";
} }
body += "<option value='1'" + returnStrSelected(MoistureSensor_Type, 1) + ">Analog capacitive</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 += "<option value='2'" + returnStrSelected(MoistureSensor_Type, 2) + ">I2C Chirp (0x20)</option>\n";
body += "</select><br>\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 // SoilmoistureDry byte
body += "Soilmoisture dry: <input type='number' id='iSoilmoistureDry' name='SoilmoistureDry' min='0' value='"; body += "Soilmoisture dry: <input type='number' id='iSoilmoistureDry' name='SoilmoistureDry' min='0' value='";
body += SoilmoistureDry; body += SoilmoistureDry;
body += "' required>\n"; body += "' required><br>\n";
body += "<p class='helpbox'><b>Analog capacitive:</b> <i>360</i><br> \
<b>I2C Chirp:</b> <i>250</i></p>";
// SoilmoistureWet byte
body += "Soilmoisture wet: <input type='number' id='iSoilmoistureWet' name='SoilmoistureWet' min='0' value='";
body += SoilmoistureWet;
body += "' required><br>\n";
if(configured == true) {
// refresh valSoilmoistureRaw to ensure we have fresh data
refreshSensors();
body += "Soilmoisture raw reading: <i><span id='iSoilmoistureRaw'>";
body += valSoilmoistureRaw;
body += "</span></i> <input type='button' class='button' value='&#x1F503; Refresh' style='padding: 3px;' onclick='SoilmoistureRefresh();'><br>\n";
body += FPSTR(HTMLsoilmoistureCalibrateText);
} else {
body += "<p class='helpbox'> <b>Calibration</b><br> \
You have to save settings first before you can calibrate the soilmoisture sensor.<p>";
}
// MoistureSensor_Type Javascript // MoistureSensor_Type Javascript
body += FPSTR(JSsoilmoistureTypeSelect); body += FPSTR(JSsoilmoisture);
// TemperatureSensor_Type byte // TemperatureSensor_Type byte
@ -679,9 +743,11 @@ void WEBsystemSettings() {
if(configured == false) { if(configured == false) {
body += "<option disabled value='' selected hidden>---</option>\n"; body += "<option disabled value='' selected hidden>---</option>\n";
} }
body += "<option value='1'" + returnStrSelected(TemperatureSensor_Type, 1) + ">I2C BME280</option>\n"; body += "<option value='1'" + returnStrSelected(TemperatureSensor_Type, 1) + ">I2C BME280 (0x76)</option>\n";
body += "<option value='2'" + returnStrSelected(TemperatureSensor_Type, 2) + ">I2C Chirp</option>\n"; body += "<option value='2'" + returnStrSelected(TemperatureSensor_Type, 2) + ">I2C BME280 (0x77)</option>\n";
body += "<option value='3'" + returnStrSelected(TemperatureSensor_Type, 3) + ">I2C SHT31</option>\n"; body += "<option value='3'" + returnStrSelected(TemperatureSensor_Type, 3) + ">I2C SHT31 (0x44)</option>\n";
body += "<option value='4'" + returnStrSelected(TemperatureSensor_Type, 4) + ">I2C SHT31 (0x45)</option>\n";
body += "<option value='5'" + returnStrSelected(TemperatureSensor_Type, 5) + ">I2C Chirp (0x20)</option>\n";
body += "</select><br>\n"; body += "</select><br>\n";
// HumiditySensor_Type byte // HumiditySensor_Type byte
@ -689,12 +755,18 @@ void WEBsystemSettings() {
if(configured == false) { if(configured == false) {
body += "<option disabled value='' selected hidden>---</option>\n"; body += "<option disabled value='' selected hidden>---</option>\n";
} }
body += "<option value='1'" + returnStrSelected(HumiditySensor_Type, 1) + ">I2C BME280</option>\n"; body += "<option value='1'" + returnStrSelected(HumiditySensor_Type, 1) + ">I2C BME280 (0x76)</option>\n";
body += "<option value='2'" + returnStrSelected(HumiditySensor_Type, 2) + ">I2C SHT31</option>\n"; body += "<option value='2'" + returnStrSelected(HumiditySensor_Type, 2) + ">I2C BME280 (0x77)</option>\n";
body += "</select><br>\n"; body += "<option value='3'" + returnStrSelected(HumiditySensor_Type, 3) + ">I2C SHT31 (0x44)</option>\n";
body += "<option value='4'" + returnStrSelected(HumiditySensor_Type, 4) + ">I2C SHT31 (0x45)</option>\n";
body += "</select><br><br>\n";
body += "<b>General configuration</b><br>";
// NtpOffset int // NtpOffset int
body += "NTP offset: <input class='inputShort' type='number' name='NtpOffset' min='-12' max='14' value='"; body += "NTP offset/UTC timezone: <input class='inputShort' type='number' name='NtpOffset' min='-12' max='14' value='";
body += NtpOffset; body += NtpOffset;
body+= "' required> Hours<br>\n"; body+= "' required> Hours<br>\n";
@ -710,12 +782,12 @@ void WEBsystemSettings() {
// DisplayScreenDuration byte // DisplayScreenDuration byte
body += "Display rotation interval: <input class='inputShort' type='number' name='DisplayScreenDuration' min='0' max='255' value='"; body += "Display rotation interval: <input class='inputShort' type='number' name='DisplayScreenDuration' min='0' max='255' value='";
body += DisplayScreenDuration; body += DisplayScreenDuration;
body += "' required> s<br>\n"; body += "' required> Seconds<br>\n";
body += "<p class='helpbox'><b>0</b> will always show sensor value screen</p>"; 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 += "ESP32-Cam IP (optional): <input type='text' name='Esp32CamIP' maxlength='16' value='";
body += Esp32CamIP; body += Esp32CamIP;
body += "' ><br>\n"; body += "' ><br><br>\n";
body += "<input type='submit' value='&#x1F4BE; Save settings'>\n"; body += "<input type='submit' value='&#x1F4BE; Save settings'>\n";
@ -784,7 +856,7 @@ void WEBwifiSettings() {
body += "IP: <input type='text' name='WIFIip'><br>\n"; body += "IP: <input type='text' name='WIFIip'><br>\n";
body += "Subnet mask: <input type='text' name='WIFInetmask'><br>\n"; body += "Subnet mask: <input type='text' name='WIFInetmask'><br>\n";
body += "Gateway: <input type='text' name='WIFIgateway'><br>\n"; body += "Gateway: <input type='text' name='WIFIgateway'><br>\n";
body += "DNS: <input type='text' name='WIFIdns'><br>\n"; body += "DNS: <input type='text' name='WIFIdns'><br><br>\n";
body += "<input type='submit' value='&#x1F4BE; Save settings'>\n"; body += "<input type='submit' value='&#x1F4BE; Save settings'>\n";
body += "</form>\n"; body += "</form>\n";
body += FPSTR(HTMLfooter); body += FPSTR(HTMLfooter);
@ -824,6 +896,11 @@ void POSTgrowSettings() {
SunriseMinute = webserver.arg("SunriseMinute").toInt(); SunriseMinute = webserver.arg("SunriseMinute").toInt();
SunFade = webserver.arg("SunFade").toInt(); SunFade = webserver.arg("SunFade").toInt();
SunFadeDuration = webserver.arg("SunFadeDuration").toInt(); SunFadeDuration = webserver.arg("SunFadeDuration").toInt();
UsePump = webserver.arg("UsePump").toInt();
PumpOnTime = webserver.arg("PumpOnTime").toInt();
SoilmoistureLow = webserver.arg("SoilmoistureLow").toInt();
PumpIntervalVeg = webserver.arg("PumpIntervalVeg").toInt(); PumpIntervalVeg = webserver.arg("PumpIntervalVeg").toInt();
PumpIntervalBloom = webserver.arg("PumpIntervalBloom").toInt(); PumpIntervalBloom = webserver.arg("PumpIntervalBloom").toInt();
@ -851,6 +928,15 @@ void POSTgrowSettings() {
EEPROM.put(248, PinFAN2PWM); EEPROM.put(248, PinFAN2PWM);
EEPROM.put(217, SunFade); EEPROM.put(217, SunFade);
EEPROM.put(218, SunFadeDuration); EEPROM.put(218, SunFadeDuration);
// size is 1 byte
EEPROM.put(163, UsePump);
// size is 1 byte
EEPROM.put(164, PumpOnTime);
// size is 1 byte
EEPROM.put(166, SoilmoistureLow);
// size is 1 byte // size is 1 byte
EEPROM.put(241, PumpIntervalVeg); EEPROM.put(241, PumpIntervalVeg);
// size is 1 byte // size is 1 byte
@ -888,6 +974,20 @@ void POSTgrowSettings() {
Serial.print("PinFAN2PWM: "); Serial.print("PinFAN2PWM: ");
Serial.println(PinFAN2PWM); Serial.println(PinFAN2PWM);
Serial.print("UsePump: ");
Serial.println(UsePump);
Serial.print("PumpOnTime: ");
Serial.println(PumpOnTime);
Serial.print("SoilmoistureLow: ");
Serial.println(SoilmoistureLow);
Serial.print("PumpIntervalVeg: ");
Serial.println(PumpIntervalVeg);
Serial.print("PumpIntervalBloom: ");
Serial.println(PumpIntervalBloom);
webserver.sendHeader("Location", String("/growSettings?success"), true); webserver.sendHeader("Location", String("/growSettings?success"), true);
webserver.send(302, "text/plain", "growSettings/save: success!\n"); webserver.send(302, "text/plain", "growSettings/save: success!\n");
} }
@ -901,9 +1001,8 @@ void POSTsystemSettings() {
NtpOffset = webserver.arg("NtpOffset").toInt(); NtpOffset = webserver.arg("NtpOffset").toInt();
MoistureSensor_Type = webserver.arg("MoistureSensor_Type").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(); UseFan = webserver.arg("UseFan").toInt();
UseLEDrelais = webserver.arg("UseLEDrelais").toInt(); UseLEDrelais = webserver.arg("UseLEDrelais").toInt();
UseFANrelais = webserver.arg("UseFANrelais").toInt(); UseFANrelais = webserver.arg("UseFANrelais").toInt();
@ -921,23 +1020,17 @@ void POSTsystemSettings() {
// when configured is false, set it to true and ensure outputs are set // when configured is false, set it to true and ensure outputs are set
if(configured == false) { if(configured == false) {
configured = true; configured = true;
pinMode(PinLED, OUTPUT); initOutputs();
pinMode(PinPUMP, OUTPUT);
pinMode(PinFAN, OUTPUT);
} }
// size is 1 byte // size is 1 byte
EEPROM.put(161, configured); EEPROM.put(161, configured);
// size is 1 byte // size is 1 byte
EEPROM.put(162, UseFan); EEPROM.put(162, UseFan);
// size is 1 byte
EEPROM.put(163, UsePump);
// size is 1 byte
EEPROM.put(164, PumpOnTime);
// size is 1 byte // size is 1 byte
EEPROM.put(165, MoistureSensor_Type); EEPROM.put(165, MoistureSensor_Type);
// size is 1 byte
EEPROM.put(166, SoilmoistureLow);
// size is 2 byte // size is 2 byte
EEPROM.put(167, NtpOffset); EEPROM.put(167, NtpOffset);
// size is 1 byte // size is 1 byte
@ -991,14 +1084,10 @@ void POSTsystemSettings() {
Serial.println(configured); Serial.println(configured);
Serial.print("UseFan: "); Serial.print("UseFan: ");
Serial.println(UseFan); Serial.println(UseFan);
Serial.print("UsePump: ");
Serial.println(UsePump);
Serial.print("PumpOnTime: ");
Serial.println(PumpOnTime);
Serial.print("MoistureSensor_Type: "); Serial.print("MoistureSensor_Type: ");
Serial.println(MoistureSensor_Type); Serial.println(MoistureSensor_Type);
Serial.print("SoilmoistureLow: ");
Serial.println(SoilmoistureLow);
Serial.print("NtpOffset: "); Serial.print("NtpOffset: ");
Serial.println(NtpOffset); Serial.println(NtpOffset);
Serial.print("UseLEDrelais: "); Serial.print("UseLEDrelais: ");
@ -1146,9 +1235,11 @@ void APIgetSensors() {
jsonSensors["soilmoisture"] = valSoilmoisture; jsonSensors["soilmoisture"] = valSoilmoisture;
jsonSensors["soilmoistureAvg"] = valSoilmoistureAvg; jsonSensors["soilmoistureAvg"] = valSoilmoistureAvg;
jsonSensors["soilmoistureRaw"] = valSoilmoistureRaw;
jsonSensors["temperature"] = valTemperature; jsonSensors["temperature"] = valTemperature;
jsonSensors["humidity"] = valHumidity; jsonSensors["humidity"] = valHumidity;
jsonSensors["waterlevel"] = valWaterlevel; jsonSensors["waterlevel"] = valWaterlevel;
jsonSensors["vpd"] = valVPD;
String body; String body;
serializeJsonPretty(jsonSensors, body); serializeJsonPretty(jsonSensors, body);
@ -1164,9 +1255,21 @@ void APIgetDebug() {
JsonObject objRuntime = jsonDebug["runtime"].add<JsonObject>(); JsonObject objRuntime = jsonDebug["runtime"].add<JsonObject>();
objRuntime["PumpOnTimePassed"] = PumpOnTimePassed; objRuntime["PumpOnTimePassed"] = PumpOnTimePassed;
objRuntime["PumpOnManual"] = PumpOnManual; objRuntime["PumpOnManual"] = PumpOnManual;
objRuntime["valTemperature"] = valTemperature;
objRuntime["valHumidity"] = valHumidity;
objRuntime["valSoilmoisture"] = valSoilmoisture;
objRuntime["valSoilmoistureRaw"] = valSoilmoistureRaw;
objRuntime["valSoilmoistureAvg"] = valSoilmoistureAvg; objRuntime["valSoilmoistureAvg"] = valSoilmoistureAvg;
objRuntime["valSoilmoistureAvg_tmp"] = valSoilmoistureAvg_tmp; objRuntime["valSoilmoistureAvg_tmp"] = valSoilmoistureAvg_tmp;
objRuntime["valSoilmoistureAvg_count"] = valSoilmoistureAvg_count; objRuntime["valSoilmoistureAvg_count"] = valSoilmoistureAvg_count;
objRuntime["NeedRestart"] = NeedRestart;
objRuntime["FirstRun"] = FirstRun;
objRuntime["ScreenToDisplay"] = ScreenToDisplay;
objRuntime["ScreenIterationPassed"] = ScreenIterationPassed;
objRuntime["DayNight"] = DayNight;
objRuntime["MaintenanceMode"] = MaintenanceMode;
objRuntime["MaintenanceStarted"] = MaintenanceStarted;
// WiFi // WiFi
JsonObject objWiFi = jsonDebug["wifi"].add<JsonObject>(); JsonObject objWiFi = jsonDebug["wifi"].add<JsonObject>();
@ -1191,6 +1294,13 @@ void APIgetDebug() {
objSystem["MaintenanceDuration"] = MaintenanceDuration; objSystem["MaintenanceDuration"] = MaintenanceDuration;
objSystem["PumpOnTime"] = PumpOnTime; objSystem["PumpOnTime"] = PumpOnTime;
objSystem["PumpLastOn"] = PumpLastOn; objSystem["PumpLastOn"] = PumpLastOn;
objSystem["OutputInvert"] = OutputInvert;
objSystem["SoilmoistureWet"] = SoilmoistureWet;
objSystem["SoilmoistureDry"] = SoilmoistureDry;
objSystem["HumiditySensor_Type"] = HumiditySensor_Type;
objSystem["PWMFrequency"] = PWMFrequency;
objSystem["DisplayScreenDuration"] = DisplayScreenDuration;
objSystem["Esp32CamIP"] = Esp32CamIP;
// Grow // Grow
JsonObject objGrow = jsonDebug["grow"].add<JsonObject>(); JsonObject objGrow = jsonDebug["grow"].add<JsonObject>();
@ -1207,25 +1317,42 @@ void APIgetDebug() {
objGrow["SunFadeDuration"] = SunFadeDuration; objGrow["SunFadeDuration"] = SunFadeDuration;
objGrow["PinLEDPWM"] = PinLEDPWM; objGrow["PinLEDPWM"] = PinLEDPWM;
objGrow["PinFANPWM"] = PinFANPWM; objGrow["PinFANPWM"] = PinFANPWM;
objGrow["PinFAN2PWM"] = PinFAN2PWM;
objGrow["DayOfGrow"] = DayOfGrow; objGrow["DayOfGrow"] = DayOfGrow;
objGrow["PumpIntervalVeg"] = PumpIntervalVeg; objGrow["PumpIntervalVeg"] = PumpIntervalVeg;
objGrow["PumpIntervalBloom"] = PumpIntervalBloom; objGrow["PumpIntervalBloom"] = PumpIntervalBloom;
// Sensors // Sensors
JsonObject objSensors = jsonDebug["sensors"].add<JsonObject>(); JsonObject objSensors = jsonDebug["sensors"].add<JsonObject>();
// Chirp // Chirp
objSensors["chirp"]["temperature"] = getTemperature(2); objSensors["chirp"]["temperature"] = getTemperature(5);
objSensors["chirp"]["soilmoisture"] = getSoilmoisture(2); objSensors["chirp"]["soilmoisture"] = getSoilmoisture(2);
objSensors["chirp"]["soilmoistureRAW"] = getSoilmoisture(2, true); objSensors["chirp"]["soilmoistureRAW"] = getSoilmoisture(2, true);
objSensors["chirp"]["light"] = getLightchirp(); objSensors["chirp"]["light"] = getLightchirp();
// BME280 // BME280 0x76
objSensors["bme280"]["temperature"] = getTemperature(1); objSensors["bme280_0x76"]["temperature"] = getTemperature(1);
objSensors["bme280"]["humidity"] = getHumidity(1); objSensors["bme280_0x76"]["humidity"] = getHumidity(1);
objSensors["bme280"]["preassure"] = bme.readPressure() / 100.0F; //objSensors["bme280_0x76"]["preassure"] = bme.readPressure() / 100.0F;
objSensors["bme280"]["appAltitude"] = bme.readAltitude(SEALEVELPRESSURE_HPA); //objSensors["bme280_0x76"]["appAltitude"] = bme.readAltitude(SEALEVELPRESSURE_HPA);
// BME280 0x77
objSensors["bme280_0x77"]["temperature"] = getTemperature(2);
objSensors["bme280_0x77"]["humidity"] = getHumidity(2);
//objSensors["bme280_0x77"]["preassure"] = bme.readPressure() / 100.0F;
//objSensors["bme280_0x77"]["appAltitude"] = bme.readAltitude(SEALEVELPRESSURE_HPA);
// SHT31 0x44
objSensors["sht31_0x44"]["temperature"] = getTemperature(3);
objSensors["sht31_0x44"]["humidity"] = getHumidity(3);
// SHT31 0x45
objSensors["sht31_0x45"]["temperature"] = getTemperature(4);
objSensors["sht31_0x45"]["humidity"] = getHumidity(4);
// Analog // Analog
objSensors["analog"]["soilmoisture"] = getSoilmoisture(1); objSensors["analog"]["soilmoisture"] = getSoilmoisture(1);

Binary file not shown.

Before

Width:  |  Height:  |  Size: 291 KiB

After

Width:  |  Height:  |  Size: 372 KiB

@ -0,0 +1 @@
Subproject commit d4720987895bc49bdb38beb9d3e288de8bd59078

@ -0,0 +1 @@
Subproject commit 9412f3d1a2e334a3415d79df706dad02925488aa

@ -0,0 +1 @@
Subproject commit 5e34a20df62371150f7cb10330919e9393b884b1

@ -0,0 +1 @@
Subproject commit 78d6a130b2045a8b7b76616da81603796757ed47

@ -0,0 +1 @@
Subproject commit 7b2473b6b24ae340f41685b5f5b2b90ad896db04

File diff suppressed because it is too large Load diff

@ -0,0 +1 @@
Subproject commit 61684d4516839b579b81105be3447499c1417908

@ -0,0 +1 @@
Subproject commit a18e50dcea4ee17285d732d39e7bc559482d1d3d

View file

@ -3,7 +3,8 @@
test -z $TTY && TTY="/dev/ttyUSB0" test -z $TTY && TTY="/dev/ttyUSB0"
test -z $IP && IP="192.168.4.20" test -z $IP && IP="192.168.4.20"
test -z $VER && VER="0.1.0"
VER="$(grep "define CANGROW_VER" Arduino/CanGrow/CanGrow_Version.h | cut -d \" -f2 |sed -e 's/\"//g')"
BUILD="$(git rev-parse --short HEAD)-$(date '+%Y%m%d%H%M%S')" BUILD="$(git rev-parse --short HEAD)-$(date '+%Y%m%d%H%M%S')"
ACLI="$HOME/.local/bin/arduino-cli" ACLI="$HOME/.local/bin/arduino-cli"
@ -74,7 +75,8 @@ case $1 in
check_acli check_acli
echo ":: Building firmware $VER $BUILD, target dir: $(pwd)/build/" echo ":: Building firmware $VER $BUILD, target dir: $(pwd)/build/"
test -d build || mkdir build test -d build || mkdir build
${ACLI_CMD} --no-color compile -b ${BOARD} "Arduino/CanGrow/CanGrow.ino" --build-property "build.extra_flags=-DCANGROW_VER=\"${VER}\" -DCANGROW_BUILD=\"${BUILD}\"" --output-dir build/ || exit 1 ${ACLI_CMD} --no-color compile -b ${BOARD} "Arduino/CanGrow/CanGrow.ino" --build-property "build.extra_flags=-DCANGROW_BUILD=\"${BUILD}\"" --output-dir build/ || exit 1
#${ACLI_CMD} --no-color compile -b ${BOARD} "Arduino/CanGrow/CanGrow.ino" --build-property "build.extra_flags=-DCANGROW_VER=\"${VER}\" -DCANGROW_BUILD=\"${BUILD}\"" --output-dir build/ || exit 1
cp build/CanGrow.ino.bin build/CanGrow_v${VER}_${BUILD}.bin cp build/CanGrow.ino.bin build/CanGrow_v${VER}_${BUILD}.bin
;; ;;
u|upload) u|upload)

View file

@ -4,19 +4,25 @@
<head> <head>
<meta charset='UTF-8'> <meta charset='UTF-8'>
<meta name='viewport' content='width=device-width, initial-scale=1.0'> <meta name='viewport' content='width=device-width, initial-scale=1.0'>
<title>CanGrow - Ruderalis Indica</title> <title>CanGrow - Amnesia Haze</title>
<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='> <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> <style>
body { body {
color: #cae0d0; color: #cae0d0;
background-color: #1d211e; background-color: #1d211e;
font-family: helvetica; font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif;
} }
.center { .center {
width: 100%; width: 100%;
margin: auto; margin: auto;
}
.centered {
display: block;
margin-left: auto;
margin-right: auto;
} }
h1, h2, h3, h4, h5 { h1, h2, h3, h4, h5 {
@ -53,6 +59,12 @@ a:active {
width: 42px; width: 42px;
} }
.helpbox {
font-size: 0.8em;
margin-left: 15px;
margin-top: 5px;
margin-bottom: 5px;
}
.nav { .nav {
background: #333; background: #333;
width: 100%; width: 100%;
@ -63,9 +75,27 @@ a:active {
border-radius: 3px; border-radius: 3px;
} }
.subnav {
text-align: center;
display: table;
margin: auto;
margin-bottom: 10px;
padding: 0;
position: relative;
border-radius: 3px;
}
.nav li { .nav li {
display: inline-block; display: inline-block;
list-style: none; list-style: none;
border-radius: 3px;
}
.subnav li {
background: #026b45;
list-style: none;
border-radius: 3px;
margin-bottom: 3px;
} }
.nav li:first-of-type { .nav li:first-of-type {
@ -73,7 +103,7 @@ a:active {
border-top-left-radius: 3px; border-top-left-radius: 3px;
border-bottom-left-radius: 3px; border-bottom-left-radius: 3px;
} }
.nav li a , .nav span, .button, .button:link, input[type=button], input[type=submit], input[type=reset] { .nav li a, .nav span, .subnav li a, .subnav span, .button, .button:link, input[type=button], input[type=submit], input[type=reset] {
color: #ddd; color: #ddd;
display: block; display: block;
font-family: 'Lucida Sans Unicode', 'Lucida Grande', sans-serif; font-family: 'Lucida Sans Unicode', 'Lucida Grande', sans-serif;
@ -83,13 +113,13 @@ a:active {
text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.5); text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.5);
} }
.nav li a:hover , .activeMenu, .button:link:hover, .button:visited:hover, input[type=button]:hover, input[type=submit]:hover, input[type=reset]:hover { .nav li a:hover, .subnav li a:hover, .activeMenu, .button:link:hover, .button:visited:hover, input[type=button]:hover, input[type=submit]:hover, input[type=reset]:hover {
background: #04AA6D; background: #04AA6D;
color: #fff; color: #fff;
border-radius: 3px; border-radius: 3px;
} }
.nav li a:active { .nav li a:active, .subnav li a:active {
background: #026b45; background: #026b45;
color: #cae0d0; color: #cae0d0;
} }
@ -125,73 +155,151 @@ input[type=text], input[type=date], input[type=number], input[type=password], se
border-radius: 3px; border-radius: 3px;
} }
@media only screen and (min-width: 1280px) { @media only screen and (min-width: 1820px) {
.center, .nav { .center, .nav {
width: 60%; min-width: 420px; width: 60%; min-width: 420px;
} }
.subnav li {
display: '';
margin-bottom: 3px;
}
} }
@media only screen and (min-width: 640px) {
.subnav li {
display: inline-block;
margin-bottom: 3px;
}
}
/* VPD colors */
.vpd_danger1 {
color: #1a6c9c;
}
.vpd_earlyveg {
color: #22ab9c;
}
.vpd_lateveg {
color: #9cc55b;
}
.vpd_latebloom {
color: #9cc55b;
}
.vpd_danger2 {
color: #1a6c9c;
}
</style> </style>
</head> </head>
<body> <body>
<ul class='nav'><li><a href='/'>&#x1F331; Ruderalis Indica</a></li> <ul class='nav'><li><a href='/'>&#x1F331; Amnesiaaaa Haze</a></li>
<li><a href='/growSettings' >&#128262; Grow settings</a></li> <li><a href='/growSettings' >&#128262; Grow settings</a></li>
<li><a href='/systemSettings' class='activeMenu'>&#9881; System settings</a></li> <li><a href='/systemSettings' class='activeMenu'>&#9881; System settings</a></li>
<li><a href='/wifiSettings' >&#128225; WiFi settings</a></li> <li><a href='/wifiSettings' >&#128225; WiFi settings</a></li>
<li><a href='/help' >&#x2753; Help</a></li> <li><a href='/help' >&#x2753; Help</a></li>
<li><span class='MenuTime'>00:03:39</span></li> <li><span class='MenuTime'>00:23:10</span></li>
<li><a href='https://git.la10cy.net/DeltaLima/CanGrow' target='_blank'>CanGrow v0.1</a></li> <li><a href='https://git.la10cy.net/DeltaLima/CanGrow' target='_blank'>CanGrow v0.1.3-dev</a></li>
</ul><div class='center'><h2>&#9881; System settings</h2><p>here you can set which features and sensors you use<br></p><form method='post' action='/systemSettings/save'> </ul><div class='center'><h2>&#9881; System settings</h2>
<table> <ul class='subnav'>
<tr> <li><a href='/system/update'>&#x1F504; Firmware update</a></li>
<td>Use FAN: </td> <li><a href='/system/restart' >&#x1F501; CanGrow restart</a></li>
<td> <li><a href='/system/wipe' >&#x1F4A3; Factory reset</a></li>
<select id='UseFan' name='UseFan' required> </ul>
<option value='1' selected >Yes</option> <p>here you can set which features and sensors you use<br></p><form method='post' action='/systemSettings/save'>
<option value='0'>No</option> <b>Output configuration</b><br>Invert Outputs: <select id='OutputInvert' name='OutputInvert' required>
</select> <option value='0'>No</option>
</td> <option value='1' selected >Yes</option>
</tr> </select><br>
<tr> <p class='helpbox'>When using CanGrow PCB v0.6, set to <b>Yes</b></p>
<td>Use PUMP: </td> Use relais for LED (disable PWM): <select id='UseLEDrelais' name='UseLEDrelais' required>
<td><select id='UsePump' name='UsePump' required> <option value='0' selected >No</option>
<option value='1' selected >Yes</option> <option value='1'>Yes</option>
<option value='0'>No</option> </select><br>
</select> Use relais for FAN1 (disable PWM): <select id='UseFANrelais' name='UseFANrelais' required>
</td> <option value='0'>No</option>
</tr> <option value='1' selected >Yes</option>
</select><br><br>
<b>Sensor configuration</b><br>Soilmoisture sensor: <select id='SelMoistureSensor_Type' name='MoistureSensor_Type' onchange='MoistureSensorType();' required>
<option value='1' selected >Analog capacitive</option>
<option value='2'>I2C Chirp (0x20)</option>
</select><br>
Soilmoisture dry: <input type='number' id='iSoilmoistureDry' name='SoilmoistureDry' min='0' value='360' required><br>
Soilmoisture wet: <input type='number' id='iSoilmoistureWet' name='SoilmoistureWet' min='0' value='160' required><br>
Soilmoisture raw reading: <i><span id='iSoilmoistureRaw'>123</span></i> <input type='button' class='button' value='&#x1F503; Refresh' style='padding: 3px;' onclick='SoilmoistureRefresh();'>
<p class='helpbox'>
<b>Calibration</b><br>
Put your soilmoisture sensor into dry soil and hit Refresh.<br>
Adjust the value of 'Soilmoisture dry' if needed according to the reading.<br>
Repeat this with wet soil for 'Soilmoisture wet'.
</p>
<tr> <script>
<td>Use relais for LED:
</td>
<td>
<select id='UseLEDrelais' name='UseLEDrelais' required>
<option value='1'>Yes</option>
<option value='0' selected >No</option>
</select>
</td>
</tr>
<tr>
<td></td>Use relais for FAN: </td>
<select id='UseFANrelais' name='UseFANrelais' required>
<option value='1'>Yes</option>
<option value='0' selected >No</option>
</select>
</tr>
PUMP ON time: <input class='inputShort' type='number' name='PumpOnTime' min='0' max='255' value='3' required> Seconds<br>
Soilmoisture sensor: <select id='MoistureSensor_Type' name='MoistureSensor_Type' required>
<option value='1' selected >Analog capacitive</option>
<option value='2'>I2C chirp</option>
</select><br>
Soilmoisture low: <input class='inputShort' type='number' name='SoilmoistureLow' min='0' value='20' required> %<br>
Temperature sensor: <select id='TemperatureSensor_Type' name='TemperatureSensor_Type' required>
<option value='1' selected >DHT11/22</option>
<option value='2'>I2C chirp</option>
</select><br>
NTP offset: <input class='inputShort' type='number' name='NtpOffset' min='-12' max='14' value='2' required> Hours<br>
Maintenance Duration: <input class='inputShort' type='number' name='MaintenanceDuration' min='0' max='900' value='300' required> Seconds<br>
<input type='submit' value='Save'>
</table> function loadJSON(callback) {
var xobj = new XMLHttpRequest();
xobj.overrideMimeType("application/json");
xobj.open('GET', '/api/sensors', true);
xobj.onreadystatechange = function() {
if (xobj.readyState == 4 && xobj.status == "200") {
callback(xobj.responseText);
}
}
xobj.send(null);
}
function SoilmoistureRefresh() {
loadJSON(function(response) {
json = JSON.parse(response);
document.getElementById('iSoilmoistureRaw').textContent = json.soilmoistureRaw;
console.log(json.soilmoistureRaw);
});
}
function MoistureSensorType() {
let selVal = document.getElementById('SelMoistureSensor_Type').value;
let wet = document.getElementById('iSoilmoistureWet');
let dry = document.getElementById('iSoilmoistureDry');
switch(selVal) {
case '1':
wet.value = 160;
dry.value = 360;
console.log(selVal);
break;
case '2':
wet.value = 485;
dry.value = 250;
console.log(selVal);
break;
default:
wet.value = 0;
dry.value = 0;
console.log(selVal);
break;
}
}
</script>
Temperature sensor: <select id='TemperatureSensor_Type' name='TemperatureSensor_Type' required>
<option value='1' selected >I2C BME280 (0x76)</option>
<option value='2'>I2C BME280 (0x77)</option>
<option value='3'>I2C SHT31 (0x44)</option>
<option value='4'>I2C SHT31 (0x45)</option>
<option value='5'>I2C Chirp (0x20)</option>
</select><br>
Humidity sensor: <select id='HumiditySensor_Type' name='HumiditySensor_Type' required>
<option value='1' selected >I2C BME280 (0x76)</option>
<option value='2'>I2C BME280 (0x77)</option>
<option value='3'>I2C SHT31 (0x44)</option>
<option value='4'>I2C SHT31 (0x45)</option>
</select><br><br>
<b>General configuration</b><br>NTP offset/UTC timezone: <input class='inputShort' type='number' name='NtpOffset' min='-12' max='14' value='1' required> Hours<br>
Maintenance Duration: <input class='inputShort' type='number' name='MaintenanceDuration' min='0' max='900' value='300' required> Seconds<br>
PWM Frequency: <input type='number' name='PWMFrequency' min='0' max='20000' value='13370' required> Hz<br>
Display rotation interval: <input class='inputShort' type='number' name='DisplayScreenDuration' min='0' max='255' value='3' required> Seconds<br>
<p class='helpbox'><b>0</b> will always show sensor value screen</p>ESP32-Cam IP (optional): <input type='text' name='Esp32CamIP' maxlength='16' value='' ><br><br>
<input type='submit' value='&#x1F4BE; Save settings'>
</form> </form>
</div> </div>