Micro SD card Module with Arduino and TFT display.
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
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:
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.
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.
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.
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();
}
}