From a8f0210567c3d54f3e46ed8054068cb794d68bf4 Mon Sep 17 00:00:00 2001 From: clement Date: Sat, 13 Jun 2026 15:49:44 +0200 Subject: [PATCH] feat(p4): montage microSD (SDMMC 1-bit) sur /sdcard MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Nouveau composant sd_storage : monte la carte microSD du Freenove en SDMMC 1-bit (CMD=38, CLK=39, D0=40 — pinout FREENOVE_SDMMC_* du firmware Arduino) sur /sdcard via esp_vfs_fat_sdmmc_mount. Best-effort au boot : carte absente = non fatal, fallback LittleFS. Lève le plafond LittleFS 5 Mo pour les assets. Validé sur Freenove : carte ~3839 MiB montée à /sdcard au boot. Co-Authored-By: Claude Opus 4.8 (1M context) --- .../components/sd_storage/CMakeLists.txt | 11 +++ idf_zacus/components/sd_storage/sd_storage.c | 74 +++++++++++++++++++ idf_zacus/components/sd_storage/sd_storage.h | 34 +++++++++ idf_zacus/main/CMakeLists.txt | 1 + idf_zacus/main/main.c | 8 ++ 5 files changed, 128 insertions(+) create mode 100644 idf_zacus/components/sd_storage/CMakeLists.txt create mode 100644 idf_zacus/components/sd_storage/sd_storage.c create mode 100644 idf_zacus/components/sd_storage/sd_storage.h diff --git a/idf_zacus/components/sd_storage/CMakeLists.txt b/idf_zacus/components/sd_storage/CMakeLists.txt new file mode 100644 index 0000000..a670065 --- /dev/null +++ b/idf_zacus/components/sd_storage/CMakeLists.txt @@ -0,0 +1,11 @@ +## Zacus sd_storage — microSD (SDMMC 1-bit) FAT mount at /sdcard (P4). +idf_component_register( + SRCS + "sd_storage.c" + INCLUDE_DIRS + "." + REQUIRES + fatfs + sdmmc + esp_driver_sdmmc +) diff --git a/idf_zacus/components/sd_storage/sd_storage.c b/idf_zacus/components/sd_storage/sd_storage.c new file mode 100644 index 0000000..95888e4 --- /dev/null +++ b/idf_zacus/components/sd_storage/sd_storage.c @@ -0,0 +1,74 @@ +#include "sd_storage.h" + +#include "esp_log.h" +#include "esp_vfs_fat.h" +#include "sdmmc_cmd.h" +#include "driver/sdmmc_host.h" + +static const char *TAG = "sd_storage"; + +// Freenove ESP32-S3 WROOM N16R8 microSD (SDMMC 1-bit). Mirrors +// ui_freenove_allinone/include/ui_freenove_config.h FREENOVE_SDMMC_*. +#define SD_PIN_CMD 38 +#define SD_PIN_CLK 39 +#define SD_PIN_D0 40 + +static sdmmc_card_t *s_card = NULL; + +esp_err_t sd_storage_mount(void) { + if (s_card != NULL) { + return ESP_OK; + } + + const esp_vfs_fat_sdmmc_mount_config_t mount_cfg = { + .format_if_mount_failed = false, // never wipe a user's card + .max_files = 5, + .allocation_unit_size = 16 * 1024, + }; + + sdmmc_host_t host = SDMMC_HOST_DEFAULT(); + host.flags = SDMMC_HOST_FLAG_1BIT; // Freenove wires D0 only + host.max_freq_khz = SDMMC_FREQ_DEFAULT; + + sdmmc_slot_config_t slot = SDMMC_SLOT_CONFIG_DEFAULT(); + slot.width = 1; + slot.clk = SD_PIN_CLK; + slot.cmd = SD_PIN_CMD; + slot.d0 = SD_PIN_D0; + slot.flags |= SDMMC_SLOT_FLAG_INTERNAL_PULLUP; + + esp_err_t err = esp_vfs_fat_sdmmc_mount(SD_STORAGE_MOUNT_POINT, &host, + &slot, &mount_cfg, &s_card); + if (err != ESP_OK) { + ESP_LOGW(TAG, "mount failed: %s — no card inserted or wiring issue", + esp_err_to_name(err)); + s_card = NULL; + return err; + } + + ESP_LOGI(TAG, "microSD mounted at %s — %lu MiB (%s)", + SD_STORAGE_MOUNT_POINT, (unsigned long) sd_storage_capacity_mb(), + s_card->cid.name); + return ESP_OK; +} + +esp_err_t sd_storage_unmount(void) { + if (s_card == NULL) { + return ESP_OK; + } + esp_err_t err = esp_vfs_fat_sdcard_unmount(SD_STORAGE_MOUNT_POINT, s_card); + s_card = NULL; + return err; +} + +bool sd_storage_ready(void) { + return s_card != NULL; +} + +uint32_t sd_storage_capacity_mb(void) { + if (s_card == NULL) { + return 0U; + } + uint64_t bytes = (uint64_t) s_card->csd.capacity * s_card->csd.sector_size; + return (uint32_t) (bytes / (1024ULL * 1024ULL)); +} diff --git a/idf_zacus/components/sd_storage/sd_storage.h b/idf_zacus/components/sd_storage/sd_storage.h new file mode 100644 index 0000000..8ba2ade --- /dev/null +++ b/idf_zacus/components/sd_storage/sd_storage.h @@ -0,0 +1,34 @@ +// Zacus sd_storage — microSD mount for the Freenove ESP32-S3 master. +// +// The Freenove WROOM N16R8 exposes the card on the SDMMC peripheral in +// 1-bit mode (CMD=GPIO38, CLK=GPIO39, D0=GPIO40 — mirrors the Arduino +// `ui_freenove_allinone` FREENOVE_SDMMC_* pinout). Mounts FAT at /sdcard so +// media_manager and the asset endpoints can stage large assets off the 5 MB +// LittleFS partition (P4). Mount is best-effort: a missing card is not fatal. +#pragma once + +#include +#include "esp_err.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#define SD_STORAGE_MOUNT_POINT "/sdcard" + +// Mount the microSD card (SDMMC 1-bit) at /sdcard. Returns ESP_OK on success; +// ESP_ERR_NOT_FOUND / ESP_FAIL when no card is present or wiring is wrong. +esp_err_t sd_storage_mount(void); + +// Unmount and release the card. +esp_err_t sd_storage_unmount(void); + +// True once a card is mounted. +bool sd_storage_ready(void); + +// Card capacity in MiB (0 when not mounted). +uint32_t sd_storage_capacity_mb(void); + +#ifdef __cplusplus +} +#endif diff --git a/idf_zacus/main/CMakeLists.txt b/idf_zacus/main/CMakeLists.txt index 0d9a88c..b4758cf 100644 --- a/idf_zacus/main/CMakeLists.txt +++ b/idf_zacus/main/CMakeLists.txt @@ -6,6 +6,7 @@ idf_component_register( REQUIRES joltwallet__littlefs ota_server + sd_storage media_manager npc_engine hints_client diff --git a/idf_zacus/main/main.c b/idf_zacus/main/main.c index 519446e..3b72fd1 100644 --- a/idf_zacus/main/main.c +++ b/idf_zacus/main/main.c @@ -40,6 +40,7 @@ #include "ota_server.h" #include "media_manager.h" +#include "sd_storage.h" #include "npc_engine.h" #include "hints_client.h" #include "voice_pipeline.h" @@ -484,6 +485,13 @@ void app_main(void) { } } + // P4: best-effort microSD mount (SDMMC 1-bit on /sdcard). A missing card + // is non-fatal — assets fall back to LittleFS. + if (sd_storage_mount() == ESP_OK) { + ESP_LOGI(TAG, "microSD ready (%lu MiB) at %s", + (unsigned long) sd_storage_capacity_mb(), SD_STORAGE_MOUNT_POINT); + } + if (mount_littlefs() == ESP_OK) { list_littlefs_root();