mirror of
https://github.com/vicliu624/trail-mate.git
synced 2026-08-29 07:08:27 +00:00
Burn down Arduino SD backend
This commit is contained in:
@@ -6,7 +6,6 @@
|
||||
#include <AudioFileSourcePROGMEM.h>
|
||||
#include <AudioGeneratorRTTTL.h>
|
||||
#include <AudioOutputI2S.h>
|
||||
#include <SD.h>
|
||||
#include <Wire.h>
|
||||
#include <ctime>
|
||||
#include <driver/gpio.h>
|
||||
@@ -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,
|
||||
|
||||
@@ -6,7 +6,6 @@
|
||||
#include <AudioFileSourcePROGMEM.h>
|
||||
#include <AudioGeneratorRTTTL.h>
|
||||
#include <AudioOutputI2S.h>
|
||||
#include <SD.h>
|
||||
#include <SPI.h>
|
||||
#include <Wire.h>
|
||||
#include <ctime>
|
||||
@@ -16,6 +15,8 @@
|
||||
#include <limits>
|
||||
#include <sys/time.h>
|
||||
|
||||
#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()
|
||||
|
||||
@@ -10,7 +10,6 @@
|
||||
|
||||
#include <AW9364LedDriver.hpp>
|
||||
#include <Arduino.h>
|
||||
#include <SD.h>
|
||||
#include <SPI.h>
|
||||
#include <Wire.h>
|
||||
#include <memory>
|
||||
|
||||
@@ -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");
|
||||
|
||||
@@ -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*/
|
||||
|
||||
-124
@@ -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 <FS.h>
|
||||
#include <SD.h>
|
||||
#include <array>
|
||||
#include <vector>
|
||||
|
||||
namespace chat
|
||||
{
|
||||
|
||||
/**
|
||||
* @brief SD-backed append-only log with per-channel ring index.
|
||||
*
|
||||
* Layout:
|
||||
* - Conversation file: /chat/<protocol>_n_<peer>.log or /chat/<protocol>_broadcast_<name>.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<ChatMessage> loadRecent(const ConversationId& conv, size_t n) override;
|
||||
std::vector<ConversationMeta> 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<IndexEntry>& entries);
|
||||
bool writeIndex(const std::vector<IndexEntry>& entries);
|
||||
bool readIndex(std::vector<IndexEntry>& entries);
|
||||
bool findIndexEntry(const ConversationId& conv,
|
||||
std::vector<IndexEntry>& 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
|
||||
+1
-2
@@ -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);
|
||||
|
||||
|
||||
@@ -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();
|
||||
lv_indev_t* lv_get_encoder_indev();
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -2,7 +2,6 @@
|
||||
#include "platform/esp/arduino_common/app_config_store.h"
|
||||
|
||||
#include <Arduino.h>
|
||||
#include <SD.h>
|
||||
|
||||
#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<chat::IChatStore> 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<chat::LogStore>(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<chat::IChatStore>(new chat::RamStore());
|
||||
}
|
||||
|
||||
@@ -8,7 +8,6 @@
|
||||
#include "ui/widgets/ble_pairing_popup.h"
|
||||
#include <Arduino.h>
|
||||
#include <Preferences.h>
|
||||
#include <SD.h>
|
||||
#include <algorithm>
|
||||
#include <cstring>
|
||||
#include <ctime>
|
||||
|
||||
@@ -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 <algorithm>
|
||||
#include <cstdio>
|
||||
#include <cstring>
|
||||
|
||||
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<IndexEntry> 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<uint8_t>(msg.protocol);
|
||||
rec.channel = static_cast<uint8_t>(msg.channel);
|
||||
rec.status = static_cast<uint8_t>(msg.status);
|
||||
rec.text_len = static_cast<uint16_t>(std::min<size_t>(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<uint16_t>((header.head + 1) % kMaxMessagesPerConv);
|
||||
if (header.count < kMaxMessagesPerConv)
|
||||
{
|
||||
header.count = static_cast<uint16_t>(header.count + 1);
|
||||
}
|
||||
|
||||
wf.seek(0);
|
||||
wf.write(reinterpret_cast<const uint8_t*>(&header), sizeof(header));
|
||||
wf.flush();
|
||||
wf.close();
|
||||
|
||||
updateIndexForMessage(msg);
|
||||
}
|
||||
|
||||
std::vector<ChatMessage> LogStore::loadRecent(const ConversationId& conv, size_t n)
|
||||
{
|
||||
std::vector<ChatMessage> 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<size_t>(n, count);
|
||||
uint16_t start = static_cast<uint16_t>(
|
||||
(header.head + kMaxMessagesPerConv - to_read) % kMaxMessagesPerConv);
|
||||
|
||||
out.reserve(to_read);
|
||||
for (size_t i = 0; i < to_read; ++i)
|
||||
{
|
||||
uint16_t slot = static_cast<uint16_t>((start + i) % kMaxMessagesPerConv);
|
||||
Record rec{};
|
||||
if (!readRecord(rf, slot, rec))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
if (rec.text_len == 0)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
ChatMessage msg;
|
||||
msg.protocol = static_cast<MeshProtocol>(rec.protocol);
|
||||
msg.channel = static_cast<ChannelId>(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<MessageStatus>(rec.status);
|
||||
out.push_back(msg);
|
||||
}
|
||||
rf.close();
|
||||
|
||||
return out;
|
||||
}
|
||||
|
||||
std::vector<ConversationMeta> LogStore::loadConversationPage(size_t offset,
|
||||
size_t limit,
|
||||
size_t* total)
|
||||
{
|
||||
std::vector<IndexEntry> 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<ConversationMeta> 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<MeshProtocol>(entry.protocol);
|
||||
meta.id.channel = static_cast<ChannelId>(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<int>(entry.unread);
|
||||
if (entry.peer == 0)
|
||||
{
|
||||
meta.name = "Broadcast";
|
||||
}
|
||||
else
|
||||
{
|
||||
char buf[16];
|
||||
snprintf(buf, sizeof(buf), "%04lX",
|
||||
static_cast<unsigned long>(entry.peer & 0xFFFF));
|
||||
meta.name = buf;
|
||||
}
|
||||
list.push_back(meta);
|
||||
}
|
||||
|
||||
return list;
|
||||
}
|
||||
|
||||
void LogStore::setUnread(const ConversationId& conv, int unread)
|
||||
{
|
||||
std::vector<IndexEntry> entries;
|
||||
if (!ensureIndex(entries))
|
||||
{
|
||||
return;
|
||||
}
|
||||
size_t idx = 0;
|
||||
if (!findIndexEntry(conv, entries, &idx))
|
||||
{
|
||||
return;
|
||||
}
|
||||
entries[idx].unread = static_cast<uint16_t>(std::max(0, unread));
|
||||
writeIndex(entries);
|
||||
}
|
||||
|
||||
int LogStore::getUnread(const ConversationId& conv) const
|
||||
{
|
||||
std::vector<IndexEntry> entries;
|
||||
if (!const_cast<LogStore*>(this)->readIndex(entries))
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
for (const auto& entry : entries)
|
||||
{
|
||||
if (entry.peer == conv.peer &&
|
||||
entry.channel == static_cast<uint8_t>(conv.channel) &&
|
||||
entry.protocol == static_cast<uint8_t>(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<IndexEntry> 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<uint8_t>(conv.channel) &&
|
||||
entry.protocol == static_cast<uint8_t>(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<IndexEntry> entries;
|
||||
if (!readIndex(entries))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
bool updated = false;
|
||||
for (auto& entry : entries)
|
||||
{
|
||||
ConversationId conv(static_cast<ChannelId>(entry.channel),
|
||||
entry.peer,
|
||||
static_cast<MeshProtocol>(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<uint16_t>((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<uint8_t>(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<uint8_t>(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<IndexEntry> entries;
|
||||
if (!const_cast<LogStore*>(this)->readIndex(entries))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
for (const auto& entry : entries)
|
||||
{
|
||||
ConversationId conv(static_cast<ChannelId>(entry.channel),
|
||||
entry.peer,
|
||||
static_cast<MeshProtocol>(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<LogStore*>(this)->loadFileHeader(rf, header))
|
||||
{
|
||||
rf.close();
|
||||
continue;
|
||||
}
|
||||
|
||||
for (uint16_t i = 0; i < header.count; ++i)
|
||||
{
|
||||
uint16_t slot =
|
||||
static_cast<uint16_t>((header.head + kMaxMessagesPerConv - header.count + i) %
|
||||
kMaxMessagesPerConv);
|
||||
Record rec{};
|
||||
if (!const_cast<LogStore*>(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<MeshProtocol>(rec.protocol);
|
||||
msg.channel = static_cast<ChannelId>(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<MessageStatus>(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<IndexEntry>& entries)
|
||||
{
|
||||
if (readIndex(entries))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
rebuildIndex();
|
||||
return readIndex(entries);
|
||||
}
|
||||
|
||||
bool LogStore::writeIndex(const std::vector<IndexEntry>& 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<uint16_t>(entries.size());
|
||||
wf.write(reinterpret_cast<const uint8_t*>(&header), sizeof(header));
|
||||
for (const auto& entry : entries)
|
||||
{
|
||||
wf.write(reinterpret_cast<const uint8_t*>(&entry), sizeof(entry));
|
||||
}
|
||||
wf.flush();
|
||||
wf.close();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool LogStore::readIndex(std::vector<IndexEntry>& 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<uint8_t*>(&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<uint8_t*>(entries.data()), expected);
|
||||
rf.close();
|
||||
if (got != expected)
|
||||
{
|
||||
entries.clear();
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool LogStore::findIndexEntry(const ConversationId& conv,
|
||||
std::vector<IndexEntry>& 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<uint8_t>(conv.channel) &&
|
||||
entries[i].protocol == static_cast<uint8_t>(conv.protocol))
|
||||
{
|
||||
if (out_idx)
|
||||
{
|
||||
*out_idx = i;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void LogStore::updateIndexForMessage(const ChatMessage& msg)
|
||||
{
|
||||
std::vector<IndexEntry> 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<uint8_t>(msg.protocol);
|
||||
entry.channel = static_cast<uint8_t>(msg.channel);
|
||||
entry.peer = msg.peer;
|
||||
entries.push_back(entry);
|
||||
idx = entries.size() - 1;
|
||||
}
|
||||
|
||||
IndexEntry& entry = entries[idx];
|
||||
entry.protocol = static_cast<uint8_t>(msg.protocol);
|
||||
entry.channel = static_cast<uint8_t>(msg.channel);
|
||||
entry.status = static_cast<uint8_t>(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<uint16_t>(std::min<size_t>(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<uint16_t>(entry.unread + 1);
|
||||
}
|
||||
writeIndex(entries);
|
||||
}
|
||||
|
||||
void LogStore::rebuildIndex()
|
||||
{
|
||||
if (!fs_) return;
|
||||
std::vector<IndexEntry> 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<uint16_t>((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<MeshProtocol>(rec.protocol);
|
||||
msg.channel = static_cast<ChannelId>(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<MessageStatus>(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<uint8_t>(last_msg.protocol);
|
||||
idx.channel = static_cast<uint8_t>(last_msg.channel);
|
||||
idx.status = static_cast<uint8_t>(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<uint16_t>(std::min<size_t>(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<uint8_t*>(&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<const uint8_t*>(&header), sizeof(header));
|
||||
file.flush();
|
||||
}
|
||||
|
||||
bool LogStore::readRecord(File& file, uint16_t slot, Record& rec)
|
||||
{
|
||||
size_t offset = sizeof(FileHeader) + static_cast<size_t>(slot) * sizeof(Record);
|
||||
if (file.size() < offset + sizeof(Record))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
file.seek(offset);
|
||||
size_t read = file.read(reinterpret_cast<uint8_t*>(&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<size_t>(slot) * sizeof(Record);
|
||||
file.seek(offset);
|
||||
size_t written = file.write(reinterpret_cast<const uint8_t*>(&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<unsigned long>(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
|
||||
@@ -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<unsigned long>(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;
|
||||
|
||||
@@ -1,8 +1,7 @@
|
||||
#include "platform/esp/arduino_common/storage/sd_card_runtime.h"
|
||||
|
||||
#include <Arduino.h>
|
||||
#include <FS.h>
|
||||
#include <SD.h>
|
||||
#include <SPI.h>
|
||||
#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<uint32_t>(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<unsigned>(attempt),
|
||||
ready ? 1 : 0,
|
||||
static_cast<unsigned>(last_token));
|
||||
delay(25);
|
||||
}
|
||||
|
||||
spi.endTransaction();
|
||||
Serial.printf("[SD] SdFat preflight CMD0 -> %d token=0x%02X attempts=%u\n",
|
||||
ok ? 1 : 0,
|
||||
static_cast<unsigned>(last_token),
|
||||
static_cast<unsigned>(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<unsigned long>(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<unsigned long>(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<unsigned long long>(s_info.card_size_bytes / (1024ULL * 1024ULL)),
|
||||
static_cast<unsigned long long>(s_info.total_bytes / (1024ULL * 1024ULL)),
|
||||
static_cast<unsigned long>(s_info.sector_count),
|
||||
static_cast<unsigned long>(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<unsigned long>(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<uint8_t*>(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<const uint8_t*>(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<uint8_t>(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<unsigned long>(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<unsigned long>(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<uint8_t*>(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);
|
||||
|
||||
@@ -51,7 +51,7 @@ extern "C" esp_err_t esp_crt_bundle_attach(void* conf);
|
||||
#if defined(ARDUINO)
|
||||
#include <Arduino.h>
|
||||
#include <FS.h>
|
||||
#include <SD.h>
|
||||
#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<bool>(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<bool>(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<bool>(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<bool>(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 : "<none>",
|
||||
binding.volume_path.c_str(),
|
||||
static_cast<unsigned long>(len));
|
||||
return false;
|
||||
}
|
||||
|
||||
const std::uint8_t* bytes = static_cast<const std::uint8_t*>(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 : "<none>",
|
||||
binding.volume_path.c_str(),
|
||||
static_cast<unsigned long>(written),
|
||||
static_cast<unsigned long>(chunk),
|
||||
static_cast<unsigned long>(chunk_written),
|
||||
static_cast<unsigned long>(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<std::uint8_t>
|
||||
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<std::size_t>(file.size());
|
||||
out.resize(size);
|
||||
const int read = file.read(out.data(), size);
|
||||
file.close();
|
||||
if (read < 0 || static_cast<std::size_t>(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<std::size_t>(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<bool>(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<bool>(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 : "<none>",
|
||||
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<std::size_t>(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<std::size_t>(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<std::size_t>(read) == len;
|
||||
}
|
||||
if (!file_ || !file_.seek(static_cast<uint32_t>(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
|
||||
|
||||
@@ -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 <SD.h>
|
||||
#include <cmath>
|
||||
#include <cstdio>
|
||||
#include <ctime>
|
||||
|
||||
@@ -1,15 +1,17 @@
|
||||
#pragma once
|
||||
|
||||
#include <Arduino.h>
|
||||
#include <SD.h>
|
||||
#include <SPI.h>
|
||||
#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 <typename Lockable>
|
||||
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<uint32_t>(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<uint32_t>(card_size / (1024ULL * 1024ULL));
|
||||
Serial.printf("[SD] card=%llu MB total=%llu MB sectors=%lu sector_size=%lu\n",
|
||||
static_cast<unsigned long long>(card_size / (1024ULL * 1024ULL)),
|
||||
static_cast<unsigned long long>(total_size / (1024ULL * 1024ULL)),
|
||||
static_cast<unsigned long>(SD.numSectors()),
|
||||
static_cast<unsigned long>(SD.sectorSize()));
|
||||
}
|
||||
else
|
||||
{
|
||||
ok = false;
|
||||
SD.end();
|
||||
}
|
||||
#endif
|
||||
}
|
||||
#if defined(ARDUINO_ARCH_ESP32)
|
||||
if (use_lock)
|
||||
{
|
||||
bus.unlock();
|
||||
}
|
||||
#endif
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user