Hi everyone. I'm having a problem with my Retro-Go, specifically Card Mount Failed. I formatted it, converted it to FAT32, and swapped it with another Micro SD card. I don't know what to do. Can anyone with experience in this matter offer advice? I'm using an ESP32-S3-N16R8. The configuration file is attached, as is a photo of the module.
P.S. My module Vcc pin on 5V.
config.h
`
// Target definition
#define RG_TARGET_NAME "ESP32-S3-N16-R8"
// Storage
#define RG_STORAGE_ROOT "/sd"
#define RG_STORAGE_SDSPI_HOST SPI2_HOST
#define RG_STORAGE_SDSPI_SPEED 4000 // 4MHz
// Audio
#define RG_AUDIO_USE_INT_DAC 0 // 0 = Disable, 1 = GPIO25, 2 = GPIO26, 3 = Both
#define RG_AUDIO_USE_EXT_DAC 0 // 0 = Disable, 1 = Enable
#define RG_AUDIO_USE_BUZZER_PIN 40 // PWM Buzzer on GPIO 40
// Video
#define RG_SCREEN_DRIVER 0 // 0 = ILI9341/ST7789
#define RG_SCREEN_HOST SPI2_HOST
#define RG_SCREEN_SPEED SPI_MASTER_FREQ_40M
#define RG_SCREEN_BACKLIGHT 1
#define RG_SCREEN_WIDTH 320
#define RG_SCREEN_HEIGHT 240
#define RG_SCREEN_ROTATE 2
#define RG_SCREEN_VISIBLE_AREA {0, 0, 0, 0}
#define RG_SCREEN_SAFE_AREA {0, 0, 0, 0}
#define RG_SCREEN_INIT()
ILI9341_CMD(0xCF, 0x00, 0xc3, 0x30);
ILI9341_CMD(0xED, 0x64, 0x03, 0x12, 0x81);
ILI9341_CMD(0xE8, 0x85, 0x00, 0x78);
ILI9341_CMD(0xCB, 0x39, 0x2c, 0x00, 0x34, 0x02);
ILI9341_CMD(0xF7, 0x20);
ILI9341_CMD(0xEA, 0x00, 0x00);
ILI9341_CMD(0xC0, 0x1B); /* Power control //VRH[5:0] /
ILI9341_CMD(0xC1, 0x12); / Power control //SAP[2:0];BT[3:0] /
ILI9341_CMD(0xC5, 0x32, 0x3C); / VCM control /
ILI9341_CMD(0xC7, 0x91); / VCM control2 /
ILI9341_CMD(0x36, 0xA8); / Memory Access Control (MX|MV|BGR) /
ILI9341_CMD(0xB1, 0x00, 0x10); / Frame Rate Control (1B=70, 1F=61, 10=119) /
ILI9341_CMD(0xB6, 0x0A, 0xA2); / Display Function Control /
ILI9341_CMD(0xF6, 0x01, 0x30);
ILI9341_CMD(0xF2, 0x00); / 3Gamma Function Disable /
ILI9341_CMD(0x26, 0x01); / Gamma curve selected */
ILI9341_CMD(0xE0, 0x0F, 0x31, 0x2B, 0x0C, 0x0E, 0x08, 0x4E, 0xF1, 0x37, 0x07, 0x10, 0x03, 0x0E, 0x09, 0x00);
ILI9341_CMD(0xE1, 0x00, 0x0E, 0x14, 0x03, 0x11, 0x07, 0x31, 0xC1, 0x48, 0x08, 0x0F, 0x0C, 0x31, 0x36, 0x0F);
// Input
#define RG_RECOVERY_BTN RG_KEY_MENU
#define RG_GAMEPAD_ADC_MAP { }
#define RG_GAMEPAD_GPIO_MAP {
{RG_KEY_UP, .num = GPIO_NUM_7, .pullup = 1, .level = 0},
{RG_KEY_RIGHT, .num = GPIO_NUM_6, .pullup = 1, .level = 0},
{RG_KEY_DOWN, .num = GPIO_NUM_46, .pullup = 1, .level = 0},
{RG_KEY_LEFT, .num = GPIO_NUM_45, .pullup = 1, .level = 0},
{RG_KEY_SELECT, .num = GPIO_NUM_16, .pullup = 1, .level = 0},
{RG_KEY_START, .num = GPIO_NUM_17, .pullup = 1, .level = 0},
{RG_KEY_A, .num = GPIO_NUM_15, .pullup = 1, .level = 0},
{RG_KEY_B, .num = GPIO_NUM_5, .pullup = 1, .level = 0},
{RG_KEY_X, .num = GPIO_NUM_4, .pullup = 1, .level = 0},
{RG_KEY_Y, .num = GPIO_NUM_21, .pullup = 1, .level = 0},
{RG_KEY_L, .num = GPIO_NUM_18, .pullup = 1, .level = 0},
{RG_KEY_R, .num = GPIO_NUM_8, .pullup = 1, .level = 0},
}
#define RG_GAMEPAD_VIRT_MAP {
{RG_KEY_MENU, (RG_KEY_START | RG_KEY_SELECT)},
{RG_KEY_OPTION, (RG_KEY_SELECT | RG_KEY_A)},
}
// Battery
#define RG_BATTERY_DRIVER 1
#define RG_BATTERY_ADC_UNIT ADC_UNIT_1
#define RG_BATTERY_ADC_CHANNEL ADC_CHANNEL_3
#define RG_BATTERY_CALC_PERCENT(raw) (((raw) * 2.f - 3500.f) / (4200.f - 3500.f) * 100.f)
#define RG_BATTERY_CALC_VOLTAGE(raw) ((raw) * 2.f * 0.001f)
// Status LED
#define RG_GPIO_LED GPIO_NUM_38
// SPI Display
#define RG_GPIO_LCD_MISO GPIO_NUM_NC
#define RG_GPIO_LCD_MOSI GPIO_NUM_12
#define RG_GPIO_LCD_CLK GPIO_NUM_48
#define RG_GPIO_LCD_CS GPIO_NUM_14
#define RG_GPIO_LCD_DC GPIO_NUM_47
#define RG_GPIO_LCD_BCKL GPIO_NUM_39
#define RG_GPIO_LCD_RST GPIO_NUM_3
#define RG_GPIO_SDSPI_MISO GPIO_NUM_1
#define RG_GPIO_SDSPI_MOSI GPIO_NUM_2
#define RG_GPIO_SDSPI_CLK GPIO_NUM_48
#define RG_GPIO_SDSPI_CS GPIO_NUM_41
// External I2S DAC (Disabled)
// #define RG_GPIO_SND_I2S_BCK 41
// #define RG_GPIO_SND_I2S_WS 42
// #define RG_GPIO_SND_I2S_DATA 40
`
My module(photo)

