From e8a73463bb0da1509c1cabd66c82df0f194c7bb2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Patrick=20Ha=C3=9Fel?= Date: Thu, 23 Jan 2025 13:49:32 +0100 Subject: [PATCH] filesystem + Config --- platformio.ini | 8 +-- src/demo/NodeTest.h | 8 +++ src/patrix/core/Config.h | 106 +++++++++++++++++++++++++++++++++ src/patrix/core/filesystem.cpp | 64 ++++++++++++++++++++ src/patrix/core/filesystem.h | 13 ++++ 5 files changed, 195 insertions(+), 4 deletions(-) create mode 100644 src/patrix/core/Config.h create mode 100644 src/patrix/core/filesystem.cpp create mode 100644 src/patrix/core/filesystem.h diff --git a/platformio.ini b/platformio.ini index 60c714e..ecfd4b7 100644 --- a/platformio.ini +++ b/platformio.ini @@ -2,8 +2,8 @@ platform = espressif32 board = esp32dev framework = arduino -lib_deps = - knolleary/pubsubclient@2.8 - bblanchon/ArduinoJson@7.3.0 - me-no-dev/ESPAsyncWebServer@1.2.4 +board_build.filesystem = littlefs +lib_deps = knolleary/pubsubclient@2.8 + bblanchon/ArduinoJson@7.3.0 + me-no-dev/ESPAsyncWebServer@1.2.4 build_flags = -DWIFI_SSID=\"HappyNet\" -DWIFI_PKEY=\"1Grausame!Sackratte7\" -DWIFI_HOST=\"PatrixTest\" \ No newline at end of file diff --git a/src/demo/NodeTest.h b/src/demo/NodeTest.h index 6731597..daa4d98 100644 --- a/src/demo/NodeTest.h +++ b/src/demo/NodeTest.h @@ -1,11 +1,14 @@ #ifndef PATRIX_NODE_TEST_H #define PATRIX_NODE_TEST_H +#include #include #include DisplayMatrix<32, 8> display; +Config config("/test.json"); + class NodeTest final : public PatrixNode { public: @@ -15,9 +18,14 @@ public: } void setup() override { + config.read(); display.printf("Test"); } + void loop() override { + config.loop(); + } + }; #endif diff --git a/src/patrix/core/Config.h b/src/patrix/core/Config.h new file mode 100644 index 0000000..49ac6e5 --- /dev/null +++ b/src/patrix/core/Config.h @@ -0,0 +1,106 @@ +#ifndef PATRIX_CONFIG_H +#define PATRIX_CONFIG_H + +#include +#include + +#include + +#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(); + } + 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 + T get(const char *key, T fallback) { + if (config[key].is()) { + return config[key].as(); + } + warn("config key \"%s\" not found!", key); + return fallback; + } + + template + 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()) { + 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(); + return true; + } + +}; + +#endif diff --git a/src/patrix/core/filesystem.cpp b/src/patrix/core/filesystem.cpp new file mode 100644 index 0000000..bba2b32 --- /dev/null +++ b/src/patrix/core/filesystem.cpp @@ -0,0 +1,64 @@ +#include "filesystem.h" + +#include +#include +#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(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(); +} diff --git a/src/patrix/core/filesystem.h b/src/patrix/core/filesystem.h new file mode 100644 index 0000000..1668ddd --- /dev/null +++ b/src/patrix/core/filesystem.h @@ -0,0 +1,13 @@ +#ifndef PATRIX_FILESYSTEM_H +#define PATRIX_FILESYSTEM_H + +#include +#include + +bool fsMount(); + +bool fsIsMounted(); + +void fsList(FS& fs, const char *path, const String& indent = ""); + +#endif