filesystem + Config

This commit is contained in:
Patrick Haßel 2025-01-23 13:49:32 +01:00
parent e517e3d717
commit e8a73463bb
5 changed files with 195 additions and 4 deletions

View File

@ -2,8 +2,8 @@
platform = espressif32 platform = espressif32
board = esp32dev board = esp32dev
framework = arduino framework = arduino
lib_deps = board_build.filesystem = littlefs
knolleary/pubsubclient@2.8 lib_deps = knolleary/pubsubclient@2.8
bblanchon/ArduinoJson@7.3.0 bblanchon/ArduinoJson@7.3.0
me-no-dev/ESPAsyncWebServer@1.2.4 me-no-dev/ESPAsyncWebServer@1.2.4
build_flags = -DWIFI_SSID=\"HappyNet\" -DWIFI_PKEY=\"1Grausame!Sackratte7\" -DWIFI_HOST=\"PatrixTest\" build_flags = -DWIFI_SSID=\"HappyNet\" -DWIFI_PKEY=\"1Grausame!Sackratte7\" -DWIFI_HOST=\"PatrixTest\"

View File

@ -1,11 +1,14 @@
#ifndef PATRIX_NODE_TEST_H #ifndef PATRIX_NODE_TEST_H
#define PATRIX_NODE_TEST_H #define PATRIX_NODE_TEST_H
#include <patrix/core/Config.h>
#include <patrix/display/DisplayMatrix.h> #include <patrix/display/DisplayMatrix.h>
#include <patrix/node/PatrixNode.h> #include <patrix/node/PatrixNode.h>
DisplayMatrix<32, 8> display; DisplayMatrix<32, 8> display;
Config config("/test.json");
class NodeTest final : public PatrixNode { class NodeTest final : public PatrixNode {
public: public:
@ -15,9 +18,14 @@ public:
} }
void setup() override { void setup() override {
config.read();
display.printf("Test"); display.printf("Test");
} }
void loop() override {
config.loop();
}
}; };
#endif #endif

106
src/patrix/core/Config.h Normal file
View File

@ -0,0 +1,106 @@
#ifndef PATRIX_CONFIG_H
#define PATRIX_CONFIG_H
#include <ArduinoJson.h>
#include <LittleFS.h>
#include <utility>
#include "filesystem.h"
#include "log.h"
#define CONFIG_WRITE_DELAY_MILLIS (30 * 1000)
class Config final {
const String path;
JsonDocument config;
unsigned long dirty = 0;
bool doForceNextHexBuffer = true;
public:
explicit Config(String path) : path(std::move(path)) {
//
}
~Config() = default;
void read() {
fsMount();
auto file = LittleFS.open(path, "r");
if (!file) {
error("failed to open file for config: %s", path.c_str());
return;
}
if (!deserialize(file)) {
config.clear();
config = config.to<JsonObject>();
}
file.close();
}
void write() const {
fsMount();
auto write = LittleFS.open(path, "w");
if (!write) {
error("failed to open file for config write: %s", path.c_str());
return;
}
const auto size = measureJson(config);
if (serializeJson(config, write) == size) {
char buffer[256];
serializeJson(config, buffer, sizeof buffer);
info("config written: %s => %s", path.c_str(), buffer);
} else {
error("failed to write config: %s", path.c_str());
}
write.close();
}
void loop() {
if (dirty != 0 && millis() - dirty > CONFIG_WRITE_DELAY_MILLIS) {
dirty = 0;
write();
}
}
template<typename T>
T get(const char *key, T fallback) {
if (config[key].is<T>()) {
return config[key].as<T>();
}
warn("config key \"%s\" not found!", key);
return fallback;
}
template<typename T>
void set(const char *key, T value) {
config[key] = value;
dirty = max(1UL, millis()); // avoid special value zero (=> not dirty)
}
private:
bool deserialize(File file) {
if (deserializeJson(config, file) != DeserializationError::Ok) {
error("failed to deserialize config: %s", file.path());
return false;
}
if (!config.is<JsonObject>()) {
error("config not a json-object: %s", file.path());
return false;
}
char buffer[256];
serializeJson(config, buffer, sizeof buffer);
info("config loaded: %s => %s", path.c_str(), buffer);
config = config.as<JsonObject>();
return true;
}
};
#endif

View File

@ -0,0 +1,64 @@
#include "filesystem.h"
#include <FS.h>
#include <LittleFS.h>
#include "log.h"
auto fsMounted = false;
bool fsMount() {
if (!fsMounted) {
fsMounted = LittleFS.begin(true);
}
if (fsMounted) {
info("filesystem mounted: %3d%% used (%d bytes)", static_cast<int>(round(100.0 * LittleFS.usedBytes() / LittleFS.totalBytes())), LittleFS.usedBytes());
} else {
error("failed to mount filesystem");
}
return fsMounted;
}
bool fsIsMounted() {
return fsMounted;
}
void fsList(FS& fs, const char *path, const String& indent) { // NOLINT(*-no-recursion)
auto dir = fs.open(path);
if (!dir) {
error("not found: %s", path);
return;
}
if (!dir.isDirectory()) {
error("not a directory: %s", path);
return;
}
info("LS: %s %4s %s", indent.c_str(), "", dir.name());
const auto indent2 = indent + " ";
while (true) {
auto file = dir.openNextFile();
if (!file) {
break;
}
if (file.isDirectory()) {
fsList(fs, file.path());
}
file.close();
}
dir.rewindDirectory();
while (true) {
auto file = dir.openNextFile();
if (!file) {
break;
}
if (!file.isDirectory()) {
info("LS: %s %4d %s", indent2.c_str(), file.size(), file.name());
}
file.close();
}
dir.close();
}

View File

@ -0,0 +1,13 @@
#ifndef PATRIX_FILESYSTEM_H
#define PATRIX_FILESYSTEM_H
#include <FS.h>
#include <WString.h>
bool fsMount();
bool fsIsMounted();
void fsList(FS& fs, const char *path, const String& indent = "");
#endif