feat(p4): montage microSD (SDMMC 1-bit) sur /sdcard
CI / platformio (pull_request) Failing after 6m0s

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) <noreply@anthropic.com>
This commit is contained in:
clement
2026-06-13 15:49:44 +02:00
parent c35c9673f4
commit a8f0210567
5 changed files with 128 additions and 0 deletions
@@ -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
)
@@ -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));
}
@@ -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 <stdbool.h>
#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
+1
View File
@@ -6,6 +6,7 @@ idf_component_register(
REQUIRES
joltwallet__littlefs
ota_server
sd_storage
media_manager
npc_engine
hints_client
+8
View File
@@ -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();