#pragma once #define DEBUGLOG_DEFAULT_LOG_LEVEL_DEBUG #include #include #include #include class Config { public: static Config &getInstance() { static Config instance; return instance; } private: Config() = default; Config(const Config &) = delete; Config &operator=(const Config &) = delete; public: void init() { FSmount mount; // scoped mount of the filesystem // Initialize and mount filesystem LOG_INFO("Initializing Config"); if (!FFat.exists("/config.json")) { LOG_WARN("Initializing default config"); saveConfig(); } File file = FFat.open("/config.json", FILE_READ, false); if (!file) { LOG_ERROR("Unable to open config.json"); return; } if (ArduinoJson::deserializeJson(m_configJson, file) != ArduinoJson::DeserializationError::Ok) { LOG_ERROR("Unable to load config.json"); } std::string loadedConf; ArduinoJson::serializeJsonPretty(m_configJson, loadedConf); LOG_INFO("Loaded Configuration\n", loadedConf.c_str()); deserialize(); // convert from json format to class members file.close(); // close config file before unmounting filesystem }; ArduinoJson::JsonDocument& getConfig() { std::lock_guard lock(m_mutex); serialize(); return m_configJson; } void setConfig(const ArduinoJson::JsonDocument &json) { std::lock_guard lock(m_mutex); { FSmount mount; m_configJson = json; deserialize(); saveConfig(); }; // filesystem is unmounted here } void resetConfig() { std::lock_guard lock(m_mutex); { FSmount mount; LOG_WARN("Removing config.json"); if (!FFat.remove("/config.json")) { LOG_ERROR("Unable to remove config.json"); } LOG_WARN("Configuration reset, Restarting"); }; // filesystem is unmounted here delay(500); esp_restart(); } private: void saveConfig() // write configuration to flash memory { File file = FFat.open("/config.json", FILE_WRITE, true); if (!file) { LOG_ERROR("Unable to open config.json for writing"); return; } serialize(); // serialize default configuration if (ArduinoJson::serializeJson(m_configJson, file) == 0) { LOG_ERROR("Serialization Failed"); } file.close(); } ////////////////////////////////////////////////////////////// ////////////// SERIALIZATION + DESERIALIZATION /////////////// ////////////////////////////////////////////////////////////// void serialize() { // form class members to json document { auto globals = m_configJson["globals"].to(); globals["loopDelay"] = m_globalLoopDelay; }; { auto ethernet = m_configJson["ethernet"].to(); ethernet["hostname"] = m_ethHostname; ethernet["ipAddr"] = m_ethIpAddr; ethernet["netmask"] = m_ethNetmask; ethernet["gateway"] = m_ethGateway; }; { auto modbus = m_configJson["modbus"].to(); modbus["relayAddr"] = m_modbusRelayAddr; modbus["temperatureAddr"] = m_modbusTemperatureAddr; modbus["senecaAddr"] = m_modbusSenecaAddr; modbus["flowmeterAddr"] = m_modbusFlowmeterAddr; modbus["tankLevelAddr"] = m_modbusTankLevelAddr; }; { auto temperature = m_configJson["temperature"].to(); temperature["expectedSensors"] = m_tempExpectedSensors; auto values = temperature["correctionValues"].to(); for (auto v : m_tempCorrectionValues) { values.add(v); } }; { auto ntp = m_configJson["ntp"].to(); ntp["pool"] = m_ntpPool; ntp["timezone"] = m_ntpTimezone; ntp["updateInterval"] = m_ntpUpdateInterval; ntp["retries"] = m_ntpRetries; }; { auto mqtt = m_configJson["mqtt"].to(); mqtt["host"] = m_mqttHost; mqtt["port"] = m_mqttPort; mqtt["loopTime"] = m_mqttLoopTime; mqtt["clientName"] = m_mqttClientName; mqtt["retries"] = m_mqttRetries; mqtt["keepalive"] = m_mqttKeepalive; auto publish = mqtt["publish"].to(); for (auto v : m_mqttPublish) { publish[v.first] = v.second; } auto subscribe = mqtt["subscribe"].to(); for (auto v : m_mqttSubscribe) { subscribe[v.first] = v.second; } }; }; void deserialize() { // from json document to class members if (m_configJson.isNull()) { LOG_ERROR("NUll config document"); return; } { auto globals = m_configJson["globals"]; m_globalLoopDelay = globals["loopDelay"].as(); }; { auto ethernet = m_configJson["ethernet"]; m_ethHostname = ethernet["hostname"].as(); m_ethIpAddr = ethernet["ipAddr"].as(); m_ethNetmask = ethernet["netmask"].as(); m_ethGateway = ethernet["gateway"].as(); }; { auto modbus = m_configJson["modbus"]; m_modbusRelayAddr = modbus["relayAddr"].as(); m_modbusTemperatureAddr = modbus["temperatureAddr"].as(); m_modbusSenecaAddr = modbus["senecaAddr"].as(); m_modbusFlowmeterAddr = modbus["flowmeterAddr"].as(); m_modbusTankLevelAddr = modbus["tankLevelAddr"].as(); }; { auto temperature = m_configJson["temperature"]; m_tempExpectedSensors = temperature["expectedSensors"].as(); auto values = temperature["correctionValues"].as(); m_tempCorrectionValues.clear(); m_tempCorrectionValues.reserve(values.size()); for (auto v : values) { m_tempCorrectionValues.emplace_back(v.as()); } }; { auto ntp = m_configJson["ntp"]; m_ntpPool = ntp["pool"].as(); m_ntpTimezone = ntp["timezone"].as(); m_ntpUpdateInterval = ntp["updateInterval"].as(); m_ntpRetries = ntp["retries"].as(); }; { auto mqtt = m_configJson["mqtt"]; m_mqttHost = mqtt["host"].as(); m_mqttPort = mqtt["port"].as(); m_mqttLoopTime = mqtt["loopTime"].as(); m_mqttKeepalive = mqtt["keepalive"].as(); m_mqttRetries = mqtt["retries"].as(); auto subscribe = mqtt["subscribe"].as(); for (auto v : subscribe) { m_mqttSubscribe[v.key().c_str()] = v.value().as(); } auto publish = mqtt["publish"].as(); for (auto v : publish) { m_mqttPublish[v.key().c_str()] = v.value().as(); } }; }; private: ArduinoJson::JsonDocument m_configJson; std::mutex m_mutex; public: // Globals std::uint16_t m_globalLoopDelay = 5000; // in milliseconds // Ethernet std::string m_ethHostname = "ETcontroller_PRO"; std::string m_ethIpAddr = "10.0.2.251"; std::string m_ethNetmask = "255.255.255.0"; std::string m_ethGateway = "10.0.2.1"; // MODBUS uint8_t m_modbusRelayAddr = 0x01; uint8_t m_modbusTemperatureAddr = 0xAA; uint8_t m_modbusSenecaAddr = 0xBB; uint8_t m_modbusFlowmeterAddr = 0xCC; uint8_t m_modbusTankLevelAddr = 0xDD; // Temperature Board uint8_t m_tempExpectedSensors = 1; std::vector m_tempCorrectionValues = std::vector(8, 0.0f); // NTP std::string m_ntpPool = "pool.ntp.org"; uint16_t m_ntpTimezone = 3600; // GTM +1 uint16_t m_ntpUpdateInterval = 3600; // every hour uint8_t m_ntpRetries = 5; // MQTT std::string m_mqttHost = "10.0.2.249"; uint16_t m_mqttPort = 1883; uint16_t m_mqttLoopTime = 100; // in milliseconds uint8_t m_mqttKeepalive = 15; uint8_t m_mqttRetries = 5; std::string m_mqttClientName = "etcontrollerPRO"; std::map m_mqttSubscribe = { {"commands", "etcontroller/hw/commands"}}; std::map m_mqttPublish = { {"cronjobs", "etcontroller/hw/cronjobs"}, {"answers", "etcontroller/hw/answers"}, {"heatpump", "etcontroller/hw/heatpump"}, {"temperatures", "etcontroller/hw/temperatures"}, {"irrigation", "etcontroller/hw/irrigation"}}; };