PCB lives now in its own git repo https://git.la10cy.net/DeltaLima/CanGrow-12V-PCB
855 lines
29 KiB
C
855 lines
29 KiB
C
/*
|
|
*
|
|
* include/CanGrow_LittleFS.h - LittleFS handling header file
|
|
*
|
|
*
|
|
*
|
|
*/
|
|
|
|
|
|
// LittleFS auto format
|
|
#define FORMAT_LITTLEFS_IF_FAILED true
|
|
|
|
void LFS_Init() {
|
|
const static char LogLoc[] PROGMEM = "[LittleFS:Init]";
|
|
Log.notice(F("%s" CR), LogLoc);
|
|
// ESP8266 crashes with first argument set
|
|
#ifdef ESP8266
|
|
if(!LittleFS.begin()) {
|
|
#endif
|
|
// ESP32 works, do autoformat if mount fails
|
|
#ifdef ESP32
|
|
if(!LittleFS.begin(FORMAT_LITTLEFS_IF_FAILED)) {
|
|
#endif
|
|
|
|
Log.notice(F("%s FAILED initializing. You have to format LittleFS manually. Will now restart." CR), LogLoc);
|
|
Restart();
|
|
}
|
|
}
|
|
|
|
void LFS_Format() {
|
|
const static char LogLoc[] PROGMEM = "[LittleFS:Format]";
|
|
Log.notice(F("%s formatting ..." CR), LogLoc);
|
|
// ESP32 LittleFS needs begin() first, otherwise it would crash
|
|
// ESP8266 does not need it, so we leave it
|
|
#ifdef ESP32
|
|
LittleFS.begin();
|
|
#endif
|
|
if(LittleFS.format()) {
|
|
Log.notice(F("%s done!" CR), LogLoc);
|
|
} else {
|
|
Log.error(F("%s FAILED" CR), LogLoc);
|
|
}
|
|
}
|
|
|
|
bool existFile(const char *path) {
|
|
const static char LogLoc[] PROGMEM = "[LittleFS]";
|
|
#ifdef ESP8266
|
|
File file = LittleFS.open(path, "r");
|
|
#endif
|
|
|
|
#ifdef ESP32
|
|
fs::FS &fs = LittleFS;
|
|
File file = fs.open(path);
|
|
#endif
|
|
|
|
if (!file) {
|
|
Log.notice(F("%s file exists: %s" CR), LogLoc, path);
|
|
file.close();
|
|
return false;
|
|
} else {
|
|
Log.warning(F("%s file does not exist: %s" CR), LogLoc, path);
|
|
file.close();
|
|
return true;
|
|
}
|
|
}
|
|
|
|
String readFile(const char *path) {
|
|
const static char LogLoc[] PROGMEM = "[LittleFS]";
|
|
String fileContent;
|
|
|
|
#ifdef ESP8266
|
|
File file = LittleFS.open(path, "r");
|
|
#endif
|
|
|
|
#ifdef ESP32
|
|
fs::FS &fs = LittleFS;
|
|
File file = fs.open(path);
|
|
#endif
|
|
|
|
if (!file) {
|
|
Log.error(F("%s FAILED to open file for reading: %s" CR), LogLoc, path);
|
|
return String(F("ERROR CANNOT OPEN"));
|
|
}
|
|
|
|
Log.notice(F("%s file content: %s" CR), LogLoc, path);
|
|
Log.notice(F("%s ----------" CR), LogLoc);
|
|
while (file.available()) { Serial.write(file.read()); }
|
|
Log.notice(F("%s ----------" CR), LogLoc);
|
|
fileContent = file.readString();
|
|
file.close();
|
|
return fileContent;
|
|
}
|
|
|
|
void writeFile(const char *path, const char *message) {
|
|
const static char LogLoc[] PROGMEM = "[LittleFS]";
|
|
|
|
#ifdef ESP8266
|
|
File file = LittleFS.open(path, "w");
|
|
#endif
|
|
|
|
#ifdef ESP32
|
|
fs::FS &fs = LittleFS;
|
|
File file = fs.open(path, FILE_WRITE);
|
|
#endif
|
|
|
|
if (!file) {
|
|
Log.error(F("%s FAILED to open file for reading: %s" CR), LogLoc, path);
|
|
return;
|
|
}
|
|
if (file.print(message)) {
|
|
Log.notice(F("%s file written: %s" CR), LogLoc, path);
|
|
} else {
|
|
Log.error(F("%s writing file FAILED: %s" CR), LogLoc, path);
|
|
}
|
|
//delay(2000); // Make sure the CREATE and LASTWRITE times are different
|
|
file.close();
|
|
}
|
|
|
|
void deleteFile(const char *path) {
|
|
const static char LogLoc[] PROGMEM = "[LittleFS]";
|
|
|
|
#ifdef ESP32
|
|
fs::FS &fs = LittleFS;
|
|
File file = fs.open(path, FILE_WRITE);
|
|
#endif
|
|
|
|
Log.notice(F("%s deleting file: %s" CR), LogLoc, path);
|
|
#ifdef ESP8266
|
|
if (LittleFS.remove(path)) {
|
|
#endif
|
|
#ifdef ESP32
|
|
if (fs.remove(path)) {
|
|
#endif
|
|
Log.notice(F("%s deleted file: %s" CR), LogLoc, path);
|
|
} else {
|
|
Log.error(F("%s deleting file FAILED: %s" CR), LogLoc, path);
|
|
}
|
|
}
|
|
|
|
// https://arduinojson.org/v7/example/config/
|
|
// https://arduinojson.org/v7/assistant/
|
|
bool LoadConfig() {
|
|
const static char LogLoc[] PROGMEM = "[LittleFS:LoadConfig]";
|
|
#ifdef ESP8266
|
|
File file = LittleFS.open(CANGROW_CFG, "r");
|
|
#endif
|
|
|
|
#ifdef ESP32
|
|
fs::FS &fs = LittleFS;
|
|
File file = fs.open(CANGROW_CFG);
|
|
#endif
|
|
|
|
Log.notice(F("%s loading config from: %s" CR), LogLoc, CANGROW_CFG);
|
|
|
|
JsonDocument doc;
|
|
// Deserialize the JSON document
|
|
DeserializationError error = deserializeJson(doc, file);
|
|
if(error) {
|
|
Log.error(F("%s FAILED to load config: %s" CR), LogLoc, CANGROW_CFG);
|
|
if (existFile(CANGROW_CFG)) {
|
|
readFile(CANGROW_CFG);
|
|
}
|
|
return false;
|
|
}
|
|
|
|
/*
|
|
* put json values into config structs
|
|
*/
|
|
|
|
// Copy strings from the JsonDocument to the Config struct as char
|
|
|
|
strlcpy(config.test, doc["test"], sizeof(config.test));
|
|
|
|
/* WiFi */
|
|
JsonObject objWifi = doc["wifi"][0];
|
|
if(objWifi.containsKey("ssid"))
|
|
strlcpy(config.wifi.ssid, objWifi["ssid"], sizeof(config.wifi.ssid));
|
|
if(objWifi.containsKey("password"))
|
|
strlcpy(config.wifi.password, objWifi["password"], sizeof(config.wifi.password));
|
|
// Copy bool / int directly into struct
|
|
if(objWifi.containsKey("dhcp"))
|
|
config.wifi.dhcp = objWifi["dhcp"];
|
|
// load the ip addresses as array
|
|
if(objWifi.containsKey("ip")) {
|
|
for(byte i=0; i < 4 ; i++) {
|
|
config.wifi.ip[i] = objWifi["ip"][i];
|
|
config.wifi.netmask[i] = objWifi["netmask"][i];
|
|
config.wifi.gateway[i] = objWifi["gateway"][i];
|
|
config.wifi.dns[i] = objWifi["dns"][i];
|
|
}
|
|
}
|
|
|
|
|
|
/* System */
|
|
JsonObject objSystem = doc["system"][0];
|
|
if(objSystem.containsKey("ntpOffset"))
|
|
config.system.ntpOffset = objSystem["ntpOffset"];
|
|
if(objSystem.containsKey("maintenanceDuration"))
|
|
config.system.maintenanceDuration = objSystem["maintenanceDuration"];
|
|
if(objSystem.containsKey("esp32cam"))
|
|
strlcpy(config.system.esp32cam, objSystem["esp32cam"], sizeof(config.system.esp32cam));
|
|
if(objSystem.containsKey("httpUser"))
|
|
strlcpy(config.system.httpUser, objSystem["httpUser"], sizeof(config.system.httpUser));
|
|
if(objSystem.containsKey("httpPass"))
|
|
strlcpy(config.system.httpPass, objSystem["httpPass"], sizeof(config.system.httpPass));
|
|
if(objSystem.containsKey("httpLogSerial"))
|
|
config.system.httpLogSerial = objSystem["httpLogSerial"];
|
|
|
|
if(objSystem.containsKey("schedulerInterval"))
|
|
config.system.schedulerInterval = objSystem["schedulerInterval"];
|
|
|
|
if(objSystem.containsKey("ntp"))
|
|
config.system.ntp = objSystem["ntp"];
|
|
if(objSystem.containsKey("rtc"))
|
|
config.system.rtc = objSystem["rtc"];
|
|
if(objSystem.containsKey("time2fs"))
|
|
config.system.time2fs = objSystem["time2fs"];
|
|
if(objSystem.containsKey("pwmFreq"))
|
|
config.system.pwmFreq = objSystem["pwmFreq"];
|
|
|
|
/* System Outputs */
|
|
JsonObject objSystemOutput = objSystem["output"][0];
|
|
for(byte i=0; i < Max_Outputs; i++) {
|
|
if(objSystemOutput["type"][i] > 0) {
|
|
|
|
if(objSystemOutput.containsKey("type"))
|
|
config.system.output.type[i] = objSystemOutput["type"][i];
|
|
if(objSystemOutput.containsKey("device"))
|
|
config.system.output.device[i] = objSystemOutput["device"][i];
|
|
if(objSystemOutput.containsKey("name"))
|
|
strlcpy(config.system.output.name[i], objSystemOutput["name"][i], sizeof(config.system.output.name[i]));
|
|
if(objSystemOutput.containsKey("enabled"))
|
|
config.system.output.enabled[i] = objSystemOutput["enabled"][i];
|
|
// gpio
|
|
if(objSystemOutput.containsKey("gpio"))
|
|
config.system.output.gpio[i] = objSystemOutput["gpio"][i];
|
|
if(objSystemOutput.containsKey("invert"))
|
|
config.system.output.invert[i] = objSystemOutput["invert"][i];
|
|
if(objSystemOutput.containsKey("gpio_pwm"))
|
|
config.system.output.gpio_pwm[i] = objSystemOutput["gpio_pwm"][i];
|
|
// i2c type
|
|
if(objSystemOutput.containsKey("i2c_type"))
|
|
config.system.output.i2c_type[i] = objSystemOutput["i2c_type"][i];
|
|
// i2c addr
|
|
if(objSystemOutput.containsKey("i2c_addr"))
|
|
config.system.output.i2c_addr[i] = objSystemOutput["i2c_addr"][i];
|
|
// i2c port
|
|
if(objSystemOutput.containsKey("i2c_port"))
|
|
config.system.output.i2c_port[i] = objSystemOutput["i2c_port"][i];
|
|
// web
|
|
if(objSystemOutput.containsKey("webcall_host"))
|
|
strlcpy(config.system.output.webcall_host[i], objSystemOutput["webcall_host"][i], sizeof(config.system.output.webcall_host[i]));
|
|
if(objSystemOutput.containsKey("webcall_path_on"))
|
|
strlcpy(config.system.output.webcall_path_on[i], objSystemOutput["webcall_path_on"][i], sizeof(config.system.output.webcall_path_on[i]));
|
|
if(objSystemOutput.containsKey("webcall_path_off"))
|
|
strlcpy(config.system.output.webcall_path_off[i], objSystemOutput["webcall_path_off"][i], sizeof(config.system.output.webcall_path_off[i]));
|
|
}
|
|
}
|
|
|
|
|
|
/* System Sensors */
|
|
JsonObject objSystemSensor = objSystem["sensor"][0];
|
|
for(byte i=0; i < Max_Sensors; i++) {
|
|
if(objSystemSensor["type"][i] > 0) {
|
|
if(objSystemSensor.containsKey("type"))
|
|
config.system.sensor.type[i] = objSystemSensor["type"][i];
|
|
if(objSystemSensor.containsKey("name"))
|
|
strlcpy(config.system.sensor.name[i], objSystemSensor["name"][i], sizeof(config.system.sensor.name[i]));
|
|
if(objSystemSensor.containsKey("i2c_addr"))
|
|
//strlcpy(config.system.sensor.i2c_addr[i], objSystemSensor["i2c_addr"][i], sizeof(config.system.sensor.i2c_addr[i]));
|
|
config.system.sensor.i2c_addr[i] = objSystemSensor["i2c_addr"][i];
|
|
// gpio
|
|
if(objSystemSensor.containsKey("gpio")) {
|
|
for(byte j = 0; j < Max_Sensors_GPIO; j++) {
|
|
config.system.sensor.gpio[i][j] = objSystemSensor["gpio"][i][j];
|
|
}
|
|
}
|
|
|
|
// offset
|
|
if(objSystemSensor.containsKey("offset")) {
|
|
for(byte j = 0; j < Max_Sensors_Read; j++) {
|
|
config.system.sensor.offset[i][j] = objSystemSensor["offset"][i][j];
|
|
}
|
|
}
|
|
|
|
// low
|
|
if(objSystemSensor.containsKey("low")) {
|
|
for(byte j = 0; j < Max_Sensors_Read; j++) {
|
|
config.system.sensor.low[i][j] = objSystemSensor["low"][i][j];
|
|
}
|
|
}
|
|
|
|
// high
|
|
if(objSystemSensor.containsKey("high")) {
|
|
for(byte j = 0; j < Max_Sensors_Read; j++) {
|
|
config.system.sensor.high[i][j] = objSystemSensor["high"][i][j];
|
|
}
|
|
}
|
|
|
|
// rawConvert
|
|
if(objSystemSensor.containsKey("rawConvert")) {
|
|
for(byte j = 0; j < Max_Sensors_Read; j++) {
|
|
config.system.sensor.rawConvert[i][j] = objSystemSensor["rawConvert"][i][j];
|
|
}
|
|
}
|
|
|
|
}
|
|
}
|
|
|
|
|
|
/* Grow */
|
|
JsonObject objGrow = doc["grow"][0];
|
|
if(objGrow.containsKey("name"))
|
|
strlcpy(config.grow.name, objGrow["name"], sizeof(config.grow.name));
|
|
if(objGrow.containsKey("start"))
|
|
config.grow.start = objGrow["start"];
|
|
if(objGrow.containsKey("daysVeg"))
|
|
config.grow.daysVeg = objGrow["daysVeg"];
|
|
if(objGrow.containsKey("daysBloom"))
|
|
config.grow.daysBloom = objGrow["daysBloom"];
|
|
|
|
/* Grow Light */
|
|
JsonObject objLight = objGrow["light"][0];
|
|
for(byte i = 0; i < Max_Outputs; i++) {
|
|
/* get light.configured */
|
|
if(objLight.containsKey("configured"))
|
|
config.grow.light.configured[i] = objLight["configured"][i];
|
|
/* check if light is configured */
|
|
if(config.grow.light.configured[i] == true) {
|
|
/* get the rest of the config */
|
|
if(objLight.containsKey("output"))
|
|
config.grow.light.output[i] = objLight["output"][i];
|
|
if(objLight.containsKey("sunriseHourVeg"))
|
|
config.grow.light.sunriseHourVeg[i] = objLight["sunriseHourVeg"][i];
|
|
if(objLight.containsKey("sunriseMinuteVeg"))
|
|
config.grow.light.sunriseMinuteVeg[i] = objLight["sunriseMinuteVeg"][i];
|
|
if(objLight.containsKey("sunsetHourVeg"))
|
|
config.grow.light.sunsetHourVeg[i] = objLight["sunsetHourVeg"][i];
|
|
if(objLight.containsKey("sunsetMinuteVeg"))
|
|
config.grow.light.sunsetMinuteVeg[i] = objLight["sunsetMinuteVeg"][i];
|
|
if(objLight.containsKey("sunriseHourBloom"))
|
|
config.grow.light.sunriseHourBloom[i] = objLight["sunriseHourBloom"][i];
|
|
if(objLight.containsKey("sunriseMinuteBloom"))
|
|
config.grow.light.sunriseMinuteBloom[i] = objLight["sunriseMinuteBloom"][i];
|
|
if(objLight.containsKey("sunsetHourBloom"))
|
|
config.grow.light.sunsetHourBloom[i] = objLight["sunsetHourBloom"][i];
|
|
if(objLight.containsKey("sunsetMinuteBloom"))
|
|
config.grow.light.sunsetMinuteBloom[i] = objLight["sunsetMinuteBloom"][i];
|
|
|
|
if(objLight.containsKey("power"))
|
|
config.grow.light.power[i] = objLight["power"][i];
|
|
if(objLight.containsKey("fade"))
|
|
config.grow.light.fade[i] = objLight["fade"][i];
|
|
if(objLight.containsKey("fadeDuration"))
|
|
config.grow.light.fadeDuration[i] = objLight["fadeDuration"][i];
|
|
}
|
|
}
|
|
|
|
/* Grow Air */
|
|
JsonObject objAir = objGrow["air"][0];
|
|
for(byte i = 0; i < Max_Outputs; i++) {
|
|
/* get air.configured */
|
|
if(objAir.containsKey("configured"))
|
|
config.grow.air.configured[i] = objAir["configured"][i];
|
|
/* check if air is configured */
|
|
if(config.grow.air.configured[i] == true) {
|
|
/* get the rest of the config */
|
|
if(objAir.containsKey("output"))
|
|
config.grow.air.output[i] = objAir["output"][i];
|
|
if(objAir.containsKey("power"))
|
|
config.grow.air.power[i] = objAir["power"][i];
|
|
if(objAir.containsKey("controlSensor"))
|
|
config.grow.air.controlSensor[i] = objAir["controlSensor"][i];
|
|
if(objAir.containsKey("controlRead"))
|
|
config.grow.air.controlRead[i] = objAir["controlRead"][i];
|
|
if(objAir.containsKey("controlMode"))
|
|
config.grow.air.controlMode[i] = objAir["controlMode"][i];
|
|
if(objAir.containsKey("min"))
|
|
config.grow.air.min[i] = objAir["min"][i];
|
|
if(objAir.containsKey("max"))
|
|
config.grow.air.max[i] = objAir["max"][i];
|
|
}
|
|
}
|
|
|
|
/* Grow Water */
|
|
JsonObject objWater = objGrow["water"][0];
|
|
for(byte i = 0; i < Max_Outputs; i++) {
|
|
/* get air.configured */
|
|
if(objWater.containsKey("configured"))
|
|
config.grow.water.configured[i] = objWater["configured"][i];
|
|
/* check if air is configured */
|
|
if(config.grow.water.configured[i] == true) {
|
|
/* get the rest of the config */
|
|
if(objWater.containsKey("output"))
|
|
config.grow.water.output[i] = objWater["output"][i];
|
|
if(objWater.containsKey("onTime"))
|
|
config.grow.water.onTime[i] = objWater["onTime"][i];
|
|
if(objWater.containsKey("controlSensor"))
|
|
config.grow.water.controlSensor[i] = objWater["controlSensor"][i];
|
|
if(objWater.containsKey("controlRead"))
|
|
config.grow.water.controlRead[i] = objWater["controlRead"][i];
|
|
if(objWater.containsKey("controlMode"))
|
|
config.grow.water.controlMode[i] = objWater["controlMode"][i];
|
|
if(objWater.containsKey("min"))
|
|
config.grow.water.min[i] = objWater["min"][i];
|
|
if(objWater.containsKey("max"))
|
|
config.grow.water.max[i] = objWater["max"][i];
|
|
if(objWater.containsKey("interval"))
|
|
config.grow.water.interval[i] = objWater["interval"][i];
|
|
if(objWater.containsKey("intervalUnit"))
|
|
config.grow.water.intervalUnit[i] = objWater["intervalUnit"][i];
|
|
}
|
|
}
|
|
|
|
// Close the file (Curiously, File's destructor doesn't close the file)
|
|
file.close();
|
|
Log.notice(F("%s config successfully loaded" CR), LogLoc);
|
|
#ifdef DEBUG
|
|
Log.verbose(F("%s --- runtime config ---" CR), LogLoc);
|
|
serializeJsonPretty(doc, Serial);
|
|
// Json output does not end with NewLine
|
|
Serial.println("");
|
|
Log.verbose(F("%s ----------------------" CR), LogLoc);
|
|
#endif
|
|
return true;
|
|
}
|
|
|
|
bool SaveConfig(bool writeToSerial = false) {
|
|
const static char LogLoc[] PROGMEM = "[LittleFS:SaveConfig]";
|
|
/*
|
|
* Building config.json here
|
|
*/
|
|
JsonDocument doc;
|
|
|
|
/* Root */
|
|
doc["test"] = config.test;
|
|
|
|
/* WiFi */
|
|
JsonObject objWifi = doc["wifi"].add<JsonObject>();
|
|
objWifi["ssid"] = config.wifi.ssid;
|
|
objWifi["password"] = config.wifi.password;
|
|
// save the ip addressess as array
|
|
int i;
|
|
for(i=0; i <4 ; i++) {
|
|
objWifi["ip"][i] = config.wifi.ip[i];
|
|
objWifi["netmask"][i] = config.wifi.netmask[i];
|
|
objWifi["gateway"][i] = config.wifi.gateway[i];
|
|
objWifi["dns"][i] = config.wifi.dns[i];
|
|
}
|
|
objWifi["dhcp"] = config.wifi.dhcp;
|
|
|
|
/* System */
|
|
JsonObject objSystem = doc["system"].add<JsonObject>();
|
|
objSystem["ntpOffset"] = config.system.ntpOffset;
|
|
objSystem["maintenanceDuration"] = config.system.maintenanceDuration;
|
|
objSystem["esp32cam"] = config.system.esp32cam;
|
|
objSystem["httpUser"] = config.system.httpUser;
|
|
objSystem["httpPass"] = config.system.httpPass;
|
|
objSystem["httpLogSerial"] = config.system.httpLogSerial;
|
|
objSystem["schedulerInterval"] = config.system.schedulerInterval;
|
|
objSystem["ntp"] = config.system.ntp;
|
|
objSystem["rtc"] = config.system.rtc;
|
|
objSystem["time2fs"] = config.system.time2fs;
|
|
objSystem["pwmFreq"] = config.system.pwmFreq;
|
|
|
|
/* System Outputs */
|
|
JsonObject objSystemOutput = objSystem["output"].add<JsonObject>();
|
|
for(byte i=0; i < Max_Outputs; i++) {
|
|
if(config.system.output.type[i] > 0) {
|
|
objSystemOutput["type"][i] = config.system.output.type[i];
|
|
objSystemOutput["device"][i] = config.system.output.device[i];
|
|
objSystemOutput["name"][i] = config.system.output.name[i];
|
|
objSystemOutput["enabled"][i] = config.system.output.enabled[i];
|
|
// gpio
|
|
objSystemOutput["gpio"][i] = config.system.output.gpio[i];
|
|
objSystemOutput["invert"][i] = config.system.output.invert[i];
|
|
objSystemOutput["gpio_pwm"][i] = config.system.output.gpio_pwm[i];
|
|
// i2c type
|
|
objSystemOutput["i2c_type"][i] = config.system.output.i2c_type[i];
|
|
objSystemOutput["i2c_addr"][i] = config.system.output.i2c_addr[i];
|
|
objSystemOutput["i2c_port"][i] = config.system.output.i2c_port[i];
|
|
// web
|
|
objSystemOutput["webcall_host"][i] = config.system.output.webcall_host[i];
|
|
objSystemOutput["webcall_path_on"][i] = config.system.output.webcall_path_on[i];
|
|
objSystemOutput["webcall_path_off"][i] = config.system.output.webcall_path_off[i];
|
|
|
|
}
|
|
}
|
|
|
|
/* System Sensors */
|
|
JsonObject objSystemSensor = objSystem["sensor"].add<JsonObject>();
|
|
for(byte i=0; i < Max_Sensors; i++) {
|
|
if(config.system.sensor.type[i] > 0) {
|
|
objSystemSensor["type"][i] = config.system.sensor.type[i];
|
|
objSystemSensor["name"][i] = config.system.sensor.name[i];
|
|
objSystemSensor["i2c_addr"][i] = config.system.sensor.i2c_addr[i];
|
|
for(byte j = 0; j < Max_Sensors_GPIO; j++) {
|
|
objSystemSensor["gpio"][i][j] = config.system.sensor.gpio[i][j];
|
|
}
|
|
|
|
/* offset reading */
|
|
for(byte j = 0; j < Max_Sensors_Read; j++) {
|
|
objSystemSensor["offset"][i][j] = config.system.sensor.offset[i][j];
|
|
}
|
|
|
|
/* low reading */
|
|
for(byte j = 0; j < Max_Sensors_Read; j++) {
|
|
objSystemSensor["low"][i][j] = config.system.sensor.low[i][j];
|
|
}
|
|
|
|
/* high reading */
|
|
for(byte j = 0; j < Max_Sensors_Read; j++) {
|
|
objSystemSensor["high"][i][j] = config.system.sensor.high[i][j];
|
|
}
|
|
|
|
/* rawConvert reading */
|
|
for(byte j = 0; j < Max_Sensors_Read; j++) {
|
|
objSystemSensor["rawConvert"][i][j] = config.system.sensor.rawConvert[i][j];
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
/* Grow */
|
|
JsonObject objGrow = doc["grow"].add<JsonObject>();
|
|
objGrow["name"] = config.grow.name;
|
|
objGrow["start"] = config.grow.start;
|
|
objGrow["daysVeg"] = config.grow.daysVeg;
|
|
objGrow["daysBloom"] = config.grow.daysBloom;
|
|
|
|
/* Grow Light */
|
|
JsonObject objLight = objGrow["light"].add<JsonObject>();
|
|
for(byte i = 0; i < Max_Outputs; i++) {
|
|
#ifdef DEBUG
|
|
Log.verbose(F("%s LightId %d, Max_Outputs %d, light.configured %T" CR), LogLoc, i, Max_Outputs, config.grow.light.configured[i]);
|
|
#endif
|
|
if(config.grow.light.configured[i] == true) {
|
|
objLight["configured"][i] = config.grow.light.configured[i];
|
|
objLight["output"][i] = config.grow.light.output[i];
|
|
objLight["sunriseHourVeg"][i] = config.grow.light.sunriseHourVeg[i];
|
|
objLight["sunriseMinuteVeg"][i] = config.grow.light.sunriseMinuteVeg[i];
|
|
objLight["sunsetHourVeg"][i] = config.grow.light.sunsetHourVeg[i];
|
|
objLight["sunsetMinuteVeg"][i] = config.grow.light.sunsetMinuteVeg[i];
|
|
|
|
objLight["sunriseHourBloom"][i] = config.grow.light.sunriseHourBloom[i];
|
|
objLight["sunriseMinuteBloom"][i] = config.grow.light.sunriseMinuteBloom[i];
|
|
objLight["sunsetHourBloom"][i] = config.grow.light.sunsetHourBloom[i];
|
|
objLight["sunsetMinuteBloom"][i] = config.grow.light.sunsetMinuteBloom[i];
|
|
|
|
objLight["power"][i] = config.grow.light.power[i];
|
|
objLight["fade"][i] = config.grow.light.fade[i];
|
|
objLight["fadeDuration"][i] = config.grow.light.fadeDuration[i];
|
|
}
|
|
}
|
|
|
|
/* Grow Air */
|
|
JsonObject objAir = objGrow["air"].add<JsonObject>();
|
|
for(byte i = 0; i < Max_Outputs; i++) {
|
|
//Log.verbose(F("%s LightId %d, Max_Outputs %d, light.configured %T" CR), LogLoc, i, Max_Outputs, config.grow.light.configured[i]);
|
|
if(config.grow.air.configured[i] == true) {
|
|
objAir["configured"][i] = config.grow.air.configured[i];
|
|
objAir["output"][i] = config.grow.air.output[i];
|
|
objAir["power"][i] = config.grow.air.power[i];
|
|
objAir["controlSensor"][i] = config.grow.air.controlSensor[i];
|
|
objAir["controlRead"][i] = config.grow.air.controlRead[i];
|
|
objAir["controlMode"][i] = config.grow.air.controlMode[i];
|
|
objAir["min"][i] = config.grow.air.min[i];
|
|
objAir["max"][i] = config.grow.air.max[i];
|
|
|
|
}
|
|
}
|
|
|
|
/* Grow Water */
|
|
JsonObject objWater = objGrow["water"].add<JsonObject>();
|
|
for(byte i = 0; i < Max_Outputs; i++) {
|
|
//Log.verbose(F("%s LightId %d, Max_Outputs %d, light.configured %T" CR), LogLoc, i, Max_Outputs, config.grow.light.configured[i]);
|
|
if(config.grow.water.configured[i] == true) {
|
|
objWater["configured"][i] = config.grow.water.configured[i];
|
|
objWater["output"][i] = config.grow.water.output[i];
|
|
objWater["onTime"][i] = config.grow.water.onTime[i];
|
|
objWater["controlSensor"][i] = config.grow.water.controlSensor[i];
|
|
objWater["controlRead"][i] = config.grow.water.controlRead[i];
|
|
objWater["controlMode"][i] = config.grow.water.controlMode[i];
|
|
objWater["min"][i] = config.grow.water.min[i];
|
|
objWater["max"][i] = config.grow.water.max[i];
|
|
objWater["interval"][i] = config.grow.water.interval[i];
|
|
objWater["intervalUnit"][i] = config.grow.water.intervalUnit[i];
|
|
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
* END Building config.json here
|
|
*/
|
|
|
|
// if writeToSerial is true, output json to serial, but do not write to LittleFS
|
|
if(writeToSerial == false) {
|
|
#ifdef ESP8266
|
|
File file = LittleFS.open(CANGROW_CFG, "w");
|
|
#endif
|
|
|
|
#ifdef ESP32
|
|
fs::FS &fs = LittleFS;
|
|
File file = fs.open(CANGROW_CFG, FILE_WRITE);
|
|
#endif
|
|
|
|
if (!file) {
|
|
//Log.notice(F("%s loading config from: %s" CR), LogLoc, CANGROW_CFG);
|
|
Log.error(F("%s FAILED to open configfile for writing: %s" CR), LogLoc, CANGROW_CFG);
|
|
return false;
|
|
} else {
|
|
Log.notice(F("%s opened for writing %s" CR), LogLoc, CANGROW_CFG);
|
|
}
|
|
// Serialize JSON to file
|
|
if (serializeJson(doc, file) == 0) {
|
|
Log.error(F("%s FAILED to write configfile: %s" CR), LogLoc, CANGROW_CFG);
|
|
} else {
|
|
Log.notice(F("%s successfully written %s" CR), LogLoc, CANGROW_CFG);
|
|
}
|
|
file.close();
|
|
} else {
|
|
Log.notice(F("%s --- %s ---" CR), LogLoc, CANGROW_CFG);
|
|
serializeJsonPretty(doc, Serial);
|
|
Serial.println("");
|
|
Log.notice(F("%s ----------------------" CR), LogLoc, CANGROW_CFG);
|
|
}
|
|
|
|
/* every time config get saved, we save the actual time too
|
|
* so when ntp is not available, we hopefully do not lack behind too much
|
|
* (better then nothing) */
|
|
Time2FS_Save();
|
|
return true;
|
|
|
|
}
|
|
|
|
///*
|
|
//* ESP8266 functions
|
|
//*/
|
|
|
|
///*functions from https://github.com/esp8266/Arduino/blob/master/libraries/LittleFS/examples/LittleFS_Timestamp/LittleFS_Timestamp.ino*/
|
|
//#ifdef ESP8266
|
|
//void listDir(const char *dirname) {
|
|
//Serial.printf("Listing directory: %s\n", dirname);
|
|
|
|
//Dir root = LittleFS.openDir(dirname);
|
|
|
|
//while (root.next()) {
|
|
//File file = root.openFile("r");
|
|
//Serial.print(" FILE: ");
|
|
//Serial.print(root.fileName());
|
|
//Serial.print(" SIZE: ");
|
|
//Serial.print(file.size());
|
|
//time_t cr = file.getCreationTime();
|
|
//time_t lw = file.getLastWrite();
|
|
//file.close();
|
|
//struct tm *tmstruct = localtime(&cr);
|
|
//Serial.printf(" CREATION: %d-%02d-%02d %02d:%02d:%02d\n", (tmstruct->tm_year) + 1900, (tmstruct->tm_mon) + 1, tmstruct->tm_mday, tmstruct->tm_hour, tmstruct->tm_min, tmstruct->tm_sec);
|
|
//tmstruct = localtime(&lw);
|
|
//Serial.printf(" LAST WRITE: %d-%02d-%02d %02d:%02d:%02d\n", (tmstruct->tm_year) + 1900, (tmstruct->tm_mon) + 1, tmstruct->tm_mday, tmstruct->tm_hour, tmstruct->tm_min, tmstruct->tm_sec);
|
|
//}
|
|
//}
|
|
|
|
|
|
//void readFile(const char *path) {
|
|
//Serial.printf("Reading file: %s\n", path);
|
|
|
|
//File file = LittleFS.open(path, "r");
|
|
//if (!file) {
|
|
//Serial.println("Failed to open file for reading");
|
|
//return;
|
|
//}
|
|
|
|
//Serial.print("Read from file: ");
|
|
//while (file.available()) { Serial.write(file.read()); }
|
|
//file.close();
|
|
//}
|
|
|
|
//void writeFile(const char *path, const char *message) {
|
|
//Serial.printf("Writing file: %s\n", path);
|
|
|
|
//File file = LittleFS.open(path, "w");
|
|
//if (!file) {
|
|
//Serial.println("Failed to open file for writing");
|
|
//return;
|
|
//}
|
|
//if (file.print(message)) {
|
|
//Serial.println("File written");
|
|
//} else {
|
|
//Serial.println("Write failed");
|
|
//}
|
|
//delay(2000); // Make sure the CREATE and LASTWRITE times are different
|
|
//file.close();
|
|
//}
|
|
|
|
//void appendFile(const char *path, const char *message) {
|
|
//Serial.printf("Appending to file: %s\n", path);
|
|
|
|
//File file = LittleFS.open(path, "a");
|
|
//if (!file) {
|
|
//Serial.println("Failed to open file for appending");
|
|
//return;
|
|
//}
|
|
//if (file.print(message)) {
|
|
//Serial.println("Message appended");
|
|
//} else {
|
|
//Serial.println("Append failed");
|
|
//}
|
|
//file.close();
|
|
//}
|
|
|
|
//void renameFile(const char *path1, const char *path2) {
|
|
//Serial.printf("Renaming file %s to %s\n", path1, path2);
|
|
//if (LittleFS.rename(path1, path2)) {
|
|
//Serial.println("File renamed");
|
|
//} else {
|
|
//Serial.println("Rename failed");
|
|
//}
|
|
//}
|
|
|
|
//void deleteFile(const char *path) {
|
|
//Serial.printf("Deleting file: %s\n", path);
|
|
//if (LittleFS.remove(path)) {
|
|
//Serial.println("File deleted");
|
|
//} else {
|
|
//Serial.println("Delete failed");
|
|
//}
|
|
//}
|
|
//#endif
|
|
|
|
|
|
///*
|
|
//* ESP32 functions
|
|
//*/
|
|
|
|
///*functions from https://github.com/espressif/arduino-esp32/blob/master/libraries/LittleFS/examples/LITTLEFS_time/LITTLEFS_time.ino*/
|
|
//#ifdef ESP32
|
|
//void listDir(fs::FS &fs, const char *dirname, uint8_t levels) {
|
|
//Serial.printf("Listing directory: %s\n", dirname);
|
|
|
|
//File root = fs.open(dirname);
|
|
//if (!root) {
|
|
//Serial.println("Failed to open directory");
|
|
//return;
|
|
//}
|
|
//if (!root.isDirectory()) {
|
|
//Serial.println("Not a directory");
|
|
//return;
|
|
//}
|
|
|
|
//File file = root.openNextFile();
|
|
//while (file) {
|
|
//if (file.isDirectory()) {
|
|
//Serial.print(" DIR : ");
|
|
//Serial.print(file.name());
|
|
//time_t t = file.getLastWrite();
|
|
//struct tm *tmstruct = localtime(&t);
|
|
//Serial.printf(
|
|
//" LAST WRITE: %d-%02d-%02d %02d:%02d:%02d\n", (tmstruct->tm_year) + 1900, (tmstruct->tm_mon) + 1, tmstruct->tm_mday, tmstruct->tm_hour,
|
|
//tmstruct->tm_min, tmstruct->tm_sec
|
|
//);
|
|
//if (levels) {
|
|
//listDir(fs, file.path(), levels - 1);
|
|
//}
|
|
//} else {
|
|
//Serial.print(" FILE: ");
|
|
//Serial.print(file.name());
|
|
//Serial.print(" SIZE: ");
|
|
//Serial.print(file.size());
|
|
//time_t t = file.getLastWrite();
|
|
//struct tm *tmstruct = localtime(&t);
|
|
//Serial.printf(
|
|
//" LAST WRITE: %d-%02d-%02d %02d:%02d:%02d\n", (tmstruct->tm_year) + 1900, (tmstruct->tm_mon) + 1, tmstruct->tm_mday, tmstruct->tm_hour,
|
|
//tmstruct->tm_min, tmstruct->tm_sec
|
|
//);
|
|
//}
|
|
//file = root.openNextFile();
|
|
//}
|
|
//}
|
|
|
|
//void removeDir(fs::FS &fs, const char *path) {
|
|
//Serial.printf("Removing Dir: %s\n", path);
|
|
//if (fs.rmdir(path)) {
|
|
//Serial.println("Dir removed");
|
|
//} else {
|
|
//Serial.println("rmdir failed");
|
|
//}
|
|
//}
|
|
|
|
//void readFile(fs::FS &fs, const char *path) {
|
|
//Serial.printf("Reading file: %s\n", path);
|
|
|
|
//File file = fs.open(path);
|
|
//if (!file) {
|
|
//Serial.println("Failed to open file for reading");
|
|
//return;
|
|
//}
|
|
|
|
//Serial.print("Read from file: ");
|
|
//while (file.available()) {
|
|
//Serial.write(file.read());
|
|
//}
|
|
//file.close();
|
|
//}
|
|
|
|
//void writeFile(fs::FS &fs, const char *path, const char *message) {
|
|
//Serial.printf("Writing file: %s\n", path);
|
|
|
|
//File file = fs.open(path, FILE_WRITE);
|
|
//if (!file) {
|
|
//Serial.println("Failed to open file for writing");
|
|
//return;
|
|
//}
|
|
//if (file.print(message)) {
|
|
//Serial.println("File written");
|
|
//} else {
|
|
//Serial.println("Write failed");
|
|
//}
|
|
//file.close();
|
|
//}
|
|
|
|
//void appendFile(fs::FS &fs, const char *path, const char *message) {
|
|
//Serial.printf("Appending to file: %s\n", path);
|
|
|
|
//File file = fs.open(path, FILE_APPEND);
|
|
//if (!file) {
|
|
//Serial.println("Failed to open file for appending");
|
|
//return;
|
|
//}
|
|
//if (file.print(message)) {
|
|
//Serial.println("Message appended");
|
|
//} else {
|
|
//Serial.println("Append failed");
|
|
//}
|
|
//file.close();
|
|
//}
|
|
|
|
//void renameFile(fs::FS &fs, const char *path1, const char *path2) {
|
|
//Serial.printf("Renaming file %s to %s\n", path1, path2);
|
|
//if (fs.rename(path1, path2)) {
|
|
//Serial.println("File renamed");
|
|
//} else {
|
|
//Serial.println("Rename failed");
|
|
//}
|
|
//}
|
|
|
|
//void deleteFile(fs::FS &fs, const char *path) {
|
|
//Serial.printf("Deleting file: %s\n", path);
|
|
//if (fs.remove(path)) {
|
|
//Serial.println("File deleted");
|
|
//} else {
|
|
//Serial.println("Delete failed");
|
|
//}
|
|
//}
|
|
//#endif
|