diff --git a/boards/tdeck/src/tdeck_board.cpp b/boards/tdeck/src/tdeck_board.cpp index 037de0e4..beeea634 100644 --- a/boards/tdeck/src/tdeck_board.cpp +++ b/boards/tdeck/src/tdeck_board.cpp @@ -6,7 +6,6 @@ #include #include #include -#include #include #include #include @@ -652,7 +651,7 @@ bool TDeckBoard::installSD() extra_cs_count = 1; #endif - uint8_t cardType = CARD_NONE; + uint8_t cardType = sdutil::kCardNone; uint32_t cardSizeMB = 0; bool ok = sdutil::installSpiSd(*this, SD_CS, SD_SPI_FREQUENCY, "/sd", extra_cs, extra_cs_count, diff --git a/boards/tdeck_pro/src/tdeck_pro_board.cpp b/boards/tdeck_pro/src/tdeck_pro_board.cpp index 3f5b13ab..ff2300f9 100644 --- a/boards/tdeck_pro/src/tdeck_pro_board.cpp +++ b/boards/tdeck_pro/src/tdeck_pro_board.cpp @@ -6,7 +6,6 @@ #include #include #include -#include #include #include #include @@ -16,6 +15,8 @@ #include #include +#include "platform/esp/arduino_common/storage/sd_card_runtime.h" + namespace boards::tdeck_pro { @@ -26,6 +27,7 @@ constexpr time_t kMinValidEpochSeconds = 1577836800; // 2020-01-01 UTC constexpr uint8_t kKeyboardRows = 4; constexpr uint8_t kKeyboardCols = 10; constexpr uint32_t kEpdSpiHz = 2000000; +constexpr uint32_t kSdSpiHz = 4000000; constexpr uint8_t kFlushLogLimit = 8; constexpr uint32_t kRadioTxMaxTimeoutMs = 120000; SemaphoreHandle_t g_shared_spi_mutex = nullptr; @@ -346,16 +348,30 @@ bool TDeckProBoard::installSD() { pinMode(profile().sd.cs, OUTPUT); digitalWrite(profile().sd.cs, HIGH); + static const int extra_cs_pins[] = { + profile().lora.cs, + profile().epd.cs, + }; + for (int pin : extra_cs_pins) + { + pinMode(pin, OUTPUT); + digitalWrite(pin, HIGH); + } sharedSpiLock(); sharedSpiPrepareDevice(profile().sd.cs); - const bool ok = SD.begin(profile().sd.cs, SPI); + const bool ok = ::platform::esp::arduino_common::storage::mount_sd_card( + profile().sd.cs, + SPI, + kSdSpiHz, + "/sd", + 8); sharedSpiUnlock(); return ok; } void TDeckProBoard::uninstallSD() { - SD.end(); + ::platform::esp::arduino_common::storage::unmount_sd_card(); } uint32_t TDeckProBoard::begin(uint32_t disable_hw_init) @@ -453,7 +469,7 @@ int TDeckProBoard::getBatteryLevel() bool TDeckProBoard::isCardReady() { - return sd_ready_ && SD.cardType() != CARD_NONE; + return sd_ready_ && ::platform::esp::arduino_common::storage::sd_card_ready(); } void TDeckProBoard::vibrator() diff --git a/boards/tlora_pager/include/boards/tlora_pager/tlora_pager_board.h b/boards/tlora_pager/include/boards/tlora_pager/tlora_pager_board.h index a1739945..34e6c0b9 100644 --- a/boards/tlora_pager/include/boards/tlora_pager/tlora_pager_board.h +++ b/boards/tlora_pager/include/boards/tlora_pager/tlora_pager_board.h @@ -10,7 +10,6 @@ #include #include -#include #include #include #include diff --git a/boards/tlora_pager/src/tlora_pager_board.cpp b/boards/tlora_pager/src/tlora_pager_board.cpp index 6055908c..f55c3454 100644 --- a/boards/tlora_pager/src/tlora_pager_board.cpp +++ b/boards/tlora_pager/src/tlora_pager_board.cpp @@ -772,10 +772,13 @@ bool TLoRaPagerBoard::installSD() // Ensure SPI pins are initialized initShareSPIPins(); - uint8_t card_type = CARD_NONE; + uint8_t card_type = sdutil::kCardNone; uint32_t card_size_mb = 0; + static const int extra_cs_pins[] = {NFC_CS, LORA_CS}; bool ok = sdutil::installSpiSd(*this, SD_CS, SD_SPI_FREQUENCY, "/sd", - nullptr, 0, &card_type, &card_size_mb); + extra_cs_pins, + sizeof(extra_cs_pins) / sizeof(extra_cs_pins[0]), + &card_type, &card_size_mb); if (!ok) { log_w("SD card initialization failed"); diff --git a/platform/esp/arduino_common/include/lv_conf.h b/platform/esp/arduino_common/include/lv_conf.h index 54b02534..2df57c2c 100644 --- a/platform/esp/arduino_common/include/lv_conf.h +++ b/platform/esp/arduino_common/include/lv_conf.h @@ -738,12 +738,17 @@ #define LV_FS_STDIO_CACHE_SIZE 0 /*>0 to cache this number of bytes in lv_fs_read()*/ #endif +/* TrailMate registers its own nonblocking shared-SPI SD driver on A:. + * Do not also enable LVGL's built-in POSIX A: driver; duplicate A: drivers + * make SD routing ambiguous and can bypass the shared-SPI backpressure path. + */ +#define TRAIL_MATE_LVGL_SD_FS_LETTER 'A' +#define TRAIL_MATE_LVGL_SD_FS_PATH "/sd" + /*API for open, read, etc*/ -#define LV_USE_FS_POSIX 1 +#define LV_USE_FS_POSIX 0 #if LV_USE_FS_POSIX - #define LV_FS_POSIX_LETTER 'A' /*Set an upper cased letter on which the drive will accessible (e.g. 'A')*/ - #define LV_FS_POSIX_PATH "/sd" /* /sd : SD Card /fs: FFat */ - #define LV_FS_POSIX_CACHE_SIZE 0 /*>0 to cache this number of bytes in lv_fs_read()*/ + #error "TrailMate ESP builds must use the custom A: SD driver, not LVGL POSIX." #endif /*API for CreateFile, ReadFile, etc*/ diff --git a/platform/esp/arduino_common/include/platform/esp/arduino_common/chat/infra/store/log_store.h b/platform/esp/arduino_common/include/platform/esp/arduino_common/chat/infra/store/log_store.h deleted file mode 100644 index dc168e40..00000000 --- a/platform/esp/arduino_common/include/platform/esp/arduino_common/chat/infra/store/log_store.h +++ /dev/null @@ -1,124 +0,0 @@ -/** - * @file log_store.h - * @brief Per-conversation ring log store (SD-based) - */ - -#pragma once - -#include "chat/ports/i_chat_store.h" -#include -#include -#include -#include - -namespace chat -{ - -/** - * @brief SD-backed append-only log with per-channel ring index. - * - * Layout: - * - Conversation file: /chat/_n_.log or /chat/_broadcast_.log - * - Index file: /chat/index.bin (conversation metadata) - */ -class LogStore : public IChatStore -{ - public: - static constexpr const char* kDir = "/chat"; - static constexpr const char* kIndexFile = "/chat/index.bin"; - static constexpr size_t kMaxMessagesPerConv = 100; - static constexpr size_t kMaxTextLen = 233; - static constexpr size_t kPreviewLen = 48; - - LogStore() : fs_(nullptr) {} - ~LogStore() override = default; - - /** - * @brief Initialize storage. Expects SD already mounted. - * @param fs Filesystem (e.g., SD) - * @return true if ready - */ - bool begin(fs::FS& fs); - - void append(const ChatMessage& msg) override; - std::vector loadRecent(const ConversationId& conv, size_t n) override; - std::vector loadConversationPage(size_t offset, - size_t limit, - size_t* total) override; - void setUnread(const ConversationId& conv, int unread) override; - int getUnread(const ConversationId& conv) const override; - void clearConversation(const ConversationId& conv) override; - void clearAll() override; - bool updateMessageStatus(MessageId msg_id, MessageStatus status) override; - bool getMessage(MessageId msg_id, ChatMessage* out) const override; - - private: - fs::FS* fs_; - - struct FileHeader - { - uint32_t magic; - uint16_t version; - uint16_t head; - uint16_t count; - uint16_t reserved; - } __attribute__((packed)); - - struct Record - { - uint8_t protocol; - uint8_t channel; - uint8_t status; - uint16_t text_len; - uint32_t from; - uint32_t peer; - uint32_t msg_id; - uint32_t timestamp; - char text[kMaxTextLen]; - } __attribute__((packed)); - - struct IndexHeader - { - uint32_t magic; - uint16_t version; - uint16_t count; - } __attribute__((packed)); - - struct IndexEntry - { - uint8_t protocol; - uint8_t channel; - uint8_t status; - uint16_t unread; - uint32_t peer; - uint32_t last_msg_id; - uint32_t last_timestamp; - uint32_t last_from; - uint16_t preview_len; - char preview[kPreviewLen]; - } __attribute__((packed)); - - static constexpr uint32_t kFileMagic = 0x474F4C43; // "CLOG" - static constexpr uint32_t kIndexMagic = 0x54414843; // "CHAT" - static constexpr uint16_t kVersion = 2; - - bool ensureDir(); - bool ensureIndex(std::vector& entries); - bool writeIndex(const std::vector& entries); - bool readIndex(std::vector& entries); - bool findIndexEntry(const ConversationId& conv, - std::vector& entries, - size_t* out_idx); - void updateIndexForMessage(const ChatMessage& msg); - void rebuildIndex(); - bool loadFileHeader(File& file, FileHeader& header); - void initFileHeader(File& file); - bool readRecord(File& file, uint16_t slot, Record& rec); - bool writeRecord(File& file, uint16_t slot, const Record& rec); - void buildConversationPath(const ConversationId& conv, - char* out, - size_t out_len) const; - const char* channelName(ChannelId channel) const; -}; - -} // namespace chat 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 d5c2993e..d49acd3b 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 @@ -11,7 +11,6 @@ namespace platform::esp::arduino_common::storage enum class SdCardBackend : uint8_t { None = 0, - ArduinoSd, SdFat, }; @@ -35,7 +34,6 @@ bool mount_sd_card(int sd_cs, void unmount_sd_card(); bool sd_card_ready(); -bool sd_card_uses_arduino_sd(); bool sd_card_uses_sdfat(); bool sd_card_is_exfat(); SdCardBackend sd_card_backend(); @@ -46,6 +44,7 @@ const char* sd_card_filesystem_name(); bool sd_exists(const char* path); bool sd_is_directory(const char* path); bool sd_mkdir(const char* path); +bool sd_rmdir(const char* path); bool sd_remove(const char* path); bool sd_rename(const char* old_path, const char* new_path); diff --git a/platform/esp/arduino_common/include/ui/LV_Helper.h b/platform/esp/arduino_common/include/ui/LV_Helper.h index 0efc3bc1..972aa42c 100644 --- a/platform/esp/arduino_common/include/ui/LV_Helper.h +++ b/platform/esp/arduino_common/include/ui/LV_Helper.h @@ -4,12 +4,12 @@ class LilyGo_Display; -#if LV_USE_FS_POSIX != 1 || LV_FS_POSIX_LETTER != 'A' -#warning "Lvgl fs mismatch, may not be able to use fs function" +#if !defined(TRAIL_MATE_LVGL_SD_FS_LETTER) || TRAIL_MATE_LVGL_SD_FS_LETTER != 'A' +#warning "TrailMate LVGL SD fs mismatch, A: paths may not resolve" #endif void beginLvglHelper(LilyGo_Display& display, bool debug = false); void lv_set_default_group(lv_group_t* group); lv_indev_t* lv_get_touch_indev(); lv_indev_t* lv_get_keyboard_indev(); -lv_indev_t* lv_get_encoder_indev(); \ No newline at end of file +lv_indev_t* lv_get_encoder_indev(); diff --git a/platform/esp/arduino_common/src/LV_Helper_v9.cpp b/platform/esp/arduino_common/src/LV_Helper_v9.cpp index 380df04c..2762fafe 100644 --- a/platform/esp/arduino_common/src/LV_Helper_v9.cpp +++ b/platform/esp/arduino_common/src/LV_Helper_v9.cpp @@ -600,7 +600,7 @@ void init_sd_fs_driver() } lv_fs_drv_init(&s_sd_fs_drv); - s_sd_fs_drv.letter = LV_FS_POSIX_LETTER; + s_sd_fs_drv.letter = TRAIL_MATE_LVGL_SD_FS_LETTER; s_sd_fs_drv.ready_cb = sd_fs_ready_cb; s_sd_fs_drv.open_cb = sd_fs_open; s_sd_fs_drv.close_cb = sd_fs_close; @@ -618,8 +618,8 @@ void init_sd_fs_driver() letters[0] = '\0'; lv_fs_get_letters(letters); Serial.printf("[LVGL][FS] SD driver registered letter=%c ready=%d letters=%s\n", - LV_FS_POSIX_LETTER, - lv_fs_is_ready(LV_FS_POSIX_LETTER) ? 1 : 0, + TRAIL_MATE_LVGL_SD_FS_LETTER, + lv_fs_is_ready(TRAIL_MATE_LVGL_SD_FS_LETTER) ? 1 : 0, letters); } } // namespace diff --git a/platform/esp/arduino_common/src/app_context_platform_bindings.cpp b/platform/esp/arduino_common/src/app_context_platform_bindings.cpp index 38a32da5..c598e7bc 100644 --- a/platform/esp/arduino_common/src/app_context_platform_bindings.cpp +++ b/platform/esp/arduino_common/src/app_context_platform_bindings.cpp @@ -2,7 +2,6 @@ #include "platform/esp/arduino_common/app_config_store.h" #include -#include #include "app/app_facades.h" #include "board/GpsBoard.h" @@ -14,11 +13,9 @@ #include "platform/esp/arduino_common/chat/infra/mesh_adapter_router.h" #include "platform/esp/arduino_common/chat/infra/meshtastic/node_store.h" #include "platform/esp/arduino_common/chat/infra/protocol_factory.h" -#include "platform/esp/arduino_common/chat/infra/store/log_store.h" #include "platform/esp/arduino_common/device_identity.h" #include "platform/esp/arduino_common/gps/gps_service.h" #include "platform/esp/arduino_common/gps/track_recorder.h" -#include "platform/esp/arduino_common/storage/sd_card_runtime.h" #include "platform/esp/arduino_common/team/crypto/team_crypto.h" #include "platform/esp/arduino_common/team/event/team_app_data_event_bus_bridge.h" #include "platform/esp/arduino_common/team/event/team_event_bus_sink.h" @@ -113,18 +110,6 @@ void set_team_mode_active(bool active) std::unique_ptr create_chat_store() { - const bool sd_available = - ::platform::esp::arduino_common::storage::sd_card_uses_arduino_sd(); - if (sd_available) - { - auto log_store = std::unique_ptr(new chat::LogStore()); - if (log_store->begin(SD)) - { - Serial.printf("[AppContext] chat store=LogStore (SD)\n"); - return log_store; - } - } - Serial.printf("[AppContext] chat store=RamStore\n"); return std::unique_ptr(new chat::RamStore()); } diff --git a/platform/esp/arduino_common/src/ble/meshcore_ble.cpp b/platform/esp/arduino_common/src/ble/meshcore_ble.cpp index 4b05cdee..f692c6fb 100644 --- a/platform/esp/arduino_common/src/ble/meshcore_ble.cpp +++ b/platform/esp/arduino_common/src/ble/meshcore_ble.cpp @@ -8,7 +8,6 @@ #include "ui/widgets/ble_pairing_popup.h" #include #include -#include #include #include #include diff --git a/platform/esp/arduino_common/src/chat/infra/store/log_store.cpp b/platform/esp/arduino_common/src/chat/infra/store/log_store.cpp deleted file mode 100644 index 562ce6fd..00000000 --- a/platform/esp/arduino_common/src/chat/infra/store/log_store.cpp +++ /dev/null @@ -1,818 +0,0 @@ -/** - * @file log_store.cpp - * @brief Per-conversation ring log store (SD-based) - */ - -#include "platform/esp/arduino_common/chat/infra/store/log_store.h" -#include "chat/infra/mesh_protocol_utils.h" -#include -#include -#include - -namespace chat -{ -namespace -{ -const char* protocolTag(MeshProtocol protocol) -{ - return chat::infra::meshProtocolSlug(protocol); -} -} // namespace - -bool LogStore::begin(fs::FS& fs) -{ - fs_ = &fs; - if (!ensureDir()) - { - fs_ = nullptr; - return false; - } - std::vector entries; - if (!readIndex(entries)) - { - rebuildIndex(); - } - return true; -} - -void LogStore::append(const ChatMessage& msg) -{ - if (!fs_) return; - if (!ensureDir()) return; - - ConversationId conv(msg.channel, msg.peer, msg.protocol); - char path[64]; - buildConversationPath(conv, path, sizeof(path)); - - FileHeader header{}; - bool have_header = false; - if (fs_->exists(path)) - { - File rf = fs_->open(path, FILE_READ); - if (rf) - { - have_header = loadFileHeader(rf, header); - rf.close(); - } - } - - if (!have_header) - { - File wf = fs_->open(path, FILE_WRITE); - if (!wf) - { - return; - } - initFileHeader(wf); - header.magic = kFileMagic; - header.version = kVersion; - header.head = 0; - header.count = 0; - wf.close(); - } - - Record rec{}; - rec.protocol = static_cast(msg.protocol); - rec.channel = static_cast(msg.channel); - rec.status = static_cast(msg.status); - rec.text_len = static_cast(std::min(msg.text.size(), kMaxTextLen)); - rec.from = msg.from; - rec.peer = msg.peer; - rec.msg_id = msg.msg_id; - rec.timestamp = msg.timestamp; - if (rec.text_len > 0) - { - memcpy(rec.text, msg.text.data(), rec.text_len); - } - - File wf = fs_->open(path, FILE_WRITE); - if (!wf) - { - return; - } - - if (!writeRecord(wf, header.head, rec)) - { - wf.close(); - return; - } - - header.head = static_cast((header.head + 1) % kMaxMessagesPerConv); - if (header.count < kMaxMessagesPerConv) - { - header.count = static_cast(header.count + 1); - } - - wf.seek(0); - wf.write(reinterpret_cast(&header), sizeof(header)); - wf.flush(); - wf.close(); - - updateIndexForMessage(msg); -} - -std::vector LogStore::loadRecent(const ConversationId& conv, size_t n) -{ - std::vector out; - if (!fs_ || n == 0) return out; - - char path[64]; - buildConversationPath(conv, path, sizeof(path)); - if (!fs_->exists(path)) - { - return out; - } - - File rf = fs_->open(path, FILE_READ); - if (!rf) - { - return out; - } - - FileHeader header{}; - if (!loadFileHeader(rf, header)) - { - rf.close(); - return out; - } - - uint16_t count = header.count; - if (count == 0) - { - rf.close(); - return out; - } - - size_t to_read = std::min(n, count); - uint16_t start = static_cast( - (header.head + kMaxMessagesPerConv - to_read) % kMaxMessagesPerConv); - - out.reserve(to_read); - for (size_t i = 0; i < to_read; ++i) - { - uint16_t slot = static_cast((start + i) % kMaxMessagesPerConv); - Record rec{}; - if (!readRecord(rf, slot, rec)) - { - continue; - } - if (rec.text_len == 0) - { - continue; - } - ChatMessage msg; - msg.protocol = static_cast(rec.protocol); - msg.channel = static_cast(rec.channel); - msg.from = rec.from; - msg.peer = rec.peer; - msg.msg_id = rec.msg_id; - msg.timestamp = rec.timestamp; - msg.text.assign(rec.text, rec.text_len); - msg.status = static_cast(rec.status); - out.push_back(msg); - } - rf.close(); - - return out; -} - -std::vector LogStore::loadConversationPage(size_t offset, - size_t limit, - size_t* total) -{ - std::vector entries; - if (!ensureIndex(entries)) - { - if (total) - { - *total = 0; - } - return {}; - } - - std::sort(entries.begin(), entries.end(), - [](const IndexEntry& a, const IndexEntry& b) - { - return a.last_timestamp > b.last_timestamp; - }); - - if (total) - { - *total = entries.size(); - } - - size_t start = offset; - if (start >= entries.size()) - { - return {}; - } - size_t end = entries.size(); - if (limit != 0 && start + limit < end) - { - end = start + limit; - } - - std::vector list; - list.reserve(end - start); - for (size_t i = start; i < end; ++i) - { - const IndexEntry& entry = entries[i]; - ConversationMeta meta; - meta.id.protocol = static_cast(entry.protocol); - meta.id.channel = static_cast(entry.channel); - meta.id.peer = entry.peer; - meta.preview.assign(entry.preview, entry.preview_len); - meta.last_timestamp = entry.last_timestamp; - meta.unread = static_cast(entry.unread); - if (entry.peer == 0) - { - meta.name = "Broadcast"; - } - else - { - char buf[16]; - snprintf(buf, sizeof(buf), "%04lX", - static_cast(entry.peer & 0xFFFF)); - meta.name = buf; - } - list.push_back(meta); - } - - return list; -} - -void LogStore::setUnread(const ConversationId& conv, int unread) -{ - std::vector entries; - if (!ensureIndex(entries)) - { - return; - } - size_t idx = 0; - if (!findIndexEntry(conv, entries, &idx)) - { - return; - } - entries[idx].unread = static_cast(std::max(0, unread)); - writeIndex(entries); -} - -int LogStore::getUnread(const ConversationId& conv) const -{ - std::vector entries; - if (!const_cast(this)->readIndex(entries)) - { - return 0; - } - for (const auto& entry : entries) - { - if (entry.peer == conv.peer && - entry.channel == static_cast(conv.channel) && - entry.protocol == static_cast(conv.protocol)) - { - return entry.unread; - } - } - return 0; -} - -void LogStore::clearConversation(const ConversationId& conv) -{ - if (!fs_) return; - char path[64]; - buildConversationPath(conv, path, sizeof(path)); - if (fs_->exists(path)) - { - fs_->remove(path); - } - - std::vector entries; - if (!readIndex(entries)) - { - return; - } - - entries.erase(std::remove_if(entries.begin(), entries.end(), - [&](const IndexEntry& entry) - { - return entry.peer == conv.peer && - entry.channel == static_cast(conv.channel) && - entry.protocol == static_cast(conv.protocol); - }), - entries.end()); - writeIndex(entries); -} - -void LogStore::clearAll() -{ - if (!fs_) return; - if (fs_->exists(kIndexFile)) - { - fs_->remove(kIndexFile); - } - - File dir = fs_->open(kDir); - if (!dir) - { - return; - } - File entry = dir.openNextFile(); - while (entry) - { - if (!entry.isDirectory()) - { - const char* name = entry.name(); - if (name && strstr(name, ".log")) - { - char path[96]; - if (name[0] == '/') - { - snprintf(path, sizeof(path), "%s", name); - } - else - { - snprintf(path, sizeof(path), "%s/%s", kDir, name); - } - entry.close(); - fs_->remove(path); - entry = dir.openNextFile(); - continue; - } - } - entry.close(); - entry = dir.openNextFile(); - } - dir.close(); -} - -bool LogStore::updateMessageStatus(MessageId msg_id, MessageStatus status) -{ - if (!fs_ || msg_id == 0) - { - return false; - } - - std::vector entries; - if (!readIndex(entries)) - { - return false; - } - - bool updated = false; - for (auto& entry : entries) - { - ConversationId conv(static_cast(entry.channel), - entry.peer, - static_cast(entry.protocol)); - char path[64]; - buildConversationPath(conv, path, sizeof(path)); - if (!fs_->exists(path)) - { - continue; - } - File rf = fs_->open(path, FILE_READ); - if (!rf) - { - continue; - } - FileHeader header{}; - if (!loadFileHeader(rf, header)) - { - rf.close(); - continue; - } - bool updated_in_file = false; - for (uint16_t i = 0; i < header.count; ++i) - { - uint16_t slot = - static_cast((header.head + kMaxMessagesPerConv - header.count + i) % - kMaxMessagesPerConv); - Record rec{}; - if (!readRecord(rf, slot, rec)) - { - continue; - } - if (rec.msg_id != msg_id) - { - continue; - } - if (rec.from != 0) - { - continue; - } - rec.status = static_cast(status); - rf.close(); - File wf = fs_->open(path, FILE_WRITE); - if (!wf) - { - return updated; - } - writeRecord(wf, slot, rec); - wf.flush(); - wf.close(); - updated_in_file = true; - break; - } - if (updated_in_file) - { - updated = true; - if (entry.last_msg_id == msg_id) - { - entry.status = static_cast(status); - } - break; - } - rf.close(); - } - - if (updated) - { - writeIndex(entries); - } - return updated; -} - -bool LogStore::getMessage(MessageId msg_id, ChatMessage* out) const -{ - if (!fs_ || msg_id == 0) - { - return false; - } - - std::vector entries; - if (!const_cast(this)->readIndex(entries)) - { - return false; - } - - for (const auto& entry : entries) - { - ConversationId conv(static_cast(entry.channel), - entry.peer, - static_cast(entry.protocol)); - char path[64]; - buildConversationPath(conv, path, sizeof(path)); - if (!fs_->exists(path)) - { - continue; - } - - File rf = fs_->open(path, FILE_READ); - if (!rf) - { - continue; - } - - FileHeader header{}; - if (!const_cast(this)->loadFileHeader(rf, header)) - { - rf.close(); - continue; - } - - for (uint16_t i = 0; i < header.count; ++i) - { - uint16_t slot = - static_cast((header.head + kMaxMessagesPerConv - header.count + i) % - kMaxMessagesPerConv); - Record rec{}; - if (!const_cast(this)->readRecord(rf, slot, rec)) - { - continue; - } - if (rec.text_len == 0 || rec.msg_id != msg_id) - { - continue; - } - - if (out) - { - ChatMessage msg; - msg.protocol = static_cast(rec.protocol); - msg.channel = static_cast(rec.channel); - msg.from = rec.from; - msg.peer = rec.peer; - msg.msg_id = rec.msg_id; - msg.timestamp = rec.timestamp; - msg.text.assign(rec.text, rec.text_len); - msg.status = static_cast(rec.status); - *out = msg; - } - rf.close(); - return true; - } - rf.close(); - } - return false; -} - -bool LogStore::ensureDir() -{ - if (!fs_) return false; - if (fs_->exists(kDir)) - { - return true; - } - return fs_->mkdir(kDir); -} - -bool LogStore::ensureIndex(std::vector& entries) -{ - if (readIndex(entries)) - { - return true; - } - rebuildIndex(); - return readIndex(entries); -} - -bool LogStore::writeIndex(const std::vector& entries) -{ - if (!fs_) return false; - if (fs_->exists(kIndexFile)) - { - fs_->remove(kIndexFile); - } - File wf = fs_->open(kIndexFile, FILE_WRITE); - if (!wf) - { - return false; - } - IndexHeader header{}; - header.magic = kIndexMagic; - header.version = kVersion; - header.count = static_cast(entries.size()); - wf.write(reinterpret_cast(&header), sizeof(header)); - for (const auto& entry : entries) - { - wf.write(reinterpret_cast(&entry), sizeof(entry)); - } - wf.flush(); - wf.close(); - return true; -} - -bool LogStore::readIndex(std::vector& entries) -{ - entries.clear(); - if (!fs_ || !fs_->exists(kIndexFile)) - { - return false; - } - File rf = fs_->open(kIndexFile, FILE_READ); - if (!rf) - { - return false; - } - IndexHeader header{}; - size_t read = rf.read(reinterpret_cast(&header), sizeof(header)); - if (read != sizeof(header) || header.magic != kIndexMagic || header.version != kVersion) - { - rf.close(); - return false; - } - entries.resize(header.count); - size_t expected = header.count * sizeof(IndexEntry); - size_t got = rf.read(reinterpret_cast(entries.data()), expected); - rf.close(); - if (got != expected) - { - entries.clear(); - return false; - } - return true; -} - -bool LogStore::findIndexEntry(const ConversationId& conv, - std::vector& entries, - size_t* out_idx) -{ - for (size_t i = 0; i < entries.size(); ++i) - { - if (entries[i].peer == conv.peer && - entries[i].channel == static_cast(conv.channel) && - entries[i].protocol == static_cast(conv.protocol)) - { - if (out_idx) - { - *out_idx = i; - } - return true; - } - } - return false; -} - -void LogStore::updateIndexForMessage(const ChatMessage& msg) -{ - std::vector entries; - if (!ensureIndex(entries)) - { - return; - } - - ConversationId conv(msg.channel, msg.peer, msg.protocol); - size_t idx = 0; - if (!findIndexEntry(conv, entries, &idx)) - { - IndexEntry entry{}; - entry.protocol = static_cast(msg.protocol); - entry.channel = static_cast(msg.channel); - entry.peer = msg.peer; - entries.push_back(entry); - idx = entries.size() - 1; - } - - IndexEntry& entry = entries[idx]; - entry.protocol = static_cast(msg.protocol); - entry.channel = static_cast(msg.channel); - entry.status = static_cast(msg.status); - entry.peer = msg.peer; - entry.last_msg_id = msg.msg_id; - entry.last_timestamp = msg.timestamp; - entry.last_from = msg.from; - entry.preview_len = static_cast(std::min(msg.text.size(), kPreviewLen)); - memset(entry.preview, 0, sizeof(entry.preview)); - if (entry.preview_len > 0) - { - memcpy(entry.preview, msg.text.data(), entry.preview_len); - } - if (msg.status == MessageStatus::Incoming) - { - entry.unread = static_cast(entry.unread + 1); - } - writeIndex(entries); -} - -void LogStore::rebuildIndex() -{ - if (!fs_) return; - std::vector entries; - - File dir = fs_->open(kDir); - if (!dir) - { - return; - } - - File entry = dir.openNextFile(); - while (entry) - { - if (!entry.isDirectory()) - { - const char* name = entry.name(); - if (name && strstr(name, ".log")) - { - FileHeader header{}; - if (!loadFileHeader(entry, header)) - { - entry = dir.openNextFile(); - continue; - } - - ChatMessage last_msg; - bool have_last = false; - for (uint16_t i = 0; i < header.count; ++i) - { - uint16_t slot = - static_cast((header.head + kMaxMessagesPerConv - header.count + i) % - kMaxMessagesPerConv); - Record rec{}; - if (!readRecord(entry, slot, rec)) - { - continue; - } - if (rec.text_len == 0) - { - continue; - } - ChatMessage msg; - msg.protocol = static_cast(rec.protocol); - msg.channel = static_cast(rec.channel); - msg.from = rec.from; - msg.peer = rec.peer; - msg.msg_id = rec.msg_id; - msg.timestamp = rec.timestamp; - msg.text.assign(rec.text, rec.text_len); - msg.status = static_cast(rec.status); - if (!have_last || msg.timestamp >= last_msg.timestamp) - { - last_msg = msg; - have_last = true; - } - } - - if (have_last) - { - IndexEntry idx{}; - idx.protocol = static_cast(last_msg.protocol); - idx.channel = static_cast(last_msg.channel); - idx.status = static_cast(last_msg.status); - idx.unread = 0; - idx.peer = last_msg.peer; - idx.last_msg_id = last_msg.msg_id; - idx.last_timestamp = last_msg.timestamp; - idx.last_from = last_msg.from; - idx.preview_len = - static_cast(std::min(last_msg.text.size(), kPreviewLen)); - if (idx.preview_len > 0) - { - memcpy(idx.preview, last_msg.text.data(), idx.preview_len); - } - entries.push_back(idx); - } - } - } - entry.close(); - entry = dir.openNextFile(); - } - dir.close(); - writeIndex(entries); -} - -bool LogStore::loadFileHeader(File& file, FileHeader& header) -{ - if (!file) - { - return false; - } - size_t size = file.size(); - if (size < sizeof(FileHeader)) - { - return false; - } - file.seek(0); - size_t read = file.read(reinterpret_cast(&header), sizeof(header)); - if (read != sizeof(header)) - { - return false; - } - return header.magic == kFileMagic && header.version == kVersion; -} - -void LogStore::initFileHeader(File& file) -{ - FileHeader header{}; - header.magic = kFileMagic; - header.version = kVersion; - header.head = 0; - header.count = 0; - file.seek(0); - file.write(reinterpret_cast(&header), sizeof(header)); - file.flush(); -} - -bool LogStore::readRecord(File& file, uint16_t slot, Record& rec) -{ - size_t offset = sizeof(FileHeader) + static_cast(slot) * sizeof(Record); - if (file.size() < offset + sizeof(Record)) - { - return false; - } - file.seek(offset); - size_t read = file.read(reinterpret_cast(&rec), sizeof(rec)); - return read == sizeof(rec); -} - -bool LogStore::writeRecord(File& file, uint16_t slot, const Record& rec) -{ - size_t offset = sizeof(FileHeader) + static_cast(slot) * sizeof(Record); - file.seek(offset); - size_t written = file.write(reinterpret_cast(&rec), sizeof(rec)); - return written == sizeof(rec); -} - -void LogStore::buildConversationPath(const ConversationId& conv, - char* out, - size_t out_len) const -{ - if (!out || out_len == 0) - { - return; - } - if (conv.peer == 0) - { - const char* name = channelName(conv.channel); - snprintf(out, out_len, "%s/%s_broadcast_%s.log", kDir, protocolTag(conv.protocol), name); - } - else - { - snprintf(out, out_len, "%s/%s_n_%08lX.log", kDir, protocolTag(conv.protocol), - static_cast(conv.peer)); - } -} - -const char* LogStore::channelName(ChannelId channel) const -{ - switch (channel) - { - case ChannelId::PRIMARY: - return "LongFast"; - case ChannelId::SECONDARY: - return "Squad"; - default: - return "Unknown"; - } -} - -} // namespace chat diff --git a/platform/esp/arduino_common/src/sstv/sstv_service.cpp b/platform/esp/arduino_common/src/sstv/sstv_service.cpp index 5323055c..74305ccc 100644 --- a/platform/esp/arduino_common/src/sstv/sstv_service.cpp +++ b/platform/esp/arduino_common/src/sstv/sstv_service.cpp @@ -147,7 +147,7 @@ struct StorageFacade } }; -StorageFacade SD; +StorageFacade s_tab5_storage; constexpr int FILE_WRITE = 1; constexpr int CARD_NONE = 0; constexpr int POWER_SPEAK = 0; @@ -338,9 +338,9 @@ const char* get_saved_path() bool ensure_sstv_dir() { #if defined(TRAIL_MATE_ESP_BOARD_TAB5) - if (!SD.exists("/sstv")) + if (!s_tab5_storage.exists("/sstv")) { - if (!SD.mkdir("/sstv")) + if (!s_tab5_storage.mkdir("/sstv")) { return false; } @@ -386,7 +386,7 @@ bool build_save_path(char* out_path, size_t out_len) static_cast(millis()), i); } #if defined(TRAIL_MATE_ESP_BOARD_TAB5) - if (!SD.exists(out_path)) + if (!s_tab5_storage.exists(out_path)) #else if (!::platform::esp::arduino_common::storage::sd_exists(out_path)) #endif @@ -482,7 +482,7 @@ bool save_frame_to_sd() return false; } #if defined(TRAIL_MATE_ESP_BOARD_TAB5) - if (SD.cardType() == CARD_NONE) + if (s_tab5_storage.cardType() == CARD_NONE) #else if (!::platform::esp::arduino_common::storage::sd_card_ready()) #endif @@ -511,7 +511,7 @@ bool save_frame_to_sd() const uint32_t data_offset = 14 + 40; #if defined(TRAIL_MATE_ESP_BOARD_TAB5) - File f = SD.open(path, FILE_WRITE); + File f = s_tab5_storage.open(path, FILE_WRITE); if (!f) #else ::platform::esp::arduino_common::storage::SdRuntimeFile f; 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 499a5c9f..fba1d069 100644 --- a/platform/esp/arduino_common/src/storage/sd_card_runtime.cpp +++ b/platform/esp/arduino_common/src/storage/sd_card_runtime.cpp @@ -1,8 +1,7 @@ #include "platform/esp/arduino_common/storage/sd_card_runtime.h" #include -#include -#include +#include #ifndef DISABLE_FS_H_WARNING #define DISABLE_FS_H_WARNING 1 #endif @@ -20,9 +19,14 @@ namespace { constexpr uint8_t kRuntimeCardNone = 0; +constexpr uint8_t kRuntimeCardSd = 2; +constexpr uint8_t kRuntimeCardSdhc = 3; +constexpr uint8_t kRuntimeCardUnknown = 4; constexpr uint32_t kSdSectorSize = 512; constexpr uint32_t kDefaultSharedSpiSdHz = 4000000U; constexpr uint32_t kMaxSharedSpiSdHz = 10000000U; +constexpr uint32_t kSdInitHz = 400000U; +constexpr uint8_t kSdR1IdleState = 0x01U; #ifndef TRAIL_MATE_SD_IO_LOG_ENABLE #define TRAIL_MATE_SD_IO_LOG_ENABLE 1 @@ -50,8 +54,6 @@ const char* backend_name_from_info() { switch (s_info.backend) { - case SdCardBackend::ArduinoSd: - return "arduino"; case SdCardBackend::SdFat: return "sdfat"; case SdCardBackend::None: @@ -151,17 +153,17 @@ uint8_t card_type_from_sdfat(SdFs& fs) const uint8_t type = card->type(); if (type == SD_CARD_TYPE_SD1) { - return CARD_SD; + return kRuntimeCardSd; } if (type == SD_CARD_TYPE_SD2) { - return CARD_SD; + return kRuntimeCardSd; } if (type == SD_CARD_TYPE_SDHC) { - return CARD_SDHC; + return kRuntimeCardSdhc; } - return CARD_UNKNOWN; + return kRuntimeCardUnknown; } bool path_empty(const char* path) @@ -234,17 +236,87 @@ void reset_info() s_info = SdCardInfo{}; } -void record_arduino_info() +void sd_clock_bytes(SPIClass& spi, uint8_t count) { - s_info = SdCardInfo{}; - s_info.backend = SdCardBackend::ArduinoSd; - s_info.card_type = SD.cardType(); - s_info.fat_type = 0; - s_info.sector_size = SD.sectorSize(); - s_info.sector_count = SD.numSectors(); - s_info.card_size_bytes = SD.cardSize(); - s_info.total_bytes = SD.totalBytes(); - s_info.used_bytes = SD.usedBytes(); + for (uint8_t i = 0; i < count; ++i) + { + spi.transfer(0xFF); + } +} + +bool sd_wait_not_busy(SPIClass& spi, uint32_t timeout_ms) +{ + const uint32_t start_ms = millis(); + do + { + if (spi.transfer(0xFF) != 0x00) + { + return true; + } + delay(1); + } while (static_cast(millis() - start_ms) < timeout_ms); + return false; +} + +uint8_t sd_send_cmd0(SPIClass& spi) +{ + static constexpr uint8_t kCmd0Packet[] = {0x40, 0, 0, 0, 0, 0x95}; + for (uint8_t byte : kCmd0Packet) + { + spi.transfer(byte); + } + + for (uint8_t i = 0; i < 16; ++i) + { + const uint8_t token = spi.transfer(0xFF); + if ((token & 0x80U) == 0) + { + return token; + } + } + return 0xFF; +} + +bool sd_preflight_go_idle(int sd_cs, SPIClass& spi) +{ + uint8_t last_token = 0xFF; + bool ok = false; + uint8_t attempt = 0; + uint8_t attempts_used = 0; + + spi.beginTransaction(SPISettings(kSdInitHz, MSBFIRST, SPI_MODE0)); + digitalWrite(sd_cs, HIGH); + sd_clock_bytes(spi, 20); + + for (attempt = 1; attempt <= 4; ++attempt) + { + attempts_used = attempt; + digitalWrite(sd_cs, LOW); + const bool ready = sd_wait_not_busy(spi, 500); + last_token = sd_send_cmd0(spi); + digitalWrite(sd_cs, HIGH); + sd_clock_bytes(spi, 2); + + if (last_token == kSdR1IdleState) + { + ok = true; + break; + } + + Serial.printf("[SD] SdFat preflight CMD0 retry=%u ready=%d token=0x%02X\n", + static_cast(attempt), + ready ? 1 : 0, + static_cast(last_token)); + delay(25); + } + + spi.endTransaction(); + Serial.printf("[SD] SdFat preflight CMD0 -> %d token=0x%02X attempts=%u\n", + ok ? 1 : 0, + static_cast(last_token), + static_cast(attempts_used)); + delay(2); + return ok; } void record_sdfat_info() @@ -282,8 +354,9 @@ bool mount_sd_card(int sd_cs, const char* mount_point, uint8_t max_files) { + (void)mount_point; + (void)max_files; clear_sdfat(); - SD.end(); reset_info(); const uint32_t effective_hz = sanitize_sd_spi_hz(spi_hz); @@ -294,25 +367,14 @@ bool mount_sd_card(int sd_cs, static_cast(effective_hz)); } - const bool arduino_ok = SD.begin(sd_cs, spi, effective_hz, mount_point, max_files, false); - Serial.printf("[SD] Arduino SD.begin hz=%lu -> %d\n", - static_cast(effective_hz), - arduino_ok ? 1 : 0); - if (arduino_ok && SD.cardType() != CARD_NONE && SD.sectorSize() != 0) + const bool preflight_ok = sd_preflight_go_idle(sd_cs, spi); + if (!preflight_ok) { - record_arduino_info(); - Serial.printf("[SD] backend=arduino fs=fat card=%llu MB total=%llu MB sectors=%lu sector_size=%lu\n", - static_cast(s_info.card_size_bytes / (1024ULL * 1024ULL)), - static_cast(s_info.total_bytes / (1024ULL * 1024ULL)), - static_cast(s_info.sector_count), - static_cast(s_info.sector_size)); - return true; + Serial.println("[SD] SdFat preflight failed; continuing to SdFat.begin for detailed error"); } - SD.end(); - delay(10); - - const bool sdfat_ok = s_sdfat.begin(SdSpiConfig(sd_cs, SHARED_SPI, effective_hz, &spi)); + const bool sdfat_ok = s_sdfat.begin( + SdSpiConfig(sd_cs, SHARED_SPI | USER_SPI_BEGIN, effective_hz, &spi)); Serial.printf("[SD] SdFat.begin hz=%lu -> %d\n", static_cast(effective_hz), sdfat_ok ? 1 : 0); @@ -337,10 +399,6 @@ bool mount_sd_card(int sd_cs, void unmount_sd_card() { - if (s_info.backend == SdCardBackend::ArduinoSd) - { - SD.end(); - } clear_sdfat(); reset_info(); } @@ -351,11 +409,6 @@ bool sd_card_ready() s_info.card_type != kRuntimeCardNone; } -bool sd_card_uses_arduino_sd() -{ - return s_info.backend == SdCardBackend::ArduinoSd; -} - bool sd_card_uses_sdfat() { return s_info.backend == SdCardBackend::SdFat; @@ -380,8 +433,6 @@ const char* sd_card_backend_name() { switch (s_info.backend) { - case SdCardBackend::ArduinoSd: - return "arduino"; case SdCardBackend::SdFat: return "sdfat"; case SdCardBackend::None: @@ -392,10 +443,6 @@ const char* sd_card_backend_name() const char* sd_card_filesystem_name() { - if (s_info.backend == SdCardBackend::ArduinoSd) - { - return "fat"; - } if (s_info.backend == SdCardBackend::SdFat) { switch (s_info.fat_type) @@ -420,12 +467,6 @@ bool sd_exists(const char* path) const char* normalized = normalize_sd_path(path); const uint32_t start_ms = sd_io_begin("exists", normalized); bool result = false; - if (s_info.backend == SdCardBackend::ArduinoSd) - { - result = SD.exists(normalized); - sd_io_end("exists", normalized, start_ms, true, 0, result ? 1 : 0); - return result; - } if (s_info.backend == SdCardBackend::SdFat) { result = s_sdfat.exists(normalized); @@ -441,14 +482,6 @@ bool sd_is_directory(const char* path) const char* normalized = normalize_sd_path(path); const uint32_t start_ms = sd_io_begin("is_dir", normalized); bool result = false; - if (s_info.backend == SdCardBackend::ArduinoSd) - { - File dir = SD.open(normalized, FILE_READ); - result = dir && dir.isDirectory(); - dir.close(); - sd_io_end("is_dir", normalized, start_ms, true, 0, result ? 1 : 0); - return result; - } if (s_info.backend == SdCardBackend::SdFat) { FsFile dir = s_sdfat.open(normalized, O_RDONLY); @@ -466,12 +499,6 @@ bool sd_mkdir(const char* path) const char* normalized = normalize_sd_path(path); const uint32_t start_ms = sd_io_begin("mkdir", normalized); bool result = false; - if (s_info.backend == SdCardBackend::ArduinoSd) - { - result = SD.mkdir(normalized); - sd_io_end("mkdir", normalized, start_ms, result); - return result; - } if (s_info.backend == SdCardBackend::SdFat) { result = s_sdfat.mkdir(normalized, true); @@ -482,17 +509,26 @@ bool sd_mkdir(const char* path) return false; } +bool sd_rmdir(const char* path) +{ + const char* normalized = normalize_sd_path(path); + const uint32_t start_ms = sd_io_begin("rmdir", normalized); + bool result = false; + if (s_info.backend == SdCardBackend::SdFat) + { + result = s_sdfat.rmdir(normalized); + sd_io_end("rmdir", normalized, start_ms, result); + return result; + } + sd_io_end("rmdir", normalized, start_ms, false, 0, -1); + return false; +} + bool sd_remove(const char* path) { const char* normalized = normalize_sd_path(path); const uint32_t start_ms = sd_io_begin("remove", normalized); bool result = false; - if (s_info.backend == SdCardBackend::ArduinoSd) - { - result = SD.remove(normalized); - sd_io_end("remove", normalized, start_ms, result); - return result; - } if (s_info.backend == SdCardBackend::SdFat) { result = s_sdfat.remove(normalized); @@ -509,12 +545,6 @@ bool sd_rename(const char* old_path, const char* new_path) const char* normalized_new = normalize_sd_path(new_path); const uint32_t start_ms = sd_io_begin("rename", normalized_old); bool result = false; - if (s_info.backend == SdCardBackend::ArduinoSd) - { - result = SD.rename(normalized_old, normalized_new); - sd_io_end("rename", normalized_old, start_ms, result, 0, result ? 0 : -1); - return result; - } if (s_info.backend == SdCardBackend::SdFat) { result = s_sdfat.rename(normalized_old, normalized_new); @@ -528,7 +558,6 @@ bool sd_rename(const char* old_path, const char* new_path) class SdRuntimeFile::Impl { public: - File arduino_file; FsFile sdfat_file; SdCardBackend backend = SdCardBackend::None; char path[128]{}; @@ -558,14 +587,6 @@ bool SdRuntimeFile::open(const char* path, const char* mode) copy_path(impl_->path, sizeof(impl_->path), normalized); copy_path(impl_->mode, sizeof(impl_->mode), mode ? mode : "r"); const uint32_t start_ms = sd_io_begin("file_open", impl_->path); - if (s_info.backend == SdCardBackend::ArduinoSd) - { - impl_->arduino_file = SD.open(normalized, mode ? mode : FILE_READ); - impl_->backend = impl_->arduino_file ? SdCardBackend::ArduinoSd : SdCardBackend::None; - sd_io_end("file_open", impl_->path, start_ms, impl_->backend == SdCardBackend::ArduinoSd); - return impl_->backend == SdCardBackend::ArduinoSd; - } - if (s_info.backend == SdCardBackend::SdFat) { impl_->sdfat_file = s_sdfat.open(normalized, sdfat_open_flags(mode)); @@ -584,13 +605,7 @@ void SdRuntimeFile::close() { return; } - if (impl_->backend == SdCardBackend::ArduinoSd) - { - const uint32_t start_ms = sd_io_begin("file_close", impl_->path); - impl_->arduino_file.close(); - sd_io_end("file_close", impl_->path, start_ms, true); - } - else if (impl_->backend == SdCardBackend::SdFat) + if (impl_->backend == SdCardBackend::SdFat) { const uint32_t start_ms = sd_io_begin("file_close", impl_->path); impl_->sdfat_file.close(); @@ -612,10 +627,6 @@ int SdRuntimeFile::available() const { return 0; } - if (impl_->backend == SdCardBackend::ArduinoSd) - { - return impl_->arduino_file.available(); - } if (impl_->backend == SdCardBackend::SdFat) { return impl_->sdfat_file.available(); @@ -629,13 +640,6 @@ int SdRuntimeFile::read(void* buffer, std::size_t bytes_to_read) { return 0; } - if (impl_->backend == SdCardBackend::ArduinoSd) - { - const uint32_t start_ms = sd_io_begin("file_read", impl_->path, bytes_to_read); - const int result = impl_->arduino_file.read(static_cast(buffer), bytes_to_read); - sd_io_end("file_read", impl_->path, start_ms, result >= 0, bytes_to_read, result); - return result; - } if (impl_->backend == SdCardBackend::SdFat) { const uint32_t start_ms = sd_io_begin("file_read", impl_->path, bytes_to_read); @@ -652,10 +656,6 @@ int SdRuntimeFile::read_byte() { return -1; } - if (impl_->backend == SdCardBackend::ArduinoSd) - { - return impl_->arduino_file.read(); - } if (impl_->backend == SdCardBackend::SdFat) { return impl_->sdfat_file.read(); @@ -669,13 +669,6 @@ std::size_t SdRuntimeFile::read_bytes(char* buffer, std::size_t bytes_to_read) { return 0; } - if (impl_->backend == SdCardBackend::ArduinoSd) - { - const uint32_t start_ms = sd_io_begin("file_read_bytes", impl_->path, bytes_to_read); - const std::size_t result = impl_->arduino_file.readBytes(buffer, bytes_to_read); - sd_io_end("file_read_bytes", impl_->path, start_ms, true, bytes_to_read, result); - return result; - } if (impl_->backend == SdCardBackend::SdFat) { const uint32_t start_ms = sd_io_begin("file_read_bytes", impl_->path, bytes_to_read); @@ -692,14 +685,6 @@ std::size_t SdRuntimeFile::write(const void* buffer, std::size_t bytes_to_write) { return 0; } - if (impl_->backend == SdCardBackend::ArduinoSd) - { - const uint32_t start_ms = sd_io_begin("file_write", impl_->path, bytes_to_write); - const std::size_t result = - impl_->arduino_file.write(static_cast(buffer), bytes_to_write); - sd_io_end("file_write", impl_->path, start_ms, result == bytes_to_write, bytes_to_write, result); - return result; - } if (impl_->backend == SdCardBackend::SdFat) { const uint32_t start_ms = sd_io_begin("file_write", impl_->path, bytes_to_write); @@ -716,10 +701,6 @@ std::size_t SdRuntimeFile::write_byte(uint8_t value) { return 0; } - if (impl_->backend == SdCardBackend::ArduinoSd) - { - return impl_->arduino_file.write(value); - } if (impl_->backend == SdCardBackend::SdFat) { return impl_->sdfat_file.write(value); @@ -733,10 +714,6 @@ std::size_t SdRuntimeFile::print(const char* text) { return 0; } - if (impl_->backend == SdCardBackend::ArduinoSd) - { - return impl_->arduino_file.print(text); - } if (impl_->backend == SdCardBackend::SdFat) { return impl_->sdfat_file.print(text); @@ -751,10 +728,6 @@ std::size_t SdRuntimeFile::print(double value, int digits) 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); @@ -793,10 +766,6 @@ bool SdRuntimeFile::seek(uint64_t offset) { return false; } - if (impl_->backend == SdCardBackend::ArduinoSd) - { - return impl_->arduino_file.seek(offset); - } if (impl_->backend == SdCardBackend::SdFat) { return impl_->sdfat_file.seekSet(offset); @@ -810,10 +779,6 @@ uint64_t SdRuntimeFile::position() const { return 0; } - if (impl_->backend == SdCardBackend::ArduinoSd) - { - return impl_->arduino_file.position(); - } if (impl_->backend == SdCardBackend::SdFat) { return impl_->sdfat_file.curPosition(); @@ -827,10 +792,6 @@ uint64_t SdRuntimeFile::size() const { return 0; } - if (impl_->backend == SdCardBackend::ArduinoSd) - { - return impl_->arduino_file.size(); - } if (impl_->backend == SdCardBackend::SdFat) { return impl_->sdfat_file.fileSize(); @@ -844,13 +805,6 @@ bool SdRuntimeFile::flush() { return false; } - if (impl_->backend == SdCardBackend::ArduinoSd) - { - const uint32_t start_ms = sd_io_begin("file_flush", impl_->path); - impl_->arduino_file.flush(); - sd_io_end("file_flush", impl_->path, start_ms, true); - return true; - } if (impl_->backend == SdCardBackend::SdFat) { const uint32_t start_ms = sd_io_begin("file_flush", impl_->path); @@ -864,7 +818,6 @@ bool SdRuntimeFile::flush() class SdRuntimeDir::Impl { public: - File arduino_dir; FsFile sdfat_dir; SdCardBackend backend = SdCardBackend::None; char path[128]{}; @@ -891,15 +844,6 @@ bool SdRuntimeDir::open(const char* path) const char* normalized = normalize_sd_path(path); copy_path(impl_->path, sizeof(impl_->path), normalized); const uint32_t start_ms = sd_io_begin("dir_open", impl_->path); - if (s_info.backend == SdCardBackend::ArduinoSd) - { - impl_->arduino_dir = SD.open(normalized, FILE_READ); - impl_->backend = (impl_->arduino_dir && impl_->arduino_dir.isDirectory()) - ? SdCardBackend::ArduinoSd - : SdCardBackend::None; - sd_io_end("dir_open", impl_->path, start_ms, impl_->backend == SdCardBackend::ArduinoSd); - return impl_->backend == SdCardBackend::ArduinoSd; - } if (s_info.backend == SdCardBackend::SdFat) { impl_->sdfat_dir = s_sdfat.open(normalized, O_RDONLY); @@ -919,13 +863,7 @@ void SdRuntimeDir::close() { return; } - if (impl_->backend == SdCardBackend::ArduinoSd) - { - const uint32_t start_ms = sd_io_begin("dir_close", impl_->path); - impl_->arduino_dir.close(); - sd_io_end("dir_close", impl_->path, start_ms, true); - } - else if (impl_->backend == SdCardBackend::SdFat) + if (impl_->backend == SdCardBackend::SdFat) { const uint32_t start_ms = sd_io_begin("dir_close", impl_->path); impl_->sdfat_dir.close(); @@ -952,26 +890,6 @@ bool SdRuntimeDir::read_next(char* name, std::size_t name_size, bool* is_dir) *is_dir = false; } - if (impl_->backend == SdCardBackend::ArduinoSd) - { - const uint32_t start_ms = sd_io_begin("dir_read", impl_->path); - File entry = impl_->arduino_dir.openNextFile(); - if (!entry) - { - sd_io_end("dir_read", impl_->path, start_ms, true, 0, 0); - return false; - } - const char* raw_name = entry.name(); - std::snprintf(name, name_size, "%s", raw_name ? raw_name : ""); - if (is_dir != nullptr) - { - *is_dir = entry.isDirectory(); - } - entry.close(); - sd_io_end("dir_read", impl_->path, start_ms, true, 0, name[0] != '\0' ? 1 : 0); - return name[0] != '\0'; - } - if (impl_->backend == SdCardBackend::SdFat) { const uint32_t start_ms = sd_io_begin("dir_read", impl_->path); @@ -1000,12 +918,6 @@ bool sd_read_raw(uint32_t lba, uint8_t* buffer) std::snprintf(path, sizeof(path), "raw:%lu", static_cast(lba)); const uint32_t start_ms = sd_io_begin("raw_read", path, kSdSectorSize); bool result = false; - if (s_info.backend == SdCardBackend::ArduinoSd) - { - result = SD.readRAW(buffer, lba); - sd_io_end("raw_read", path, start_ms, result, kSdSectorSize); - return result; - } if (s_info.backend == SdCardBackend::SdFat && s_sdfat.card() != nullptr) { result = s_sdfat.card()->readSector(lba, buffer); @@ -1022,12 +934,6 @@ bool sd_write_raw(uint32_t lba, const uint8_t* buffer) std::snprintf(path, sizeof(path), "raw:%lu", static_cast(lba)); const uint32_t start_ms = sd_io_begin("raw_write", path, kSdSectorSize); bool result = false; - if (s_info.backend == SdCardBackend::ArduinoSd) - { - result = SD.writeRAW(const_cast(buffer), lba); - sd_io_end("raw_write", path, start_ms, result, kSdSectorSize); - return result; - } if (s_info.backend == SdCardBackend::SdFat && s_sdfat.card() != nullptr) { result = s_sdfat.card()->writeSector(lba, buffer); diff --git a/platform/esp/arduino_common/src/ui/runtime/pack_repository.cpp b/platform/esp/arduino_common/src/ui/runtime/pack_repository.cpp index 82b93e1f..fd495820 100644 --- a/platform/esp/arduino_common/src/ui/runtime/pack_repository.cpp +++ b/platform/esp/arduino_common/src/ui/runtime/pack_repository.cpp @@ -51,7 +51,7 @@ extern "C" esp_err_t esp_crt_bundle_attach(void* conf); #if defined(ARDUINO) #include #include -#include +#include "platform/esp/arduino_common/storage/sd_card_runtime.h" #else #include "platform/esp/idf_common/bsp_runtime.h" #endif @@ -352,6 +352,7 @@ int compare_versions(const std::string& lhs, const std::string& rhs) struct ArduinoStorageBinding { ::fs::FS* volume = nullptr; + bool sd_runtime = false; std::string volume_path; const char* storage = nullptr; }; @@ -439,7 +440,7 @@ bool resolve_storage_binding(const std::string& logical_path, return false; } - out.volume = &SD; + out.sd_runtime = true; out.storage = kStorageSd; out.volume_path = is_explicit_sd_logical_path(logical_path) ? strip_mount_prefix(logical_path, "/sd") @@ -522,10 +523,10 @@ std::string host_path_from_normalized_lvgl_path(const std::string& normalized_pa return std::string("/fs") + suffix; } -#if LV_USE_FS_POSIX - if (letter == LV_FS_POSIX_LETTER) +#if defined(TRAIL_MATE_LVGL_SD_FS_LETTER) + if (letter == TRAIL_MATE_LVGL_SD_FS_LETTER) { - std::string root = LV_FS_POSIX_PATH; + std::string root = TRAIL_MATE_LVGL_SD_FS_PATH; if (root.empty() || root == "/") { return suffix; @@ -583,17 +584,30 @@ bool ensure_dir_recursive(const std::string& logical_dir) current.push_back('/'); current += segment; - File existing = binding.volume->open(current.c_str(), FILE_READ); - const bool exists = static_cast(existing); - const bool is_dir = exists && existing.isDirectory(); - if (existing) + bool exists = false; + bool is_dir = false; + if (binding.sd_runtime) { - existing.close(); + exists = ::platform::esp::arduino_common::storage::sd_exists(current.c_str()); + is_dir = exists && ::platform::esp::arduino_common::storage::sd_is_directory(current.c_str()); + } + else + { + File existing = binding.volume->open(current.c_str(), FILE_READ); + exists = static_cast(existing); + is_dir = exists && existing.isDirectory(); + if (existing) + { + existing.close(); + } } if (!exists) { - if (!binding.volume->mkdir(current.c_str())) + const bool mkdir_ok = binding.sd_runtime + ? ::platform::esp::arduino_common::storage::sd_mkdir(current.c_str()) + : binding.volume->mkdir(current.c_str()); + if (!mkdir_ok) { std::printf("[Packs][Storage] mkdir failed logical=%s storage=%s path=%s\n", logical_dir.c_str(), @@ -629,6 +643,12 @@ bool logical_file_exists(const std::string& logical_path) return false; } + if (binding.sd_runtime) + { + return ::platform::esp::arduino_common::storage::sd_exists(binding.volume_path.c_str()) && + !::platform::esp::arduino_common::storage::sd_is_directory(binding.volume_path.c_str()); + } + File file = binding.volume->open(binding.volume_path.c_str(), FILE_READ); const bool exists = static_cast(file) && !file.isDirectory(); if (file) @@ -646,6 +666,11 @@ bool logical_dir_exists(const std::string& logical_path) return false; } + if (binding.sd_runtime) + { + return ::platform::esp::arduino_common::storage::sd_is_directory(binding.volume_path.c_str()); + } + File dir = binding.volume->open(binding.volume_path.c_str(), FILE_READ); const bool exists = static_cast(dir) && dir.isDirectory(); if (dir) @@ -718,6 +743,45 @@ bool write_binary_file(const std::string& logical_path, const void* data, std::s return false; } + if (binding.sd_runtime) + { + ::platform::esp::arduino_common::storage::SdRuntimeFile file; + if (!file.open(binding.volume_path.c_str(), "w")) + { + std::printf("[Packs][Storage] open for write failed logical=%s storage=%s path=%s len=%lu\n", + logical_path.c_str(), + binding.storage ? binding.storage : "", + binding.volume_path.c_str(), + static_cast(len)); + return false; + } + + const std::uint8_t* bytes = static_cast(data); + std::size_t written = 0; + while (written < len) + { + const std::size_t chunk = std::min(kFileWriteChunkBytes, len - written); + const std::size_t chunk_written = file.write(bytes + written, chunk); + if (chunk_written != chunk) + { + std::printf("[Packs][Storage] write failed logical=%s storage=%s path=%s offset=%lu chunk=%lu wrote=%lu total=%lu\n", + logical_path.c_str(), + binding.storage ? binding.storage : "", + binding.volume_path.c_str(), + static_cast(written), + static_cast(chunk), + static_cast(chunk_written), + static_cast(len)); + file.close(); + return false; + } + written += chunk_written; + } + file.flush(); + file.close(); + return true; + } + File file = binding.volume->open(binding.volume_path.c_str(), FILE_WRITE); if (!file) { @@ -770,6 +834,30 @@ bool read_binary_file(const std::string& logical_path, std::vector return false; } + if (binding.sd_runtime) + { + if (::platform::esp::arduino_common::storage::sd_is_directory(binding.volume_path.c_str())) + { + return false; + } + + ::platform::esp::arduino_common::storage::SdRuntimeFile file; + if (!file.open(binding.volume_path.c_str(), "r")) + { + return false; + } + const std::size_t size = static_cast(file.size()); + out.resize(size); + const int read = file.read(out.data(), size); + file.close(); + if (read < 0 || static_cast(read) != size) + { + out.clear(); + return false; + } + return true; + } + File file = binding.volume->open(binding.volume_path.c_str(), FILE_READ); if (!file || file.isDirectory()) { @@ -802,6 +890,23 @@ bool logical_file_size(const std::string& logical_path, std::size_t& out_size) return false; } + if (binding.sd_runtime) + { + if (::platform::esp::arduino_common::storage::sd_is_directory(binding.volume_path.c_str())) + { + return false; + } + + ::platform::esp::arduino_common::storage::SdRuntimeFile file; + if (!file.open(binding.volume_path.c_str(), "r")) + { + return false; + } + out_size = static_cast(file.size()); + file.close(); + return true; + } + File file = binding.volume->open(binding.volume_path.c_str(), FILE_READ); if (!file || file.isDirectory()) { @@ -856,12 +961,22 @@ bool remove_file_if_exists(const std::string& logical_path) return false; } - File file = binding.volume->open(binding.volume_path.c_str(), FILE_READ); - const bool exists = static_cast(file); - const bool is_dir = exists && file.isDirectory(); - if (file) + bool exists = false; + bool is_dir = false; + if (binding.sd_runtime) { - file.close(); + exists = ::platform::esp::arduino_common::storage::sd_exists(binding.volume_path.c_str()); + is_dir = exists && ::platform::esp::arduino_common::storage::sd_is_directory(binding.volume_path.c_str()); + } + else + { + File file = binding.volume->open(binding.volume_path.c_str(), FILE_READ); + exists = static_cast(file); + is_dir = exists && file.isDirectory(); + if (file) + { + file.close(); + } } if (!exists) { @@ -871,7 +986,10 @@ bool remove_file_if_exists(const std::string& logical_path) { return false; } - if (!binding.volume->remove(binding.volume_path.c_str())) + const bool remove_ok = binding.sd_runtime + ? ::platform::esp::arduino_common::storage::sd_remove(binding.volume_path.c_str()) + : binding.volume->remove(binding.volume_path.c_str()); + if (!remove_ok) { std::printf("[Packs][Storage] remove file failed logical=%s storage=%s path=%s\n", logical_path.c_str(), @@ -895,6 +1013,52 @@ bool remove_dir_recursive_if_exists(const std::string& logical_path) return false; } + if (binding.sd_runtime) + { + if (!::platform::esp::arduino_common::storage::sd_exists(binding.volume_path.c_str())) + { + return true; + } + if (!::platform::esp::arduino_common::storage::sd_is_directory(binding.volume_path.c_str())) + { + return ::platform::esp::arduino_common::storage::sd_remove(binding.volume_path.c_str()); + } + + ::platform::esp::arduino_common::storage::SdRuntimeDir dir; + if (!dir.open(binding.volume_path.c_str())) + { + return false; + } + + char name_buf[128]; + bool child_is_dir = false; + while (dir.read_next(name_buf, sizeof(name_buf), &child_is_dir)) + { + const std::string name = file_entry_name(name_buf); + if (!name.empty()) + { + const std::string child_logical = join_logical_path(logical_path, name); + const bool ok = child_is_dir ? remove_dir_recursive_if_exists(child_logical) + : remove_file_if_exists(child_logical); + if (!ok) + { + dir.close(); + return false; + } + } + } + dir.close(); + if (!::platform::esp::arduino_common::storage::sd_rmdir(binding.volume_path.c_str())) + { + std::printf("[Packs][Storage] rmdir failed logical=%s storage=%s path=%s\n", + logical_path.c_str(), + binding.storage ? binding.storage : "", + binding.volume_path.c_str()); + return false; + } + return true; + } + File node = binding.volume->open(binding.volume_path.c_str(), FILE_READ); if (!node) { @@ -946,12 +1110,27 @@ class RandomAccessFile public: bool open(const std::string& logical_path) { + close(); + ArduinoStorageBinding binding; if (!resolve_storage_binding(logical_path, false, binding)) { return false; } + sd_runtime_ = binding.sd_runtime; + if (sd_runtime_) + { + if (::platform::esp::arduino_common::storage::sd_is_directory(binding.volume_path.c_str()) || + !sd_file_.open(binding.volume_path.c_str(), "r")) + { + sd_runtime_ = false; + return false; + } + size_ = static_cast(sd_file_.size()); + return true; + } + file_ = binding.volume->open(binding.volume_path.c_str(), FILE_READ); if (!file_ || file_.isDirectory()) { @@ -959,6 +1138,7 @@ class RandomAccessFile { file_.close(); } + sd_runtime_ = false; return false; } size_ = static_cast(file_.size()); @@ -967,6 +1147,13 @@ class RandomAccessFile void close() { + if (sd_runtime_) + { + sd_file_.close(); + sd_runtime_ = false; + size_ = 0; + return; + } if (file_) { file_.close(); @@ -981,6 +1168,15 @@ class RandomAccessFile bool read_at(std::size_t offset, void* out, std::size_t len) { + if (sd_runtime_) + { + if (!sd_file_.seek(offset)) + { + return false; + } + const int read = sd_file_.read(out, len); + return read >= 0 && static_cast(read) == len; + } if (!file_ || !file_.seek(static_cast(offset))) { return false; @@ -990,6 +1186,8 @@ class RandomAccessFile private: File file_{}; + ::platform::esp::arduino_common::storage::SdRuntimeFile sd_file_{}; + bool sd_runtime_ = false; std::size_t size_ = 0; }; @@ -998,6 +1196,8 @@ class SequentialWriteFile public: bool open(const std::string& logical_path) { + close(); + const std::size_t slash = logical_path.find_last_of('/'); if (slash != std::string::npos && !ensure_dir_recursive(logical_path.substr(0, slash))) @@ -1013,9 +1213,21 @@ class SequentialWriteFile return false; } + sd_runtime_ = binding.sd_runtime; + if (sd_runtime_) + { + if (!sd_file_.open(binding.volume_path.c_str(), "w")) + { + sd_runtime_ = false; + return false; + } + return true; + } + file_ = binding.volume->open(binding.volume_path.c_str(), FILE_WRITE); if (!file_) { + sd_runtime_ = false; return false; } return true; @@ -1023,6 +1235,10 @@ class SequentialWriteFile bool write(const void* data, std::size_t len) { + if (sd_runtime_) + { + return sd_file_.write(data, len) == len; + } if (!file_) { return false; @@ -1032,6 +1248,13 @@ class SequentialWriteFile void close() { + if (sd_runtime_) + { + sd_file_.flush(); + sd_file_.close(); + sd_runtime_ = false; + return; + } if (file_) { file_.flush(); @@ -1041,6 +1264,8 @@ class SequentialWriteFile private: File file_{}; + ::platform::esp::arduino_common::storage::SdRuntimeFile sd_file_{}; + bool sd_runtime_ = false; }; #else diff --git a/platform/esp/arduino_common/src/ui_common.cpp b/platform/esp/arduino_common/src/ui_common.cpp index 6ac95cc4..f18ff749 100644 --- a/platform/esp/arduino_common/src/ui_common.cpp +++ b/platform/esp/arduino_common/src/ui_common.cpp @@ -13,7 +13,6 @@ extern "C" lv_draw_buf_t* lv_snapshot_take(lv_obj_t* obj, lv_color_format_t cf); extern "C" void lv_draw_buf_destroy(lv_draw_buf_t* draw_buf); #endif -#include #include #include #include diff --git a/platform/shared/include/board/sd_utils.h b/platform/shared/include/board/sd_utils.h index fe6d4c5a..92216697 100644 --- a/platform/shared/include/board/sd_utils.h +++ b/platform/shared/include/board/sd_utils.h @@ -1,15 +1,17 @@ #pragma once #include -#include #include -#if defined(ARDUINO_ARCH_ESP32) -#include "platform/esp/arduino_common/storage/sd_card_runtime.h" +#if !defined(ARDUINO_ARCH_ESP32) +#error "sd_utils requires the ESP32 SdFat runtime; Arduino SD fallback is intentionally unsupported." #endif +#include "platform/esp/arduino_common/storage/sd_card_runtime.h" namespace sdutil { +constexpr uint8_t kCardNone = 0; + inline void setCsHigh(int pin) { if (pin < 0) @@ -20,6 +22,26 @@ inline void setCsHigh(int pin) digitalWrite(pin, HIGH); } +inline void releaseSdBusDevices(int sd_cs, const int* extra_cs, size_t extra_cs_count) +{ + for (size_t i = 0; i < extra_cs_count; ++i) + { + setCsHigh(extra_cs[i]); + } + setCsHigh(sd_cs); +} + +inline void resetSharedSpiForSd(int sd_cs, const int* extra_cs, size_t extra_cs_count) +{ + releaseSdBusDevices(sd_cs, extra_cs, extra_cs_count); + pinMode(MISO, INPUT_PULLUP); + SPI.end(); + delay(2); + SPI.begin(SCK, MISO, MOSI); + releaseSdBusDevices(sd_cs, extra_cs, extra_cs_count); + delay(2); +} + template inline bool installSpiSd(Lockable& bus, int sd_cs, uint32_t spi_hz, const char* mount_point, const int* extra_cs, size_t extra_cs_count, @@ -31,23 +53,8 @@ inline bool installSpiSd(Lockable& bus, int sd_cs, uint32_t spi_hz, const char* return false; } - for (size_t i = 0; i < extra_cs_count; ++i) - { - setCsHigh(extra_cs[i]); - } - setCsHigh(sd_cs); - - pinMode(MISO, INPUT_PULLUP); - SPI.end(); - delay(2); - SPI.begin(SCK, MISO, MOSI); + resetSharedSpiForSd(sd_cs, extra_cs, extra_cs_count); SPIClass& sd_bus = SPI; - for (size_t i = 0; i < extra_cs_count; ++i) - { - setCsHigh(extra_cs[i]); - } - setCsHigh(sd_cs); - delay(2); Serial.printf("[SD] SPI pins sck=%d miso=%d mosi=%d cs=%d hz=%lu\n", SCK, MISO, MOSI, sd_cs, (unsigned long)spi_hz); for (size_t i = 0; i < extra_cs_count; ++i) @@ -57,19 +64,14 @@ inline bool installSpiSd(Lockable& bus, int sd_cs, uint32_t spi_hz, const char* Serial.printf("[SD] sd CS pin=%d level=%d\n", sd_cs, digitalRead(sd_cs)); bool ok = false; - uint8_t card_type = CARD_NONE; + uint8_t card_type = kCardNone; uint32_t card_size_mb = 0; bool locked = true; -#if defined(ARDUINO_ARCH_ESP32) if (use_lock) { locked = bus.lock(portMAX_DELAY); } -#else - (void)bus; - (void)use_lock; -#endif if (locked) { @@ -97,38 +99,27 @@ inline bool installSpiSd(Lockable& bus, int sd_cs, uint32_t spi_hz, const char* continue; } tried_freqs[tried_count++] = hz_try; - SD.end(); - setCsHigh(sd_cs); + resetSharedSpiForSd(sd_cs, extra_cs, extra_cs_count); delay(10); Serial.printf("[SD] try hz=%lu\n", (unsigned long)hz_try); -#if defined(ARDUINO_ARCH_ESP32) ok = ::platform::esp::arduino_common::storage::mount_sd_card( sd_cs, sd_bus, hz_try, mount_point, max_files); -#else - ok = SD.begin(sd_cs, sd_bus, hz_try, mount_point); - if (!ok) - { - ok = SD.begin(sd_cs, sd_bus, hz_try); - } -#endif - Serial.printf("[SD] SD.begin -> %d\n", ok ? 1 : 0); + Serial.printf("[SD] mount -> %d\n", ok ? 1 : 0); if (ok) { break; } - SD.end(); delay(25); } if (ok) { -#if defined(ARDUINO_ARCH_ESP32) const auto info = ::platform::esp::arduino_common::storage::sd_card_info(); card_type = info.card_type; Serial.printf("[SD] cardType=%u backend=%s fs=%s\n", (unsigned)card_type, ::platform::esp::arduino_common::storage::sd_card_backend_name(), ::platform::esp::arduino_common::storage::sd_card_filesystem_name()); - if (card_type != CARD_NONE) + if (card_type != kCardNone) { card_size_mb = static_cast(info.card_size_bytes / (1024ULL * 1024ULL)); Serial.printf("[SD] card=%llu MB total=%llu MB sectors=%lu sector_size=%lu\n", @@ -142,33 +133,11 @@ inline bool installSpiSd(Lockable& bus, int sd_cs, uint32_t spi_hz, const char* ok = false; ::platform::esp::arduino_common::storage::unmount_sd_card(); } -#else - card_type = SD.cardType(); - Serial.printf("[SD] cardType=%u\n", (unsigned)card_type); - if (card_type != CARD_NONE) - { - const uint64_t card_size = SD.cardSize(); - const uint64_t total_size = SD.totalBytes(); - card_size_mb = static_cast(card_size / (1024ULL * 1024ULL)); - Serial.printf("[SD] card=%llu MB total=%llu MB sectors=%lu sector_size=%lu\n", - static_cast(card_size / (1024ULL * 1024ULL)), - static_cast(total_size / (1024ULL * 1024ULL)), - static_cast(SD.numSectors()), - static_cast(SD.sectorSize())); - } - else - { - ok = false; - SD.end(); - } -#endif } -#if defined(ARDUINO_ARCH_ESP32) if (use_lock) { bus.unlock(); } -#endif } else {