From b1edb13b3c32c0e8f0e0196eed14de1c0ee8c923 Mon Sep 17 00:00:00 2001 From: Bernhard Kirchen Date: Thu, 17 Oct 2024 21:46:32 +0200 Subject: [PATCH] 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. --- include/Configuration.h | 24 +++++++++++-- src/Configuration.cpp | 58 ++++++++++++++++++++++++++++-- src/WebApi.cpp | 4 +-- src/WebApi_device.cpp | 35 ++++++++++-------- src/WebApi_dtu.cpp | 20 ++++++----- src/WebApi_inverter.cpp | 78 +++++++++++++++++++++++------------------ src/WebApi_mqtt.cpp | 64 +++++++++++++++++---------------- src/WebApi_network.cpp | 64 +++++++++++++++++---------------- src/WebApi_ntp.cpp | 18 ++++++---- src/WebApi_security.cpp | 10 ++++-- src/main.cpp | 17 ++------- 11 files changed, 243 insertions(+), 149 deletions(-) diff --git a/include/Configuration.h b/include/Configuration.h index 4a802e4e1..ce7bfbeea 100644 --- a/include/Configuration.h +++ b/include/Configuration.h @@ -3,6 +3,9 @@ #include "PinMapping.h" #include +#include +#include +#include #define CONFIG_FILENAME "/config.json" #define CONFIG_VERSION 0x00011c00 // 0.1.28 // make sure to clean all after change @@ -161,15 +164,32 @@ struct CONFIG_T { class ConfigurationClass { public: - void init(); + void init(Scheduler& scheduler); bool read(); bool write(); void migrate(); - CONFIG_T& get(); + CONFIG_T const& get(); + + class WriteGuard { + public: + WriteGuard(); + CONFIG_T& getConfig(); + ~WriteGuard(); + + private: + std::unique_lock _lock; + }; + + WriteGuard getWriteGuard(); INVERTER_CONFIG_T* getFreeInverterSlot(); INVERTER_CONFIG_T* getInverterConfig(const uint64_t serial); void deleteInverterById(const uint8_t id); + +private: + void loop(); + + Task _loopTask; }; extern ConfigurationClass Configuration; diff --git a/src/Configuration.cpp b/src/Configuration.cpp index db47d9c8a..34902e94f 100644 --- a/src/Configuration.cpp +++ b/src/Configuration.cpp @@ -13,8 +13,17 @@ 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)); } @@ -318,6 +327,20 @@ bool ConfigurationClass::read() } 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; } @@ -390,11 +413,16 @@ void ConfigurationClass::migrate() read(); } -CONFIG_T& ConfigurationClass::get() +CONFIG_T const& ConfigurationClass::get() { return config; } +ConfigurationClass::WriteGuard ConfigurationClass::getWriteGuard() +{ + return WriteGuard(); +} + INVERTER_CONFIG_T* ConfigurationClass::getFreeInverterSlot() { 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 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; diff --git a/src/WebApi.cpp b/src/WebApi.cpp index b3255c417..31eb122cc 100644 --- a/src/WebApi.cpp +++ b/src/WebApi.cpp @@ -47,7 +47,7 @@ void WebApiClass::reload() bool WebApiClass::checkCredentials(AsyncWebServerRequest* request) { - CONFIG_T& config = Configuration.get(); + auto const& config = Configuration.get(); if (request->authenticate(AUTH_USERNAME, config.Security.Password)) { return true; } @@ -65,7 +65,7 @@ bool WebApiClass::checkCredentials(AsyncWebServerRequest* request) bool WebApiClass::checkCredentialsReadonly(AsyncWebServerRequest* request) { - CONFIG_T& config = Configuration.get(); + auto const& config = Configuration.get(); if (config.Security.AllowReadonly) { return true; } else { diff --git a/src/WebApi_device.cpp b/src/WebApi_device.cpp index 29686fe08..109ff1a99 100644 --- a/src/WebApi_device.cpp +++ b/src/WebApi_device.cpp @@ -129,23 +129,28 @@ void WebApiDeviceClass::onDeviceAdminPost(AsyncWebServerRequest* request) return; } - CONFIG_T& config = Configuration.get(); - bool performRestart = root["curPin"]["name"].as() != config.Dev_PinMapping; - - strlcpy(config.Dev_PinMapping, root["curPin"]["name"].as().c_str(), sizeof(config.Dev_PinMapping)); - config.Display.Rotation = root["display"]["rotation"].as(); - config.Display.PowerSafe = root["display"]["power_safe"].as(); - config.Display.ScreenSaver = root["display"]["screensaver"].as(); - config.Display.Contrast = root["display"]["contrast"].as(); - config.Display.Language = root["display"]["language"].as(); - config.Display.Diagram.Duration = root["display"]["diagramduration"].as(); - config.Display.Diagram.Mode = root["display"]["diagrammode"].as(); - - for (uint8_t i = 0; i < PINMAPPING_LED_COUNT; i++) { - config.Led_Single[i].Brightness = root["led"][i]["brightness"].as(); - config.Led_Single[i].Brightness = min(100, config.Led_Single[i].Brightness); + { + auto guard = Configuration.getWriteGuard(); + auto& config = guard.getConfig(); + + strlcpy(config.Dev_PinMapping, root["curPin"]["name"].as().c_str(), sizeof(config.Dev_PinMapping)); + config.Display.Rotation = root["display"]["rotation"].as(); + config.Display.PowerSafe = root["display"]["power_safe"].as(); + config.Display.ScreenSaver = root["display"]["screensaver"].as(); + config.Display.Contrast = root["display"]["contrast"].as(); + config.Display.Language = root["display"]["language"].as(); + config.Display.Diagram.Duration = root["display"]["diagramduration"].as(); + config.Display.Diagram.Mode = root["display"]["diagrammode"].as(); + + for (uint8_t i = 0; i < PINMAPPING_LED_COUNT; i++) { + config.Led_Single[i].Brightness = root["led"][i]["brightness"].as(); + config.Led_Single[i].Brightness = min(100, config.Led_Single[i].Brightness); + } } + auto const& config = Configuration.get(); + bool performRestart = root["curPin"]["name"].as() != config.Dev_PinMapping; + Display.setDiagramMode(static_cast(config.Display.Diagram.Mode)); Display.setOrientation(config.Display.Rotation); Display.enablePowerSafe = config.Display.PowerSafe; diff --git a/src/WebApi_dtu.cpp b/src/WebApi_dtu.cpp index 7c6c3f738..e6fb8994c 100644 --- a/src/WebApi_dtu.cpp +++ b/src/WebApi_dtu.cpp @@ -27,7 +27,7 @@ void WebApiDtuClass::init(AsyncWebServer& server, Scheduler& scheduler) void WebApiDtuClass::applyDataTaskCb() { // 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.getRadioCmt()->setPALevel(config.Dtu.Cmt.PaLevel); Hoymiles.getRadioNrf()->setDtuSerial(config.Dtu.Serial); @@ -153,14 +153,16 @@ void WebApiDtuClass::onDtuAdminPost(AsyncWebServerRequest* request) return; } - CONFIG_T& config = Configuration.get(); - - config.Dtu.Serial = serial; - config.Dtu.PollInterval = root["pollinterval"].as(); - config.Dtu.Nrf.PaLevel = root["nrf_palevel"].as(); - config.Dtu.Cmt.PaLevel = root["cmt_palevel"].as(); - config.Dtu.Cmt.Frequency = root["cmt_frequency"].as(); - config.Dtu.Cmt.CountryMode = root["cmt_country"].as(); + { + auto guard = Configuration.getWriteGuard(); + auto& config = guard.getConfig(); + config.Dtu.Serial = serial; + config.Dtu.PollInterval = root["pollinterval"].as(); + config.Dtu.Nrf.PaLevel = root["nrf_palevel"].as(); + config.Dtu.Cmt.PaLevel = root["cmt_palevel"].as(); + config.Dtu.Cmt.Frequency = root["cmt_frequency"].as(); + config.Dtu.Cmt.CountryMode = root["cmt_country"].as(); + } WebApi.writeConfig(retMsg); diff --git a/src/WebApi_inverter.cpp b/src/WebApi_inverter.cpp index ef353158f..4db9a57c9 100644 --- a/src/WebApi_inverter.cpp +++ b/src/WebApi_inverter.cpp @@ -184,9 +184,9 @@ void WebApiInverterClass::onInverterEdit(AsyncWebServerRequest* request) } // Interpret the string as a hex value and convert it to uint64_t - const uint64_t serial = strtoll(root["serial"].as().c_str(), NULL, 16); + const uint64_t new_serial = strtoll(root["serial"].as().c_str(), NULL, 16); - if (serial == 0) { + if (new_serial == 0) { retMsg["message"] = "Serial must be a number > 0!"; retMsg["code"] = WebApiError::InverterSerialZero; WebApi.sendJsonResponse(request, response, __FUNCTION__, __LINE__); @@ -209,37 +209,42 @@ void WebApiInverterClass::onInverterEdit(AsyncWebServerRequest* request) return; } - INVERTER_CONFIG_T& inverter = Configuration.get().Inverter[root["id"].as()]; - - uint64_t new_serial = serial; - uint64_t old_serial = inverter.Serial; - - // Interpret the string as a hex value and convert it to uint64_t - inverter.Serial = new_serial; - strncpy(inverter.Name, root["name"].as().c_str(), INV_MAX_NAME_STRLEN); - - inverter.Poll_Enable = root["poll_enable"] | true; - inverter.Poll_Enable_Night = root["poll_enable_night"] | true; - inverter.Command_Enable = root["command_enable"] | true; - 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; - for (JsonVariant channel : channelArray) { - inverter.channel[arrayCount].MaxChannelPower = channel["max_power"].as(); - inverter.channel[arrayCount].YieldTotalOffset = channel["yield_total_offset"].as(); - strncpy(inverter.channel[arrayCount].Name, channel["name"] | "", sizeof(inverter.channel[arrayCount].Name)); - arrayCount++; + uint64_t old_serial = 0; + + { + auto guard = Configuration.getWriteGuard(); + auto& config = guard.getConfig(); + + INVERTER_CONFIG_T& inverter = config.Inverter[root["id"].as()]; + + old_serial = inverter.Serial; + inverter.Serial = new_serial; + strncpy(inverter.Name, root["name"].as().c_str(), INV_MAX_NAME_STRLEN); + + inverter.Poll_Enable = root["poll_enable"] | true; + inverter.Poll_Enable_Night = root["poll_enable_night"] | true; + inverter.Command_Enable = root["command_enable"] | true; + 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; + for (JsonVariant channel : channelArray) { + inverter.channel[arrayCount].MaxChannelPower = channel["max_power"].as(); + inverter.channel[arrayCount].YieldTotalOffset = channel["yield_total_offset"].as(); + strncpy(inverter.channel[arrayCount].Name, channel["name"] | "", sizeof(inverter.channel[arrayCount].Name)); + arrayCount++; + } } WebApi.writeConfig(retMsg, WebApiError::InverterChanged, "Inverter changed!"); WebApi.sendJsonResponse(request, response, __FUNCTION__, __LINE__); + INVERTER_CONFIG_T const& inverter = Configuration.get().Inverter[root["id"].as()]; std::shared_ptr inv = Hoymiles.getInverterBySerial(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(); - INVERTER_CONFIG_T& inverter = Configuration.get().Inverter[inverter_id]; + INVERTER_CONFIG_T const& inverter = Configuration.get().Inverter[inverter_id]; Hoymiles.removeInverterBySerial(inverter.Serial); @@ -337,13 +342,18 @@ void WebApiInverterClass::onInverterOrder(AsyncWebServerRequest* request) // The order array contains list or id in the right order JsonArray orderArray = root["order"].as(); uint8_t order = 0; - for (JsonVariant id : orderArray) { - uint8_t inverter_id = id.as(); - if (inverter_id < INV_MAX_COUNT) { - INVERTER_CONFIG_T& inverter = Configuration.get().Inverter[inverter_id]; - inverter.Order = order; + { + auto guard = Configuration.getWriteGuard(); + auto& config = guard.getConfig(); + + for (JsonVariant id : orderArray) { + uint8_t inverter_id = id.as(); + 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!"); diff --git a/src/WebApi_mqtt.cpp b/src/WebApi_mqtt.cpp index 0c8ee5c6e..7558110e3 100644 --- a/src/WebApi_mqtt.cpp +++ b/src/WebApi_mqtt.cpp @@ -271,36 +271,40 @@ void WebApiMqttClass::onMqttAdminPost(AsyncWebServerRequest* request) } } - CONFIG_T& config = Configuration.get(); - config.Mqtt.Enabled = root["mqtt_enabled"].as(); - config.Mqtt.Retain = root["mqtt_retain"].as(); - config.Mqtt.Tls.Enabled = root["mqtt_tls"].as(); - strlcpy(config.Mqtt.Tls.RootCaCert, root["mqtt_root_ca_cert"].as().c_str(), sizeof(config.Mqtt.Tls.RootCaCert)); - config.Mqtt.Tls.CertLogin = root["mqtt_tls_cert_login"].as(); - strlcpy(config.Mqtt.Tls.ClientCert, root["mqtt_client_cert"].as().c_str(), sizeof(config.Mqtt.Tls.ClientCert)); - strlcpy(config.Mqtt.Tls.ClientKey, root["mqtt_client_key"].as().c_str(), sizeof(config.Mqtt.Tls.ClientKey)); - config.Mqtt.Port = root["mqtt_port"].as(); - strlcpy(config.Mqtt.Hostname, root["mqtt_hostname"].as().c_str(), sizeof(config.Mqtt.Hostname)); - strlcpy(config.Mqtt.ClientId, root["mqtt_clientid"].as().c_str(), sizeof(config.Mqtt.ClientId)); - strlcpy(config.Mqtt.Username, root["mqtt_username"].as().c_str(), sizeof(config.Mqtt.Username)); - strlcpy(config.Mqtt.Password, root["mqtt_password"].as().c_str(), sizeof(config.Mqtt.Password)); - strlcpy(config.Mqtt.Lwt.Topic, root["mqtt_lwt_topic"].as().c_str(), sizeof(config.Mqtt.Lwt.Topic)); - strlcpy(config.Mqtt.Lwt.Value_Online, root["mqtt_lwt_online"].as().c_str(), sizeof(config.Mqtt.Lwt.Value_Online)); - strlcpy(config.Mqtt.Lwt.Value_Offline, root["mqtt_lwt_offline"].as().c_str(), sizeof(config.Mqtt.Lwt.Value_Offline)); - config.Mqtt.Lwt.Qos = root["mqtt_lwt_qos"].as(); - config.Mqtt.PublishInterval = root["mqtt_publish_interval"].as(); - config.Mqtt.CleanSession = root["mqtt_clean_session"].as(); - config.Mqtt.Hass.Enabled = root["mqtt_hass_enabled"].as(); - config.Mqtt.Hass.Expire = root["mqtt_hass_expire"].as(); - config.Mqtt.Hass.Retain = root["mqtt_hass_retain"].as(); - config.Mqtt.Hass.IndividualPanels = root["mqtt_hass_individualpanels"].as(); - strlcpy(config.Mqtt.Hass.Topic, root["mqtt_hass_topic"].as().c_str(), sizeof(config.Mqtt.Hass.Topic)); - - // Check if base topic was changed - if (strcmp(config.Mqtt.Topic, root["mqtt_topic"].as().c_str())) { - MqttHandleInverter.unsubscribeTopics(); - strlcpy(config.Mqtt.Topic, root["mqtt_topic"].as().c_str(), sizeof(config.Mqtt.Topic)); - MqttHandleInverter.subscribeTopics(); + { + auto guard = Configuration.getWriteGuard(); + auto& config = guard.getConfig(); + + config.Mqtt.Enabled = root["mqtt_enabled"].as(); + config.Mqtt.Retain = root["mqtt_retain"].as(); + config.Mqtt.Tls.Enabled = root["mqtt_tls"].as(); + strlcpy(config.Mqtt.Tls.RootCaCert, root["mqtt_root_ca_cert"].as().c_str(), sizeof(config.Mqtt.Tls.RootCaCert)); + config.Mqtt.Tls.CertLogin = root["mqtt_tls_cert_login"].as(); + strlcpy(config.Mqtt.Tls.ClientCert, root["mqtt_client_cert"].as().c_str(), sizeof(config.Mqtt.Tls.ClientCert)); + strlcpy(config.Mqtt.Tls.ClientKey, root["mqtt_client_key"].as().c_str(), sizeof(config.Mqtt.Tls.ClientKey)); + config.Mqtt.Port = root["mqtt_port"].as(); + strlcpy(config.Mqtt.Hostname, root["mqtt_hostname"].as().c_str(), sizeof(config.Mqtt.Hostname)); + strlcpy(config.Mqtt.ClientId, root["mqtt_clientid"].as().c_str(), sizeof(config.Mqtt.ClientId)); + strlcpy(config.Mqtt.Username, root["mqtt_username"].as().c_str(), sizeof(config.Mqtt.Username)); + strlcpy(config.Mqtt.Password, root["mqtt_password"].as().c_str(), sizeof(config.Mqtt.Password)); + strlcpy(config.Mqtt.Lwt.Topic, root["mqtt_lwt_topic"].as().c_str(), sizeof(config.Mqtt.Lwt.Topic)); + strlcpy(config.Mqtt.Lwt.Value_Online, root["mqtt_lwt_online"].as().c_str(), sizeof(config.Mqtt.Lwt.Value_Online)); + strlcpy(config.Mqtt.Lwt.Value_Offline, root["mqtt_lwt_offline"].as().c_str(), sizeof(config.Mqtt.Lwt.Value_Offline)); + config.Mqtt.Lwt.Qos = root["mqtt_lwt_qos"].as(); + config.Mqtt.PublishInterval = root["mqtt_publish_interval"].as(); + config.Mqtt.CleanSession = root["mqtt_clean_session"].as(); + config.Mqtt.Hass.Enabled = root["mqtt_hass_enabled"].as(); + config.Mqtt.Hass.Expire = root["mqtt_hass_expire"].as(); + config.Mqtt.Hass.Retain = root["mqtt_hass_retain"].as(); + config.Mqtt.Hass.IndividualPanels = root["mqtt_hass_individualpanels"].as(); + strlcpy(config.Mqtt.Hass.Topic, root["mqtt_hass_topic"].as().c_str(), sizeof(config.Mqtt.Hass.Topic)); + + // Check if base topic was changed + if (strcmp(config.Mqtt.Topic, root["mqtt_topic"].as().c_str())) { + MqttHandleInverter.unsubscribeTopics(); + strlcpy(config.Mqtt.Topic, root["mqtt_topic"].as().c_str(), sizeof(config.Mqtt.Topic)); + MqttHandleInverter.subscribeTopics(); + } } WebApi.writeConfig(retMsg); diff --git a/src/WebApi_network.cpp b/src/WebApi_network.cpp index 75275755f..51db32e43 100644 --- a/src/WebApi_network.cpp +++ b/src/WebApi_network.cpp @@ -164,37 +164,41 @@ void WebApiNetworkClass::onNetworkAdminPost(AsyncWebServerRequest* request) return; } - CONFIG_T& config = Configuration.get(); - config.WiFi.Ip[0] = ipaddress[0]; - config.WiFi.Ip[1] = ipaddress[1]; - config.WiFi.Ip[2] = ipaddress[2]; - config.WiFi.Ip[3] = ipaddress[3]; - config.WiFi.Netmask[0] = netmask[0]; - config.WiFi.Netmask[1] = netmask[1]; - config.WiFi.Netmask[2] = netmask[2]; - config.WiFi.Netmask[3] = netmask[3]; - config.WiFi.Gateway[0] = gateway[0]; - config.WiFi.Gateway[1] = gateway[1]; - config.WiFi.Gateway[2] = gateway[2]; - config.WiFi.Gateway[3] = gateway[3]; - config.WiFi.Dns1[0] = dns1[0]; - config.WiFi.Dns1[1] = dns1[1]; - config.WiFi.Dns1[2] = dns1[2]; - config.WiFi.Dns1[3] = dns1[3]; - config.WiFi.Dns2[0] = dns2[0]; - config.WiFi.Dns2[1] = dns2[1]; - config.WiFi.Dns2[2] = dns2[2]; - config.WiFi.Dns2[3] = dns2[3]; - strlcpy(config.WiFi.Ssid, root["ssid"].as().c_str(), sizeof(config.WiFi.Ssid)); - strlcpy(config.WiFi.Password, root["password"].as().c_str(), sizeof(config.WiFi.Password)); - strlcpy(config.WiFi.Hostname, root["hostname"].as().c_str(), sizeof(config.WiFi.Hostname)); - if (root["dhcp"].as()) { - config.WiFi.Dhcp = true; - } else { - config.WiFi.Dhcp = false; + { + auto guard = Configuration.getWriteGuard(); + auto& config = guard.getConfig(); + + config.WiFi.Ip[0] = ipaddress[0]; + config.WiFi.Ip[1] = ipaddress[1]; + config.WiFi.Ip[2] = ipaddress[2]; + config.WiFi.Ip[3] = ipaddress[3]; + config.WiFi.Netmask[0] = netmask[0]; + config.WiFi.Netmask[1] = netmask[1]; + config.WiFi.Netmask[2] = netmask[2]; + config.WiFi.Netmask[3] = netmask[3]; + config.WiFi.Gateway[0] = gateway[0]; + config.WiFi.Gateway[1] = gateway[1]; + config.WiFi.Gateway[2] = gateway[2]; + config.WiFi.Gateway[3] = gateway[3]; + config.WiFi.Dns1[0] = dns1[0]; + config.WiFi.Dns1[1] = dns1[1]; + config.WiFi.Dns1[2] = dns1[2]; + config.WiFi.Dns1[3] = dns1[3]; + config.WiFi.Dns2[0] = dns2[0]; + config.WiFi.Dns2[1] = dns2[1]; + config.WiFi.Dns2[2] = dns2[2]; + config.WiFi.Dns2[3] = dns2[3]; + strlcpy(config.WiFi.Ssid, root["ssid"].as().c_str(), sizeof(config.WiFi.Ssid)); + strlcpy(config.WiFi.Password, root["password"].as().c_str(), sizeof(config.WiFi.Password)); + strlcpy(config.WiFi.Hostname, root["hostname"].as().c_str(), sizeof(config.WiFi.Hostname)); + if (root["dhcp"].as()) { + config.WiFi.Dhcp = true; + } else { + config.WiFi.Dhcp = false; + } + config.WiFi.ApTimeout = root["aptimeout"].as(); + config.Mdns.Enabled = root["mdnsenabled"].as(); } - config.WiFi.ApTimeout = root["aptimeout"].as(); - config.Mdns.Enabled = root["mdnsenabled"].as(); WebApi.writeConfig(retMsg); diff --git a/src/WebApi_ntp.cpp b/src/WebApi_ntp.cpp index 5dc874b53..f58a2bbd6 100644 --- a/src/WebApi_ntp.cpp +++ b/src/WebApi_ntp.cpp @@ -135,13 +135,17 @@ void WebApiNtpClass::onNtpAdminPost(AsyncWebServerRequest* request) return; } - CONFIG_T& config = Configuration.get(); - strlcpy(config.Ntp.Server, root["ntp_server"].as().c_str(), sizeof(config.Ntp.Server)); - strlcpy(config.Ntp.Timezone, root["ntp_timezone"].as().c_str(), sizeof(config.Ntp.Timezone)); - strlcpy(config.Ntp.TimezoneDescr, root["ntp_timezone_descr"].as().c_str(), sizeof(config.Ntp.TimezoneDescr)); - config.Ntp.Latitude = root["latitude"].as(); - config.Ntp.Longitude = root["longitude"].as(); - config.Ntp.SunsetType = root["sunsettype"].as(); + { + auto guard = Configuration.getWriteGuard(); + auto& config = guard.getConfig(); + + strlcpy(config.Ntp.Server, root["ntp_server"].as().c_str(), sizeof(config.Ntp.Server)); + strlcpy(config.Ntp.Timezone, root["ntp_timezone"].as().c_str(), sizeof(config.Ntp.Timezone)); + strlcpy(config.Ntp.TimezoneDescr, root["ntp_timezone_descr"].as().c_str(), sizeof(config.Ntp.TimezoneDescr)); + config.Ntp.Latitude = root["latitude"].as(); + config.Ntp.Longitude = root["longitude"].as(); + config.Ntp.SunsetType = root["sunsettype"].as(); + } WebApi.writeConfig(retMsg); diff --git a/src/WebApi_security.cpp b/src/WebApi_security.cpp index 6be21ca6a..8ebd6fb6a 100644 --- a/src/WebApi_security.cpp +++ b/src/WebApi_security.cpp @@ -64,9 +64,13 @@ void WebApiSecurityClass::onSecurityPost(AsyncWebServerRequest* request) return; } - CONFIG_T& config = Configuration.get(); - strlcpy(config.Security.Password, root["password"].as().c_str(), sizeof(config.Security.Password)); - config.Security.AllowReadonly = root["allow_readonly"].as(); + { + auto guard = Configuration.getWriteGuard(); + auto& config = guard.getConfig(); + + strlcpy(config.Security.Password, root["password"].as().c_str(), sizeof(config.Security.Password)); + config.Security.AllowReadonly = root["allow_readonly"].as(); + } WebApi.writeConfig(retMsg); diff --git a/src/main.cpp b/src/main.cpp index b1d974d74..7b04e7cd6 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -65,11 +65,11 @@ void setup() MessageOutput.println("done"); } + Configuration.init(scheduler); + // Read configuration values MessageOutput.print("Reading configuration... "); if (!Configuration.read()) { - MessageOutput.print("initializing... "); - Configuration.init(); if (Configuration.write()) { MessageOutput.print("written... "); } else { @@ -146,19 +146,6 @@ void setup() LedSingle.init(scheduler); 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); Datastore.init(scheduler);