Micro SD card Module with Arduino and TFT display.

micro SD card module with Arduino

A micro sd card module is preferred in cases you need to store large amounts of data and other information for your Arduino project. The EEPROM of the Arduino microcontroller has a limited storage capacity and is specific with the format and nature of data it can hold. This makes it not the best for storing data like text, CSV, audio, video or image files.

The micro SD card module overview

micro sd card module overview

The Voltage Regulator will convert voltages from 3.3V – 6V down to ~3.3V which is the operating voltage of micro SD Cards.

A 74LVC125A Level Shifter on the module converts the interface logic from 3.3V-5V to 3.3V. This enables us to use this device with both 3.3V and 5V microcontrollers like Arduino.

Using the Micro SD Card Module with Arduino

Before inserting the micro SD card into the module and hooking it up to the Arduino, make sure it is properly formatted and is using the FAT16 or FAT32 file system. Most new SD cards come when already pre-formatted with a FAT file system but it’s always good to do it yourself to be certain.

A good recommendation where you can buy this SD Card module and TFT display is the link below:

  • Micro SD Card Module:……  Amazon
  • ST7735 TFT Display:………  Amazon
  • Disclosure: These are affiliate links. I earn a commission as an Amazon Associate for qualifying purchases. I would appreciate your support in this way.

    The SD card module uses the SPI communication protocol and is therefore connected to the Arduino hardware SPI pins. The SPI pins depend on the type of Arduino used but in this case we are using the UNO whose SPI pins are from pin 10 to 12.

    The connections are done as shown below. Make sure you don’t change the order of connection for the SCK, MOSI and MISO pins because they are declared in that order within the SD.h library.

    micro sd card module with arduino schematic

    Arduino IDE comes with the SD.h library which simplifies reading from and writing to SD cards.

    Before using the card we need to first check if it’s valid. This is done by first running the CardInfo sketch from the SD.h library.

    The CardInfo sketch will not write any data to the card but it tells you if the card is recognized and also displays some information about it. This information is shown in the serial monitor like shown below.

    After running the CardInfo sketch, you can run other sketches like the ReadWrite sketch so that you can learn how to use the various functions for reading and writing data to the SD card.

    Displaying Bitmap images on TFT LCD from micro SD card module.

    After setting up the micro SD card module for use with Arduino, we can now be able to store data like images in the sd card and display them on a TFT LCD screen. The best images to display are bitmap images so make sure you convert your images to bitmap format before storing them into the SD card. You can use an online image convertor.

    Another important aspect you should keep in mind when converting your images is the size of the images. This depends on the size of the screen you are using, for example am using a 128×128 pixels screen therefore my images should be that size. Also make sure the images don’t exceed 60kb so that the can easily be processed by the microcontroller to be displayed on the TFT screen.

    The setup of components and connections for the various pins of the Arduino, TFT LCD and SD card module are done as shown below.

    pin connections

    I have included a button on pin 2 which is going to be used as a switch for changing the images displayed on the TFT screen.

    You can also make reference to my previous post on how to use the 1.44″ TFT display.

  • Using the 1.44″ TFT color Display with Arduino.
  • Code for displaying Bitmap images from SD card on TFT display.

    #include <Adafruit_GFX.h>    // include Adafruit graphics library
    #include <Adafruit_ST7735.h> // include Adafruit ST7735 display library
    #include <SPI.h>             // include Arduino SPI library
    #include <SD.h>              // include Arduino SD library
     
    // define ST7735 TFT display connections
    #define TFT_RST  5   // reset line (optional, pass -1 if not used)
    #define TFT_CS   6   // chip select line
    #define TFT_DC   7   // data/command line
     
    #define button   2  // button pin
     
    // initialize Adafruit ST7735 TFT library
    Adafruit_ST7735 tft = Adafruit_ST7735(TFT_CS, TFT_DC, TFT_RST);
     
    void setup(void) {
     Serial.begin(9600);
     
      pinMode(TFT_CS, OUTPUT);
      digitalWrite(TFT_CS, HIGH);
      pinMode(button, INPUT_PULLUP);
     
      // initialize ST7735S TFT display
      tft.initR(INITR_144GREENTAB);
      tft.setRotation(0);
      tft.fillScreen(ST77XX_BLUE);
     
      Serial.print("Initializing SD card...");
      if (!SD.begin()) {
        Serial.println("failed!");
        while(1);  // stay here
      }
      Serial.println("OK!");
     
      File root = SD.open("/");  // open SD card main root
      printDirectory(root, 0);   // print all files names and sizes
      root.close();              // close the opened root
    
    }
     
    void loop() {
      File root = SD.open("/");  // open SD card main root
     
      while (true) {
        File entry =  root.openNextFile();  // open file
     
        if (! entry) {
          // no more files
          root.close();
          return;
        }
     
        uint8_t nameSize = String(entry.name()).length();  // get file name size
        String str1 = String(entry.name()).substring( nameSize - 4 );  // save the last 4 characters (file extension)
     
        if ( str1.equalsIgnoreCase(".bmp") )  // if the file has '.bmp' extension
     bmpDraw(entry.name(), 0, 0);        // draw it
     
        entry.close();  // close the file
     
        delay(500);
        while( digitalRead(button) ) ;  // wait for button press
      }
    }
     
    // This function opens a Windows Bitmap (BMP) file and
    // displays it at the given coordinates.  It's sped up
    // by reading many pixels worth of data at a time
    // (rather than pixel by pixel).  Increasing the buffer
    // size takes more of the Arduino's precious RAM but
    // makes loading a little faster.  20 pixels seems a
    // good balance.
     
    #define BUFFPIXEL 20
    void bmpDraw(char *filename, uint8_t x, uint16_t y) {
     
      File     bmpFile;
      int      bmpWidth, bmpHeight;   // W+H in pixels
      uint8_t  bmpDepth;              // Bit depth (currently must be 24)
      uint32_t bmpImageoffset;        // Start of image data in file
      uint32_t rowSize;               // Not always = bmpWidth; may have padding
      uint8_t  sdbuffer[3*BUFFPIXEL]; // pixel buffer (R+G+B per pixel)
      uint8_t  buffidx = sizeof(sdbuffer); // Current position in sdbuffer
      boolean  goodBmp = false;       // Set to true on valid header parse
      boolean  flip    = true;        // BMP is stored bottom-to-top
     int      w, h, row, col;
      uint8_t  r, g, b;
      uint32_t pos = 0, startTime = millis();
     
      if((x >= tft.width()) || (y >= tft.height())) return;
     
      Serial.println();
      Serial.print(F("Loading image '"));
      Serial.print(filename);
      Serial.println('\'');
     
      // Open requested file on SD card
      if ((bmpFile = SD.open(filename)) == NULL) {
        Serial.print(F("File not found"));
        return;
      }
     
      // Parse BMP header
      if(read16(bmpFile) == 0x4D42) { // BMP signature
        Serial.print(F("File size: ")); Serial.println(read32(bmpFile));
        (void)read32(bmpFile); // Read & ignore
    creator bytes
        bmpImageoffset = read32(bmpFile); // Start of image data
        Serial.print(F("Image Offset: ")); Serial.println(bmpImageoffset, DEC);
        // Read DIB header
        Serial.print(F("Header size: ")); Serial.println(read32(bmpFile));
        bmpWidth  = read32(bmpFile);
        bmpHeight = read32(bmpFile);
        if(read16(bmpFile) == 1) { // # planes -- must be '1'
          bmpDepth = read16(bmpFile); // bits per pixel
          Serial.print(F("Bit Depth: ")); Serial.println(bmpDepth);
          if((bmpDepth == 24) && (read32(bmpFile) == 0)) { // 0 = uncompressed
     
            goodBmp = true; //
    Supported BMP format -- proceed!
            Serial.print(F("Image size: "));
            Serial.print(bmpWidth);
            Serial.print('x');
            Serial.println(bmpHeight);
     
            // BMP rows are padded (if needed) to 4-byte boundary
            rowSize = (bmpWidth * 3 + 3) & ~3;
     
            // If bmpHeight is negative, image is in top-down order.
            // This is not canon but has been observed in the wild.
            if(bmpHeight < 0) {
              bmpHeight = -bmpHeight;
              flip      = false;
     }
     
            // Crop area to be loaded
            w = bmpWidth;
            h = bmpHeight;
            if((x+w-1) >= tft.width())  w = tft.width()  - x;
            if((y+h-1) >= tft.height()) h = tft.height() - y;
     
            // Set TFT address window to clipped image bounds
            tft.startWrite();
            tft.setAddrWindow(x, y, w, h);
     
            for (row=0; row<h; row++) { // For each scanline...
     
              // Seek to start of scan line.  It might seem labor-
              // intensive to be doing this on every line, but this
     // method covers a lot of gritty details like cropping
              // and scanline padding.  Also, the seek only takes
              // place if the file position actually needs to change
              // (avoids a lot of cluster math in SD library).
              if(flip) // Bitmap is stored bottom-to-top order (normal BMP)
                pos = bmpImageoffset + (bmpHeight - 1 - row) * rowSize;
              else     // Bitmap is stored top-to-bottom
                pos =
    bmpImageoffset + row * rowSize;
              if(bmpFile.position() != pos) { // Need seek?
                tft.endWrite();
                bmpFile.seek(pos);
                buffidx = sizeof(sdbuffer); // Force buffer reload
              }
     
              for (col=0; col<w; col++) { // For each pixel...
                // Time to read more pixel data?
                if (buffidx >= sizeof(sdbuffer)) { // Indeed
                  bmpFile.read(sdbuffer, sizeof(sdbuffer));
                  buffidx = 0; // Set index to beginning
     tft.startWrite();
                }
     
                // Convert pixel from BMP to TFT format, push to display
                b = sdbuffer[buffidx++];
                g = sdbuffer[buffidx++];
                r = sdbuffer[buffidx++];
                tft.pushColor(tft.color565(r,g,b));
              } // end pixel
            } // end scanline
            tft.endWrite();
            Serial.print(F("Loaded in "));
            Serial.print(millis() - startTime);
            Serial.println(" ms");
          } // end goodBmp
        }
      }
     bmpFile.close();
      if(!goodBmp) Serial.println(F("BMP format not recognized."));
    }
    // These read 16- and 32-bit types from the SD card file.
    // BMP data is stored little-endian, Arduino is little-endian too.
    // May need to reverse subscript order if porting elsewhere.
     
    uint16_t read16(File f) {
      uint16_t result;
      ((uint8_t *)&result)[0] = f.read(); // LSB
      ((uint8_t *)&result)[1] = f.read(); // MSB
      return result;
    }
     
    uint32_t read32(File f) {
      uint32_t result;
      ((uint8_t *)&result)[0] = f.read(); // LSB
      ((uint8_t *)&result)[1] = f.read();
      ((uint8_t *)&result)[2] = f.read();
      ((uint8_t *)&result)[3] = f.read(); // MSB
      return result;
    }
     
     
    void printDirectory(File dir, int numTabs) {
      while (true) {
     
        File entry =  dir.openNextFile();
        if (! entry) {
          // no more files
          break;
        }
        for (uint8_t i = 0; i < numTabs; i++) {
          Serial.print('\t');
        }
        Serial.print(entry.name());
        if (entry.isDirectory()) {
          Serial.println("/");
          printDirectory(entry, numTabs + 1);
        } else {
          // files have sizes, directories do not
          Serial.print("\t\t");
          Serial.println(entry.size(), DEC);
        }
        entry.close();
      }
    }