XIAO Round Display Not Detecting SD Card

I recently found the XIAO RP2040 and Round Display, and they seemed perfect for a project I was working on.
However, I’m having issues getting it to detect the SD card.

Just using the example code, such as Hardware test, it’s unable to detect the SD card. I tried using the GIF player example and it also doesn’t detect it.

The card is a functional sandisk SDHC Class 10 formatted to FAT32.

I followed the setup instructions provided by Seeed, but no such luck yet.

Any ideas as to what I’m missing?

Hi there and Welcome,
Have a look at my test run of the gif player, etc. with the round display.
I know it’s the ESP32c3 but check the overall function of

the SD, does it ask you to insert it or just bombs the compile , what errors do you get?
If it fails after the begin then check the versions of the LIBs your using.
GL :slight_smile: PJ

No issues compiling, with the exception I need to include <string>.

I’m using the current latest versions of the libraries.

Hi there,ognalysis
Yea, that’s probably why, Go look at my compiler output in the link the latests don’t work, roll back to the correct ones.
GL :slight_smile: PJ

Gotcha. I didn’t see that part of your original post.

Let me try that.

I don’t see where you specify what version of the libraries to use.

Try the other round display posts it’s in there.
GL :slight_smile: PJ

Unfortunately, I’m still not seeing what specific Library version to rollback to.
I see references to SD_MCC.h, but I’m not using that library, or the esp32 library.

is your SD card chipselect pin set to 2 or D2

D2, as per the setup instructions.

If I don’t #include <string>, I get this:
Compilation error: no matching function for call to 'std::vector<std::__cxx11::basic_string<char> >::push_back(const char*)'

Hi there,
These were the main things:

Using library lvgl at version 8.3.7 in folder: C:\Users\Value Pawn\Documents\Arduino\libraries\lvgl 
Using library Seeed Arduino Round display at version 1.0.0 in folder: C:\Users\Value Pawn\Documents\Arduino\libraries\Seeed_Arduino_Round_display 
Using library SPI at version 1.0 in folder: C:\Users\Value Pawn\AppData\Local\Arduino15\packages\Seeeduino\hardware\nrf52\1.1.1\libraries\SPI 
Using library TFT_eSPI at version 2.5.23 in folder: C:\Users\Value Pawn\Documents\Arduino\libraries\TFT_eSPI 
Using library SD at version 1.2.4 in folder: C:\Users\Value Pawn\AppData\Local\Arduino15\libraries\SD 
Using library I2C BM8563 RTC at version 1.0.4 in folder: C:\Users\Value Pawn\Documents\Arduino\libraries\I2C_BM8563_RTC 
Using library Adafruit TinyUSB Library at version 1.7.0 in folder: C:

some may not apply, The SD SPI, and E_SPI do.
GL :slight_smile: PJ
maybe post that sketch as well.

I just copy/pasted the code you posted, since it’s identical to the example code.

#include <vector>
#include <TFT_eSPI.h>
#include <SPI.h>
#include <SD.h>
#include "AnimatedGIF.h"
#include <string>

AnimatedGIF gif;
TFT_eSPI tft = TFT_eSPI();

// rule: loop GIF at least during 3s, maximum 5 times, and don't loop/animate longer than 30s per GIF
const int maxLoopIterations =     1; // stop after this amount of loops
const int maxLoopsDuration  =  3000; // ms, max cumulated time after the GIF will break loop
const int maxGifDuration    =240000; // ms, max GIF duration

// used to center image based on GIF dimensions
static int xOffset = 0;
static int yOffset = 0;

static int totalFiles = 0; // GIF files count
static int currentFile = 0;
static int lastFile = -1;

char GifComment[256];

static File FSGifFile; // temp gif file holder
static File GifRootFolder; // directory listing
std::vector<std::string> GifFiles; // GIF files path
#define DISPLAY_WIDTH 240

