Migrate config from binary blob to json
This commit is contained in:
parent
229f1dacf9
commit
d2feac7a00
@ -4,6 +4,7 @@
|
|||||||
#include <Arduino.h>
|
#include <Arduino.h>
|
||||||
|
|
||||||
#define CONFIG_FILENAME "/config.bin"
|
#define CONFIG_FILENAME "/config.bin"
|
||||||
|
#define CONFIG_FILENAME_JSON "/config.json"
|
||||||
#define CONFIG_VERSION 0x00011500 // 0.1.21 // make sure to clean all after change
|
#define CONFIG_VERSION 0x00011500 // 0.1.21 // make sure to clean all after change
|
||||||
|
|
||||||
#define WIFI_MAX_SSID_STRLEN 31
|
#define WIFI_MAX_SSID_STRLEN 31
|
||||||
@ -26,6 +27,8 @@
|
|||||||
#define INV_MAX_COUNT 10
|
#define INV_MAX_COUNT 10
|
||||||
#define INV_MAX_CHAN_COUNT 4
|
#define INV_MAX_CHAN_COUNT 4
|
||||||
|
|
||||||
|
#define JSON_BUFFER_SIZE 6144
|
||||||
|
|
||||||
struct INVERTER_CONFIG_T {
|
struct INVERTER_CONFIG_T {
|
||||||
uint64_t Serial;
|
uint64_t Serial;
|
||||||
char Name[INV_MAX_NAME_STRLEN + 1];
|
char Name[INV_MAX_NAME_STRLEN + 1];
|
||||||
@ -89,6 +92,9 @@ public:
|
|||||||
CONFIG_T& get();
|
CONFIG_T& get();
|
||||||
|
|
||||||
INVERTER_CONFIG_T* getFreeInverterSlot();
|
INVERTER_CONFIG_T* getFreeInverterSlot();
|
||||||
|
|
||||||
|
private:
|
||||||
|
bool readJson();
|
||||||
};
|
};
|
||||||
|
|
||||||
extern ConfigurationClass Configuration;
|
extern ConfigurationClass Configuration;
|
||||||
@ -4,6 +4,7 @@
|
|||||||
*/
|
*/
|
||||||
#include "Configuration.h"
|
#include "Configuration.h"
|
||||||
#include "defaults.h"
|
#include "defaults.h"
|
||||||
|
#include <ArduinoJson.h>
|
||||||
#include <LittleFS.h>
|
#include <LittleFS.h>
|
||||||
|
|
||||||
CONFIG_T config;
|
CONFIG_T config;
|
||||||
@ -61,21 +62,91 @@ void ConfigurationClass::init()
|
|||||||
|
|
||||||
bool ConfigurationClass::write()
|
bool ConfigurationClass::write()
|
||||||
{
|
{
|
||||||
File f = LittleFS.open(CONFIG_FILENAME, "w");
|
File f = LittleFS.open(CONFIG_FILENAME_JSON, "w");
|
||||||
if (!f) {
|
if (!f) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
config.Cfg_SaveCount++;
|
config.Cfg_SaveCount++;
|
||||||
uint8_t* bytes = reinterpret_cast<uint8_t*>(&config);
|
|
||||||
for (unsigned int i = 0; i < sizeof(CONFIG_T); i++) {
|
DynamicJsonDocument doc(JSON_BUFFER_SIZE);
|
||||||
f.write(bytes[i]);
|
|
||||||
|
JsonObject cfg = doc.createNestedObject("cfg");
|
||||||
|
cfg["version"] = config.Cfg_Version;
|
||||||
|
cfg["save_count"] = config.Cfg_SaveCount;
|
||||||
|
|
||||||
|
JsonObject wifi = doc.createNestedObject("wifi");
|
||||||
|
wifi["ssid"] = config.WiFi_Ssid;
|
||||||
|
wifi["password"] = config.WiFi_Password;
|
||||||
|
wifi["ip"] = IPAddress(config.WiFi_Ip).toString();
|
||||||
|
wifi["netmask"] = IPAddress(config.WiFi_Netmask).toString();
|
||||||
|
wifi["gateway"] = IPAddress(config.WiFi_Gateway).toString();
|
||||||
|
wifi["dns1"] = IPAddress(config.WiFi_Dns1).toString();
|
||||||
|
wifi["dns2"] = IPAddress(config.WiFi_Dns2).toString();
|
||||||
|
wifi["dhcp"] = config.WiFi_Dhcp;
|
||||||
|
wifi["hostname"] = config.WiFi_Hostname;
|
||||||
|
|
||||||
|
JsonObject ntp = doc.createNestedObject("ntp");
|
||||||
|
ntp["server"] = config.Ntp_Server;
|
||||||
|
ntp["timezone"] = config.Ntp_Timezone;
|
||||||
|
ntp["timezone_descr"] = config.Ntp_TimezoneDescr;
|
||||||
|
|
||||||
|
JsonObject mqtt = doc.createNestedObject("mqtt");
|
||||||
|
mqtt["enabled"] = config.Mqtt_Enabled;
|
||||||
|
mqtt["hostname"] = config.Mqtt_Hostname;
|
||||||
|
mqtt["port"] = config.Mqtt_Port;
|
||||||
|
mqtt["username"] = config.Mqtt_Username;
|
||||||
|
mqtt["password"] = config.Mqtt_Password;
|
||||||
|
mqtt["topic"] = config.Mqtt_Topic;
|
||||||
|
mqtt["retain"] = config.Mqtt_Retain;
|
||||||
|
mqtt["publish_invterval"] = config.Mqtt_PublishInterval;
|
||||||
|
|
||||||
|
JsonObject mqtt_lwt = mqtt.createNestedObject("lwt");
|
||||||
|
mqtt_lwt["topic"] = config.Mqtt_LwtTopic;
|
||||||
|
mqtt_lwt["value_online"] = config.Mqtt_LwtValue_Online;
|
||||||
|
mqtt_lwt["value_offline"] = config.Mqtt_LwtValue_Offline;
|
||||||
|
|
||||||
|
JsonObject mqtt_tls = mqtt.createNestedObject("tls");
|
||||||
|
mqtt_tls["enabled"] = config.Mqtt_Tls;
|
||||||
|
mqtt_tls["root_ca_cert"] = config.Mqtt_RootCaCert;
|
||||||
|
|
||||||
|
JsonObject mqtt_hass = mqtt.createNestedObject("hass");
|
||||||
|
mqtt_hass["enabled"] = config.Mqtt_Hass_Enabled;
|
||||||
|
mqtt_hass["retain"] = config.Mqtt_Hass_Retain;
|
||||||
|
mqtt_hass["topic"] = config.Mqtt_Hass_Topic;
|
||||||
|
mqtt_hass["individual_panels"] = config.Mqtt_Hass_IndividualPanels;
|
||||||
|
mqtt_hass["expire"] = config.Mqtt_Hass_Expire;
|
||||||
|
|
||||||
|
JsonObject dtu = doc.createNestedObject("dtu");
|
||||||
|
dtu["serial"] = config.Dtu_Serial;
|
||||||
|
dtu["poll_interval"] = config.Dtu_PollInterval;
|
||||||
|
dtu["pa_level"] = config.Dtu_PaLevel;
|
||||||
|
|
||||||
|
JsonArray inverters = doc.createNestedArray("inverters");
|
||||||
|
for (uint8_t i = 0; i < INV_MAX_COUNT; i++) {
|
||||||
|
JsonObject inv = inverters.createNestedObject();
|
||||||
|
inv["serial"] = config.Inverter[i].Serial;
|
||||||
|
inv["name"] = config.Inverter[i].Name;
|
||||||
|
|
||||||
|
JsonArray channels = inv.createNestedArray("channels");
|
||||||
|
for (uint8_t c = 0; c < INV_MAX_CHAN_COUNT; c++) {
|
||||||
|
channels.add(config.Inverter[i].MaxChannelPower[c]);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Serialize JSON to file
|
||||||
|
if (serializeJson(doc, f) == 0) {
|
||||||
|
Serial.println("Failed to write file");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
f.close();
|
f.close();
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool ConfigurationClass::read()
|
bool ConfigurationClass::read()
|
||||||
{
|
{
|
||||||
|
if (!LittleFS.exists(CONFIG_FILENAME_JSON)) {
|
||||||
|
Serial.println("Converting binary config to json... ");
|
||||||
File f = LittleFS.open(CONFIG_FILENAME, "r");
|
File f = LittleFS.open(CONFIG_FILENAME, "r");
|
||||||
if (!f) {
|
if (!f) {
|
||||||
return false;
|
return false;
|
||||||
@ -84,6 +155,122 @@ bool ConfigurationClass::read()
|
|||||||
for (unsigned int i = 0; i < sizeof(CONFIG_T); i++) {
|
for (unsigned int i = 0; i < sizeof(CONFIG_T); i++) {
|
||||||
bytes[i] = f.read();
|
bytes[i] = f.read();
|
||||||
}
|
}
|
||||||
|
f.close();
|
||||||
|
write();
|
||||||
|
Serial.println("done");
|
||||||
|
LittleFS.remove(CONFIG_FILENAME);
|
||||||
|
}
|
||||||
|
return readJson();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ConfigurationClass::readJson()
|
||||||
|
{
|
||||||
|
File f = LittleFS.open(CONFIG_FILENAME_JSON, "r", false);
|
||||||
|
if (!f) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
DynamicJsonDocument doc(JSON_BUFFER_SIZE);
|
||||||
|
// Deserialize the JSON document
|
||||||
|
DeserializationError error = deserializeJson(doc, f);
|
||||||
|
if (error) {
|
||||||
|
Serial.println(F("Failed to read file, using default configuration"));
|
||||||
|
}
|
||||||
|
|
||||||
|
JsonObject cfg = doc["cfg"];
|
||||||
|
config.Cfg_Version = cfg["version"] | CONFIG_VERSION;
|
||||||
|
config.Cfg_SaveCount = cfg["save_count"] | 0;
|
||||||
|
|
||||||
|
JsonObject wifi = doc["wifi"];
|
||||||
|
strlcpy(config.WiFi_Ssid, wifi["ssid"] | WIFI_SSID, sizeof(config.WiFi_Ssid));
|
||||||
|
strlcpy(config.WiFi_Password, wifi["password"] | WIFI_PASSWORD, sizeof(config.WiFi_Password));
|
||||||
|
strlcpy(config.WiFi_Hostname, wifi["hostname"] | APP_HOSTNAME, sizeof(config.WiFi_Hostname));
|
||||||
|
|
||||||
|
IPAddress wifi_ip;
|
||||||
|
wifi_ip.fromString(wifi["ip"] | "");
|
||||||
|
config.WiFi_Ip[0] = wifi_ip[0];
|
||||||
|
config.WiFi_Ip[1] = wifi_ip[1];
|
||||||
|
config.WiFi_Ip[2] = wifi_ip[2];
|
||||||
|
config.WiFi_Ip[3] = wifi_ip[3];
|
||||||
|
|
||||||
|
IPAddress wifi_netmask;
|
||||||
|
wifi_netmask.fromString(wifi["netmask"] | "");
|
||||||
|
config.WiFi_Netmask[0] = wifi_netmask[0];
|
||||||
|
config.WiFi_Netmask[1] = wifi_netmask[1];
|
||||||
|
config.WiFi_Netmask[2] = wifi_netmask[2];
|
||||||
|
config.WiFi_Netmask[3] = wifi_netmask[3];
|
||||||
|
|
||||||
|
IPAddress wifi_gateway;
|
||||||
|
wifi_gateway.fromString(wifi["gateway"] | "");
|
||||||
|
config.WiFi_Gateway[0] = wifi_gateway[0];
|
||||||
|
config.WiFi_Gateway[1] = wifi_gateway[1];
|
||||||
|
config.WiFi_Gateway[2] = wifi_gateway[2];
|
||||||
|
config.WiFi_Gateway[3] = wifi_gateway[3];
|
||||||
|
|
||||||
|
IPAddress wifi_dns1;
|
||||||
|
wifi_dns1.fromString(wifi["dns1"] | "");
|
||||||
|
config.WiFi_Dns1[0] = wifi_dns1[0];
|
||||||
|
config.WiFi_Dns1[1] = wifi_dns1[1];
|
||||||
|
config.WiFi_Dns1[2] = wifi_dns1[2];
|
||||||
|
config.WiFi_Dns1[3] = wifi_dns1[3];
|
||||||
|
|
||||||
|
IPAddress wifi_dns2;
|
||||||
|
wifi_dns2.fromString(wifi["dns2"] | "");
|
||||||
|
config.WiFi_Dns2[0] = wifi_dns2[0];
|
||||||
|
config.WiFi_Dns2[1] = wifi_dns2[1];
|
||||||
|
config.WiFi_Dns2[2] = wifi_dns2[2];
|
||||||
|
config.WiFi_Dns2[3] = wifi_dns2[3];
|
||||||
|
|
||||||
|
config.WiFi_Dhcp = wifi["dhcp"] | WIFI_DHCP;
|
||||||
|
|
||||||
|
JsonObject ntp = doc["ntp"];
|
||||||
|
strlcpy(config.Ntp_Server, ntp["server"] | NTP_SERVER, sizeof(config.Ntp_Server));
|
||||||
|
strlcpy(config.Ntp_Timezone, ntp["timezone"] | NTP_TIMEZONE, sizeof(config.Ntp_Timezone));
|
||||||
|
strlcpy(config.Ntp_TimezoneDescr, ntp["timezone_descr"] | NTP_TIMEZONEDESCR, sizeof(config.Ntp_TimezoneDescr));
|
||||||
|
|
||||||
|
JsonObject mqtt = doc["mqtt"];
|
||||||
|
config.Mqtt_Enabled = mqtt["enabled"] | MQTT_ENABLED;
|
||||||
|
strlcpy(config.Mqtt_Hostname, mqtt["hostname"] | MQTT_HOST, sizeof(config.Mqtt_Hostname));
|
||||||
|
config.Mqtt_Port = mqtt["port"] | MQTT_PORT;
|
||||||
|
strlcpy(config.Mqtt_Username, mqtt["username"] | MQTT_USER, sizeof(config.Mqtt_Username));
|
||||||
|
strlcpy(config.Mqtt_Password, mqtt["password"] | MQTT_PASSWORD, sizeof(config.Mqtt_Password));
|
||||||
|
strlcpy(config.Mqtt_Topic, mqtt["topic"] | MQTT_TOPIC, sizeof(config.Mqtt_Topic));
|
||||||
|
config.Mqtt_Retain = mqtt["retain"] | MQTT_RETAIN;
|
||||||
|
config.Mqtt_PublishInterval = mqtt["publish_invterval"] | MQTT_PUBLISH_INTERVAL;
|
||||||
|
|
||||||
|
JsonObject mqtt_lwt = mqtt["lwt"];
|
||||||
|
strlcpy(config.Mqtt_LwtTopic, mqtt_lwt["topic"] | MQTT_LWT_TOPIC, sizeof(config.Mqtt_LwtTopic));
|
||||||
|
strlcpy(config.Mqtt_LwtValue_Online, mqtt_lwt["value_online"] | MQTT_LWT_ONLINE, sizeof(config.Mqtt_LwtValue_Online));
|
||||||
|
strlcpy(config.Mqtt_LwtValue_Offline, mqtt_lwt["value_offline"] | MQTT_LWT_OFFLINE, sizeof(config.Mqtt_LwtValue_Offline));
|
||||||
|
|
||||||
|
JsonObject mqtt_tls = mqtt["tls"];
|
||||||
|
config.Mqtt_Tls = mqtt_tls["enabled"] | MQTT_TLS;
|
||||||
|
strlcpy(config.Mqtt_RootCaCert, mqtt_tls["root_ca_cert"] | MQTT_ROOT_CA_CERT, sizeof(config.Mqtt_RootCaCert));
|
||||||
|
|
||||||
|
JsonObject mqtt_hass = mqtt["hass"];
|
||||||
|
config.Mqtt_Hass_Enabled = mqtt_hass["enabled"] | MQTT_HASS_ENABLED;
|
||||||
|
config.Mqtt_Hass_Retain = mqtt_hass["retain"] | MQTT_HASS_RETAIN;
|
||||||
|
config.Mqtt_Hass_Expire = mqtt_hass["expire"] | MQTT_HASS_EXPIRE;
|
||||||
|
config.Mqtt_Hass_IndividualPanels = mqtt_hass["individual_panels"] | MQTT_HASS_INDIVIDUALPANELS;
|
||||||
|
strlcpy(config.Mqtt_Hass_Topic, mqtt_hass["topic"] | MQTT_HASS_TOPIC, sizeof(config.Mqtt_Hass_Topic));
|
||||||
|
|
||||||
|
JsonObject dtu = doc["dtu"];
|
||||||
|
config.Dtu_Serial = dtu["serial"] | DTU_SERIAL;
|
||||||
|
config.Dtu_PollInterval = dtu["poll_interval"] | DTU_POLL_INTERVAL;
|
||||||
|
config.Dtu_PaLevel = dtu["pa_level"] | DTU_PA_LEVEL;
|
||||||
|
|
||||||
|
JsonArray inverters = doc["inverters"];
|
||||||
|
for (uint8_t i = 0; i < INV_MAX_COUNT; i++) {
|
||||||
|
JsonObject inv = inverters[i].as<JsonObject>();
|
||||||
|
config.Inverter[i].Serial = inv["serial"] | 0ULL;
|
||||||
|
strlcpy(config.Inverter[i].Name, inv["name"] | "", sizeof(config.Inverter[i].Name));
|
||||||
|
|
||||||
|
JsonArray channels = inv["channels"];
|
||||||
|
for (uint8_t c = 0; c < INV_MAX_CHAN_COUNT; c++) {
|
||||||
|
config.Inverter[i].MaxChannelPower[c] = channels[c];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
f.close();
|
f.close();
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -32,7 +32,7 @@ void WebApiConfigClass::loop()
|
|||||||
|
|
||||||
void WebApiConfigClass::onConfigGet(AsyncWebServerRequest* request)
|
void WebApiConfigClass::onConfigGet(AsyncWebServerRequest* request)
|
||||||
{
|
{
|
||||||
request->send(LittleFS, CONFIG_FILENAME, String(), true);
|
request->send(LittleFS, CONFIG_FILENAME_JSON, String(), true);
|
||||||
}
|
}
|
||||||
|
|
||||||
void WebApiConfigClass::onConfigDelete(AsyncWebServerRequest* request)
|
void WebApiConfigClass::onConfigDelete(AsyncWebServerRequest* request)
|
||||||
@ -87,7 +87,7 @@ void WebApiConfigClass::onConfigDelete(AsyncWebServerRequest* request)
|
|||||||
response->setLength();
|
response->setLength();
|
||||||
request->send(response);
|
request->send(response);
|
||||||
|
|
||||||
LittleFS.remove(CONFIG_FILENAME);
|
LittleFS.remove(CONFIG_FILENAME_JSON);
|
||||||
ESP.restart();
|
ESP.restart();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -110,7 +110,7 @@ void WebApiConfigClass::onConfigUpload(AsyncWebServerRequest* request, String fi
|
|||||||
{
|
{
|
||||||
if (!index) {
|
if (!index) {
|
||||||
// open the file on first call and store the file handle in the request object
|
// open the file on first call and store the file handle in the request object
|
||||||
request->_tempFile = LittleFS.open(CONFIG_FILENAME, "w");
|
request->_tempFile = LittleFS.open(CONFIG_FILENAME_JSON, "w");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (len) {
|
if (len) {
|
||||||
|
|||||||
@ -55,7 +55,7 @@
|
|||||||
|
|
||||||
<div v-else-if="!uploading">
|
<div v-else-if="!uploading">
|
||||||
<div class="form-group pt-2 mt-3">
|
<div class="form-group pt-2 mt-3">
|
||||||
<input class="form-control" type="file" ref="file" accept=".bin" @change="uploadConfig" />
|
<input class="form-control" type="file" ref="file" accept=".json" @change="uploadConfig" />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@ -175,7 +175,7 @@ export default defineComponent({
|
|||||||
downloadConfig() {
|
downloadConfig() {
|
||||||
const link = document.createElement('a')
|
const link = document.createElement('a')
|
||||||
link.href = "/api/config/get"
|
link.href = "/api/config/get"
|
||||||
link.download = 'config.bin'
|
link.download = 'config.json'
|
||||||
link.click()
|
link.click()
|
||||||
},
|
},
|
||||||
uploadConfig(event: Event | null) {
|
uploadConfig(event: Event | null) {
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user