From 7dcccdf1719cd9fd233bc9105ad74c68a28f445f Mon Sep 17 00:00:00 2001 From: Trail Mate Dev Date: Sat, 30 May 2026 18:43:31 +0800 Subject: [PATCH] Unify SD persistence runtime --- .../esp/arduino_common/gps/track_recorder.h | 1 - .../arduino_common/storage/sd_card_runtime.h | 7 + .../src/chat/infra/contact_store.cpp | 7 +- .../src/chat/infra/meshtastic/node_store.cpp | 38 ++-- .../src/chat/internal/blob_store_io.cpp | 31 ++-- .../arduino_common/src/gps/track_recorder.cpp | 80 +++++---- .../src/platform_ui_route_storage.cpp | 20 +-- .../platform_ui_settings_backup_runtime.cpp | 54 +++--- .../src/platform_ui_tracker_runtime.cpp | 7 +- .../arduino_common/src/sstv/sstv_service.cpp | 23 ++- .../src/storage/sd_card_runtime.cpp | 131 ++++++++++++++ .../src/ui/screens/team/team_ui_store.cpp | 163 +++++++++--------- 12 files changed, 372 insertions(+), 190 deletions(-) diff --git a/platform/esp/arduino_common/include/platform/esp/arduino_common/gps/track_recorder.h b/platform/esp/arduino_common/include/platform/esp/arduino_common/gps/track_recorder.h index 72b1e46f..bee7de23 100644 --- a/platform/esp/arduino_common/include/platform/esp/arduino_common/gps/track_recorder.h +++ b/platform/esp/arduino_common/include/platform/esp/arduino_common/gps/track_recorder.h @@ -4,7 +4,6 @@ #include "freertos/semphr.h" #include -#include namespace gps { diff --git a/platform/esp/arduino_common/include/platform/esp/arduino_common/storage/sd_card_runtime.h b/platform/esp/arduino_common/include/platform/esp/arduino_common/storage/sd_card_runtime.h index e59456b8..c272983e 100644 --- a/platform/esp/arduino_common/include/platform/esp/arduino_common/storage/sd_card_runtime.h +++ b/platform/esp/arduino_common/include/platform/esp/arduino_common/storage/sd_card_runtime.h @@ -60,8 +60,15 @@ class SdRuntimeFile bool open(const char* path, const char* mode); void close(); bool is_open() const; + int available() const; int read(void* buffer, std::size_t bytes_to_read); + int read_byte(); + std::size_t read_bytes(char* buffer, std::size_t bytes_to_read); std::size_t write(const void* buffer, std::size_t bytes_to_write); + std::size_t write_byte(uint8_t value); + std::size_t print(const char* text); + std::size_t print(double value, int digits = 2); + std::size_t printf(const char* format, ...); bool seek(uint64_t offset); uint64_t position() const; uint64_t size() const; diff --git a/platform/esp/arduino_common/src/chat/infra/contact_store.cpp b/platform/esp/arduino_common/src/chat/infra/contact_store.cpp index 195758ce..55ad30fd 100644 --- a/platform/esp/arduino_common/src/chat/infra/contact_store.cpp +++ b/platform/esp/arduino_common/src/chat/infra/contact_store.cpp @@ -5,8 +5,9 @@ #include "platform/esp/arduino_common/chat/infra/contact_store.h" #include "../internal/blob_store_io.h" +#include "platform/esp/arduino_common/storage/sd_card_runtime.h" -#include +#include namespace chat { @@ -30,7 +31,9 @@ ContactStore::ContactStore() void ContactStore::begin() { - backend_ = (SD.cardType() != CARD_NONE) ? StorageBackend::Sd : StorageBackend::Flash; + backend_ = ::platform::esp::arduino_common::storage::sd_card_ready() + ? StorageBackend::Sd + : StorageBackend::Flash; CONTACT_STORE_LOG("[ContactStore] backend=%s\n", backend_ == StorageBackend::Sd ? "sd" : "flash"); core_.begin(); diff --git a/platform/esp/arduino_common/src/chat/infra/meshtastic/node_store.cpp b/platform/esp/arduino_common/src/chat/infra/meshtastic/node_store.cpp index 558f248f..052a4189 100644 --- a/platform/esp/arduino_common/src/chat/infra/meshtastic/node_store.cpp +++ b/platform/esp/arduino_common/src/chat/infra/meshtastic/node_store.cpp @@ -6,8 +6,9 @@ #include "platform/esp/arduino_common/chat/infra/meshtastic/node_store.h" #include "../../internal/blob_store_io.h" #include "chat/infra/node_store_blob_format.h" +#include "platform/esp/arduino_common/storage/sd_card_runtime.h" -#include +#include #include #include #include @@ -89,7 +90,9 @@ NodeStore::NodeStore() void NodeStore::begin() { - backend_ = (SD.cardType() != CARD_NONE) ? StorageBackend::Sd : StorageBackend::Nvs; + backend_ = ::platform::esp::arduino_common::storage::sd_card_ready() + ? StorageBackend::Sd + : StorageBackend::Nvs; NODE_STORE_LOG("[NodeStore] backend=%s\n", backend_ == StorageBackend::Sd ? "sd" : "nvs"); core_.begin(); @@ -201,9 +204,10 @@ bool NodeStore::saveBlob(const uint8_t* data, size_t len) void NodeStore::clearBlob() { - if (SD.cardType() != CARD_NONE && SD.exists(kPersistNodesFile)) + if (::platform::esp::arduino_common::storage::sd_card_ready() && + ::platform::esp::arduino_common::storage::sd_exists(kPersistNodesFile)) { - SD.remove(kPersistNodesFile); + ::platform::esp::arduino_common::storage::sd_remove(kPersistNodesFile); } clearNvs(); } @@ -300,14 +304,14 @@ bool NodeStore::loadFromNvs(std::vector& out) bool NodeStore::loadFromSd(std::vector& out) const { - if (SD.cardType() == CARD_NONE) + if (!::platform::esp::arduino_common::storage::sd_card_ready()) { NODE_STORE_LOG("[NodeStore] load SD skipped: card none\n"); return false; } - File file = SD.open(kPersistNodesFile, FILE_READ); - if (!file) + ::platform::esp::arduino_common::storage::SdRuntimeFile file; + if (!file.open(kPersistNodesFile, "r")) { NODE_STORE_LOG("[NodeStore] load SD open failed path=%s\n", kPersistNodesFile); return false; @@ -452,7 +456,7 @@ bool NodeStore::saveToNvs(const uint8_t* data, size_t len) const bool NodeStore::saveToSd(const uint8_t* data, size_t len) const { - if (SD.cardType() == CARD_NONE) + if (!::platform::esp::arduino_common::storage::sd_card_ready()) { return false; } @@ -463,13 +467,13 @@ bool NodeStore::saveToSd(const uint8_t* data, size_t len) const } const std::string temp_path = std::string(kPersistNodesFile) + ".tmp"; - if (SD.exists(temp_path.c_str())) + if (::platform::esp::arduino_common::storage::sd_exists(temp_path.c_str())) { - SD.remove(temp_path.c_str()); + ::platform::esp::arduino_common::storage::sd_remove(temp_path.c_str()); } - File file = SD.open(temp_path.c_str(), FILE_WRITE); - if (!file) + ::platform::esp::arduino_common::storage::SdRuntimeFile file; + if (!file.open(temp_path.c_str(), "w")) { return false; } @@ -485,18 +489,18 @@ bool NodeStore::saveToSd(const uint8_t* data, size_t len) const file.close(); if (!ok) { - SD.remove(temp_path.c_str()); + ::platform::esp::arduino_common::storage::sd_remove(temp_path.c_str()); return false; } - if (SD.exists(kPersistNodesFile)) + if (::platform::esp::arduino_common::storage::sd_exists(kPersistNodesFile)) { - SD.remove(kPersistNodesFile); + ::platform::esp::arduino_common::storage::sd_remove(kPersistNodesFile); } - if (!SD.rename(temp_path.c_str(), kPersistNodesFile)) + if (!::platform::esp::arduino_common::storage::sd_rename(temp_path.c_str(), kPersistNodesFile)) { - SD.remove(temp_path.c_str()); + ::platform::esp::arduino_common::storage::sd_remove(temp_path.c_str()); return false; } diff --git a/platform/esp/arduino_common/src/chat/internal/blob_store_io.cpp b/platform/esp/arduino_common/src/chat/internal/blob_store_io.cpp index cce3396a..d1e4de3d 100644 --- a/platform/esp/arduino_common/src/chat/internal/blob_store_io.cpp +++ b/platform/esp/arduino_common/src/chat/internal/blob_store_io.cpp @@ -5,8 +5,9 @@ #include "blob_store_io.h" +#include "platform/esp/arduino_common/storage/sd_card_runtime.h" + #include -#include #include namespace chat @@ -87,13 +88,14 @@ bool savePreferencesMetadata(Preferences& prefs, bool loadRawBlobFromSd(const char* path, std::vector& out) { out.clear(); - if (!path || path[0] == '\0' || SD.cardType() == CARD_NONE) + if (!path || path[0] == '\0' || + !::platform::esp::arduino_common::storage::sd_card_ready()) { return false; } - File file = SD.open(path, FILE_READ); - if (!file) + ::platform::esp::arduino_common::storage::SdRuntimeFile file; + if (!file.open(path, "r")) { return false; } @@ -118,7 +120,8 @@ bool loadRawBlobFromSd(const char* path, std::vector& out) bool saveRawBlobToSd(const char* path, const uint8_t* data, size_t len) { - if (!path || path[0] == '\0' || SD.cardType() == CARD_NONE) + if (!path || path[0] == '\0' || + !::platform::esp::arduino_common::storage::sd_card_ready()) { return false; } @@ -129,13 +132,13 @@ bool saveRawBlobToSd(const char* path, const uint8_t* data, size_t len) return false; } - if (SD.exists(temp_path.c_str())) + if (::platform::esp::arduino_common::storage::sd_exists(temp_path.c_str())) { - SD.remove(temp_path.c_str()); + ::platform::esp::arduino_common::storage::sd_remove(temp_path.c_str()); } - File file = SD.open(temp_path.c_str(), FILE_WRITE); - if (!file) + ::platform::esp::arduino_common::storage::SdRuntimeFile file; + if (!file.open(temp_path.c_str(), "w")) { return false; } @@ -148,18 +151,18 @@ bool saveRawBlobToSd(const char* path, const uint8_t* data, size_t len) file.close(); if (!ok) { - SD.remove(temp_path.c_str()); + ::platform::esp::arduino_common::storage::sd_remove(temp_path.c_str()); return false; } - if (SD.exists(path)) + if (::platform::esp::arduino_common::storage::sd_exists(path)) { - SD.remove(path); + ::platform::esp::arduino_common::storage::sd_remove(path); } - if (!SD.rename(temp_path.c_str(), path)) + if (!::platform::esp::arduino_common::storage::sd_rename(temp_path.c_str(), path)) { - SD.remove(temp_path.c_str()); + ::platform::esp::arduino_common::storage::sd_remove(temp_path.c_str()); return false; } diff --git a/platform/esp/arduino_common/src/gps/track_recorder.cpp b/platform/esp/arduino_common/src/gps/track_recorder.cpp index 47ae3bc0..e4d9c6d6 100644 --- a/platform/esp/arduino_common/src/gps/track_recorder.cpp +++ b/platform/esp/arduino_common/src/gps/track_recorder.cpp @@ -1,4 +1,5 @@ #include "platform/esp/arduino_common/gps/track_recorder.h" +#include "platform/esp/arduino_common/storage/sd_card_runtime.h" #include "platform/esp/common/shared_spi_lock.h" #include @@ -9,6 +10,14 @@ namespace gps namespace { +using ::platform::esp::arduino_common::storage::SdRuntimeDir; +using ::platform::esp::arduino_common::storage::SdRuntimeFile; +using ::platform::esp::arduino_common::storage::sd_card_ready; +using ::platform::esp::arduino_common::storage::sd_exists; +using ::platform::esp::arduino_common::storage::sd_is_directory; +using ::platform::esp::arduino_common::storage::sd_mkdir; +using ::platform::esp::arduino_common::storage::sd_remove; + constexpr const char* kGpxHeader = "\n" "\n" @@ -58,15 +67,15 @@ TrackRecorder& TrackRecorder::getInstance() bool TrackRecorder::ensureDir() const { - if (SD.cardType() == CARD_NONE) + if (!sd_card_ready()) { return false; } - if (SD.exists(kTrackDir)) + if (sd_exists(kTrackDir)) { - return true; + return sd_is_directory(kTrackDir); } - return SD.mkdir(kTrackDir); + return sd_mkdir(kTrackDir); } String TrackRecorder::makeTrackPath() const @@ -126,8 +135,8 @@ const char* TrackRecorder::formatExtension() const void TrackRecorder::beginNewFile() { current_path_ = makeTrackPath(); - File f = SD.open(current_path_.c_str(), FILE_WRITE); - if (!f) + SdRuntimeFile f; + if (!f.open(current_path_.c_str(), "w")) { current_path_ = ""; return; @@ -207,8 +216,8 @@ void TrackRecorder::stop() if (recording_ && current_path_.length() > 0) { - File f = SD.open(current_path_.c_str(), FILE_APPEND); - if (f) + SdRuntimeFile f; + if (f.open(current_path_.c_str(), "a")) { if (format_ == TrackFormat::GPX) { @@ -250,8 +259,8 @@ void TrackRecorder::setAutoRecording(bool enabled) { if (current_path_.length() > 0) { - File f = SD.open(current_path_.c_str(), FILE_APPEND); - if (f) + SdRuntimeFile f; + if (f.open(current_path_.c_str(), "a")) { if (format_ == TrackFormat::GPX) { @@ -311,8 +320,8 @@ void TrackRecorder::setFormat(TrackFormat format) if (current_path_.length() > 0) { - File f = SD.open(current_path_.c_str(), FILE_APPEND); - if (f) + SdRuntimeFile f; + if (f.open(current_path_.c_str(), "a")) { if (prev_format == TrackFormat::GPX) { @@ -367,7 +376,7 @@ void TrackRecorder::appendPoint(const TrackPoint& pt) return; } - if (SD.cardType() == CARD_NONE) + if (!sd_card_ready()) { if (mutex_) { @@ -424,8 +433,8 @@ void TrackRecorder::appendPoint(const TrackPoint& pt) } } - File f = SD.open(current_path_.c_str(), FILE_APPEND); - if (f) + SdRuntimeFile f; + if (f.open(current_path_.c_str(), "a")) { if (format_ == TrackFormat::CSV) { @@ -489,16 +498,16 @@ bool TrackRecorder::restoreActiveSession() bool ok = false; do { - if (SD.cardType() == CARD_NONE) + if (!sd_card_ready()) { break; } - if (!SD.exists(kActivePath)) + if (!sd_exists(kActivePath)) { break; } - File f = SD.open(kActivePath, FILE_READ); - if (!f) + SdRuntimeFile f; + if (!f.open(kActivePath, "r")) { break; } @@ -532,7 +541,7 @@ bool TrackRecorder::restoreActiveSession() f.close(); String path(path_buf); - if (path.isEmpty() || !SD.exists(path.c_str())) + if (path.isEmpty() || !sd_exists(path.c_str())) { clearActiveStateLocked(); break; @@ -577,7 +586,7 @@ void TrackRecorder::updateActiveStateLocked() bool TrackRecorder::writeActiveStateLocked() const { - if (SD.cardType() == CARD_NONE) + if (!sd_card_ready()) { return false; } @@ -586,12 +595,12 @@ bool TrackRecorder::writeActiveStateLocked() const return false; } - if (SD.exists(kActivePath)) + if (sd_exists(kActivePath)) { - SD.remove(kActivePath); + sd_remove(kActivePath); } - File f = SD.open(kActivePath, FILE_WRITE); - if (!f) + SdRuntimeFile f; + if (!f.open(kActivePath, "w")) { return false; } @@ -631,37 +640,38 @@ bool TrackRecorder::writeActiveStateLocked() const void TrackRecorder::clearActiveStateLocked() const { - if (SD.cardType() == CARD_NONE) + if (!sd_card_ready()) { return; } - if (SD.exists(kActivePath)) + if (sd_exists(kActivePath)) { - SD.remove(kActivePath); + sd_remove(kActivePath); } } size_t TrackRecorder::listTracks(String* out_names, size_t max_names) const { - if (SD.cardType() == CARD_NONE || max_names == 0 || out_names == nullptr) + if (!sd_card_ready() || max_names == 0 || out_names == nullptr) { return 0; } - File dir = SD.open(kTrackDir); - if (!dir || !dir.isDirectory()) + SdRuntimeDir dir; + if (!dir.open(kTrackDir)) { return 0; } size_t count = 0; - for (File f = dir.openNextFile(); f && count < max_names; f = dir.openNextFile()) + char name_buf[128]; + bool is_dir = false; + while (count < max_names && dir.read_next(name_buf, sizeof(name_buf), &is_dir)) { - if (!f.isDirectory()) + if (!is_dir) { - out_names[count++] = String(f.name()); + out_names[count++] = String(name_buf); } - f.close(); } dir.close(); return count; diff --git a/platform/esp/arduino_common/src/platform_ui_route_storage.cpp b/platform/esp/arduino_common/src/platform_ui_route_storage.cpp index 9ab42120..280fc385 100644 --- a/platform/esp/arduino_common/src/platform_ui_route_storage.cpp +++ b/platform/esp/arduino_common/src/platform_ui_route_storage.cpp @@ -1,8 +1,8 @@ #include "platform/ui/route_storage.h" +#include "platform/esp/arduino_common/storage/sd_card_runtime.h" #include "platform/ui/device_runtime.h" -#include #include #include @@ -40,24 +40,24 @@ bool list_routes(std::vector& out_routes, std::size_t max_count) return false; } - File dir = SD.open(kRouteDir); - if (!dir || !dir.isDirectory()) + ::platform::esp::arduino_common::storage::SdRuntimeDir dir; + if (!dir.open(kRouteDir)) { return false; } - for (File file = dir.openNextFile(); file && out_routes.size() < max_count; file = dir.openNextFile()) + char name_buf[128]; + bool is_dir = false; + while (out_routes.size() < max_count && dir.read_next(name_buf, sizeof(name_buf), &is_dir)) { - if (!file.isDirectory()) + if (!is_dir) { - const char* raw_name = file.name(); - std::string name = raw_name ? raw_name : ""; + std::string name = name_buf; if (has_kml_extension(name)) { out_routes.push_back(name); } } - file.close(); } dir.close(); @@ -71,7 +71,7 @@ bool remove_route(const std::string& path) { return false; } - return SD.remove(path.c_str()); + return ::platform::esp::arduino_common::storage::sd_remove(path.c_str()); } const char* route_dir() @@ -79,4 +79,4 @@ const char* route_dir() return kRouteDir; } -} // namespace platform::ui::route_storage \ No newline at end of file +} // namespace platform::ui::route_storage diff --git a/platform/esp/arduino_common/src/platform_ui_settings_backup_runtime.cpp b/platform/esp/arduino_common/src/platform_ui_settings_backup_runtime.cpp index b1b9f09f..32bddaba 100644 --- a/platform/esp/arduino_common/src/platform_ui_settings_backup_runtime.cpp +++ b/platform/esp/arduino_common/src/platform_ui_settings_backup_runtime.cpp @@ -1,8 +1,9 @@ #include "platform/ui/settings_backup_runtime.h" +#include "platform/esp/arduino_common/storage/sd_card_runtime.h" + #include #include -#include #include #include @@ -80,22 +81,17 @@ void set_status_message(Status& out, const char* message, const char* detail = n bool sd_available() { - return ::platform::ui::device::card_ready() && SD.cardType() != CARD_NONE; + return ::platform::ui::device::card_ready() && + ::platform::esp::arduino_common::storage::sd_card_ready(); } bool ensure_backup_dir() { - if (SD.exists(kBackupDir)) + if (::platform::esp::arduino_common::storage::sd_exists(kBackupDir)) { - File dir = SD.open(kBackupDir, FILE_READ); - const bool ok = static_cast(dir) && dir.isDirectory(); - if (dir) - { - dir.close(); - } - return ok; + return ::platform::esp::arduino_common::storage::sd_is_directory(kBackupDir); } - return SD.mkdir(kBackupDir); + return ::platform::esp::arduino_common::storage::sd_mkdir(kBackupDir); } const char* value_type_name(ValueType type) @@ -808,12 +804,12 @@ bool write_text_atomic(const char* path, const char* temp_path, const char* text { return false; } - if (SD.exists(temp_path)) + if (::platform::esp::arduino_common::storage::sd_exists(temp_path)) { - SD.remove(temp_path); + ::platform::esp::arduino_common::storage::sd_remove(temp_path); } - File file = SD.open(temp_path, FILE_WRITE); - if (!file) + ::platform::esp::arduino_common::storage::SdRuntimeFile file; + if (!file.open(temp_path, "w")) { return false; } @@ -821,16 +817,16 @@ bool write_text_atomic(const char* path, const char* temp_path, const char* text file.close(); if (!wrote) { - SD.remove(temp_path); + ::platform::esp::arduino_common::storage::sd_remove(temp_path); return false; } - if (SD.exists(path)) + if (::platform::esp::arduino_common::storage::sd_exists(path)) { - SD.remove(path); + ::platform::esp::arduino_common::storage::sd_remove(path); } - if (!SD.rename(temp_path, path)) + if (!::platform::esp::arduino_common::storage::sd_rename(temp_path, path)) { - SD.remove(temp_path); + ::platform::esp::arduino_common::storage::sd_remove(temp_path); return false; } return true; @@ -839,8 +835,8 @@ bool write_text_atomic(const char* path, const char* temp_path, const char* text bool read_file_text(const char* path, std::string& out) { out.clear(); - File file = SD.open(path, FILE_READ); - if (!file) + ::platform::esp::arduino_common::storage::SdRuntimeFile file; + if (!file.open(path, "r")) { return false; } @@ -851,7 +847,7 @@ bool read_file_text(const char* path, std::string& out) return false; } out.resize(size); - const std::size_t read = file.readBytes(&out[0], size); + const std::size_t read = file.read_bytes(&out[0], size); file.close(); if (read != size) { @@ -900,7 +896,8 @@ Status status() Status out{}; out.supported = true; out.sd_present = sd_available(); - out.has_backup = out.sd_present && SD.exists(kBackupPath); + out.has_backup = out.sd_present && + ::platform::esp::arduino_common::storage::sd_exists(kBackupPath); out.busy = false; if (!out.sd_present) { @@ -945,7 +942,7 @@ bool backup() bool restore() { - if (!sd_available() || !SD.exists(kBackupPath)) + if (!sd_available() || !::platform::esp::arduino_common::storage::sd_exists(kBackupPath)) { return false; } @@ -983,11 +980,12 @@ bool remove() { return false; } - if (SD.exists(kBackupTempPath)) + if (::platform::esp::arduino_common::storage::sd_exists(kBackupTempPath)) { - SD.remove(kBackupTempPath); + ::platform::esp::arduino_common::storage::sd_remove(kBackupTempPath); } - return !SD.exists(kBackupPath) || SD.remove(kBackupPath); + return !::platform::esp::arduino_common::storage::sd_exists(kBackupPath) || + ::platform::esp::arduino_common::storage::sd_remove(kBackupPath); } } // namespace platform::ui::settings_backup diff --git a/platform/esp/arduino_common/src/platform_ui_tracker_runtime.cpp b/platform/esp/arduino_common/src/platform_ui_tracker_runtime.cpp index 0e33b32c..92df668c 100644 --- a/platform/esp/arduino_common/src/platform_ui_tracker_runtime.cpp +++ b/platform/esp/arduino_common/src/platform_ui_tracker_runtime.cpp @@ -1,10 +1,9 @@ #include "platform/ui/tracker_runtime.h" #include "platform/esp/arduino_common/gps/track_recorder.h" +#include "platform/esp/arduino_common/storage/sd_card_runtime.h" #include "platform/ui/device_runtime.h" -#include - namespace platform::ui::tracker { @@ -64,7 +63,7 @@ bool remove_track(const std::string& path) { return false; } - return SD.remove(path.c_str()); + return ::platform::esp::arduino_common::storage::sd_remove(path.c_str()); } const char* track_dir() @@ -92,4 +91,4 @@ void set_format(Format format) ::gps::TrackRecorder::getInstance().setFormat(static_cast<::gps::TrackFormat>(format)); } -} // namespace platform::ui::tracker \ No newline at end of file +} // namespace platform::ui::tracker diff --git a/platform/esp/arduino_common/src/sstv/sstv_service.cpp b/platform/esp/arduino_common/src/sstv/sstv_service.cpp index 66cbb3c0..605fce57 100644 --- a/platform/esp/arduino_common/src/sstv/sstv_service.cpp +++ b/platform/esp/arduino_common/src/sstv/sstv_service.cpp @@ -24,8 +24,8 @@ #include "sys/clock.h" #else #include "boards/tlora_pager/tlora_pager_board.h" +#include "platform/esp/arduino_common/storage/sd_card_runtime.h" #include "platform/esp/common/shared_spi_lock.h" -#include #endif #include @@ -337,6 +337,7 @@ const char* get_saved_path() bool ensure_sstv_dir() { +#if defined(TRAIL_MATE_ESP_BOARD_TAB5) if (!SD.exists("/sstv")) { if (!SD.mkdir("/sstv")) @@ -345,6 +346,13 @@ bool ensure_sstv_dir() } } return true; +#else + if (::platform::esp::arduino_common::storage::sd_exists("/sstv")) + { + return ::platform::esp::arduino_common::storage::sd_is_directory("/sstv"); + } + return ::platform::esp::arduino_common::storage::sd_mkdir("/sstv"); +#endif } bool build_save_path(char* out_path, size_t out_len) @@ -377,7 +385,11 @@ bool build_save_path(char* out_path, size_t out_len) snprintf(out_path, out_len, "/sstv/%lu_%03d.bmp", static_cast(millis()), i); } +#if defined(TRAIL_MATE_ESP_BOARD_TAB5) if (!SD.exists(out_path)) +#else + if (!::platform::esp::arduino_common::storage::sd_exists(out_path)) +#endif { return true; } @@ -464,7 +476,11 @@ bool save_frame_to_sd() return false; } ::platform::esp::common::SharedSpiLockGuard guard; +#if defined(TRAIL_MATE_ESP_BOARD_TAB5) if (SD.cardType() == CARD_NONE) +#else + if (!::platform::esp::arduino_common::storage::sd_card_ready()) +#endif { set_error("SD not ready"); return false; @@ -489,8 +505,13 @@ bool save_frame_to_sd() const uint32_t file_size = 14 + 40 + pixel_bytes; const uint32_t data_offset = 14 + 40; +#if defined(TRAIL_MATE_ESP_BOARD_TAB5) File f = SD.open(path, FILE_WRITE); if (!f) +#else + ::platform::esp::arduino_common::storage::SdRuntimeFile f; + if (!f.open(path, "w")) +#endif { set_error("SD open failed"); return false; diff --git a/platform/esp/arduino_common/src/storage/sd_card_runtime.cpp b/platform/esp/arduino_common/src/storage/sd_card_runtime.cpp index 84b7a838..d462610d 100644 --- a/platform/esp/arduino_common/src/storage/sd_card_runtime.cpp +++ b/platform/esp/arduino_common/src/storage/sd_card_runtime.cpp @@ -8,9 +8,11 @@ #endif #include #include +#include #include #include #include +#include namespace platform::esp::arduino_common::storage { @@ -431,6 +433,23 @@ bool SdRuntimeFile::is_open() const return impl_ != nullptr && impl_->backend != SdCardBackend::None; } +int SdRuntimeFile::available() const +{ + if (!is_open()) + { + return 0; + } + if (impl_->backend == SdCardBackend::ArduinoSd) + { + return impl_->arduino_file.available(); + } + if (impl_->backend == SdCardBackend::SdFat) + { + return impl_->sdfat_file.available(); + } + return 0; +} + int SdRuntimeFile::read(void* buffer, std::size_t bytes_to_read) { if (!is_open() || buffer == nullptr || bytes_to_read == 0) @@ -448,6 +467,41 @@ int SdRuntimeFile::read(void* buffer, std::size_t bytes_to_read) return -1; } +int SdRuntimeFile::read_byte() +{ + if (!is_open()) + { + return -1; + } + if (impl_->backend == SdCardBackend::ArduinoSd) + { + return impl_->arduino_file.read(); + } + if (impl_->backend == SdCardBackend::SdFat) + { + return impl_->sdfat_file.read(); + } + return -1; +} + +std::size_t SdRuntimeFile::read_bytes(char* buffer, std::size_t bytes_to_read) +{ + if (!is_open() || buffer == nullptr || bytes_to_read == 0) + { + return 0; + } + if (impl_->backend == SdCardBackend::ArduinoSd) + { + return impl_->arduino_file.readBytes(buffer, bytes_to_read); + } + if (impl_->backend == SdCardBackend::SdFat) + { + int result = impl_->sdfat_file.read(buffer, bytes_to_read); + return result > 0 ? static_cast(result) : 0; + } + return 0; +} + std::size_t SdRuntimeFile::write(const void* buffer, std::size_t bytes_to_write) { if (!is_open() || buffer == nullptr || bytes_to_write == 0) @@ -465,6 +519,83 @@ std::size_t SdRuntimeFile::write(const void* buffer, std::size_t bytes_to_write) return 0; } +std::size_t SdRuntimeFile::write_byte(uint8_t value) +{ + if (!is_open()) + { + return 0; + } + if (impl_->backend == SdCardBackend::ArduinoSd) + { + return impl_->arduino_file.write(value); + } + if (impl_->backend == SdCardBackend::SdFat) + { + return impl_->sdfat_file.write(value); + } + return 0; +} + +std::size_t SdRuntimeFile::print(const char* text) +{ + if (!is_open() || text == nullptr) + { + return 0; + } + if (impl_->backend == SdCardBackend::ArduinoSd) + { + return impl_->arduino_file.print(text); + } + if (impl_->backend == SdCardBackend::SdFat) + { + return impl_->sdfat_file.print(text); + } + return 0; +} + +std::size_t SdRuntimeFile::print(double value, int digits) +{ + if (!is_open()) + { + return 0; + } + const uint8_t precision = digits < 0 ? 0 : static_cast(digits); + if (impl_->backend == SdCardBackend::ArduinoSd) + { + return impl_->arduino_file.print(value, precision); + } + if (impl_->backend == SdCardBackend::SdFat) + { + return impl_->sdfat_file.print(value, precision); + } + return 0; +} + +std::size_t SdRuntimeFile::printf(const char* format, ...) +{ + if (!is_open() || format == nullptr) + { + return 0; + } + + va_list args; + va_start(args, format); + va_list args_copy; + va_copy(args_copy, args); + int len = std::vsnprintf(nullptr, 0, format, args_copy); + va_end(args_copy); + if (len <= 0) + { + va_end(args); + return 0; + } + + std::vector buffer(static_cast(len) + 1U); + std::vsnprintf(buffer.data(), buffer.size(), format, args); + va_end(args); + return write(buffer.data(), static_cast(len)); +} + bool SdRuntimeFile::seek(uint64_t offset) { if (!is_open()) diff --git a/platform/esp/arduino_common/src/ui/screens/team/team_ui_store.cpp b/platform/esp/arduino_common/src/ui/screens/team/team_ui_store.cpp index efd87ed2..64ceab2e 100644 --- a/platform/esp/arduino_common/src/ui/screens/team/team_ui_store.cpp +++ b/platform/esp/arduino_common/src/ui/screens/team/team_ui_store.cpp @@ -7,7 +7,7 @@ #include "ui/team_persistence/team_ui_snapshot_codec.h" #include "sys/clock.h" -#include +#include "platform/esp/arduino_common/storage/sd_card_runtime.h" #include #include #include @@ -26,6 +26,13 @@ TeamUiSnapshot TeamUiSnapshotMemoryStore::snapshot_{}; namespace { +using ::platform::esp::arduino_common::storage::SdRuntimeFile; +using ::platform::esp::arduino_common::storage::sd_card_ready; +using ::platform::esp::arduino_common::storage::sd_exists; +using ::platform::esp::arduino_common::storage::sd_mkdir; +using ::platform::esp::arduino_common::storage::sd_remove; +using ::platform::esp::arduino_common::storage::sd_rename; + constexpr const char* kBaseDir = "/team"; constexpr const char* kCurrentPath = "/team/current.txt"; constexpr const char* kCurrentTmpPath = "/team/current.tmp"; @@ -144,11 +151,11 @@ std::string team_dir_from_id(const TeamId& id) bool ensure_dir(const char* path) { - if (SD.exists(path)) + if (sd_exists(path)) { return true; } - return SD.mkdir(path); + return sd_mkdir(path); } std::string iso_time(time_t t) @@ -186,12 +193,12 @@ std::string trim_copy(const std::string& value) return value.substr(start, end - start); } -std::string read_line(File& file) +std::string read_line(SdRuntimeFile& file) { std::string line; while (file.available()) { - const int raw = file.read(); + const int raw = file.read_byte(); if (raw < 0 || raw == '\n') { break; @@ -206,12 +213,12 @@ std::string read_line(File& file) bool read_current_dir(std::string& out_dir) { - if (SD.cardType() == CARD_NONE || !SD.exists(kCurrentPath)) + if (!sd_card_ready() || !sd_exists(kCurrentPath)) { return false; } - File f = SD.open(kCurrentPath, FILE_READ); - if (!f) + SdRuntimeFile f; + if (!f.open(kCurrentPath, "r")) { return false; } @@ -227,7 +234,7 @@ bool read_current_dir(std::string& out_dir) bool write_current_dir(const std::string& dir) { - if (SD.cardType() == CARD_NONE) + if (!sd_card_ready()) { return false; } @@ -235,8 +242,8 @@ bool write_current_dir(const std::string& dir) { return false; } - File f = SD.open(kCurrentTmpPath, FILE_WRITE); - if (!f) + SdRuntimeFile f; + if (!f.open(kCurrentTmpPath, "w")) { return false; } @@ -244,33 +251,33 @@ bool write_current_dir(const std::string& dir) f.print("\n"); f.flush(); f.close(); - if (SD.exists(kCurrentPath)) + if (sd_exists(kCurrentPath)) { - SD.remove(kCurrentPath); + sd_remove(kCurrentPath); } - return SD.rename(kCurrentTmpPath, kCurrentPath); + return sd_rename(kCurrentTmpPath, kCurrentPath); } bool clear_current_dir() { - if (SD.cardType() == CARD_NONE) + if (!sd_card_ready()) { return false; } - if (SD.exists(kCurrentPath)) + if (sd_exists(kCurrentPath)) { - SD.remove(kCurrentPath); + sd_remove(kCurrentPath); } - if (SD.exists(kCurrentTmpPath)) + if (sd_exists(kCurrentTmpPath)) { - SD.remove(kCurrentTmpPath); + sd_remove(kCurrentTmpPath); } return true; } bool ensure_team_dir_for_id_internal(const TeamId& id, std::string& out_dir_path, bool update_current) { - if (SD.cardType() == CARD_NONE) + if (!sd_card_ready()) { return false; } @@ -296,12 +303,12 @@ bool ensure_team_dir_for_id(const TeamId& id, std::string& out_dir_path) return ensure_team_dir_for_id_internal(id, out_dir_path, true); } -void write_u8(File& f, uint8_t v) +void write_u8(SdRuntimeFile& f, uint8_t v) { f.write(&v, 1); } -void write_u16(File& f, uint16_t v) +void write_u16(SdRuntimeFile& f, uint16_t v) { uint8_t b[2] = { static_cast(v & 0xFF), @@ -309,7 +316,7 @@ void write_u16(File& f, uint16_t v) f.write(b, 2); } -void write_u32(File& f, uint32_t v) +void write_u32(SdRuntimeFile& f, uint32_t v) { uint8_t b[4]; for (int i = 0; i < 4; ++i) @@ -319,7 +326,7 @@ void write_u32(File& f, uint32_t v) f.write(b, 4); } -void write_u64(File& f, uint64_t v) +void write_u64(SdRuntimeFile& f, uint64_t v) { uint8_t b[8]; for (int i = 0; i < 8; ++i) @@ -508,12 +515,12 @@ void apply_key_event(TeamUiSnapshot& snap, TeamKeyEventType type, const std::vec bool load_snapshot_from_path(const std::string& snapshot_path, TeamUiSnapshot& out) { - if (!SD.exists(snapshot_path.c_str())) + if (!sd_exists(snapshot_path.c_str())) { return false; } - File f = SD.open(snapshot_path.c_str(), FILE_READ); - if (!f) + SdRuntimeFile f; + if (!f.open(snapshot_path.c_str(), "r")) { return false; } @@ -533,7 +540,7 @@ bool load_snapshot_from_path(const std::string& snapshot_path, TeamUiSnapshot& o bool save_snapshot_to_path(const std::string& dir_path, const TeamUiSnapshot& in) { - if (SD.cardType() == CARD_NONE) + if (!sd_card_ready()) { return false; } @@ -555,8 +562,8 @@ bool save_snapshot_to_path(const std::string& dir_path, const TeamUiSnapshot& in return false; } - File f = SD.open(tmp_path.c_str(), FILE_WRITE); - if (!f) + SdRuntimeFile f; + if (!f.open(tmp_path.c_str(), "w")) { return false; } @@ -567,21 +574,21 @@ bool save_snapshot_to_path(const std::string& dir_path, const TeamUiSnapshot& in f.close(); std::string out_path_c = out_path; - if (SD.exists(out_path_c.c_str())) + if (sd_exists(out_path_c.c_str())) { - SD.remove(out_path_c.c_str()); + sd_remove(out_path_c.c_str()); } - return SD.rename(tmp_path.c_str(), out_path_c.c_str()); + return sd_rename(tmp_path.c_str(), out_path_c.c_str()); } bool load_keys_from_path(const std::string& keys_path, TeamUiSnapshot& out) { - if (!SD.exists(keys_path.c_str())) + if (!sd_exists(keys_path.c_str())) { return false; } - File f = SD.open(keys_path.c_str(), FILE_READ); - if (!f) + SdRuntimeFile f; + if (!f.open(keys_path.c_str(), "r")) { return false; } @@ -634,8 +641,8 @@ bool save_keys_to_path(const std::string& dir_path, const TeamUiSnapshot& in) } std::string tmp_path = dir_path + "/" + kKeysTmpName; std::string out_path = dir_path + "/" + kKeysName; - File f = SD.open(tmp_path.c_str(), FILE_WRITE); - if (!f) + SdRuntimeFile f; + if (!f.open(tmp_path.c_str(), "w")) { return false; } @@ -648,21 +655,21 @@ bool save_keys_to_path(const std::string& dir_path, const TeamUiSnapshot& in) f.write(in.team_psk.data(), in.team_psk.size()); f.flush(); f.close(); - if (SD.exists(out_path.c_str())) + if (sd_exists(out_path.c_str())) { - SD.remove(out_path.c_str()); + sd_remove(out_path.c_str()); } - return SD.rename(tmp_path.c_str(), out_path.c_str()); + return sd_rename(tmp_path.c_str(), out_path.c_str()); } bool load_events_apply(const std::string& events_path, TeamUiSnapshot& out) { - if (!SD.exists(events_path.c_str())) + if (!sd_exists(events_path.c_str())) { return false; } - File f = SD.open(events_path.c_str(), FILE_READ); - if (!f) + SdRuntimeFile f; + if (!f.open(events_path.c_str(), "r")) { return false; } @@ -731,7 +738,7 @@ bool append_event(const TeamId& team_id, TeamKeyEventType type, uint32_t event_seq, uint32_t ts, const uint8_t* payload, size_t len) { - if (SD.cardType() == CARD_NONE) + if (!sd_card_ready()) { return false; } @@ -742,8 +749,8 @@ bool append_event(const TeamId& team_id, TeamKeyEventType type, } std::string events_path = dir_path + "/" + kEventsName; - File f = SD.open(events_path.c_str(), FILE_APPEND); - if (!f) + SdRuntimeFile f; + if (!f.open(events_path.c_str(), "a")) { return false; } @@ -817,7 +824,7 @@ bool should_write_pos(uint32_t member_id, int32_t lat_e7, int32_t lon_e7, uint32 return true; } -bool init_posring(File& f) +bool init_posring(SdRuntimeFile& f) { f.seek(0); f.write(reinterpret_cast("PSR1"), 4); @@ -833,7 +840,7 @@ bool init_posring(File& f) return true; } -bool read_posring_header(File& f, uint32_t& write_offset) +bool read_posring_header(SdRuntimeFile& f, uint32_t& write_offset) { f.seek(0); uint8_t header[kPosHeaderSize]; @@ -869,7 +876,7 @@ bool read_posring_header(File& f, uint32_t& write_offset) return true; } -bool write_posring_header(File& f, uint32_t write_offset) +bool write_posring_header(SdRuntimeFile& f, uint32_t write_offset) { f.seek(0); f.write(reinterpret_cast("PSR1"), 4); @@ -890,7 +897,7 @@ class TeamUiSnapshotStorePersisted : public ITeamUiSnapshotStore public: bool load(TeamUiSnapshot& out) override { - if (SD.cardType() == CARD_NONE) + if (!sd_card_ready()) { return false; } @@ -938,7 +945,7 @@ class TeamUiSnapshotStorePersisted : public ITeamUiSnapshotStore void save(const TeamUiSnapshot& in) override { - if (SD.cardType() == CARD_NONE) + if (!sd_card_ready()) { return; } @@ -1124,14 +1131,14 @@ bool team_ui_posring_append(const TeamId& team_id, } std::string path = dir_path + "/" + kPosringName; - File f = SD.open(path.c_str(), FILE_READ); - bool exists = f; - if (f) + SdRuntimeFile f; + bool exists = f.open(path.c_str(), "r"); + if (exists) { f.close(); } - File rw = SD.open(path.c_str(), FILE_WRITE); - if (!rw) + SdRuntimeFile rw; + if (!rw.open(path.c_str(), exists ? "r+" : "w+")) { return false; } @@ -1173,19 +1180,19 @@ bool team_ui_posring_load_latest(const TeamId& team_id, std::vector& out) { out.clear(); - if (SD.cardType() == CARD_NONE) + if (!sd_card_ready()) { return false; } std::string dir = team_dir_from_id(team_id); std::string dir_path = std::string(kBaseDir) + "/" + dir; std::string path = dir_path + "/" + kPosringName; - if (!SD.exists(path.c_str())) + if (!sd_exists(path.c_str())) { return false; } - File f = SD.open(path.c_str(), FILE_READ); - if (!f) + SdRuntimeFile f; + if (!f.open(path.c_str(), "r")) { return false; } @@ -1294,27 +1301,27 @@ bool TeamUiSdChatLogStore::appendStructured(const TeamId& team_id, std::string path = dir_path + "/" + kChatlogName; size_t record_len = 2 + 1 + 1 + 4 + 4 + 1 + 3 + 2 + 2 + payload.size(); - if (SD.exists(path.c_str())) + if (sd_exists(path.c_str())) { - File f = SD.open(path.c_str(), FILE_READ); - if (f) + SdRuntimeFile f; + if (f.open(path.c_str(), "r")) { size_t size = f.size(); f.close(); if (size + record_len > kChatlogMaxBytes) { std::string old_path = dir_path + "/" + kChatlogOldName; - if (SD.exists(old_path.c_str())) + if (sd_exists(old_path.c_str())) { - SD.remove(old_path.c_str()); + sd_remove(old_path.c_str()); } - SD.rename(path.c_str(), old_path.c_str()); + sd_rename(path.c_str(), old_path.c_str()); } } } - File out = SD.open(path.c_str(), FILE_APPEND); - if (!out) + SdRuntimeFile out; + if (!out.open(path.c_str(), "a")) { return false; } @@ -1359,7 +1366,7 @@ bool TeamUiSdChatLogStore::loadRecent(const TeamId& team_id, std::vector& out) { out.clear(); - if (SD.cardType() == CARD_NONE) + if (!sd_card_ready()) { return false; } @@ -1369,12 +1376,12 @@ bool TeamUiSdChatLogStore::loadRecent(const TeamId& team_id, return false; } std::string path = dir_path + "/" + kChatlogName; - if (!SD.exists(path.c_str())) + if (!sd_exists(path.c_str())) { return false; } - File f = SD.open(path.c_str(), FILE_READ); - if (!f) + SdRuntimeFile f; + if (!f.open(path.c_str(), "r")) { return false; } @@ -1492,7 +1499,7 @@ bool team_ui_save_keys_now(const TeamId& team_id, uint32_t key_id, const std::array& psk) { - if (SD.cardType() == CARD_NONE) + if (!sd_card_ready()) { return false; } @@ -1518,7 +1525,7 @@ bool team_ui_get_member_track_path(const TeamId& team_id, uint32_t member_id, std::string& out_path) { - if (SD.cardType() == CARD_NONE) + if (!sd_card_ready()) { return false; } @@ -1542,7 +1549,7 @@ bool team_ui_append_member_track(const TeamId& team_id, uint32_t member_id, const team::proto::TeamTrackMessage& track) { - if (SD.cardType() == CARD_NONE) + if (!sd_card_ready()) { return false; } @@ -1578,8 +1585,8 @@ bool team_ui_append_member_track(const TeamId& team_id, snprintf(name, sizeof(name), "%08lX.gpx", static_cast(member_id)); std::string path = tracks_dir + "/" + name; - File f = SD.open(path.c_str(), FILE_WRITE); - if (!f) + SdRuntimeFile f; + if (!f.open(path.c_str(), "a+")) { return false; }