#include "config.h" #include "log.h" #include #include #define DIRTY_WRITE_DELAY (30 * 1000) JsonDocument config; unsigned long lastChangeMillis = 0; bool configRead(); bool configWrite(); bool configWriteBytes(int *address, uint8_t *data, size_t size); bool configReadBytes(int *address, uint8_t *data, size_t size); void configSetup() { configRead(); } void configLoop() { if (lastChangeMillis > 0 && millis() - lastChangeMillis > DIRTY_WRITE_DELAY) { configWrite(); } } void configReset() { config.clear(); configWrite(); } String configGetString(const char *name, const char *fallback, bool allowEmpty) { if (!config.containsKey(name)) { return fallback; } String value = config[name]; if (!allowEmpty && value.isEmpty()) { return fallback; } return value; } void configPutString(const char *name, const char *value) { if (config[name] != value) { config[name] = value; lastChangeMillis = millis(); } } double configGetDouble(const char *name, double fallback) { if (!config.containsKey(name)) { return fallback; } return config[name]; } void configPutDouble(const char *name, double value) { if (config[name] != value) { config[name] = value; lastChangeMillis = millis(); } } uint64_t configGetUint64_t(const char *name, uint64_t fallback) { if (!config.containsKey(name)) { return fallback; } return config[name]; } void configPutUint64_t(const char *name, uint64_t value) { if (config[name] != value) { config[name] = value; lastChangeMillis = millis(); } } void configPrint() { info("Config (%s):", lastChangeMillis == 0 ? "PERSISTED" : "TRANSIENT"); for (JsonPair pair: config.as()) { const char *key = pair.key().c_str(); const JsonVariant &value = pair.value(); char valueStr[64]; if (strcmp(key, "WIFI_PKEY") == 0) { snprintf(valueStr, sizeof valueStr, "[***]"); } else { if (value.is()) { snprintf(valueStr, sizeof valueStr, "%s", value.as()); } else if (value.is()) { snprintf(valueStr, sizeof valueStr, "%f", value.as()); } else if (value.is()) { snprintf(valueStr, sizeof valueStr, "%d", value.as()); } else if (value.is()) { snprintf(valueStr, sizeof valueStr, "%s", value.as() ? "true" : "false"); } else { snprintf(valueStr, sizeof valueStr, "[UNKNOWN TYPE]"); } } info(" %-15s = %s", key, valueStr); } } bool configRead() { size_t length; char buffer[512]; bool ok = true; int address = 0; EEPROM.begin(512); ok &= configReadBytes(&address, reinterpret_cast(&length), sizeof length); ok &= configReadBytes(&address, reinterpret_cast(buffer), length); ok &= EEPROM.end(); if (ok) { JsonDocument tmp; deserializeJson(tmp, buffer); if (tmp.is() && !tmp.isNull()) { info("Config loaded"); config = tmp; return true; } else { error("Failed to parse config JSON"); } } else { error("Failed to load config from EEPROM"); } return false; } bool configWrite() { lastChangeMillis = 0; char buffer[512]; size_t length = serializeJson(config, buffer, sizeof buffer); bool ok = true; int address = 0; EEPROM.begin(512); ok &= configWriteBytes(&address, reinterpret_cast(&length), sizeof length); ok &= configWriteBytes(&address, reinterpret_cast(buffer), length); ok &= EEPROM.end(); if (ok) { info("Successfully wrote config to EEPROM"); } else { info("Failed to write config to EEPROM"); } return ok; } bool configWriteBytes(int *address, uint8_t *data, size_t size) { uint8_t *b = data; for (; b < data + size; b++) { EEPROM.write((*address)++, *b); if ((size_t) *address >= EEPROM.length()) { error("END OF EEPROM!!!"); break; } } return (size_t) (b - data) == size; } bool configReadBytes(int *address, uint8_t *data, size_t size) { uint8_t *b = data; for (; b < data + size; b++) { *b = EEPROM.read((*address)++); if ((size_t) *address >= EEPROM.length()) { error("END OF EEPROM!!!"); break; } } return (size_t) (b - data) == size; }