212 lines
7.1 KiB
C++
212 lines
7.1 KiB
C++
#include "SD_Card.h"
|
|
|
|
// ── Globals ──────────────────────────────────────────────────────────────────
|
|
Memory g_memories[MAX_MEMORIES];
|
|
int g_memoryCount = 0;
|
|
int g_currentIndex = 0;
|
|
uint8_t g_volume = 12;
|
|
uint16_t SDCard_Size = 0;
|
|
uint16_t Flash_Size = 0;
|
|
|
|
// ── SD Init ───────────────────────────────────────────────────────────────────
|
|
void SD_Init() {
|
|
if (!SD_MMC.setPins(SD_CLK_PIN, SD_CMD_PIN, SD_D0_PIN)) {
|
|
printf("SD MMC: Pin change failed!\r\n");
|
|
return;
|
|
}
|
|
if (!SD_MMC.begin("/sdcard", true, true)) {
|
|
printf("SD card initialization failed!\r\n");
|
|
return;
|
|
}
|
|
printf("SD card OK (1-bit mode)\r\n");
|
|
uint8_t cardType = SD_MMC.cardType();
|
|
if (cardType == CARD_NONE) { printf("No SD card attached\r\n"); return; }
|
|
|
|
uint64_t total = SD_MMC.totalBytes();
|
|
uint64_t used = SD_MMC.usedBytes();
|
|
SDCard_Size = total / (1024 * 1024);
|
|
printf("SD: %llu MB total, %llu MB used\r\n", total/(1024*1024), used/(1024*1024));
|
|
|
|
// Ensure required directories exist
|
|
if (!SD_MMC.exists("/photos")) SD_MMC.mkdir("/photos");
|
|
if (!SD_MMC.exists("/audio")) SD_MMC.mkdir("/audio");
|
|
}
|
|
|
|
void Flash_test() {
|
|
uint32_t flashSize = ESP.getFlashChipSize();
|
|
Flash_Size = flashSize / 1024 / 1024;
|
|
printf("Flash: %d MB\r\n", Flash_Size);
|
|
}
|
|
|
|
// ── Config helpers ────────────────────────────────────────────────────────────
|
|
// We use a dead-simple line-based text format so we don't need ArduinoJson:
|
|
// ID|name|photo_filename|audio_filename
|
|
// Volume is stored on its own line: VOLUME|12
|
|
// This is robust against partial reads and avoids heap fragmentation.
|
|
|
|
static const char* CONFIG_PATH = "/config.txt";
|
|
|
|
bool Config_Load() {
|
|
g_memoryCount = 0;
|
|
g_currentIndex = 0;
|
|
g_volume = 12;
|
|
|
|
File f = SD_MMC.open(CONFIG_PATH, "r");
|
|
if (!f) {
|
|
printf("Config: no config file found, starting fresh\r\n");
|
|
return true; // not an error — first boot
|
|
}
|
|
|
|
char line[200];
|
|
while (f.available()) {
|
|
// Read a line
|
|
int len = 0;
|
|
while (f.available() && len < (int)sizeof(line) - 1) {
|
|
char c = f.read();
|
|
if (c == '\n') break;
|
|
line[len++] = c;
|
|
}
|
|
line[len] = '\0';
|
|
// Trim trailing \r
|
|
if (len > 0 && line[len-1] == '\r') { line[len-1] = '\0'; len--; }
|
|
if (len == 0) continue;
|
|
|
|
if (strncmp(line, "VOLUME|", 7) == 0) {
|
|
g_volume = (uint8_t)atoi(line + 7);
|
|
if (g_volume > 21) g_volume = 12;
|
|
continue;
|
|
}
|
|
if (strncmp(line, "INDEX|", 6) == 0) {
|
|
g_currentIndex = atoi(line + 6);
|
|
continue;
|
|
}
|
|
|
|
// Parse memory line: id|name|photo|audio
|
|
if (g_memoryCount >= MAX_MEMORIES) continue;
|
|
char* tok = strtok(line, "|");
|
|
if (!tok) continue;
|
|
int id = atoi(tok);
|
|
|
|
tok = strtok(NULL, "|"); if (!tok) continue;
|
|
char name[MAX_NAME_LEN]; strncpy(name, tok, MAX_NAME_LEN-1); name[MAX_NAME_LEN-1]='\0';
|
|
|
|
tok = strtok(NULL, "|"); if (!tok) continue;
|
|
char photo[MAX_FILE_LEN]; strncpy(photo, tok, MAX_FILE_LEN-1); photo[MAX_FILE_LEN-1]='\0';
|
|
|
|
tok = strtok(NULL, "|"); if (!tok) continue;
|
|
char audio[MAX_FILE_LEN]; strncpy(audio, tok, MAX_FILE_LEN-1); audio[MAX_FILE_LEN-1]='\0';
|
|
|
|
Memory& m = g_memories[g_memoryCount++];
|
|
m.id = id;
|
|
strncpy(m.name, name, MAX_NAME_LEN-1); m.name[MAX_NAME_LEN-1] = '\0';
|
|
strncpy(m.photo, photo, MAX_FILE_LEN-1); m.photo[MAX_FILE_LEN-1] = '\0';
|
|
strncpy(m.audio, audio, MAX_FILE_LEN-1); m.audio[MAX_FILE_LEN-1] = '\0';
|
|
}
|
|
f.close();
|
|
|
|
// Clamp currentIndex
|
|
if (g_memoryCount > 0 && g_currentIndex >= g_memoryCount)
|
|
g_currentIndex = 0;
|
|
|
|
printf("Config: loaded %d memories, volume=%d\r\n", g_memoryCount, g_volume);
|
|
return true;
|
|
}
|
|
|
|
bool Config_Save() {
|
|
File f = SD_MMC.open(CONFIG_PATH, "w");
|
|
if (!f) {
|
|
printf("Config: failed to open for writing\r\n");
|
|
return false;
|
|
}
|
|
f.printf("VOLUME|%d\n", g_volume);
|
|
f.printf("INDEX|%d\n", g_currentIndex);
|
|
for (int i = 0; i < g_memoryCount; i++) {
|
|
Memory& m = g_memories[i];
|
|
f.printf("%d|%s|%s|%s\n", m.id, m.name, m.photo, m.audio);
|
|
}
|
|
f.close();
|
|
printf("Config: saved %d memories\r\n", g_memoryCount);
|
|
return true;
|
|
}
|
|
|
|
int Config_Add_Memory(const char* name, const char* photo, const char* audio) {
|
|
if (g_memoryCount >= MAX_MEMORIES) return -1;
|
|
|
|
// Generate a new unique id (max existing + 1)
|
|
int newId = 1;
|
|
for (int i = 0; i < g_memoryCount; i++)
|
|
if (g_memories[i].id >= newId) newId = g_memories[i].id + 1;
|
|
|
|
Memory& m = g_memories[g_memoryCount];
|
|
m.id = newId;
|
|
strncpy(m.name, name, MAX_NAME_LEN-1); m.name[MAX_NAME_LEN-1] = '\0';
|
|
strncpy(m.photo, photo, MAX_FILE_LEN-1); m.photo[MAX_FILE_LEN-1] = '\0';
|
|
strncpy(m.audio, audio, MAX_FILE_LEN-1); m.audio[MAX_FILE_LEN-1] = '\0';
|
|
g_memoryCount++;
|
|
|
|
Config_Save();
|
|
printf("Config: added memory id=%d name='%s'\r\n", newId, name);
|
|
return newId;
|
|
}
|
|
|
|
bool Config_Delete_Memory(int id) {
|
|
for (int i = 0; i < g_memoryCount; i++) {
|
|
if (g_memories[i].id == id) {
|
|
// Optionally delete the files from SD
|
|
char path[80];
|
|
snprintf(path, sizeof(path), "/photos/%s", g_memories[i].photo);
|
|
SD_MMC.remove(path);
|
|
snprintf(path, sizeof(path), "/audio/%s", g_memories[i].audio);
|
|
SD_MMC.remove(path);
|
|
|
|
// Shift array down
|
|
for (int j = i; j < g_memoryCount - 1; j++)
|
|
g_memories[j] = g_memories[j+1];
|
|
g_memoryCount--;
|
|
if (g_currentIndex >= g_memoryCount && g_memoryCount > 0)
|
|
g_currentIndex = g_memoryCount - 1;
|
|
Config_Save();
|
|
printf("Config: deleted memory id=%d\r\n", id);
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
// ── Legacy helpers ─────────────────────────────────────────────────────────────
|
|
bool File_Search(const char* directory, const char* fileName) {
|
|
File dir = SD_MMC.open(directory);
|
|
if (!dir) return false;
|
|
File file = dir.openNextFile();
|
|
while (file) {
|
|
if (strcmp(file.name(), fileName) == 0) { dir.close(); return true; }
|
|
file = dir.openNextFile();
|
|
}
|
|
dir.close();
|
|
return false;
|
|
}
|
|
|
|
uint16_t Folder_retrieval(const char* directory, const char* fileExtension,
|
|
char File_Name[][100], uint16_t maxFiles) {
|
|
File dir = SD_MMC.open(directory);
|
|
if (!dir) { printf("Folder not found: %s\r\n", directory); return 0; }
|
|
|
|
uint16_t count = 0;
|
|
File file = dir.openNextFile();
|
|
while (file && count < maxFiles) {
|
|
if (!file.isDirectory() && strstr(file.name(), fileExtension)) {
|
|
strncpy(File_Name[count], file.name(), 99);
|
|
File_Name[count][99] = '\0';
|
|
count++;
|
|
}
|
|
file = dir.openNextFile();
|
|
}
|
|
dir.close();
|
|
return count;
|
|
}
|
|
|
|
void remove_file_extension(char* file_name) {
|
|
char* last_dot = strrchr(file_name, '.');
|
|
if (last_dot) *last_dot = '\0';
|
|
}
|