Tech6 min read

M5Stack CoreS3 microSD not recognized: card compatibility tested, SPI clock limits, and a soft-reset gotcha

IkesanContents

I put a microSD card into a brand-new M5Stack CoreS3 and the stock firmware immediately said “SD Card initialization failed!”. The card reads fine on a PC. It took a while to figure out this was a two-layer problem: card compatibility first, SPI clock second. Here is the full isolation log.

The setup:

ItemDetails
DeviceM5Stack CoreS3 Development Kit (K128)
ChipESP32-S3 (QFN56) rev v0.2, 16MB flash
microSDLAZOS 16GB (FAT32) / LAZOS 32GB
HostWindows 11 + arduino-cli 1.5.1
Core/Libsesp32:esp32 3.3.10 / M5Unified 0.2.17

Unboxing and the first wall

I picked up the CoreS3 Development Kit at Marutsu in Akihabara.

Opening the box

The Development Kit ships with a DIN BASE bottom: DC 9-24V input, a power switch, and a speaker live on the base. The microSD slot is a push-push type on the left side of the core unit: push until it clicks in, push again to eject.

microSD slot on the side, DC input on the DIN BASE

Power it over USB and the factory firmware (UserDemo) draws a map of the hardware: ESP32-S3, 16M flash, 8M PSRAM, a 2-inch IPS LCD (ILI9342D, 320x240), and the microSD slot.

UserDemo hardware map screen

UserDemo has an SD-CARD page that lists the card contents. With no card it says “Please insert SD card…”.

SD-CARD page with no card inserted

Insert my LAZOS 32GB and this happens:

SD Card initialization failed!

“SD Card initialization failed! Please try reinsert SD card…”. Reinserting does nothing. At this point not a single line of my code was involved — a stock device with a card in it — so the cause had to be the card or the hardware.

The CoreS3 spec sheet says microSD up to 16GB, so I later bought a LAZOS 16GB (FAT32, reads and writes fine on a PC) specifically to stay in spec. Same screen.

Building a toolchain to isolate it

I wanted serial logs, so I set up arduino-cli:

winget install --id ArduinoSA.CLI
arduino-cli core update-index
arduino-cli core install esp32:esp32
arduino-cli lib install M5Unified

The CoreS3 board definition lives in Espressif’s official esp32 core. The FQBN is esp32:esp32:m5stack_cores3.

arduino-cli compile --fqbn esp32:esp32:m5stack_cores3 .\sdtest
arduino-cli upload -p COM5 --fqbn esp32:esp32:m5stack_cores3 .\sdtest

The CoreS3 talks to the microSD over SPI, sharing the bus with the LCD. Only CS is dedicated:

SignalGPIO
CS4
SCK36
MISO35
MOSI37

A minimal mount test:

#include <M5Unified.h>
#include <SD.h>

void setup() {
  auto cfg = M5.config();
  M5.begin(cfg);
  Serial.begin(115200);
  delay(2000);

  SPI.begin(36, 35, 37, 4);
  if (!SD.begin(4, SPI, 25000000)) {
    Serial.println("SD.begin() FAILED");
    return;
  }
  Serial.printf("Size: %u MB\n", (uint32_t)(SD.cardSize() / 1024 / 1024));
}

void loop() { M5.update(); delay(100); }

Same result as the factory firmware: SD.begin() FAILED.

My own sketch also fails

Suspecting the clock, I stepped down 25MHz → 15MHz → 4MHz → 400kHz. The LAZOS 16GB failed at every speed. I then switched to a loop that retries SD.begin() every 2 seconds and reseated the card while it ran. 111 attempts, zero mounts.

Retry loop at attempt 111, never mounts

  • 400kHz still fails
  • Reseating and reinserting changes nothing
  • The same card reads fine in a PC card reader

At this point the card itself became the prime suspect.

The LAZOS 32GB mounts

I swapped back to the LAZOS 32GB — the card that failed on the factory demo on unboxing day — and the retry loop picked it up immediately. A card the factory firmware rejected mounts fine from my own sketch. And the 16GB card I bought specifically to stay in spec is the one that never worked anywhere.

The mount passed, but adding a file write test produced a new failure:

Mount OK, write FAILED

SD.open("/hello.txt", FILE_WRITE) returns no file handle. Mounting and size queries (30000MB / 29985MB total) work at 25MHz, so reads pass but writes do not.

So the write test also steps down the clock:

// If 25MHz fails to open for write, remount at 10MHz then 4MHz
const uint32_t freqs[] = {25000000, 10000000, 4000000};
for (uint32_t f : freqs) {
  if (f != 25000000) {
    SD.end();
    delay(200);
    if (!SD.begin(4, SPI, f)) continue;
  }
  File w = SD.open("/hello.txt", FILE_WRITE);
  if (!w) continue;
  w.println("hello from CoreS3");
  w.close();
  // read back and verify...
}

Writes succeed at 10MHz.

Write OK at 10MHz, hello.txt visible

open(W) fail @25MHz
write OK @10MHz: hello from CoreS3
--- root files ---
[D] System Volume Information
    hello.txt

The soft-reset gotcha

There was one more surprise along the way. After a successful mount, any soft reset — a sketch upload or a serial monitor disconnect, logged as rst:0x15 (USB_UART_CHIP_RESET) — leaves the next boot unable to mount the same card.

The card apparently keeps the state of the previous session. Software recovery did not work on this card:

  • 8192 dummy clocks with CS deasserted → no effect
  • Clocking out pending read data with CS asserted, then issuing CMD12 (stop transmission) → no effect
  • Unplugging USB for a full power cycle → mounts on attempt 1 at the next boot

Only a power cycle reliably recovers it. During development every sketch upload puts you in this state, so “flash, then replug USB” becomes one motion.

Results

CardPCFactory demoMy sketch (mount)Write
LAZOS 16GB (FAT32)OKFailFail (all clocks, reseating)-
LAZOS 32GB-FailOK25MHz fail / 10MHz OK

Other people have hit the same wall on the CoreS3. This post (Japanese) saw ESP_ERR_INVALID_RESPONSE on init until switching to a Kioxia card.

If a card is not recognized on a CoreS3, try a different brand before touching the format or the code. And if you need writes, keep the SPI clock around 10MHz.