From be771818f9b43acce27885b87cb79348b0bd4d91 Mon Sep 17 00:00:00 2001 From: DeFiDude <59237470+DeFiDude@users.noreply.github.com> Date: Thu, 16 Apr 2026 21:54:44 -0600 Subject: [PATCH] Silence vfs_api error spam and rename /ratputer/ SD paths to /ratdeck/ CORE_DEBUG_LEVEL=0 drops Arduino core noise on expected-missing files, LittleFSFileSystem::open_file now actually works so path_table persists across reboots. --- platformio.ini | 4 +++ src/config/Config.h | 12 +++---- src/config/UserConfig.cpp | 8 ++--- src/main.cpp | 4 +-- src/reticulum/AnnounceManager.cpp | 4 +-- src/reticulum/IdentityManager.cpp | 4 +-- src/reticulum/ReticulumManager.cpp | 52 ++++++++++++++++++++++++++--- src/storage/MessageStore.cpp | 4 +-- src/storage/SDStore.cpp | 40 +++++++++++----------- src/storage/SDStore.h | 4 +-- src/ui/screens/LvSettingsScreen.cpp | 6 ++-- 11 files changed, 94 insertions(+), 48 deletions(-) diff --git a/platformio.ini b/platformio.ini index 9b04d53..3e7a98c 100644 --- a/platformio.ini +++ b/platformio.ini @@ -15,6 +15,10 @@ build_flags = -DRATDECK=1 -DARDUINO_USB_CDC_ON_BOOT=1 -DARDUINO_USB_MODE=1 + ; Silence vfs_api log_e noise — it fires on every open/remove of a missing + ; file (normal for our atomic-save + tiered-read patterns). log_e routes to + ; ets_printf, so esp_log_level_set doesn't help; only compile-time works. + -DCORE_DEBUG_LEVEL=0 -DRNS_USE_FS -DRNS_PERSIST_PATHS -DMSGPACK_USE_BOOST=OFF diff --git a/src/config/Config.h b/src/config/Config.h index 89fb581..ed15961 100644 --- a/src/config/Config.h +++ b/src/config/Config.h @@ -33,12 +33,12 @@ #define PATH_CONTACTS "/contacts/" #define PATH_MESSAGES "/messages/" -// --- SD Card Paths (shared with Ratputer) --- -#define SD_PATH_CONFIG_DIR "/ratputer/config" -#define SD_PATH_USER_CONFIG "/ratputer/config/user.json" -#define SD_PATH_MESSAGES "/ratputer/messages/" -#define SD_PATH_CONTACTS "/ratputer/contacts/" -#define SD_PATH_IDENTITY "/ratputer/identity/identity.key" +// --- SD Card Paths --- +#define SD_PATH_CONFIG_DIR "/ratdeck/config" +#define SD_PATH_USER_CONFIG "/ratdeck/config/user.json" +#define SD_PATH_MESSAGES "/ratdeck/messages/" +#define SD_PATH_CONTACTS "/ratdeck/contacts/" +#define SD_PATH_IDENTITY "/ratdeck/identity/identity.key" // --- TCP Client --- #define MAX_TCP_CONNECTIONS 4 diff --git a/src/config/UserConfig.cpp b/src/config/UserConfig.cpp index 908234c..4118ed6 100644 --- a/src/config/UserConfig.cpp +++ b/src/config/UserConfig.cpp @@ -168,8 +168,8 @@ bool UserConfig::load(SDStore& sd, FlashStore& flash) { // Auto-migrate: flash had config but SD didn't — copy to SD if (ok && sd.isReady()) { Serial.println("[CONFIG] Migrating config from flash to SD..."); - sd.ensureDir("/ratputer"); - sd.ensureDir("/ratputer/config"); + sd.ensureDir("/ratdeck"); + sd.ensureDir("/ratdeck/config"); String migrateJson = serializeToJson(); if (sd.writeString(SD_PATH_USER_CONFIG, migrateJson)) { Serial.println("[CONFIG] Migration complete"); @@ -185,8 +185,8 @@ bool UserConfig::save(SDStore& sd, FlashStore& flash) { // Write to SD (primary) if (sd.isReady()) { - sd.ensureDir("/ratputer"); - sd.ensureDir("/ratputer/config"); + sd.ensureDir("/ratdeck"); + sd.ensureDir("/ratdeck/config"); if (sd.writeString(SD_PATH_USER_CONFIG, json)) { Serial.println("[CONFIG] Saved to SD"); ok = true; diff --git a/src/main.cpp b/src/main.cpp index 3b11dcd..100535b 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -370,7 +370,7 @@ void setup() { digitalWrite(LORA_CS, HIGH); delay(10); if (sdStore.begin(&sharedSPI, SD_CS)) { - sdStore.formatForRatputer(); + sdStore.formatForRatdeck(); Serial.println("[SD] Card ready"); } else { Serial.println("[SD] Not detected"); @@ -796,7 +796,7 @@ void setup() { if (wipe) { Serial.println("[BOOT] User chose to wipe old data"); lvDataCleanScreen.showStatus("Clearing old data..."); - sdStore.wipeRatputer(); + sdStore.wipeRatdeck(); if (announceManager) announceManager->clearAll(); Serial.println("[BOOT] Old data cleared"); lvDataCleanScreen.showStatus("Done! Rebooting..."); diff --git a/src/reticulum/AnnounceManager.cpp b/src/reticulum/AnnounceManager.cpp index 730db39..ae422bf 100644 --- a/src/reticulum/AnnounceManager.cpp +++ b/src/reticulum/AnnounceManager.cpp @@ -435,7 +435,7 @@ void AnnounceManager::saveNameCache() { String json; serializeJson(doc, json); if (_sd && _sd->isReady()) { - _sd->writeString("/ratputer/config/names.json", json); + _sd->writeString("/ratdeck/config/names.json", json); } if (_flash) { _flash->writeString("/config/names.json", json); @@ -446,7 +446,7 @@ void AnnounceManager::saveNameCache() { void AnnounceManager::loadNameCache() { String json; if (_sd && _sd->isReady()) { - json = _sd->readString("/ratputer/config/names.json"); + json = _sd->readString("/ratdeck/config/names.json"); } if (json.isEmpty() && _flash) { json = _flash->readString("/config/names.json"); diff --git a/src/reticulum/IdentityManager.cpp b/src/reticulum/IdentityManager.cpp index 89f23b3..13e5068 100644 --- a/src/reticulum/IdentityManager.cpp +++ b/src/reticulum/IdentityManager.cpp @@ -117,8 +117,8 @@ bool IdentityManager::switchTo(int index, RNS::Identity& outIdentity) { // Copy the active key to the standard identity path for ReticulumManager _flash->writeAtomic("/identity/identity.key", keyData.data(), keyData.size()); if (_sd && _sd->isReady()) { - _sd->ensureDir("/ratputer/identity"); - _sd->writeAtomic("/ratputer/identity/identity.key", keyData.data(), keyData.size()); + _sd->ensureDir("/ratdeck/identity"); + _sd->writeAtomic("/ratdeck/identity/identity.key", keyData.data(), keyData.size()); } outIdentity = id; diff --git a/src/reticulum/ReticulumManager.cpp b/src/reticulum/ReticulumManager.cpp index 46d478b..49ba7bf 100644 --- a/src/reticulum/ReticulumManager.cpp +++ b/src/reticulum/ReticulumManager.cpp @@ -35,7 +35,49 @@ size_t LittleFSFileSystem::write_file(const char* p, const RNS::Bytes& data) { return w; } -RNS::FileStream LittleFSFileSystem::open_file(const char*, RNS::FileStream::MODE) { return {RNS::Type::NONE}; } +// Arduino-LittleFS wrapper for RNS::FileStreamImpl. Needed by microReticulum's +// Persistence::serialize> streaming path in Transport::write_path_table. +// Without this, write_path_table always logs "serialize failed" because the +// default stub returns NONE, and the path table never persists across reboots. +namespace { +class LittleFSStreamImpl : public RNS::FileStreamImpl { +public: + LittleFSStreamImpl(File&& f) : _f(std::move(f)) {} + ~LittleFSStreamImpl() override { if (_f) _f.close(); } + +protected: + const char* name() override { return _f ? _f.name() : ""; } + size_t size() override { return _f ? _f.size() : 0; } + void close() override { if (_f) _f.close(); } + + size_t write(uint8_t byte) override { return _f ? _f.write(byte) : 0; } + size_t write(const uint8_t* buffer, size_t len) override { return _f ? _f.write(buffer, len) : 0; } + + int available() override { return _f ? _f.available() : 0; } + int read() override { return _f ? _f.read() : -1; } + int peek() override { return _f ? _f.peek() : -1; } + void flush() override { if (_f) _f.flush(); } + +private: + File _f; +}; +} // namespace + +RNS::FileStream LittleFSFileSystem::open_file(const char* path, RNS::FileStream::MODE mode) { + const char* openMode = "r"; + if (mode == RNS::FileStream::MODE_WRITE) openMode = "w"; + if (mode == RNS::FileStream::MODE_APPEND) openMode = "a"; + if (mode != RNS::FileStream::MODE_READ) { + String p(path); + int lastSlash = p.lastIndexOf('/'); + if (lastSlash > 0 && !LittleFS.exists(p.substring(0, lastSlash).c_str())) { + LittleFS.mkdir(p.substring(0, lastSlash).c_str()); + } + } + File f = LittleFS.open(path, openMode); + if (!f) return {RNS::Type::NONE}; + return RNS::FileStream(new LittleFSStreamImpl(std::move(f))); +} bool LittleFSFileSystem::remove_file(const char* p) { return LittleFS.remove(p); } bool LittleFSFileSystem::rename_file(const char* f, const char* t) { return LittleFS.rename(f, t); } bool LittleFSFileSystem::directory_exists(const char* p) { return LittleFS.exists(p); } @@ -74,7 +116,7 @@ bool ReticulumManager::begin(SX1262* radio, FlashStore* flash) { for (const char* name : files) { if (!LittleFS.exists(name)) { char sdPath[64]; - snprintf(sdPath, sizeof(sdPath), "/ratputer/transport%s", name); + snprintf(sdPath, sizeof(sdPath), "/ratdeck/transport%s", name); uint8_t buf[4096]; size_t len = 0; if (_sd->readFile(sdPath, buf, sizeof(buf), len) && len > 0) { @@ -236,7 +278,7 @@ void ReticulumManager::saveIdentityToAll(const RNS::Bytes& keyData) { _flash->writeAtomic(PATH_IDENTITY, keyData.data(), keyData.size()); // SD if (_sd && _sd->isReady()) { - _sd->ensureDir("/ratputer/identity"); + _sd->ensureDir("/ratdeck/identity"); _sd->writeAtomic(SD_PATH_IDENTITY, keyData.data(), keyData.size()); } // NVS (always available, survives flash/SD failures) @@ -282,8 +324,8 @@ void ReticulumManager::persistData() { if (buf) { f.readBytes((char*)buf, sz); char sdPath[64]; - snprintf(sdPath, sizeof(sdPath), "/ratputer/transport%s", name); - _sd->ensureDir("/ratputer/transport"); + snprintf(sdPath, sizeof(sdPath), "/ratdeck/transport%s", name); + _sd->ensureDir("/ratdeck/transport"); _sd->writeSimple(sdPath, buf, sz); free(buf); } diff --git a/src/storage/MessageStore.cpp b/src/storage/MessageStore.cpp index 8754818..f945964 100644 --- a/src/storage/MessageStore.cpp +++ b/src/storage/MessageStore.cpp @@ -16,8 +16,8 @@ bool MessageStore::begin(FlashStore* flash, SDStore* sd) { _flash->ensureDir(PATH_MESSAGES); if (_sd && _sd->isReady()) { - _sd->ensureDir("/ratputer"); - _sd->ensureDir("/ratputer/messages"); + _sd->ensureDir("/ratdeck"); + _sd->ensureDir("/ratdeck/messages"); migrateFlashToSD(); } diff --git a/src/storage/SDStore.cpp b/src/storage/SDStore.cpp index 6190069..28d5146 100644 --- a/src/storage/SDStore.cpp +++ b/src/storage/SDStore.cpp @@ -169,17 +169,17 @@ String SDStore::readString(const char* path) { return result; } -bool SDStore::wipeRatputer() { +bool SDStore::wipeRatdeck() { if (!_ready) return false; - Serial.println("[SD] Wiping /ratputer/ ..."); - wipeDir("/ratputer/messages"); - wipeDir("/ratputer/contacts"); - wipeDir("/ratputer/identity"); - wipeDir("/ratputer/config"); - wipeDir("/ratputer/transport"); - SD.rmdir("/ratputer"); + Serial.println("[SD] Wiping /ratdeck/ ..."); + wipeDir("/ratdeck/messages"); + wipeDir("/ratdeck/contacts"); + wipeDir("/ratdeck/identity"); + wipeDir("/ratdeck/config"); + wipeDir("/ratdeck/transport"); + SD.rmdir("/ratdeck"); Serial.println("[SD] Wipe complete, recreating dirs..."); - return formatForRatputer(); + return formatForRatdeck(); } void SDStore::wipeDir(const char* path) { @@ -201,9 +201,9 @@ void SDStore::wipeDir(const char* path) { bool SDStore::hasExistingData() { if (!_ready) return false; - if (SD.exists("/ratputer/config.json")) return true; - if (SD.exists("/ratputer/identity/identity")) return true; - File dir = SD.open("/ratputer/messages"); + if (SD.exists("/ratdeck/config.json")) return true; + if (SD.exists("/ratdeck/identity/identity")) return true; + File dir = SD.open("/ratdeck/messages"); if (dir && dir.isDirectory()) { File entry = dir.openNextFile(); bool found = (bool)entry; @@ -214,16 +214,16 @@ bool SDStore::hasExistingData() { return false; } -bool SDStore::formatForRatputer() { +bool SDStore::formatForRatdeck() { if (!_ready) return false; - Serial.println("[SD] Creating Ratputer directory structure..."); + Serial.println("[SD] Creating Ratdeck directory structure..."); bool ok = true; - ok &= ensureDir("/ratputer"); - ok &= ensureDir("/ratputer/config"); - ok &= ensureDir("/ratputer/messages"); - ok &= ensureDir("/ratputer/contacts"); - ok &= ensureDir("/ratputer/identity"); - ok &= ensureDir("/ratputer/transport"); + ok &= ensureDir("/ratdeck"); + ok &= ensureDir("/ratdeck/config"); + ok &= ensureDir("/ratdeck/messages"); + ok &= ensureDir("/ratdeck/contacts"); + ok &= ensureDir("/ratdeck/identity"); + ok &= ensureDir("/ratdeck/transport"); if (ok) Serial.println("[SD] Directory structure ready"); return ok; } diff --git a/src/storage/SDStore.h b/src/storage/SDStore.h index f744fe5..fd72eb3 100644 --- a/src/storage/SDStore.h +++ b/src/storage/SDStore.h @@ -25,8 +25,8 @@ public: uint64_t totalBytes() const; uint64_t usedBytes() const; - bool formatForRatputer(); - bool wipeRatputer(); + bool formatForRatdeck(); + bool wipeRatdeck(); bool hasExistingData(); private: diff --git a/src/ui/screens/LvSettingsScreen.cpp b/src/ui/screens/LvSettingsScreen.cpp index dbdac77..ac05fc2 100644 --- a/src/ui/screens/LvSettingsScreen.cpp +++ b/src/ui/screens/LvSettingsScreen.cpp @@ -569,7 +569,7 @@ void LvSettingsScreen::buildItems() { initSD.action = [this]() { if (!_sd || !_sd->isReady()) { if (_ui) _ui->lvStatusBar().showToast("No SD card!", 1200); return; } if (_ui) _ui->lvStatusBar().showToast("Initializing SD...", 2000); - bool ok = _sd->formatForRatputer(); + bool ok = _sd->formatForRatdeck(); if (_ui) _ui->lvStatusBar().showToast(ok ? "SD initialized!" : "SD init failed!", 1500); }; _items.push_back(initSD); @@ -583,7 +583,7 @@ void LvSettingsScreen::buildItems() { wipeSD.action = [this]() { if (!_sd || !_sd->isReady()) { if (_ui) _ui->lvStatusBar().showToast("No SD card!", 1200); return; } if (_ui) _ui->lvStatusBar().showToast("Wiping SD data...", 2000); - bool ok = _sd->wipeRatputer(); + bool ok = _sd->wipeRatdeck(); if (_ui) _ui->lvStatusBar().showToast(ok ? "SD wiped & reinit!" : "Wipe failed!", 1500); }; _items.push_back(wipeSD); @@ -603,7 +603,7 @@ void LvSettingsScreen::buildItems() { } _confirmingReset = false; if (_ui) _ui->lvStatusBar().showToast("Factory resetting...", 3000); - if (_sd && _sd->isReady()) _sd->wipeRatputer(); + if (_sd && _sd->isReady()) _sd->wipeRatdeck(); if (_flash) _flash->format(); nvs_flash_erase(); delay(1500); // Long enough for key state to clear before reboot