rg_storage.c
`
#include "rg_system.h"
#include <sys/types.h>
#include <sys/stat.h>
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#if defined(RG_STORAGE_SDSPI_HOST)
#include <driver/sdspi_host.h>
#define SDCARD_DO_TRANSACTION sdspi_host_do_transaction
#elif defined(RG_STORAGE_SDMMC_HOST)
#include <driver/sdmmc_host.h>
#define SDCARD_DO_TRANSACTION sdmmc_host_do_transaction
#endif
#ifdef ESP_PLATFORM
#include <esp_vfs_fat.h>
#endif
#if defined(_WIN32) || defined(_WIN64)
#include <io.h>
#include <windows.h>
#define access _access
#define mkdir(A, B) mkdir(A)
#if defined(MINGW32)
#include <dirent.h>
#endif
#else
#include <dirent.h>
#include <unistd.h>
#endif
static bool disk_mounted = false;
#if defined(RG_STORAGE_SDSPI_HOST) || defined(RG_STORAGE_SDMMC_HOST)
static sdmmc_card_t *card_handle = NULL;
#endif
#if defined(RG_STORAGE_FLASH_PARTITION)
static wl_handle_t wl_handle = WL_INVALID_HANDLE;
#endif
#define CHECK_PATH(path)
if (!(path && path[0]))
{
RG_LOGE("No path given");
return false;
}
#if defined(RG_STORAGE_SDSPI_HOST) || defined(RG_STORAGE_SDMMC_HOST)
static esp_err_t sdcard_do_transaction(int slot, sdmmc_command_t *cmdinfo)
{
// rg_system_set_indicator(RG_INDICATOR_ACTIVITY_DISK, 1);
esp_err_t ret = SDCARD_DO_TRANSACTION(slot, cmdinfo);
if (ret == ESP_ERR_NO_MEM)
{
// free some memory and try again?
}
// rg_system_set_indicator(RG_INDICATOR_ACTIVITY_DISK, 0);
return ret;
}
#endif
void rg_storage_init(void)
{
RG_ASSERT(!disk_mounted, "Storage already initialized!");
int error_code = -1;
#if defined(RG_STORAGE_SDSPI_HOST)
RG_LOGI("Looking for SD Card using SDSPI...");
#ifdef RG_GPIO_SDSPI_MISO
gpio_set_pull_mode(RG_GPIO_SDSPI_MISO, GPIO_PULLUP_ONLY);
#endif
spi_bus_config_t bus_cfg = {
.mosi_io_num = RG_GPIO_SDSPI_MOSI,
.miso_io_num = RG_GPIO_SDSPI_MISO,
.sclk_io_num = RG_GPIO_SDSPI_CLK,
.quadwp_io_num = -1,
.quadhd_io_num = -1,
};
esp_err_t err = spi_bus_initialize(RG_STORAGE_SDSPI_HOST, &bus_cfg, SPI_DMA_CH_AUTO);
if (err != ESP_OK) // check but do not abort, let esp_vfs_fat_sdspi_mount decide
RG_LOGW("SPI bus init failed (0x%x)", err);
sdmmc_host_t host_config = SDSPI_HOST_DEFAULT();
host_config.slot = RG_STORAGE_SDSPI_HOST;
host_config.max_freq_khz = RG_STORAGE_SDSPI_SPEED;
host_config.do_transaction = &sdcard_do_transaction;
sdspi_device_config_t slot_config = SDSPI_DEVICE_CONFIG_DEFAULT();
slot_config.host_id = RG_STORAGE_SDSPI_HOST;
slot_config.gpio_cs = RG_GPIO_SDSPI_CS;
// If we're using esp-idf >= 5.0 and the SPI bus is not shared, we must keep the SD card selected
// to work around slow accesses. (https://github.com/espressif/esp-idf/issues/10493)
#ifdef RG_STORAGE_SDSPI_HOLD_CS
gpio_set_direction(slot_config.gpio_cs, GPIO_MODE_OUTPUT);
gpio_set_level(slot_config.gpio_cs, 0);
slot_config.gpio_cs = GPIO_NUM_NC;
#endif
esp_vfs_fat_mount_config_t mount_config = {
.format_if_mount_failed = false,
.max_files = 4,
.allocation_unit_size = 0,
};
err = esp_vfs_fat_sdspi_mount(RG_STORAGE_ROOT, &host_config, &slot_config, &mount_config, &card_handle);
if (err == ESP_ERR_TIMEOUT || err == ESP_ERR_INVALID_RESPONSE || err == ESP_ERR_INVALID_CRC)
{
RG_LOGW("SD Card mounting failed (0x%x), retrying at lower speed...\n", err);
host_config.max_freq_khz = SDMMC_FREQ_PROBING;
err = esp_vfs_fat_sdspi_mount(RG_STORAGE_ROOT, &host_config, &slot_config, &mount_config, &card_handle);
}
error_code = (int)err;
#elif defined(RG_STORAGE_SDMMC_HOST)
RG_LOGI("Looking for SD Card using SDMMC...");
sdmmc_host_t host_config = SDMMC_HOST_DEFAULT();
host_config.flags = SDMMC_HOST_FLAG_1BIT;
host_config.slot = RG_STORAGE_SDMMC_HOST;
host_config.max_freq_khz = RG_STORAGE_SDMMC_SPEED;
host_config.do_transaction = &sdcard_do_transaction;
sdmmc_slot_config_t slot_config = SDMMC_SLOT_CONFIG_DEFAULT();
slot_config.width = 1;
#if SOC_SDMMC_USE_GPIO_MATRIX
slot_config.clk = RG_GPIO_SDSPI_CLK;
slot_config.cmd = RG_GPIO_SDSPI_CMD;
slot_config.d0 = RG_GPIO_SDSPI_D0;
// d1 and d3 normally not used in width=1 but sdmmc_host_init_slot saves them, so just in case
slot_config.d1 = slot_config.d3 = -1;
#endif
esp_vfs_fat_mount_config_t mount_config = {
.format_if_mount_failed = false,
.max_files = 4,
.allocation_unit_size = 0,
};
esp_err_t err = esp_vfs_fat_sdmmc_mount(RG_STORAGE_ROOT, &host_config, &slot_config, &mount_config, &card_handle);
if (err == ESP_ERR_TIMEOUT || err == ESP_ERR_INVALID_RESPONSE || err == ESP_ERR_INVALID_CRC)
{
RG_LOGW("SD Card mounting failed (0x%x), retrying at lower speed...\n", err);
host_config.max_freq_khz = SDMMC_FREQ_PROBING;
err = esp_vfs_fat_sdmmc_mount(RG_STORAGE_ROOT, &host_config, &slot_config, &mount_config, &card_handle);
}
error_code = (int)err;
#elif defined(RG_STORAGE_USBOTG_HOST)
#warning "USB OTG isn't available on your SOC"
RG_LOGI("Looking for USB mass storage...");
error_code = -1;
#elif !defined(RG_STORAGE_FLASH_PARTITION)
RG_LOGI("Using host (stdlib) for storage.");
// Maybe we should just check if RG_STORAGE_ROOT exists?
error_code = 0;
#endif
#if defined(RG_STORAGE_FLASH_PARTITION)
if (error_code) // only if no previous storage was successfully mounted already
{
RG_LOGI("Looking for an internal flash partition labelled '%s' to mount for storage...", RG_STORAGE_FLASH_PARTITION);
esp_vfs_fat_mount_config_t mount_config = {
.format_if_mount_failed = true, // if mount failed, it's probably because it's a clean install so the partition hasn't been formatted yet
.max_files = 4, // must be initialized, otherwise it will be 0, which doesn't make sense, and will trigger an ESP_ERR_NO_MEM error
};
esp_err_t err = esp_vfs_fat_spiflash_mount(RG_STORAGE_ROOT, RG_STORAGE_FLASH_PARTITION, &mount_config, &wl_handle);
error_code = (int)err;
}
#endif
disk_mounted = !error_code;
if (disk_mounted)
RG_LOGI("Storage mounted at %s.", RG_STORAGE_ROOT);
else
RG_LOGE("Storage mounting failed! err=0x%x", error_code);
}
void rg_storage_deinit(void)
{
if (!disk_mounted)
return;
rg_storage_commit();
int error_code = 0;
#if defined(RG_STORAGE_SDSPI_HOST) || defined(RG_STORAGE_SDMMC_HOST)
if (card_handle != NULL)
{
esp_err_t err = esp_vfs_fat_sdcard_unmount(RG_STORAGE_ROOT, card_handle);
card_handle = NULL; // NULL it regardless of success, nothing we can do on errors...
error_code = (int)err;
}
#endif
#if defined(RG_STORAGE_FLASH_PARTITION)
if (wl_handle != WL_INVALID_HANDLE)
{
esp_err_t err = esp_vfs_fat_spiflash_unmount(RG_STORAGE_ROOT, wl_handle);
wl_handle = WL_INVALID_HANDLE;
error_code = (int)err;
}
#endif
if (error_code)
RG_LOGE("Storage unmounting failed. err=0x%x", error_code);
else
RG_LOGI("Storage unmounted.");
disk_mounted = false;
}
bool rg_storage_ready(void)
{
return disk_mounted;
}
void rg_storage_commit(void)
{
if (!disk_mounted)
return;
// flush buffers();
}
bool rg_storage_mkdir(const char *dir)
{
CHECK_PATH(dir);
if (mkdir(dir, 0777) == 0)
return true;
// FIXME: Might want to stat to see if it's a dir
if (errno == EEXIST)
return true;
// Possibly missing some parents, try creating them
char *temp = strdup(dir);
for (char *p = temp + strlen(RG_STORAGE_ROOT) + 1; *p; p++)
{
if (*p == '/')
{
*p = 0;
if (strlen(temp) > 0)
{
mkdir(temp, 0777);
}
*p = '/';
while (*(p + 1) == '/')
p++;
}
}
free(temp);
// Finally try again
if (mkdir(dir, 0777) == 0)
return true;
return false;
}
static int delete_cb(const rg_scandir_t *file, void *arg)
{
rg_storage_delete(file->path);
return RG_SCANDIR_CONTINUE;
}
bool rg_storage_delete(const char *path)
{
CHECK_PATH(path);
// Try the fast way first
if (remove(path) == 0 || rmdir(path) == 0)
return true;
// If that fails, it's likely a non-empty directory and we go recursive
// (errno could confirm but it has proven unreliable across platforms...)
if (rg_storage_scandir(path, delete_cb, NULL, 0))
return rmdir(path) == 0;
return false;
}
rg_stat_t rg_storage_stat(const char *path)
{
rg_stat_t ret = {0};
struct stat statbuf;
if (path && stat(path, &statbuf) == 0)
{
ret.basename = rg_basename(path);
ret.extension = rg_extension(path);
ret.size = statbuf.st_size;
ret.mtime = statbuf.st_mtime;
ret.is_file = S_ISREG(statbuf.st_mode);
ret.is_dir = S_ISDIR(statbuf.st_mode);
ret.exists = true;
}
return ret;
}
bool rg_storage_exists(const char *path)
{
CHECK_PATH(path);
return access(path, F_OK) == 0;
}
bool rg_storage_scandir(const char *path, rg_scandir_cb_t *callback, void *arg, uint32_t flags)
{
CHECK_PATH(path);
uint32_t types = flags & (RG_SCANDIR_FILES | RG_SCANDIR_DIRS);
size_t path_len = strlen(path) + 1;
struct stat statbuf;
struct dirent *ent;
if (path_len > RG_PATH_MAX - 5)
{
RG_LOGE("Folder path too long '%s'", path);
return false;
}
DIR *dir = opendir(path);
if (!dir)
{
if (errno != ENOENT) // Only log unusual errors. Path not found isn't unusual.
RG_LOGE("Opendir failed (%d): '%s'", errno, path);
return false;
}
// We allocate on heap in case we go recursive through rg_storage_delete
rg_scandir_t *result = calloc(1, sizeof(rg_scandir_t));
if (!result)
{
RG_LOGE("Memory allocation failed: '%s'", path);
closedir(dir);
return false;
}
strcat(strcpy(result->path, path), "/");
result->basename = result->path + path_len;
result->dirname = path;
while ((ent = readdir(dir)))
{
if (ent->d_name[0] == '.' && (!ent->d_name[1] || ent->d_name[1] == '.'))
{
// Skip self and parent
continue;
}
if (path_len + strlen(ent->d_name) >= RG_PATH_MAX)
{
RG_LOGE("File path too long '%s/%s'", path, ent->d_name);
continue;
}
strcpy((char *)result->basename, ent->d_name);
#if defined(DT_REG) && defined(DT_DIR)
result->is_file = ent->d_type == DT_REG;
result->is_dir = ent->d_type == DT_DIR;
#else
result->is_file = 0;
result->is_dir = 0;
// We're forced to stat() if the OS doesn't provide type via dirent
flags |= RG_SCANDIR_STAT;
#endif
if ((flags & RG_SCANDIR_STAT) && stat(result->path, &statbuf) == 0)
{
result->is_file = S_ISREG(statbuf.st_mode);
result->is_dir = S_ISDIR(statbuf.st_mode);
result->size = statbuf.st_size;
result->mtime = statbuf.st_mtime;
}
if ((result->is_dir && types != RG_SCANDIR_FILES) || (result->is_file && types != RG_SCANDIR_DIRS))
{
int ret = (callback)(result, arg);
if (ret == RG_SCANDIR_STOP)
break;
if (ret == RG_SCANDIR_SKIP)
continue;
}
if ((flags & RG_SCANDIR_RECURSIVE) && result->is_dir)
{
rg_storage_scandir(result->path, callback, arg, flags);
}
}
closedir(dir);
free(result);
return true;
}
int64_t rg_storage_get_free_space(const char *path)
{
// Here we should translate the provided VFS path to the matching filesystem driver and drive
// But we don't. Instead we just assume it's drive 0 of the fatfs driver. Yay laziness.
#ifdef ESP_PLATFORM
DWORD nclst;
FATFS *fatfs;
if (f_getfree("0:", &nclst, &fatfs) == FR_OK)
{
return (int64_t)nclst * fatfs->csize * fatfs->ssize;
}
#endif
}
bool rg_storage_read_file(const char *path, void **data_out, size_t *data_len, uint32_t flags)
{
RG_ASSERT_ARG(data_out && data_len);
CHECK_PATH(path);
size_t output_buffer_alloc_size;
size_t output_buffer_size;
void *output_buffer;
size_t file_size;
FILE *fp = fopen(path, "rb");
if (!fp)
{
if (errno != ENOENT) // Only log unusual errors. Path not found isn't unusual.
RG_LOGE("Fopen failed (%d): '%s'", errno, path);
return false;
}
fseek(fp, 0, SEEK_END);
file_size = ftell(fp);
fseek(fp, 0, SEEK_SET);
if (flags & RG_FILE_USER_BUFFER)
{
output_buffer_alloc_size = *data_len;
output_buffer_size = RG_MIN(*data_len, file_size);
output_buffer = *data_out;
}
else
{
size_t blocksize = RG_MAX(0x400, (flags & 0xF) * 0x2000);
output_buffer_alloc_size = (file_size + (blocksize - 1)) & ~(blocksize - 1);
output_buffer_size = file_size;
output_buffer = malloc(output_buffer_alloc_size);
}
if (!output_buffer)
{
RG_LOGE("Memory allocation failed: '%s'", path);
fclose(fp);
return false;
}
if (!fread(output_buffer, output_buffer_size, 1, fp))
{
RG_LOGE("File read failed (%d): '%s'", errno, path);
fclose(fp);
if (!(flags & RG_FILE_USER_BUFFER))
free(output_buffer);
return false;
}
fclose(fp);
// Wipe the extra allocated space, if any
if (output_buffer_alloc_size > output_buffer_size)
{
memset(output_buffer + output_buffer_size, 0, output_buffer_alloc_size - output_buffer_size);
}
*data_out = output_buffer;
*data_len = output_buffer_size;
return true;
}
bool rg_storage_write_file(const char *path, const void *data_ptr, size_t data_len, uint32_t flags)
{
RG_ASSERT_ARG(data_ptr || !data_len);
CHECK_PATH(path);
// TODO: If atomic is true we should write to a temp file and only replace the target on success
FILE *fp = fopen(path, "wb");
if (!fp)
{
RG_LOGE("Fopen failed (%d): '%s'", errno, path);
return false;
}
if (data_len && !fwrite(data_ptr, data_len, 1, fp))
{
RG_LOGE("Fwrite failed (%d): '%s'", errno, path);
fclose(fp);
return false;
}
fclose(fp);
return true;
}
/**
- This is a minimal UNZIP implementation that utilizes only the miniz primitives found in ESP32's ROM.
- I think that we should use miniz' ZIP API instead and bundle miniz with retro-go. But first I need
- to do some testing to determine if the increased executable size is acceptable...
*/
#if RG_ZIP_SUPPORT
#if defined(ESP_PLATFORM) && ESP_IDF_VERSION_MAJOR < 5
#include <rom/miniz.h>
#else
#include <miniz.h>
#endif
#define ZIP_MAGIC 0x04034b50
typedef struct attribute((packed))
{
uint32_t magic;
uint16_t version;
uint16_t flags;
uint16_t compression;
uint16_t modified_time;
uint16_t modified_date;
uint32_t checksum;
uint32_t compressed_size;
uint32_t uncompressed_size;
uint16_t filename_size;
uint16_t extra_field_size;
uint8_t filename[226];
// uint8_t extra_field[];
// uint8_t compressed_data[];
} zip_header_t;
bool rg_storage_unzip_file(const char *zip_path, const char *filter, void **data_out, size_t *data_len, uint32_t flags)
{
RG_ASSERT_ARG(data_out && data_len);
CHECK_PATH(zip_path);
zip_header_t header = {0};
int header_pos = 0;
FILE *fp = fopen(zip_path, "rb");
if (!fp)
{
if (errno != ENOENT) // Only log unusual errors. Path not found isn't unusual.
RG_LOGE("Fopen failed (%d): '%s'", errno, zip_path);
return false;
}
// Very inefficient, we should read a block at a time and search it for a header. But I'm lazy.
// Thankfully the header is usually found on the very first read :)
for (header_pos = 0; !feof(fp) && header_pos < 0x10000; ++header_pos)
{
fseek(fp, header_pos, SEEK_SET);
fread(&header, sizeof(header), 1, fp);
if (header.magic == ZIP_MAGIC)
break;
}
if (header.magic != ZIP_MAGIC)
{
RG_LOGE("No valid header found: '%s'", zip_path);
fclose(fp);
return false;
}
// Zero terminate or truncate filename just in case
header.filename[RG_MIN(header.filename_size, 225)] = 0;
RG_LOGI("Found file at %d, name: '%s', size: %d", header_pos, header.filename, (int)header.uncompressed_size);
size_t stream_offset = header_pos + 30 + header.filename_size + header.extra_field_size;
size_t stream_remaining = header.compressed_size;
size_t output_buffer_align = RG_MAX(0x1000, (flags & 0xF) * 0x2000);
size_t output_buffer_size;
size_t output_buffer_pos = 0;
uint8_t *output_buffer = NULL;
if (flags & RG_FILE_USER_BUFFER)
{
output_buffer_size = RG_MIN(*data_len, header.uncompressed_size);
output_buffer = *data_out;
}
else
{
output_buffer_size = header.uncompressed_size;
output_buffer = malloc((output_buffer_size + (output_buffer_align - 1)) & ~(output_buffer_align - 1));
}
size_t read_buffer_size = 0x8000;
uint8_t *read_buffer = malloc(read_buffer_size);
tinfl_decompressor *decomp = malloc(sizeof(tinfl_decompressor));
if (!read_buffer || !output_buffer || !decomp)
{
RG_LOGE("Memory allocation failed: '%s'", zip_path);
goto _fail;
}
tinfl_status status;
tinfl_init(decomp);
do
{
size_t input_size = RG_MIN(read_buffer_size, stream_remaining);
size_t output_size = output_buffer_size - output_buffer_pos;
if (fseek(fp, stream_offset, SEEK_SET) != 0 || fread(read_buffer, input_size, 1, fp) != 1)
{
RG_LOGE("Read error (%d): '%s'", errno, zip_path);
goto _fail;
}
stream_offset += input_size;
stream_remaining -= input_size;
status = tinfl_decompress(
decomp, read_buffer, &input_size, output_buffer, output_buffer + output_buffer_pos, &output_size,
TINFL_FLAG_USING_NON_WRAPPING_OUTPUT_BUF | (stream_remaining ? TINFL_FLAG_HAS_MORE_INPUT : 0));
output_buffer_pos += output_size;
} while (status == TINFL_STATUS_NEEDS_MORE_INPUT);
// With user-provided buffer we might not reach TINFL_STATUS_DONE, but it doesn't mean we've failed
if (status < TINFL_STATUS_DONE || output_buffer_pos != output_buffer_size) // (status != TINFL_STATUS_DONE)
{
RG_LOGE("Decompression failed (%d): %s", (int)status, zip_path);
goto _fail;
}
free(read_buffer);
free(decomp);
fclose(fp);
*data_out = output_buffer;
*data_len = output_buffer_size;
return true;
_fail:
if (!(flags & RG_FILE_USER_BUFFER))
free(output_buffer);
free(read_buffer);
free(decomp);
fclose(fp);
return false;
}
#else
bool rg_storage_unzip_file(const char *zip_path, const char *filter, void **data_out, size_t *data_len, uint32_t flags)
{
RG_LOGE("ZIP support hasn't been enabled!");
return false;
}
#endif
`
Hi everyone. I'm having a problem with my Retro-Go, specifically Card Mount Failed. I formatted it, converted it to FAT32, and swapped it with another Micro SD card. I don't know what to do. Can anyone with experience in this matter offer advice? I'm using an ESP32-S3-N16R8. The configuration file is attached, as is a photo of the module.
P.S. My module Vcc pin on 5V.
config.h
`
// Target definition
#define RG_TARGET_NAME "ESP32-S3-N16-R8"
// Storage
#define RG_STORAGE_ROOT "/sd"
#define RG_STORAGE_SDSPI_HOST SPI2_HOST
#define RG_STORAGE_SDSPI_SPEED 4000 // 4MHz
// Audio
#define RG_AUDIO_USE_INT_DAC 0 // 0 = Disable, 1 = GPIO25, 2 = GPIO26, 3 = Both
#define RG_AUDIO_USE_EXT_DAC 0 // 0 = Disable, 1 = Enable
#define RG_AUDIO_USE_BUZZER_PIN 40 // PWM Buzzer on GPIO 40
// Video
#define RG_SCREEN_DRIVER 0 // 0 = ILI9341/ST7789
#define RG_SCREEN_HOST SPI2_HOST
#define RG_SCREEN_SPEED SPI_MASTER_FREQ_40M
#define RG_SCREEN_BACKLIGHT 1
#define RG_SCREEN_WIDTH 320
#define RG_SCREEN_HEIGHT 240
#define RG_SCREEN_ROTATE 2
#define RG_SCREEN_VISIBLE_AREA {0, 0, 0, 0}
#define RG_SCREEN_SAFE_AREA {0, 0, 0, 0}
#define RG_SCREEN_INIT()
ILI9341_CMD(0xCF, 0x00, 0xc3, 0x30);
ILI9341_CMD(0xED, 0x64, 0x03, 0x12, 0x81);
ILI9341_CMD(0xE8, 0x85, 0x00, 0x78);
ILI9341_CMD(0xCB, 0x39, 0x2c, 0x00, 0x34, 0x02);
ILI9341_CMD(0xF7, 0x20);
ILI9341_CMD(0xEA, 0x00, 0x00);
ILI9341_CMD(0xC0, 0x1B); /* Power control //VRH[5:0] /
ILI9341_CMD(0xC1, 0x12); / Power control //SAP[2:0];BT[3:0] /
ILI9341_CMD(0xC5, 0x32, 0x3C); / VCM control /
ILI9341_CMD(0xC7, 0x91); / VCM control2 /
ILI9341_CMD(0x36, 0xA8); / Memory Access Control (MX|MV|BGR) /
ILI9341_CMD(0xB1, 0x00, 0x10); / Frame Rate Control (1B=70, 1F=61, 10=119) /
ILI9341_CMD(0xB6, 0x0A, 0xA2); / Display Function Control /
ILI9341_CMD(0xF6, 0x01, 0x30);
ILI9341_CMD(0xF2, 0x00); / 3Gamma Function Disable /
ILI9341_CMD(0x26, 0x01); / Gamma curve selected */
ILI9341_CMD(0xE0, 0x0F, 0x31, 0x2B, 0x0C, 0x0E, 0x08, 0x4E, 0xF1, 0x37, 0x07, 0x10, 0x03, 0x0E, 0x09, 0x00);
ILI9341_CMD(0xE1, 0x00, 0x0E, 0x14, 0x03, 0x11, 0x07, 0x31, 0xC1, 0x48, 0x08, 0x0F, 0x0C, 0x31, 0x36, 0x0F);
// Input
#define RG_RECOVERY_BTN RG_KEY_MENU
#define RG_GAMEPAD_ADC_MAP { }
#define RG_GAMEPAD_GPIO_MAP {
{RG_KEY_UP, .num = GPIO_NUM_7, .pullup = 1, .level = 0},
{RG_KEY_RIGHT, .num = GPIO_NUM_6, .pullup = 1, .level = 0},
{RG_KEY_DOWN, .num = GPIO_NUM_46, .pullup = 1, .level = 0},
{RG_KEY_LEFT, .num = GPIO_NUM_45, .pullup = 1, .level = 0},
{RG_KEY_SELECT, .num = GPIO_NUM_16, .pullup = 1, .level = 0},
{RG_KEY_START, .num = GPIO_NUM_17, .pullup = 1, .level = 0},
{RG_KEY_A, .num = GPIO_NUM_15, .pullup = 1, .level = 0},
{RG_KEY_B, .num = GPIO_NUM_5, .pullup = 1, .level = 0},
{RG_KEY_X, .num = GPIO_NUM_4, .pullup = 1, .level = 0},
{RG_KEY_Y, .num = GPIO_NUM_21, .pullup = 1, .level = 0},
{RG_KEY_L, .num = GPIO_NUM_18, .pullup = 1, .level = 0},
{RG_KEY_R, .num = GPIO_NUM_8, .pullup = 1, .level = 0},
}
#define RG_GAMEPAD_VIRT_MAP {
{RG_KEY_MENU, (RG_KEY_START | RG_KEY_SELECT)},
{RG_KEY_OPTION, (RG_KEY_SELECT | RG_KEY_A)},
}
// Battery
#define RG_BATTERY_DRIVER 1
#define RG_BATTERY_ADC_UNIT ADC_UNIT_1
#define RG_BATTERY_ADC_CHANNEL ADC_CHANNEL_3
#define RG_BATTERY_CALC_PERCENT(raw) (((raw) * 2.f - 3500.f) / (4200.f - 3500.f) * 100.f)
#define RG_BATTERY_CALC_VOLTAGE(raw) ((raw) * 2.f * 0.001f)
// Status LED
#define RG_GPIO_LED GPIO_NUM_38
// SPI Display
#define RG_GPIO_LCD_MISO GPIO_NUM_NC
#define RG_GPIO_LCD_MOSI GPIO_NUM_12
#define RG_GPIO_LCD_CLK GPIO_NUM_48
#define RG_GPIO_LCD_CS GPIO_NUM_14
#define RG_GPIO_LCD_DC GPIO_NUM_47
#define RG_GPIO_LCD_BCKL GPIO_NUM_39
#define RG_GPIO_LCD_RST GPIO_NUM_3
#define RG_GPIO_SDSPI_MISO GPIO_NUM_1
#define RG_GPIO_SDSPI_MOSI GPIO_NUM_2
#define RG_GPIO_SDSPI_CLK GPIO_NUM_48
#define RG_GPIO_SDSPI_CS GPIO_NUM_41
// External I2S DAC (Disabled)
// #define RG_GPIO_SND_I2S_BCK 41
// #define RG_GPIO_SND_I2S_WS 42
// #define RG_GPIO_SND_I2S_DATA 40
`
My module(photo)
rg_storage.c
`
#include "rg_system.h"
#include <sys/types.h>
#include <sys/stat.h>
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#if defined(RG_STORAGE_SDSPI_HOST)
#include <driver/sdspi_host.h>
#define SDCARD_DO_TRANSACTION sdspi_host_do_transaction
#elif defined(RG_STORAGE_SDMMC_HOST)
#include <driver/sdmmc_host.h>
#define SDCARD_DO_TRANSACTION sdmmc_host_do_transaction
#endif
#ifdef ESP_PLATFORM
#include <esp_vfs_fat.h>
#endif
#if defined(_WIN32) || defined(_WIN64)
#include <io.h>
#include <windows.h>
#define access _access
#define mkdir(A, B) mkdir(A)
#if defined(MINGW32)
#include <dirent.h>
#endif
#else
#include <dirent.h>
#include <unistd.h>
#endif
static bool disk_mounted = false;
#if defined(RG_STORAGE_SDSPI_HOST) || defined(RG_STORAGE_SDMMC_HOST)
static sdmmc_card_t *card_handle = NULL;
#endif
#if defined(RG_STORAGE_FLASH_PARTITION)
static wl_handle_t wl_handle = WL_INVALID_HANDLE;
#endif
#define CHECK_PATH(path)
if (!(path && path[0]))
{
RG_LOGE("No path given");
return false;
}
#if defined(RG_STORAGE_SDSPI_HOST) || defined(RG_STORAGE_SDMMC_HOST)
static esp_err_t sdcard_do_transaction(int slot, sdmmc_command_t *cmdinfo)
{
// rg_system_set_indicator(RG_INDICATOR_ACTIVITY_DISK, 1);
}
#endif
void rg_storage_init(void)
{
RG_ASSERT(!disk_mounted, "Storage already initialized!");
int error_code = -1;
#if defined(RG_STORAGE_SDSPI_HOST)
#elif defined(RG_STORAGE_SDMMC_HOST)
#if SOC_SDMMC_USE_GPIO_MATRIX
slot_config.clk = RG_GPIO_SDSPI_CLK;
slot_config.cmd = RG_GPIO_SDSPI_CMD;
slot_config.d0 = RG_GPIO_SDSPI_D0;
// d1 and d3 normally not used in width=1 but sdmmc_host_init_slot saves them, so just in case
slot_config.d1 = slot_config.d3 = -1;
#endif
#elif defined(RG_STORAGE_USBOTG_HOST)
#elif !defined(RG_STORAGE_FLASH_PARTITION)
#endif
#if defined(RG_STORAGE_FLASH_PARTITION)
#endif
}
void rg_storage_deinit(void)
{
if (!disk_mounted)
return;
#if defined(RG_STORAGE_SDSPI_HOST) || defined(RG_STORAGE_SDMMC_HOST)
if (card_handle != NULL)
{
esp_err_t err = esp_vfs_fat_sdcard_unmount(RG_STORAGE_ROOT, card_handle);
card_handle = NULL; // NULL it regardless of success, nothing we can do on errors...
error_code = (int)err;
}
#endif
#if defined(RG_STORAGE_FLASH_PARTITION)
if (wl_handle != WL_INVALID_HANDLE)
{
esp_err_t err = esp_vfs_fat_spiflash_unmount(RG_STORAGE_ROOT, wl_handle);
wl_handle = WL_INVALID_HANDLE;
error_code = (int)err;
}
#endif
}
bool rg_storage_ready(void)
{
return disk_mounted;
}
void rg_storage_commit(void)
{
if (!disk_mounted)
return;
// flush buffers();
}
bool rg_storage_mkdir(const char *dir)
{
CHECK_PATH(dir);
}
static int delete_cb(const rg_scandir_t *file, void *arg)
{
rg_storage_delete(file->path);
return RG_SCANDIR_CONTINUE;
}
bool rg_storage_delete(const char *path)
{
CHECK_PATH(path);
}
rg_stat_t rg_storage_stat(const char *path)
{
rg_stat_t ret = {0};
struct stat statbuf;
if (path && stat(path, &statbuf) == 0)
{
ret.basename = rg_basename(path);
ret.extension = rg_extension(path);
ret.size = statbuf.st_size;
ret.mtime = statbuf.st_mtime;
ret.is_file = S_ISREG(statbuf.st_mode);
ret.is_dir = S_ISDIR(statbuf.st_mode);
ret.exists = true;
}
return ret;
}
bool rg_storage_exists(const char *path)
{
CHECK_PATH(path);
return access(path, F_OK) == 0;
}
bool rg_storage_scandir(const char *path, rg_scandir_cb_t *callback, void *arg, uint32_t flags)
{
CHECK_PATH(path);
uint32_t types = flags & (RG_SCANDIR_FILES | RG_SCANDIR_DIRS);
size_t path_len = strlen(path) + 1;
struct stat statbuf;
struct dirent *ent;
}
int64_t rg_storage_get_free_space(const char *path)
{
// Here we should translate the provided VFS path to the matching filesystem driver and drive
// But we don't. Instead we just assume it's drive 0 of the fatfs driver. Yay laziness.
#ifdef ESP_PLATFORM
DWORD nclst;
FATFS *fatfs;
if (f_getfree("0:", &nclst, &fatfs) == FR_OK)
{
return (int64_t)nclst * fatfs->csize * fatfs->ssize;
}
#endif
}
bool rg_storage_read_file(const char *path, void **data_out, size_t *data_len, uint32_t flags)
{
RG_ASSERT_ARG(data_out && data_len);
CHECK_PATH(path);
}
bool rg_storage_write_file(const char *path, const void *data_ptr, size_t data_len, uint32_t flags)
{
RG_ASSERT_ARG(data_ptr || !data_len);
CHECK_PATH(path);
}
/**
*/
#if RG_ZIP_SUPPORT
#if defined(ESP_PLATFORM) && ESP_IDF_VERSION_MAJOR < 5
#include <rom/miniz.h>
#else
#include <miniz.h>
#endif
#define ZIP_MAGIC 0x04034b50
typedef struct attribute((packed))
{
uint32_t magic;
uint16_t version;
uint16_t flags;
uint16_t compression;
uint16_t modified_time;
uint16_t modified_date;
uint32_t checksum;
uint32_t compressed_size;
uint32_t uncompressed_size;
uint16_t filename_size;
uint16_t extra_field_size;
uint8_t filename[226];
// uint8_t extra_field[];
// uint8_t compressed_data[];
} zip_header_t;
bool rg_storage_unzip_file(const char *zip_path, const char *filter, void **data_out, size_t *data_len, uint32_t flags)
{
RG_ASSERT_ARG(data_out && data_len);
CHECK_PATH(zip_path);
_fail:
if (!(flags & RG_FILE_USER_BUFFER))
free(output_buffer);
free(read_buffer);
free(decomp);
fclose(fp);
return false;
}
#else
bool rg_storage_unzip_file(const char *zip_path, const char *filter, void **data_out, size_t *data_len, uint32_t flags)
{
RG_LOGE("ZIP support hasn't been enabled!");
return false;
}
#endif
`