add and use configuration write guard
the configuration write guard is now required when the configuration struct shall be mutated. the write guards locks multiple writers against each other and also, more importantly, makes the writes synchronous to the main loop. all code running in the main loop can now be sure that (1) reads from the configuration struct are non-preemtive and (2) the configuration struct as a whole is in a consistent state when reading from it. NOTE that acquiring a write guard from within the main loop's task will immediately cause a deadlock and the watchdog will trigger a reset. if writing from inside the main loop should ever become necessary, the write guard must be updated to only lock the mutex but not wait for a signal.
This commit is contained in:
parent
dc5eb96f50
commit
b1edb13b3c
@ -3,6 +3,9 @@
|
|||||||
|
|
||||||
#include "PinMapping.h"
|
#include "PinMapping.h"
|
||||||
#include <cstdint>
|
#include <cstdint>
|
||||||
|
#include <TaskSchedulerDeclarations.h>
|
||||||
|
#include <mutex>
|
||||||
|
#include <condition_variable>
|
||||||
|
|
||||||
#define CONFIG_FILENAME "/config.json"
|
#define CONFIG_FILENAME "/config.json"
|
||||||
#define CONFIG_VERSION 0x00011c00 // 0.1.28 // make sure to clean all after change
|
#define CONFIG_VERSION 0x00011c00 // 0.1.28 // make sure to clean all after change
|
||||||
@ -161,15 +164,32 @@ struct CONFIG_T {
|
|||||||
|
|
||||||
class ConfigurationClass {
|
class ConfigurationClass {
|
||||||
public:
|
public:
|
||||||
void init();
|
void init(Scheduler& scheduler);
|
||||||
bool read();
|
bool read();
|
||||||
bool write();
|
bool write();
|
||||||
void migrate();
|
void migrate();
|
||||||
CONFIG_T& get();
|
CONFIG_T const& get();
|
||||||
|
|
||||||
|
class WriteGuard {
|
||||||
|
public:
|
||||||
|
WriteGuard();
|
||||||
|
CONFIG_T& getConfig();
|
||||||
|
~WriteGuard();
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::unique_lock<std::mutex> _lock;
|
||||||
|
};
|
||||||
|
|
||||||
|
WriteGuard getWriteGuard();
|
||||||
|
|
||||||
INVERTER_CONFIG_T* getFreeInverterSlot();
|
INVERTER_CONFIG_T* getFreeInverterSlot();
|
||||||
INVERTER_CONFIG_T* getInverterConfig(const uint64_t serial);
|
INVERTER_CONFIG_T* getInverterConfig(const uint64_t serial);
|
||||||
void deleteInverterById(const uint8_t id);
|
void deleteInverterById(const uint8_t id);
|
||||||
|
|
||||||
|
private:
|
||||||
|
void loop();
|
||||||
|
|
||||||
|
Task _loopTask;
|
||||||
};
|
};
|
||||||
|
|
||||||
extern ConfigurationClass Configuration;
|
extern ConfigurationClass Configuration;
|
||||||
|
|||||||
@ -13,8 +13,17 @@
|
|||||||
|
|
||||||
CONFIG_T config;
|
CONFIG_T config;
|
||||||
|
|
||||||
void ConfigurationClass::init()
|
static std::condition_variable sWriterCv;
|
||||||
|
static std::mutex sWriterMutex;
|
||||||
|
static unsigned sWriterCount = 0;
|
||||||
|
|
||||||
|
void ConfigurationClass::init(Scheduler& scheduler)
|
||||||
{
|
{
|
||||||
|
scheduler.addTask(_loopTask);
|
||||||
|
_loopTask.setCallback(std::bind(&ConfigurationClass::loop, this));
|
||||||
|
_loopTask.setIterations(TASK_FOREVER);
|
||||||
|
_loopTask.enable();
|
||||||
|
|
||||||
memset(&config, 0x0, sizeof(config));
|
memset(&config, 0x0, sizeof(config));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -318,6 +327,20 @@ bool ConfigurationClass::read()
|
|||||||
}
|
}
|
||||||
|
|
||||||
f.close();
|
f.close();
|
||||||
|
|
||||||
|
// Check for default DTU serial
|
||||||
|
MessageOutput.print("Check for default DTU serial... ");
|
||||||
|
if (config.Dtu.Serial == DTU_SERIAL) {
|
||||||
|
MessageOutput.print("generate serial based on ESP chip id: ");
|
||||||
|
const uint64_t dtuId = Utils::generateDtuSerial();
|
||||||
|
MessageOutput.printf("%0" PRIx32 "%08" PRIx32 "... ",
|
||||||
|
((uint32_t)((dtuId >> 32) & 0xFFFFFFFF)),
|
||||||
|
((uint32_t)(dtuId & 0xFFFFFFFF)));
|
||||||
|
config.Dtu.Serial = dtuId;
|
||||||
|
write();
|
||||||
|
}
|
||||||
|
MessageOutput.println("done");
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -390,11 +413,16 @@ void ConfigurationClass::migrate()
|
|||||||
read();
|
read();
|
||||||
}
|
}
|
||||||
|
|
||||||
CONFIG_T& ConfigurationClass::get()
|
CONFIG_T const& ConfigurationClass::get()
|
||||||
{
|
{
|
||||||
return config;
|
return config;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ConfigurationClass::WriteGuard ConfigurationClass::getWriteGuard()
|
||||||
|
{
|
||||||
|
return WriteGuard();
|
||||||
|
}
|
||||||
|
|
||||||
INVERTER_CONFIG_T* ConfigurationClass::getFreeInverterSlot()
|
INVERTER_CONFIG_T* ConfigurationClass::getFreeInverterSlot()
|
||||||
{
|
{
|
||||||
for (uint8_t i = 0; i < INV_MAX_COUNT; i++) {
|
for (uint8_t i = 0; i < INV_MAX_COUNT; i++) {
|
||||||
@ -439,4 +467,30 @@ void ConfigurationClass::deleteInverterById(const uint8_t id)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void ConfigurationClass::loop()
|
||||||
|
{
|
||||||
|
std::unique_lock<std::mutex> lock(sWriterMutex);
|
||||||
|
if (sWriterCount == 0) { return; }
|
||||||
|
|
||||||
|
sWriterCv.notify_all();
|
||||||
|
sWriterCv.wait(lock, [] { return sWriterCount == 0; });
|
||||||
|
}
|
||||||
|
|
||||||
|
CONFIG_T& ConfigurationClass::WriteGuard::getConfig()
|
||||||
|
{
|
||||||
|
return config;
|
||||||
|
}
|
||||||
|
|
||||||
|
ConfigurationClass::WriteGuard::WriteGuard()
|
||||||
|
: _lock(sWriterMutex)
|
||||||
|
{
|
||||||
|
sWriterCount++;
|
||||||
|
sWriterCv.wait(_lock);
|
||||||
|
}
|
||||||
|
|
||||||
|
ConfigurationClass::WriteGuard::~WriteGuard() {
|
||||||
|
sWriterCount--;
|
||||||
|
if (sWriterCount == 0) { sWriterCv.notify_all(); }
|
||||||
|
}
|
||||||
|
|
||||||
ConfigurationClass Configuration;
|
ConfigurationClass Configuration;
|
||||||
|
|||||||
@ -47,7 +47,7 @@ void WebApiClass::reload()
|
|||||||
|
|
||||||
bool WebApiClass::checkCredentials(AsyncWebServerRequest* request)
|
bool WebApiClass::checkCredentials(AsyncWebServerRequest* request)
|
||||||
{
|
{
|
||||||
CONFIG_T& config = Configuration.get();
|
auto const& config = Configuration.get();
|
||||||
if (request->authenticate(AUTH_USERNAME, config.Security.Password)) {
|
if (request->authenticate(AUTH_USERNAME, config.Security.Password)) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@ -65,7 +65,7 @@ bool WebApiClass::checkCredentials(AsyncWebServerRequest* request)
|
|||||||
|
|
||||||
bool WebApiClass::checkCredentialsReadonly(AsyncWebServerRequest* request)
|
bool WebApiClass::checkCredentialsReadonly(AsyncWebServerRequest* request)
|
||||||
{
|
{
|
||||||
CONFIG_T& config = Configuration.get();
|
auto const& config = Configuration.get();
|
||||||
if (config.Security.AllowReadonly) {
|
if (config.Security.AllowReadonly) {
|
||||||
return true;
|
return true;
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@ -129,23 +129,28 @@ void WebApiDeviceClass::onDeviceAdminPost(AsyncWebServerRequest* request)
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
CONFIG_T& config = Configuration.get();
|
{
|
||||||
bool performRestart = root["curPin"]["name"].as<String>() != config.Dev_PinMapping;
|
auto guard = Configuration.getWriteGuard();
|
||||||
|
auto& config = guard.getConfig();
|
||||||
|
|
||||||
strlcpy(config.Dev_PinMapping, root["curPin"]["name"].as<String>().c_str(), sizeof(config.Dev_PinMapping));
|
strlcpy(config.Dev_PinMapping, root["curPin"]["name"].as<String>().c_str(), sizeof(config.Dev_PinMapping));
|
||||||
config.Display.Rotation = root["display"]["rotation"].as<uint8_t>();
|
config.Display.Rotation = root["display"]["rotation"].as<uint8_t>();
|
||||||
config.Display.PowerSafe = root["display"]["power_safe"].as<bool>();
|
config.Display.PowerSafe = root["display"]["power_safe"].as<bool>();
|
||||||
config.Display.ScreenSaver = root["display"]["screensaver"].as<bool>();
|
config.Display.ScreenSaver = root["display"]["screensaver"].as<bool>();
|
||||||
config.Display.Contrast = root["display"]["contrast"].as<uint8_t>();
|
config.Display.Contrast = root["display"]["contrast"].as<uint8_t>();
|
||||||
config.Display.Language = root["display"]["language"].as<uint8_t>();
|
config.Display.Language = root["display"]["language"].as<uint8_t>();
|
||||||
config.Display.Diagram.Duration = root["display"]["diagramduration"].as<uint32_t>();
|
config.Display.Diagram.Duration = root["display"]["diagramduration"].as<uint32_t>();
|
||||||
config.Display.Diagram.Mode = root["display"]["diagrammode"].as<DiagramMode_t>();
|
config.Display.Diagram.Mode = root["display"]["diagrammode"].as<DiagramMode_t>();
|
||||||
|
|
||||||
for (uint8_t i = 0; i < PINMAPPING_LED_COUNT; i++) {
|
for (uint8_t i = 0; i < PINMAPPING_LED_COUNT; i++) {
|
||||||
config.Led_Single[i].Brightness = root["led"][i]["brightness"].as<uint8_t>();
|
config.Led_Single[i].Brightness = root["led"][i]["brightness"].as<uint8_t>();
|
||||||
config.Led_Single[i].Brightness = min<uint8_t>(100, config.Led_Single[i].Brightness);
|
config.Led_Single[i].Brightness = min<uint8_t>(100, config.Led_Single[i].Brightness);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
auto const& config = Configuration.get();
|
||||||
|
bool performRestart = root["curPin"]["name"].as<String>() != config.Dev_PinMapping;
|
||||||
|
|
||||||
Display.setDiagramMode(static_cast<DiagramMode_t>(config.Display.Diagram.Mode));
|
Display.setDiagramMode(static_cast<DiagramMode_t>(config.Display.Diagram.Mode));
|
||||||
Display.setOrientation(config.Display.Rotation);
|
Display.setOrientation(config.Display.Rotation);
|
||||||
Display.enablePowerSafe = config.Display.PowerSafe;
|
Display.enablePowerSafe = config.Display.PowerSafe;
|
||||||
|
|||||||
@ -27,7 +27,7 @@ void WebApiDtuClass::init(AsyncWebServer& server, Scheduler& scheduler)
|
|||||||
void WebApiDtuClass::applyDataTaskCb()
|
void WebApiDtuClass::applyDataTaskCb()
|
||||||
{
|
{
|
||||||
// Execute stuff in main thread to avoid busy SPI bus
|
// Execute stuff in main thread to avoid busy SPI bus
|
||||||
CONFIG_T& config = Configuration.get();
|
auto const& config = Configuration.get();
|
||||||
Hoymiles.getRadioNrf()->setPALevel((rf24_pa_dbm_e)config.Dtu.Nrf.PaLevel);
|
Hoymiles.getRadioNrf()->setPALevel((rf24_pa_dbm_e)config.Dtu.Nrf.PaLevel);
|
||||||
Hoymiles.getRadioCmt()->setPALevel(config.Dtu.Cmt.PaLevel);
|
Hoymiles.getRadioCmt()->setPALevel(config.Dtu.Cmt.PaLevel);
|
||||||
Hoymiles.getRadioNrf()->setDtuSerial(config.Dtu.Serial);
|
Hoymiles.getRadioNrf()->setDtuSerial(config.Dtu.Serial);
|
||||||
@ -153,14 +153,16 @@ void WebApiDtuClass::onDtuAdminPost(AsyncWebServerRequest* request)
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
CONFIG_T& config = Configuration.get();
|
{
|
||||||
|
auto guard = Configuration.getWriteGuard();
|
||||||
config.Dtu.Serial = serial;
|
auto& config = guard.getConfig();
|
||||||
config.Dtu.PollInterval = root["pollinterval"].as<uint32_t>();
|
config.Dtu.Serial = serial;
|
||||||
config.Dtu.Nrf.PaLevel = root["nrf_palevel"].as<uint8_t>();
|
config.Dtu.PollInterval = root["pollinterval"].as<uint32_t>();
|
||||||
config.Dtu.Cmt.PaLevel = root["cmt_palevel"].as<int8_t>();
|
config.Dtu.Nrf.PaLevel = root["nrf_palevel"].as<uint8_t>();
|
||||||
config.Dtu.Cmt.Frequency = root["cmt_frequency"].as<uint32_t>();
|
config.Dtu.Cmt.PaLevel = root["cmt_palevel"].as<int8_t>();
|
||||||
config.Dtu.Cmt.CountryMode = root["cmt_country"].as<CountryModeId_t>();
|
config.Dtu.Cmt.Frequency = root["cmt_frequency"].as<uint32_t>();
|
||||||
|
config.Dtu.Cmt.CountryMode = root["cmt_country"].as<CountryModeId_t>();
|
||||||
|
}
|
||||||
|
|
||||||
WebApi.writeConfig(retMsg);
|
WebApi.writeConfig(retMsg);
|
||||||
|
|
||||||
|
|||||||
@ -184,9 +184,9 @@ void WebApiInverterClass::onInverterEdit(AsyncWebServerRequest* request)
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Interpret the string as a hex value and convert it to uint64_t
|
// Interpret the string as a hex value and convert it to uint64_t
|
||||||
const uint64_t serial = strtoll(root["serial"].as<String>().c_str(), NULL, 16);
|
const uint64_t new_serial = strtoll(root["serial"].as<String>().c_str(), NULL, 16);
|
||||||
|
|
||||||
if (serial == 0) {
|
if (new_serial == 0) {
|
||||||
retMsg["message"] = "Serial must be a number > 0!";
|
retMsg["message"] = "Serial must be a number > 0!";
|
||||||
retMsg["code"] = WebApiError::InverterSerialZero;
|
retMsg["code"] = WebApiError::InverterSerialZero;
|
||||||
WebApi.sendJsonResponse(request, response, __FUNCTION__, __LINE__);
|
WebApi.sendJsonResponse(request, response, __FUNCTION__, __LINE__);
|
||||||
@ -209,37 +209,42 @@ void WebApiInverterClass::onInverterEdit(AsyncWebServerRequest* request)
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
INVERTER_CONFIG_T& inverter = Configuration.get().Inverter[root["id"].as<uint8_t>()];
|
uint64_t old_serial = 0;
|
||||||
|
|
||||||
uint64_t new_serial = serial;
|
{
|
||||||
uint64_t old_serial = inverter.Serial;
|
auto guard = Configuration.getWriteGuard();
|
||||||
|
auto& config = guard.getConfig();
|
||||||
|
|
||||||
// Interpret the string as a hex value and convert it to uint64_t
|
INVERTER_CONFIG_T& inverter = config.Inverter[root["id"].as<uint8_t>()];
|
||||||
inverter.Serial = new_serial;
|
|
||||||
strncpy(inverter.Name, root["name"].as<String>().c_str(), INV_MAX_NAME_STRLEN);
|
|
||||||
|
|
||||||
inverter.Poll_Enable = root["poll_enable"] | true;
|
old_serial = inverter.Serial;
|
||||||
inverter.Poll_Enable_Night = root["poll_enable_night"] | true;
|
inverter.Serial = new_serial;
|
||||||
inverter.Command_Enable = root["command_enable"] | true;
|
strncpy(inverter.Name, root["name"].as<String>().c_str(), INV_MAX_NAME_STRLEN);
|
||||||
inverter.Command_Enable_Night = root["command_enable_night"] | true;
|
|
||||||
inverter.ReachableThreshold = root["reachable_threshold"] | REACHABLE_THRESHOLD;
|
|
||||||
inverter.ZeroRuntimeDataIfUnrechable = root["zero_runtime"] | false;
|
|
||||||
inverter.ZeroYieldDayOnMidnight = root["zero_day"] | false;
|
|
||||||
inverter.ClearEventlogOnMidnight = root["clear_eventlog"] | false;
|
|
||||||
inverter.YieldDayCorrection = root["yieldday_correction"] | false;
|
|
||||||
|
|
||||||
uint8_t arrayCount = 0;
|
inverter.Poll_Enable = root["poll_enable"] | true;
|
||||||
for (JsonVariant channel : channelArray) {
|
inverter.Poll_Enable_Night = root["poll_enable_night"] | true;
|
||||||
inverter.channel[arrayCount].MaxChannelPower = channel["max_power"].as<uint16_t>();
|
inverter.Command_Enable = root["command_enable"] | true;
|
||||||
inverter.channel[arrayCount].YieldTotalOffset = channel["yield_total_offset"].as<float>();
|
inverter.Command_Enable_Night = root["command_enable_night"] | true;
|
||||||
strncpy(inverter.channel[arrayCount].Name, channel["name"] | "", sizeof(inverter.channel[arrayCount].Name));
|
inverter.ReachableThreshold = root["reachable_threshold"] | REACHABLE_THRESHOLD;
|
||||||
arrayCount++;
|
inverter.ZeroRuntimeDataIfUnrechable = root["zero_runtime"] | false;
|
||||||
|
inverter.ZeroYieldDayOnMidnight = root["zero_day"] | false;
|
||||||
|
inverter.ClearEventlogOnMidnight = root["clear_eventlog"] | false;
|
||||||
|
inverter.YieldDayCorrection = root["yieldday_correction"] | false;
|
||||||
|
|
||||||
|
uint8_t arrayCount = 0;
|
||||||
|
for (JsonVariant channel : channelArray) {
|
||||||
|
inverter.channel[arrayCount].MaxChannelPower = channel["max_power"].as<uint16_t>();
|
||||||
|
inverter.channel[arrayCount].YieldTotalOffset = channel["yield_total_offset"].as<float>();
|
||||||
|
strncpy(inverter.channel[arrayCount].Name, channel["name"] | "", sizeof(inverter.channel[arrayCount].Name));
|
||||||
|
arrayCount++;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
WebApi.writeConfig(retMsg, WebApiError::InverterChanged, "Inverter changed!");
|
WebApi.writeConfig(retMsg, WebApiError::InverterChanged, "Inverter changed!");
|
||||||
|
|
||||||
WebApi.sendJsonResponse(request, response, __FUNCTION__, __LINE__);
|
WebApi.sendJsonResponse(request, response, __FUNCTION__, __LINE__);
|
||||||
|
|
||||||
|
INVERTER_CONFIG_T const& inverter = Configuration.get().Inverter[root["id"].as<uint8_t>()];
|
||||||
std::shared_ptr<InverterAbstract> inv = Hoymiles.getInverterBySerial(old_serial);
|
std::shared_ptr<InverterAbstract> inv = Hoymiles.getInverterBySerial(old_serial);
|
||||||
|
|
||||||
if (inv != nullptr && new_serial != old_serial) {
|
if (inv != nullptr && new_serial != old_serial) {
|
||||||
@ -300,7 +305,7 @@ void WebApiInverterClass::onInverterDelete(AsyncWebServerRequest* request)
|
|||||||
}
|
}
|
||||||
|
|
||||||
uint8_t inverter_id = root["id"].as<uint8_t>();
|
uint8_t inverter_id = root["id"].as<uint8_t>();
|
||||||
INVERTER_CONFIG_T& inverter = Configuration.get().Inverter[inverter_id];
|
INVERTER_CONFIG_T const& inverter = Configuration.get().Inverter[inverter_id];
|
||||||
|
|
||||||
Hoymiles.removeInverterBySerial(inverter.Serial);
|
Hoymiles.removeInverterBySerial(inverter.Serial);
|
||||||
|
|
||||||
@ -337,13 +342,18 @@ void WebApiInverterClass::onInverterOrder(AsyncWebServerRequest* request)
|
|||||||
// The order array contains list or id in the right order
|
// The order array contains list or id in the right order
|
||||||
JsonArray orderArray = root["order"].as<JsonArray>();
|
JsonArray orderArray = root["order"].as<JsonArray>();
|
||||||
uint8_t order = 0;
|
uint8_t order = 0;
|
||||||
for (JsonVariant id : orderArray) {
|
{
|
||||||
uint8_t inverter_id = id.as<uint8_t>();
|
auto guard = Configuration.getWriteGuard();
|
||||||
if (inverter_id < INV_MAX_COUNT) {
|
auto& config = guard.getConfig();
|
||||||
INVERTER_CONFIG_T& inverter = Configuration.get().Inverter[inverter_id];
|
|
||||||
inverter.Order = order;
|
for (JsonVariant id : orderArray) {
|
||||||
|
uint8_t inverter_id = id.as<uint8_t>();
|
||||||
|
if (inverter_id < INV_MAX_COUNT) {
|
||||||
|
INVERTER_CONFIG_T& inverter = config.Inverter[inverter_id];
|
||||||
|
inverter.Order = order;
|
||||||
|
}
|
||||||
|
order++;
|
||||||
}
|
}
|
||||||
order++;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
WebApi.writeConfig(retMsg, WebApiError::InverterOrdered, "Inverter order saved!");
|
WebApi.writeConfig(retMsg, WebApiError::InverterOrdered, "Inverter order saved!");
|
||||||
|
|||||||
@ -271,36 +271,40 @@ void WebApiMqttClass::onMqttAdminPost(AsyncWebServerRequest* request)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
CONFIG_T& config = Configuration.get();
|
{
|
||||||
config.Mqtt.Enabled = root["mqtt_enabled"].as<bool>();
|
auto guard = Configuration.getWriteGuard();
|
||||||
config.Mqtt.Retain = root["mqtt_retain"].as<bool>();
|
auto& config = guard.getConfig();
|
||||||
config.Mqtt.Tls.Enabled = root["mqtt_tls"].as<bool>();
|
|
||||||
strlcpy(config.Mqtt.Tls.RootCaCert, root["mqtt_root_ca_cert"].as<String>().c_str(), sizeof(config.Mqtt.Tls.RootCaCert));
|
|
||||||
config.Mqtt.Tls.CertLogin = root["mqtt_tls_cert_login"].as<bool>();
|
|
||||||
strlcpy(config.Mqtt.Tls.ClientCert, root["mqtt_client_cert"].as<String>().c_str(), sizeof(config.Mqtt.Tls.ClientCert));
|
|
||||||
strlcpy(config.Mqtt.Tls.ClientKey, root["mqtt_client_key"].as<String>().c_str(), sizeof(config.Mqtt.Tls.ClientKey));
|
|
||||||
config.Mqtt.Port = root["mqtt_port"].as<uint>();
|
|
||||||
strlcpy(config.Mqtt.Hostname, root["mqtt_hostname"].as<String>().c_str(), sizeof(config.Mqtt.Hostname));
|
|
||||||
strlcpy(config.Mqtt.ClientId, root["mqtt_clientid"].as<String>().c_str(), sizeof(config.Mqtt.ClientId));
|
|
||||||
strlcpy(config.Mqtt.Username, root["mqtt_username"].as<String>().c_str(), sizeof(config.Mqtt.Username));
|
|
||||||
strlcpy(config.Mqtt.Password, root["mqtt_password"].as<String>().c_str(), sizeof(config.Mqtt.Password));
|
|
||||||
strlcpy(config.Mqtt.Lwt.Topic, root["mqtt_lwt_topic"].as<String>().c_str(), sizeof(config.Mqtt.Lwt.Topic));
|
|
||||||
strlcpy(config.Mqtt.Lwt.Value_Online, root["mqtt_lwt_online"].as<String>().c_str(), sizeof(config.Mqtt.Lwt.Value_Online));
|
|
||||||
strlcpy(config.Mqtt.Lwt.Value_Offline, root["mqtt_lwt_offline"].as<String>().c_str(), sizeof(config.Mqtt.Lwt.Value_Offline));
|
|
||||||
config.Mqtt.Lwt.Qos = root["mqtt_lwt_qos"].as<uint8_t>();
|
|
||||||
config.Mqtt.PublishInterval = root["mqtt_publish_interval"].as<uint32_t>();
|
|
||||||
config.Mqtt.CleanSession = root["mqtt_clean_session"].as<bool>();
|
|
||||||
config.Mqtt.Hass.Enabled = root["mqtt_hass_enabled"].as<bool>();
|
|
||||||
config.Mqtt.Hass.Expire = root["mqtt_hass_expire"].as<bool>();
|
|
||||||
config.Mqtt.Hass.Retain = root["mqtt_hass_retain"].as<bool>();
|
|
||||||
config.Mqtt.Hass.IndividualPanels = root["mqtt_hass_individualpanels"].as<bool>();
|
|
||||||
strlcpy(config.Mqtt.Hass.Topic, root["mqtt_hass_topic"].as<String>().c_str(), sizeof(config.Mqtt.Hass.Topic));
|
|
||||||
|
|
||||||
// Check if base topic was changed
|
config.Mqtt.Enabled = root["mqtt_enabled"].as<bool>();
|
||||||
if (strcmp(config.Mqtt.Topic, root["mqtt_topic"].as<String>().c_str())) {
|
config.Mqtt.Retain = root["mqtt_retain"].as<bool>();
|
||||||
MqttHandleInverter.unsubscribeTopics();
|
config.Mqtt.Tls.Enabled = root["mqtt_tls"].as<bool>();
|
||||||
strlcpy(config.Mqtt.Topic, root["mqtt_topic"].as<String>().c_str(), sizeof(config.Mqtt.Topic));
|
strlcpy(config.Mqtt.Tls.RootCaCert, root["mqtt_root_ca_cert"].as<String>().c_str(), sizeof(config.Mqtt.Tls.RootCaCert));
|
||||||
MqttHandleInverter.subscribeTopics();
|
config.Mqtt.Tls.CertLogin = root["mqtt_tls_cert_login"].as<bool>();
|
||||||
|
strlcpy(config.Mqtt.Tls.ClientCert, root["mqtt_client_cert"].as<String>().c_str(), sizeof(config.Mqtt.Tls.ClientCert));
|
||||||
|
strlcpy(config.Mqtt.Tls.ClientKey, root["mqtt_client_key"].as<String>().c_str(), sizeof(config.Mqtt.Tls.ClientKey));
|
||||||
|
config.Mqtt.Port = root["mqtt_port"].as<uint>();
|
||||||
|
strlcpy(config.Mqtt.Hostname, root["mqtt_hostname"].as<String>().c_str(), sizeof(config.Mqtt.Hostname));
|
||||||
|
strlcpy(config.Mqtt.ClientId, root["mqtt_clientid"].as<String>().c_str(), sizeof(config.Mqtt.ClientId));
|
||||||
|
strlcpy(config.Mqtt.Username, root["mqtt_username"].as<String>().c_str(), sizeof(config.Mqtt.Username));
|
||||||
|
strlcpy(config.Mqtt.Password, root["mqtt_password"].as<String>().c_str(), sizeof(config.Mqtt.Password));
|
||||||
|
strlcpy(config.Mqtt.Lwt.Topic, root["mqtt_lwt_topic"].as<String>().c_str(), sizeof(config.Mqtt.Lwt.Topic));
|
||||||
|
strlcpy(config.Mqtt.Lwt.Value_Online, root["mqtt_lwt_online"].as<String>().c_str(), sizeof(config.Mqtt.Lwt.Value_Online));
|
||||||
|
strlcpy(config.Mqtt.Lwt.Value_Offline, root["mqtt_lwt_offline"].as<String>().c_str(), sizeof(config.Mqtt.Lwt.Value_Offline));
|
||||||
|
config.Mqtt.Lwt.Qos = root["mqtt_lwt_qos"].as<uint8_t>();
|
||||||
|
config.Mqtt.PublishInterval = root["mqtt_publish_interval"].as<uint32_t>();
|
||||||
|
config.Mqtt.CleanSession = root["mqtt_clean_session"].as<bool>();
|
||||||
|
config.Mqtt.Hass.Enabled = root["mqtt_hass_enabled"].as<bool>();
|
||||||
|
config.Mqtt.Hass.Expire = root["mqtt_hass_expire"].as<bool>();
|
||||||
|
config.Mqtt.Hass.Retain = root["mqtt_hass_retain"].as<bool>();
|
||||||
|
config.Mqtt.Hass.IndividualPanels = root["mqtt_hass_individualpanels"].as<bool>();
|
||||||
|
strlcpy(config.Mqtt.Hass.Topic, root["mqtt_hass_topic"].as<String>().c_str(), sizeof(config.Mqtt.Hass.Topic));
|
||||||
|
|
||||||
|
// Check if base topic was changed
|
||||||
|
if (strcmp(config.Mqtt.Topic, root["mqtt_topic"].as<String>().c_str())) {
|
||||||
|
MqttHandleInverter.unsubscribeTopics();
|
||||||
|
strlcpy(config.Mqtt.Topic, root["mqtt_topic"].as<String>().c_str(), sizeof(config.Mqtt.Topic));
|
||||||
|
MqttHandleInverter.subscribeTopics();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
WebApi.writeConfig(retMsg);
|
WebApi.writeConfig(retMsg);
|
||||||
|
|||||||
@ -164,37 +164,41 @@ void WebApiNetworkClass::onNetworkAdminPost(AsyncWebServerRequest* request)
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
CONFIG_T& config = Configuration.get();
|
{
|
||||||
config.WiFi.Ip[0] = ipaddress[0];
|
auto guard = Configuration.getWriteGuard();
|
||||||
config.WiFi.Ip[1] = ipaddress[1];
|
auto& config = guard.getConfig();
|
||||||
config.WiFi.Ip[2] = ipaddress[2];
|
|
||||||
config.WiFi.Ip[3] = ipaddress[3];
|
config.WiFi.Ip[0] = ipaddress[0];
|
||||||
config.WiFi.Netmask[0] = netmask[0];
|
config.WiFi.Ip[1] = ipaddress[1];
|
||||||
config.WiFi.Netmask[1] = netmask[1];
|
config.WiFi.Ip[2] = ipaddress[2];
|
||||||
config.WiFi.Netmask[2] = netmask[2];
|
config.WiFi.Ip[3] = ipaddress[3];
|
||||||
config.WiFi.Netmask[3] = netmask[3];
|
config.WiFi.Netmask[0] = netmask[0];
|
||||||
config.WiFi.Gateway[0] = gateway[0];
|
config.WiFi.Netmask[1] = netmask[1];
|
||||||
config.WiFi.Gateway[1] = gateway[1];
|
config.WiFi.Netmask[2] = netmask[2];
|
||||||
config.WiFi.Gateway[2] = gateway[2];
|
config.WiFi.Netmask[3] = netmask[3];
|
||||||
config.WiFi.Gateway[3] = gateway[3];
|
config.WiFi.Gateway[0] = gateway[0];
|
||||||
config.WiFi.Dns1[0] = dns1[0];
|
config.WiFi.Gateway[1] = gateway[1];
|
||||||
config.WiFi.Dns1[1] = dns1[1];
|
config.WiFi.Gateway[2] = gateway[2];
|
||||||
config.WiFi.Dns1[2] = dns1[2];
|
config.WiFi.Gateway[3] = gateway[3];
|
||||||
config.WiFi.Dns1[3] = dns1[3];
|
config.WiFi.Dns1[0] = dns1[0];
|
||||||
config.WiFi.Dns2[0] = dns2[0];
|
config.WiFi.Dns1[1] = dns1[1];
|
||||||
config.WiFi.Dns2[1] = dns2[1];
|
config.WiFi.Dns1[2] = dns1[2];
|
||||||
config.WiFi.Dns2[2] = dns2[2];
|
config.WiFi.Dns1[3] = dns1[3];
|
||||||
config.WiFi.Dns2[3] = dns2[3];
|
config.WiFi.Dns2[0] = dns2[0];
|
||||||
strlcpy(config.WiFi.Ssid, root["ssid"].as<String>().c_str(), sizeof(config.WiFi.Ssid));
|
config.WiFi.Dns2[1] = dns2[1];
|
||||||
strlcpy(config.WiFi.Password, root["password"].as<String>().c_str(), sizeof(config.WiFi.Password));
|
config.WiFi.Dns2[2] = dns2[2];
|
||||||
strlcpy(config.WiFi.Hostname, root["hostname"].as<String>().c_str(), sizeof(config.WiFi.Hostname));
|
config.WiFi.Dns2[3] = dns2[3];
|
||||||
if (root["dhcp"].as<bool>()) {
|
strlcpy(config.WiFi.Ssid, root["ssid"].as<String>().c_str(), sizeof(config.WiFi.Ssid));
|
||||||
config.WiFi.Dhcp = true;
|
strlcpy(config.WiFi.Password, root["password"].as<String>().c_str(), sizeof(config.WiFi.Password));
|
||||||
} else {
|
strlcpy(config.WiFi.Hostname, root["hostname"].as<String>().c_str(), sizeof(config.WiFi.Hostname));
|
||||||
config.WiFi.Dhcp = false;
|
if (root["dhcp"].as<bool>()) {
|
||||||
|
config.WiFi.Dhcp = true;
|
||||||
|
} else {
|
||||||
|
config.WiFi.Dhcp = false;
|
||||||
|
}
|
||||||
|
config.WiFi.ApTimeout = root["aptimeout"].as<uint>();
|
||||||
|
config.Mdns.Enabled = root["mdnsenabled"].as<bool>();
|
||||||
}
|
}
|
||||||
config.WiFi.ApTimeout = root["aptimeout"].as<uint>();
|
|
||||||
config.Mdns.Enabled = root["mdnsenabled"].as<bool>();
|
|
||||||
|
|
||||||
WebApi.writeConfig(retMsg);
|
WebApi.writeConfig(retMsg);
|
||||||
|
|
||||||
|
|||||||
@ -135,13 +135,17 @@ void WebApiNtpClass::onNtpAdminPost(AsyncWebServerRequest* request)
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
CONFIG_T& config = Configuration.get();
|
{
|
||||||
strlcpy(config.Ntp.Server, root["ntp_server"].as<String>().c_str(), sizeof(config.Ntp.Server));
|
auto guard = Configuration.getWriteGuard();
|
||||||
strlcpy(config.Ntp.Timezone, root["ntp_timezone"].as<String>().c_str(), sizeof(config.Ntp.Timezone));
|
auto& config = guard.getConfig();
|
||||||
strlcpy(config.Ntp.TimezoneDescr, root["ntp_timezone_descr"].as<String>().c_str(), sizeof(config.Ntp.TimezoneDescr));
|
|
||||||
config.Ntp.Latitude = root["latitude"].as<double>();
|
strlcpy(config.Ntp.Server, root["ntp_server"].as<String>().c_str(), sizeof(config.Ntp.Server));
|
||||||
config.Ntp.Longitude = root["longitude"].as<double>();
|
strlcpy(config.Ntp.Timezone, root["ntp_timezone"].as<String>().c_str(), sizeof(config.Ntp.Timezone));
|
||||||
config.Ntp.SunsetType = root["sunsettype"].as<uint8_t>();
|
strlcpy(config.Ntp.TimezoneDescr, root["ntp_timezone_descr"].as<String>().c_str(), sizeof(config.Ntp.TimezoneDescr));
|
||||||
|
config.Ntp.Latitude = root["latitude"].as<double>();
|
||||||
|
config.Ntp.Longitude = root["longitude"].as<double>();
|
||||||
|
config.Ntp.SunsetType = root["sunsettype"].as<uint8_t>();
|
||||||
|
}
|
||||||
|
|
||||||
WebApi.writeConfig(retMsg);
|
WebApi.writeConfig(retMsg);
|
||||||
|
|
||||||
|
|||||||
@ -64,9 +64,13 @@ void WebApiSecurityClass::onSecurityPost(AsyncWebServerRequest* request)
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
CONFIG_T& config = Configuration.get();
|
{
|
||||||
strlcpy(config.Security.Password, root["password"].as<String>().c_str(), sizeof(config.Security.Password));
|
auto guard = Configuration.getWriteGuard();
|
||||||
config.Security.AllowReadonly = root["allow_readonly"].as<bool>();
|
auto& config = guard.getConfig();
|
||||||
|
|
||||||
|
strlcpy(config.Security.Password, root["password"].as<String>().c_str(), sizeof(config.Security.Password));
|
||||||
|
config.Security.AllowReadonly = root["allow_readonly"].as<bool>();
|
||||||
|
}
|
||||||
|
|
||||||
WebApi.writeConfig(retMsg);
|
WebApi.writeConfig(retMsg);
|
||||||
|
|
||||||
|
|||||||
17
src/main.cpp
17
src/main.cpp
@ -65,11 +65,11 @@ void setup()
|
|||||||
MessageOutput.println("done");
|
MessageOutput.println("done");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Configuration.init(scheduler);
|
||||||
|
|
||||||
// Read configuration values
|
// Read configuration values
|
||||||
MessageOutput.print("Reading configuration... ");
|
MessageOutput.print("Reading configuration... ");
|
||||||
if (!Configuration.read()) {
|
if (!Configuration.read()) {
|
||||||
MessageOutput.print("initializing... ");
|
|
||||||
Configuration.init();
|
|
||||||
if (Configuration.write()) {
|
if (Configuration.write()) {
|
||||||
MessageOutput.print("written... ");
|
MessageOutput.print("written... ");
|
||||||
} else {
|
} else {
|
||||||
@ -146,19 +146,6 @@ void setup()
|
|||||||
LedSingle.init(scheduler);
|
LedSingle.init(scheduler);
|
||||||
MessageOutput.println("done");
|
MessageOutput.println("done");
|
||||||
|
|
||||||
// Check for default DTU serial
|
|
||||||
MessageOutput.print("Check for default DTU serial... ");
|
|
||||||
if (config.Dtu.Serial == DTU_SERIAL) {
|
|
||||||
MessageOutput.print("generate serial based on ESP chip id: ");
|
|
||||||
const uint64_t dtuId = Utils::generateDtuSerial();
|
|
||||||
MessageOutput.printf("%0" PRIx32 "%08" PRIx32 "... ",
|
|
||||||
((uint32_t)((dtuId >> 32) & 0xFFFFFFFF)),
|
|
||||||
((uint32_t)(dtuId & 0xFFFFFFFF)));
|
|
||||||
config.Dtu.Serial = dtuId;
|
|
||||||
Configuration.write();
|
|
||||||
}
|
|
||||||
MessageOutput.println("done");
|
|
||||||
|
|
||||||
InverterSettings.init(scheduler);
|
InverterSettings.init(scheduler);
|
||||||
|
|
||||||
Datastore.init(scheduler);
|
Datastore.init(scheduler);
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user