static void MyCustomDelay( unsigned long ms ) {
  delay( ms );
  // log_d("delay %d\n", ms);

static void * GIFOpenFile(const char *fname, int32_t *pSize)
  // log_d("GIFOpenFile( %s )\n", fname );
  FSGifFile = SD.open(fname);
  if (FSGifFile) {
    *pSize = FSGifFile.size();
    return (void *)&FSGifFile;
  return NULL;

static void GIFCloseFile(void *pHandle)
  File *f = static_cast<File *>(pHandle);
  if (f != NULL)

static int32_t GIFReadFile(GIFFILE *pFile, uint8_t *pBuf, int32_t iLen)
  int32_t iBytesRead;
  iBytesRead = iLen;
  File *f = static_cast<File *>(pFile->fHandle);
  // Note: If you read a file all the way to the last byte, seek() stops working
  if ((pFile->iSize - pFile->iPos) < iLen)
      iBytesRead = pFile->iSize - pFile->iPos - 1; // <-- ugly work-around
  if (iBytesRead <= 0)
      return 0;
  iBytesRead = (int32_t)f->read(pBuf, iBytesRead);
  pFile->iPos = f->position();
  return iBytesRead;

static int32_t GIFSeekFile(GIFFILE *pFile, int32_t iPosition)
  int i = micros();
  File *f = static_cast<File *>(pFile->fHandle);
  pFile->iPos = (int32_t)f->position();
  i = micros() - i;
  // log_d("Seek time = %d us\n", i);
  return pFile->iPos;

static void TFTDraw(int x, int y, int w, int h, uint16_t* lBuf )
  tft.pushRect( x+xOffset, y+yOffset, w, h, lBuf );

// Draw a line of image directly on the LCD
void GIFDraw(GIFDRAW *pDraw)
  uint8_t *s;
  uint16_t *d, *usPalette, usTemp[320];
  int x, y, iWidth;

  iWidth = pDraw->iWidth;
  if (iWidth > DISPLAY_WIDTH)
      iWidth = DISPLAY_WIDTH;
  usPalette = pDraw->pPalette;
  y = pDraw->iY + pDraw->y; // current line

  s = pDraw->pPixels;
  if (pDraw->ucDisposalMethod == 2) {// restore to background color
    for (x=0; x<iWidth; x++) {
      if (s[x] == pDraw->ucTransparent)
          s[x] = pDraw->ucBackground;
    pDraw->ucHasTransparency = 0;
  // Apply the new pixels to the main image
  if (pDraw->ucHasTransparency) { // if transparency used
    uint8_t *pEnd, c, ucTransparent = pDraw->ucTransparent;
    int x, iCount;
    pEnd = s + iWidth;
    x = 0;
    iCount = 0; // count non-transparent pixels
    while(x < iWidth) {
      c = ucTransparent-1;
      d = usTemp;
      while (c != ucTransparent && s < pEnd) {
        c = *s++;
        if (c == ucTransparent) { // done, stop
          s--; // back up to treat it like transparent
        } else { // opaque
            *d++ = usPalette[c];
      } // while looking for opaque pixels
      if (iCount) { // any opaque pixels?
        TFTDraw( pDraw->iX+x, y, iCount, 1, (uint16_t*)usTemp );
        x += iCount;
        iCount = 0;
      // no, look for a run of transparent pixels
      c = ucTransparent;
      while (c == ucTransparent && s < pEnd) {
        c = *s++;
        if (c == ucTransparent)
      if (iCount) {
        x += iCount; // skip these
        iCount = 0;
  } else {
    s = pDraw->pPixels;
    // Translate the 8-bit pixels through the RGB565 palette (already byte reversed)
    for (x=0; x<iWidth; x++)
      usTemp[x] = usPalette[*s++];
    TFTDraw( pDraw->iX, y, iWidth, 1, (uint16_t*)usTemp );
} /* GIFDraw() */

int gifPlay( char* gifPath )
{ // 0=infinite
  if( ! gif.open( gifPath, GIFOpenFile, GIFCloseFile, GIFReadFile, GIFSeekFile, GIFDraw ) ) {
    // log_n("Could not open gif %s", gifPath );
    return maxLoopsDuration;

  int frameDelay = 0; // store delay for the last frame
  int then = 0; // store overall delay
  bool showcomment = false;

  // center the GIF !!
  int w = gif.getCanvasWidth();
  int h = gif.getCanvasHeight();
  xOffset = ( tft.width()  - w )  /2;
  yOffset = ( tft.height() - h ) /2;

  if( lastFile != currentFile ) {
    // log_n("Playing %s [%d,%d] with offset [%d,%d]", gifPath, w, h, xOffset, yOffset );
    lastFile = currentFile;
    showcomment = true;

  while (gif.playFrame(true, &frameDelay)) {
    if( showcomment )
      if (gif.getComment(GifComment))
        // log_n("GIF Comment: %s", GifComment);
    then += frameDelay;
    if( then > maxGifDuration ) { // avoid being trapped in infinite GIF's
      // log_w("Broke the GIF loop, max duration exceeded");

  return then;

int getGifInventory( const char* basePath )
  int amount = 0;
  GifRootFolder = SD.open(basePath);
    // log_n("Failed to open directory");
    return 0;

    // log_n("Not a directory");
    return 0;

  File file = GifRootFolder.openNextFile();

  tft.setTextColor( TFT_WHITE, TFT_BLACK );
  tft.setTextSize( 2 );

  int textPosX = tft.width()/2 - 16;
  int textPosY = tft.height()/2 - 10;

  tft.drawString("GIF Files:", textPosX-40, textPosY-20 );

  while( file ) {
    if(!file.isDirectory()) {
      GifFiles.push_back( file.name() );
      tft.drawString(String(amount), textPosX, textPosY );
    file = GifRootFolder.openNextFile();
  // log_n("Found %d GIF files", amount);
  return amount;

void setup()
  // Serial.begin(115200);
  // while (!Serial) ;
  // pinMode(D6, OUTPUT);
  // digitalWrite(D6, HIGH);

  int attempts = 0;
  int maxAttempts = 50;
  int delayBetweenAttempts = 300;
  bool isblinked = false;

  pinMode(D2, OUTPUT);
  while(! SD.begin(D2) ) {
    // log_n("SD Card mount failed! (attempt %d of %d)", attempts, maxAttempts );
    isblinked = !isblinked;
    if( isblinked ) {
      tft.setTextColor( TFT_WHITE, TFT_BLACK );
    } else {
      tft.setTextColor( TFT_BLACK, TFT_WHITE );
    tft.drawString( "INSERT SD", tft.width()/2, tft.height()/2 );

    if( attempts > maxAttempts ) {
      // log_n("Giving up");
    delay( delayBetweenAttempts );

  // log_n("SD Card mounted!");
  // Serial.print("SD Card mounted!");


  totalFiles = getGifInventory( "/gif" ); // scan the SD card GIF folder


void loop()

  const char * fileName = GifFiles[currentFile++%totalFiles].c_str();
  const char * fileDir = "/gif/";
  char * filePath = (char*)malloc(strlen(fileName)+strlen(fileDir)+1);
  strcpy(filePath, fileDir);
  strcat(filePath, fileName);

  int loops = maxLoopIterations; // max loops
  int durationControl = maxLoopsDuration; // force break loop after xxx ms

  while(loops-->0 && durationControl > 0 ) {
    durationControl -= gifPlay( (char*)filePath );


I get a compile error if I don’t include string.

Strange I just cut and pasted (my original post) to a new sketch, It compiled without error or edit, after I added AnimatedGIF.h lib to the ide.
Something else is up over there?
You sure that RP2040 has enough RAM to do the tricks? :thinking:

What’s your setup ?
GL :slight_smile: PJ

It doesn’t seem to detect the SD card even on the hardware test example.

I’m using the Seeed XIAO RP2040, as far as I know it’s supposed to. I’ve found other examples that run gifs that have worked, but they didn’t use the SD card.

Yea it does, my bad Thinking of the seeduino. 32K
Your good it should work, what is the SD.h version?
GL :slight_smile: PJ
ah’ you may want to edit the sd CS pin to the GPIO pin name , try that?

I used the latest version, and when I rolled the libraries back, everything stopped working.

The SD GPIO Pin is D2 and coded correctly.

The SD.h version is 1.2.4, when things were still working.

Let me try and get it working again and I’ll include my compiler output.

I have been playing with my XIAO’s alot over the hollidays and i have just noticed the same problem
sketch and SD card which works with same XIAO-SAMD21 on XIAO Expansion Board, but gets no card detected error on round display… def something going on here… no wonder i couldnt get my cam to work! what da!

My board is currently behaving the same as when I first got it. Screen glitches on/off in various rainbows. Soldering the board to the pin headers fixed that, but it’s doing it again, so I’m thinking my soldering job wasn’t good enough.

Going to get a new iron tip and try again.

Something must be wrong or the chip select pin is not 2 because i loaded the standard SD listfiles.ino onto a XIAO SAMD21 and installed in an XIAO Expansion Board with SD Card inserted and ran sketch and was detected correctly.

Took same Samd and Same SD Card to Round Display and not detected
I also has a second round display sealed in box opened and installed into round display and also not detected. What da?