I have a config.h file that contains all the default values for everything that can be configured. Some things can be changed by the user and some are just constants for classes in my project. Here are some examples of variables inside the config.h file:
#include <Arduino.h>
// * Not changable in UI *
// The software version of the NodeMCU
const String Config_Version = "0.0";
// * Not changable in UI *
// The unique serial code of the NodeMCU
const String Config_SerialCode = "1";
// * Changable in UI *
// On means the speaker beeps if the temperature or humidity gets out of range
const bool Config_Speaker_On = true;
// * Changable in UI *
// The default name this sensor is associated with.
const String Config_Network_SensorName = "Senzor 1";
// * Not Changable in UI *
// The endpoint where the app sends the request to get the current temperature and humidity.
const String Config_NetworkAccessPoint_GetDataEndpoint = "/get-data";
And i have another class named ConfigurationManager that in the constructor it checks inside the spiffs if there is a Configuration file (where there are only the values that are not declared inside the class as constant and can be changed by the user) and if yes it takes those values from there and if not it sets them as the values in the config.h. The constant values not changable by the user are initialized with values from config.h in the header of the class directly.
header:
#pragma once
#include <Arduino.h>
#include <ArduinoJson.h>
#include <FS.h>
#include "Config.h"
#include "ExceptionManager.h"
class ConfigurationManager
{
public:
ConfigurationManager(ExceptionManager & Exceptions);
~ConfigurationManager() = default;
bool SaveExists() const;
void Load();
void Save() const;
void Reset();
DynamicJsonDocument GetAsJSON() const;
void SetFromJSON(const DynamicJsonDocument & ConfigurationJSON);
bool IsConfigurationJSONValid(const DynamicJsonDocument & ConfigurationJSON) const;
const String Version = Config_Version;
const String SerialCode = Config_SerialCode;
const unsigned short Package = Config_Package;
const String UniqueUserToken = Config_UniqueUserToken.c_str();
const String UniquePasswordToken = Config_UniquePasswordToken;
unsigned short Pin_TemperatureSensor;
unsigned short Pin_SwitchButton;
unsigned short Pin_Speaker;
unsigned short Pin_HumiditySensor;
const unsigned int Timing_Restart = Config_Timing_Restart;
const unsigned int Timing_SendDataToServer = Config_Timing_SendDataToServer;
const String Memory_ConfigurationFilename = Config_Memory_ConfigurationFilename;
const unsigned int Memory_JSONConfigurationSize = Config_Memory_JSONConfigurationSize;
const unsigned int Tasks_ExpectedTaskNumber = Config_Tasks_ExpectedTaskNumber;
unsigned short TemperatureSensor_Unit;
const unsigned int TemperatureSensor_ReadingCooldown = Config_TemperatureSensor_ReadingCooldown;
const unsigned int TemperatureSensor_AcceptableWarnings = Config_TemperatureSensor_AcceptableWarnings;
const unsigned int SwitchButton_DebounceDelay = Config_SwitchButton_DebounceDelay;
bool Speaker_On;
const unsigned short (*Speaker_Alarm)[2] = Config_Speaker_Alarm;
const unsigned short Speaker_AlarmSteps = Config_Speaker_AlarmSteps;
String Network_SensorName;
const unsigned short Network_DataSize = Config_Network_DataSize;
const String NetworkAccessPoint_SSID = Config_NetworkAccessPoint_SSID;
String NetworkAccessPoint_Password;
const unsigned short NetworkAccessPoint_WebServerPort = Config_NetworkAccessPoint_WebServerPort;
const String NetworkAccessPoint_WebServerIP = Config_NetworkAccessPoint_WebServerIP;
const String NetworkAccessPoint_SubnetMask = Config_NetworkAccessPoint_SubnetMask;
const String NetworkAccessPoint_GatewayIP = Config_NetworkAccessPoint_GatewayIP;
const String NetworkAccessPoint_GetVersionEndpoint = Config_NetworkAccessPoint_GetVersionEndpoint;
const String NetworkAccessPoint_GetDataEndpoint = Config_NetworkAccessPoint_GetDataEndpoint;
const String NetworkAccessPoint_GetExceptionsEndpoint = Config_NetworkAccessPoint_GetExceptionsEndpoint;
const String NetworkAccessPoint_GetConfigurationEndpoint = Config_NetworkAccessPoint_GetConfigurationEndpoint;
const String NetworkAccessPoint_PostConfigurationEndpoint = Config_NetworkAccessPoint_PostConfigurationEndpoint;
const String NetworkAccessPoint_PostAdvancedConfigurationEndpoint = Config_NetworkAccessPoint_PostAdvancedConfigurationEndpoint;
const unsigned short NetworkAccessPoint_ExceptionByteNumber = Config_NetworkAccessPoint_ExceptionByteNumber;
const unsigned short NetworkAccessPoint_VersionSerialByteNumber = Config_NetworkAccessPoint_VersionSerialByteNumber;
String NetworkClient_SSID;
String NetworkClient_Password;
String NetworkClient_Domain;
unsigned short NetworkClient_Port;
String NetworkClient_Endpoint;
private:
ExceptionManager & _Exceptions;
};
cpp:
#include "ConfigurationManager.h"
/*-------------------------[ Public ]-------------------------*/
ConfigurationManager::ConfigurationManager(ExceptionManager & Exceptions): _Exceptions(Exceptions)
{
if(!SPIFFS.begin())
{
this -> _Exceptions.Add(Exception::Memory_Initialization);
return;
}
if(this -> SaveExists())
{
this -> Load();
}
else
{
this -> Reset();
}
}
bool ConfigurationManager::SaveExists() const
{
return SPIFFS.exists(this -> Memory_ConfigurationFilename);
}
void ConfigurationManager::Load()
{
if(!this -> SaveExists())
{
return;
}
File ConfigurationFile = SPIFFS.open(this -> Memory_ConfigurationFilename, "r");
if(!ConfigurationFile)
{
this -> _Exceptions.Add(Exception::Memory_Open);
return;
}
size_t ConfigurationSize = ConfigurationFile.size();
std::unique_ptr <char[]> ConfigurationJSONBuffer(new char[ConfigurationSize]);
size_t BytesRead = ConfigurationFile.readBytes(ConfigurationJSONBuffer.get(), ConfigurationSize);
ConfigurationFile.close();
if(BytesRead != ConfigurationSize)
{
this -> _Exceptions.Add(Exception::Memory_Read);
return;
}
DynamicJsonDocument ConfigurationJSON(this -> Memory_JSONConfigurationSize);
if(deserializeJson(ConfigurationJSON, ConfigurationJSONBuffer.get()) != DeserializationError::Ok)
{
this -> _Exceptions.Add(Exception::Memory_CorruptConfiguration);
return;
}
this -> SetFromJSON(ConfigurationJSON);
}
void ConfigurationManager::Save() const
{
File ConfigurationFile = SPIFFS.open(this -> Memory_ConfigurationFilename, "w");
if(!ConfigurationFile)
{
this -> _Exceptions.Add(Exception::Memory_Open);
return;
}
if(serializeJson(this -> GetAsJSON(), ConfigurationFile) == 0)
{
this -> _Exceptions.Add(Exception::Memory_InvalidConfiguration);
ConfigurationFile.close();
return;
}
ConfigurationFile.close();
}
void ConfigurationManager::Reset()
{
this -> Pin_TemperatureSensor = Config_Pin_TemperatureSensor;
this -> Pin_SwitchButton = Config_Pin_SwitchButton;
this -> Pin_Speaker = Config_Pin_Speaker;
this -> Pin_HumiditySensor = Config_Pin_HumiditySensor;
this -> TemperatureSensor_Unit = Config_TemperatureSensor_Unit;
this -> Speaker_On = Config_Speaker_On;
this -> Network_SensorName = Config_Network_SensorName;
this -> NetworkAccessPoint_Password = Config_NetworkAccessPoint_Password;
this -> NetworkClient_SSID = Config_NetworkClient_SSID;
this -> NetworkClient_Password = Config_NetworkClient_Password;
this -> NetworkClient_Domain = Config_NetworkClient_Domain;
this -> NetworkClient_Port = Config_NetworkClient_Port;
this -> NetworkClient_Endpoint = Config_NetworkClient_Endpoint;
}
DynamicJsonDocument ConfigurationManager::GetAsJSON() const
{
DynamicJsonDocument ConfigurationJSON(this -> Memory_JSONConfigurationSize);
ConfigurationJSON["Pin_TemperatureSensor"] = this -> Pin_TemperatureSensor;
ConfigurationJSON["Pin_SwitchButton"] = this -> Pin_SwitchButton;
ConfigurationJSON["Pin_Speaker"] = this -> Pin_Speaker;
ConfigurationJSON["Pin_HumiditySensor"] = this -> Pin_HumiditySensor;
ConfigurationJSON["TemperatureSensor_Unit"] = this -> TemperatureSensor_Unit;
ConfigurationJSON["Speaker_On"] = this -> Speaker_On;
ConfigurationJSON["Network_SensorName"] = this -> Network_SensorName;
ConfigurationJSON["NetworkAccessPoint_Password"] = this -> NetworkAccessPoint_Password;
ConfigurationJSON["NetworkClient_SSID"] = this -> NetworkClient_SSID;
ConfigurationJSON["NetworkClient_Password"] = this -> NetworkClient_Password;
ConfigurationJSON["NetworkClient_Domain"] = this -> NetworkClient_Domain;
ConfigurationJSON["NetworkClient_Port"] = this -> NetworkClient_Port;
ConfigurationJSON["NetworkClient_Endpoint"] = this -> NetworkClient_Endpoint;
return ConfigurationJSON;
}
void ConfigurationManager::SetFromJSON(const DynamicJsonDocument & ConfigurationJSON)
{
if(!this -> IsConfigurationJSONValid(ConfigurationJSON))
{
this -> _Exceptions.Add(Exception::Memory_CorruptConfiguration);
return;
}
this -> Pin_TemperatureSensor = ConfigurationJSON["Pin_TemperatureSensor"];
this -> Pin_SwitchButton = ConfigurationJSON["Pin_SwitchButton"];
this -> Pin_Speaker = ConfigurationJSON["Pin_Speaker"];
this -> Pin_HumiditySensor = ConfigurationJSON["Pin_HumiditySensor"];
this -> TemperatureSensor_Unit = ConfigurationJSON["TemperatureSensor_Unit"];
this -> Speaker_On = ConfigurationJSON["Speaker_On"];
this -> Network_SensorName = ConfigurationJSON["Network_SensorName"].as<String>();
this -> NetworkAccessPoint_Password = ConfigurationJSON["NetworkAccessPoint_Password"].as<String>();
this -> NetworkClient_SSID = ConfigurationJSON["NetworkClient_SSID"].as<String>();
this -> NetworkClient_Password = ConfigurationJSON["NetworkClient_Password"].as<String>();
this -> NetworkClient_Domain = ConfigurationJSON["NetworkClient_Domain"].as<String>();
this -> NetworkClient_Port = ConfigurationJSON["NetworkClient_Port"];
this -> NetworkClient_Endpoint = ConfigurationJSON["NetworkClient_Endpoint"].as<String>();
}
bool ConfigurationManager::IsConfigurationJSONValid(const DynamicJsonDocument & ConfigurationJSON) const
{
return(
ConfigurationJSON.containsKey("Pin_TemperatureSensor") and
ConfigurationJSON.containsKey("Pin_SwitchButton") and
ConfigurationJSON.containsKey("Pin_Speaker") and
ConfigurationJSON.containsKey("Pin_HumiditySensor") and
ConfigurationJSON.containsKey("TemperatureSensor_Unit") and
ConfigurationJSON.containsKey("Speaker_On") and
ConfigurationJSON.containsKey("Network_SensorName") and
ConfigurationJSON.containsKey("NetworkAccessPoint_Password") and
ConfigurationJSON.containsKey("NetworkClient_SSID") and
ConfigurationJSON.containsKey("NetworkClient_Password") and
ConfigurationJSON.containsKey("NetworkClient_Domain") and
ConfigurationJSON.containsKey("NetworkClient_Port") and
ConfigurationJSON.containsKey("NetworkClient_Endpoint")
);
}
The problem i face is that when i try to print the values from the config in the setup only the variables that are not of type String have values
#include "ExceptionManager.h"
#include "ConfigurationManager.h"
ExceptionManager Exceptions;
ConfigurationManager Config(Exceptions);
// after this i pass the Config instance by refference to other classes and use a .begin() method in those classes called inside the setup().
void setup()
{
Serial.begin(115200);
Serial.println("Debug: ");
// Initialized at the class definition as constants (not user changable)
Serial.println(Config.Package); // unsinged short (the value shown is correct)
Serial.println(Config.NetworkAccessPoint_SSID); // String (the value shown is empty string)
// Initialized in the class constructor either from file or config.h
Serial.println(Config.Pin_TemperatureSensor); // unsinged short (the value shown is correct)
Serial.println(Config.UniqueUserToken); // String (the value shown is empty string)
}
How do i fix the problem with the String variables in the configuration as if i make the Config instance inside the setup() everything works fine but i need it to be global so i can pass it to the other classes