// This all the necesary libaries
#include <SPI.h>
#include <FS.h>
#include <SD.h>
// It initializes the TFT_eSPI object
#include <TFT_eSPI.h>
TFT_eSPI tft = TFT_eSPI();
// JPEG decoder library
#include <JPEGDecoder.h>
//####################################################################################################
// Setup
//####################################################################################################
void setup() {
Serial.begin(115200);
// Set all chip selects high to avoid bus contention during initialisation of each peripheral
digitalWrite(22, HIGH); // Touch controller chip select (if used)
digitalWrite(15, HIGH); // TFT screen chip select
digitalWrite( 5, HIGH); // SD card chips select, must use GPIO 5 (ESP32 SS)
// Initialize the TFT screen
tft.begin();
// Initialize the SD card and check if it was successful
if (!SD.begin(5, tft.getSPIinstance())) {
// Print an error message if the SD card mount fails
Serial.println("Card Mount Failed");
return;
}
uint8_t cardType = SD.cardType();
if (cardType == CARD_NONE) {
Serial.println("No SD card attached");
return;
}
// It has adaptations for various versions of SD memory types
Serial.print("SD Card Type: ");
if (cardType == CARD_MMC) {
Serial.println("MMC");
} else if (cardType == CARD_SD) {
Serial.println("SDSC");
} else if (cardType == CARD_SDHC) {
Serial.println("SDHC");
} else {
Serial.println("UNKNOWN");
}
uint64_t cardSize = SD.cardSize() / (1024 * 1024);
Serial.printf("SD Card Size: %lluMB\n", cardSize);
Serial.println("initialisation done.");
}
// An array for all the items to display the first animation
const char* items[] = {
"/FRM_100.jpg", "/FRM_101.jpg", "/FRM_102.jpg", "/FRM_103.jpg", "/FRM_104.jpg",
"/FRM_105.jpg", "/FRM_106.jpg", "/FRM_107.jpg", "/FRM_108.jpg", "/FRM_109.jpg",
"/FRM_110.jpg", "/FRM_111.jpg", "/FRM_112.jpg", "/FRM_113.jpg", "/FRM_114.jpg",
"/FRM_115.jpg", "/FRM_116.jpg", "/FRM_117.jpg", "/FRM_118.jpg", "/FRM_119.jpg",
"/FRM_120.jpg", "/FRM_121.jpg", "/FRM_122.jpg", "/FRM_123.jpg", "/FRM_124.jpg",
"/FRM_125.jpg", "/FRM_126.jpg", "/FRM_127.jpg", "/FRM_128.jpg", "/FRM_129.jpg",
"/FRM_130.jpg", "/FRM_131.jpg", "/FRM_132.jpg", "/FRM_133.jpg", "/FRM_134.jpg",
"/FRM_135.jpg", "/FRM_136.jpg", "/FRM_137.jpg", "/FRM_138.jpg", "/FRM_139.jpg",
"/FRM_140.jpg", "/FRM_141.jpg", "/FRM_142.jpg", "/FRM_143.jpg", "/FRM_144.jpg",
"/FRM_145.jpg", "/FRM_146.jpg", "/FRM_147.jpg", "/FRM_148.jpg", "/FRM_149.jpg",
"/FRM_150.jpg", "/FRM_151.jpg", "/FRM_152.jpg", "/FRM_153.jpg", "/FRM_154.jpg",
"/FRM_155.jpg", "/FRM_156.jpg", "/FRM_157.jpg", "/FRM_158.jpg", "/FRM_159.jpg",
"/FRM_160.jpg", "/FRM_161.jpg", "/FRM_162.jpg", "/FRM_163.jpg", "/FRM_164.jpg",
"/FRM_165.jpg", "/FRM_166.jpg", "/FRM_167.jpg", "/FRM_168.jpg", "/FRM_169.jpg",
"/FRM_170.jpg", "/FRM_171.jpg", "/FRM_172.jpg", "/FRM_173.jpg", "/FRM_174.jpg",
"/FRM_175.jpg", "/FRM_176.jpg", "/FRM_177.jpg", "/FRM_178.jpg", "/FRM_179.jpg",
"/FRM_180.jpg", "/FRM_181.jpg", "/FRM_182.jpg", "/FRM_183.jpg", "/FRM_184.jpg",
"/FRM_185.jpg", "/FRM_186.jpg", "/FRM_187.jpg", "/FRM_188.jpg", "/FRM_189.jpg",
"/FRM_190.jpg", "/FRM_191.jpg", "/FRM_192.jpg", "/FRM_193.jpg", "/FRM_194.jpg",
"/FRM_195.jpg", "/FRM_196.jpg", "/FRM_197.jpg", "/FRM_198.jpg", "/FRM_199.jpg"
};
// An array for all the items to display the second animation
const char* new_I[] = {
"/Talk_1000.jpg", "/Talk_1001.jpg", "/Talk_1002.jpg", "/Talk_1003.jpg", "/Talk_1004.jpg",
"/Talk_1005.jpg", "/Talk_1006.jpg", "/Talk_1007.jpg", "/Talk_1008.jpg", "/Talk_1009.jpg",
"/Talk_1010.jpg", "/Talk_1011.jpg", "/Talk_1012.jpg", "/Talk_1013.jpg", "/Talk_1014.jpg",
"/Talk_1015.jpg", "/Talk_1016.jpg", "/Talk_1017.jpg", "/Talk_1018.jpg", "/Talk_1019.jpg",
"/Talk_1020.jpg", "/Talk_1021.jpg", "/Talk_1022.jpg", "/Talk_1023.jpg", "/Talk_1024.jpg",
"/Talk_1025.jpg", "/Talk_1026.jpg", "/Talk_1027.jpg", "/Talk_1028.jpg", "/Talk_1029.jpg",
"/Talk_1030.jpg", "/Talk_1031.jpg", "/Talk_1032.jpg", "/Talk_1033.jpg", "/Talk_1034.jpg",
"/Talk_1035.jpg", "/Talk_1036.jpg", "/Talk_1037.jpg", "/Talk_1038.jpg", "/Talk_1039.jpg",
"/Talk_1040.jpg", "/Talk_1041.jpg", "/Talk_1042.jpg", "/Talk_1043.jpg", "/Talk_1044.jpg",
"/Talk_1045.jpg", "/Talk_1046.jpg", "/Talk_1047.jpg", "/Talk_1048.jpg", "/Talk_1049.jpg",
"/Talk_1050.jpg", "/Talk_1051.jpg", "/Talk_1052.jpg", "/Talk_1053.jpg", "/Talk_1054.jpg",
"/Talk_1055.jpg", "/Talk_1056.jpg", "/Talk_1057.jpg", "/Talk_1058.jpg", "/Talk_1059.jpg",
"/Talk_1060.jpg", "/Talk_1061.jpg", "/Talk_1062.jpg", "/Talk_1063.jpg", "/Talk_1064.jpg",
"/Talk_1065.jpg", "/Talk_1066.jpg", "/Talk_1067.jpg", "/Talk_1068.jpg", "/Talk_1069.jpg",
"/Talk_1070.jpg", "/Talk_1071.jpg", "/Talk_1072.jpg", "/Talk_1073.jpg", "/Talk_1074.jpg",
"/Talk_1075.jpg", "/Talk_1076.jpg", "/Talk_1077.jpg", "/Talk_1078.jpg", "/Talk_1079.jpg",
"/Talk_1080.jpg", "/Talk_1081.jpg", "/Talk_1082.jpg", "/Talk_1083.jpg", "/Talk_1084.jpg",
"/Talk_1085.jpg", "/Talk_1086.jpg", "/Talk_1087.jpg", "/Talk_1088.jpg", "/Talk_1089.jpg",
"/Talk_1090.jpg", "/Talk_1091.jpg", "/Talk_1092.jpg", "/Talk_1093.jpg", "/Talk_1094.jpg",
"/Talk_1095.jpg", "/Talk_1096.jpg", "/Talk_1097.jpg", "/Talk_1098.jpg", "/Talk_1099.jpg",
"/Talk_1100.jpg", "/Talk_1101.jpg", "/Talk_1102.jpg", "/Talk_1103.jpg", "/Talk_1104.jpg",
"/Talk_1105.jpg", "/Talk_1106.jpg", "/Talk_1107.jpg", "/Talk_1108.jpg", "/Talk_1109.jpg",
"/Talk_1110.jpg", "/Talk_1111.jpg", "/Talk_1112.jpg", "/Talk_1113.jpg", "/Talk_1114.jpg",
"/Talk_1115.jpg", "/Talk_1116.jpg", "/Talk_1117.jpg", "/Talk_1118.jpg", "/Talk_1119.jpg",
"/Talk_1120.jpg", "/Talk_1121.jpg", "/Talk_1122.jpg", "/Talk_1123.jpg", "/Talk_1124.jpg",
"/Talk_1125.jpg", "/Talk_1126.jpg", "/Talk_1127.jpg", "/Talk_1128.jpg", "/Talk_1129.jpg",
"/Talk_1130.jpg", "/Talk_1131.jpg", "/Talk_1132.jpg", "/Talk_1133.jpg", "/Talk_1134.jpg",
"/Talk_1135.jpg", "/Talk_1136.jpg", "/Talk_1137.jpg", "/Talk_1138.jpg", "/Talk_1139.jpg",
"/Talk_1140.jpg", "/Talk_1141.jpg", "/Talk_1142.jpg", "/Talk_1143.jpg", "/Talk_1144.jpg",
"/Talk_1145.jpg", "/Talk_1146.jpg", "/Talk_1147.jpg", "/Talk_1148.jpg", "/Talk_1149.jpg",
"/Talk_1150.jpg", "/Talk_1151.jpg", "/Talk_1152.jpg", "/Talk_1153.jpg", "/Talk_1154.jpg",
"/Talk_1155.jpg", "/Talk_1156.jpg", "/Talk_1157.jpg", "/Talk_1158.jpg", "/Talk_1159.jpg",
"/Talk_1160.jpg", "/Talk_1161.jpg", "/Talk_1162.jpg", "/Talk_1163.jpg", "/Talk_1164.jpg",
"/Talk_1165.jpg", "/Talk_1166.jpg", "/Talk_1167.jpg"
};
//####################################################################################################
// Main loop
//####################################################################################################
void loop() {
int numItems = sizeof(items) / sizeof(items[0]);
int menu_I = sizeof(new_I) / sizeof(new_I[0]);
// Set the correct position
tft.setRotation(3);
tft.fillScreen(0xFFFF);
while (true) {
// It clears the screen before any iteration
tft.fillScreen(0xFFFF);
// It calculates the correct position for the image
int x = (tft.width() - 320) / 2;
int y = (tft.height() - 240) / 2;
// It reaches all the items in the array and displays
for (int i = 0; i < numItems; i++) {
drawSdJpeg(items[i], x, y);
delay(100);
}
}
}
//####################################################################################################
// Draw a JPEG on the TFT pulled from SD Card
//####################################################################################################
// xpos, ypos is top left corner of plotted image
void drawSdJpeg(const char *filename, int xpos, int ypos) {
// Open the named file (the Jpeg decoder library will close it)
File jpegFile = SD.open( filename, FILE_READ); // or, file handle reference for SD library
if ( !jpegFile ) {
Serial.print("ERROR: File \""); Serial.print(filename); Serial.println ("\" not found!");
return;
}
Serial.println("===========================");
Serial.print("Drawing file: "); Serial.println(filename);
Serial.println("===========================");
// Use one of the following methods to initialise the decoder:
bool decoded = JpegDec.decodeSdFile(jpegFile); // Pass the SD file handle to the decoder,
//bool decoded = JpegDec.decodeSdFile(filename); // or pass the filename (String or character array)
if (decoded) {
// print information about the image to the serial port
jpegInfo();
// render the image onto the screen at given coordinates
jpegRender(xpos, ypos);
}
else {
Serial.println("Jpeg file format not supported!");
}
}
//####################################################################################################
// Draw a JPEG on the TFT, images will be cropped on the right/bottom sides if they do not fit
//####################################################################################################
// This function assumes xpos,ypos is a valid screen coordinate. For convenience images that do not
// fit totally on the screen are cropped to the nearest MCU size and may leave right/bottom borders.
void jpegRender(int xpos, int ypos) {
//jpegInfo(); // Print information from the JPEG file (could comment this line out)
uint16_t *pImg;
uint16_t mcu_w = JpegDec.MCUWidth;
uint16_t mcu_h = JpegDec.MCUHeight;
uint32_t max_x = JpegDec.width;
uint32_t max_y = JpegDec.height;
bool swapBytes = tft.getSwapBytes();
tft.setSwapBytes(true);
// Jpeg images are draw as a set of image block (tiles) called Minimum Coding Units (MCUs)
// Typically these MCUs are 16x16 pixel blocks
// Determine the width and height of the right and bottom edge image blocks
uint32_t min_w = jpg_min(mcu_w, max_x % mcu_w);
uint32_t min_h = jpg_min(mcu_h, max_y % mcu_h);
// save the current image block size
uint32_t win_w = mcu_w;
uint32_t win_h = mcu_h;
// record the current time so we can measure how long it takes to draw an image
uint32_t drawTime = millis();
// save the coordinate of the right and bottom edges to assist image cropping
// to the screen size
max_x += xpos;
max_y += ypos;
// Fetch data from the file, decode and display
while (JpegDec.read()) { // While there is more data in the file
pImg = JpegDec.pImage ; // Decode a MCU (Minimum Coding Unit, typically a 8x8 or 16x16 pixel block)
// Calculate coordinates of top left corner of current MCU
int mcu_x = JpegDec.MCUx * mcu_w + xpos;
int mcu_y = JpegDec.MCUy * mcu_h + ypos;
// check if the image block size needs to be changed for the right edge
if (mcu_x + mcu_w <= max_x) win_w = mcu_w;
else win_w = min_w;
// check if the image block size needs to be changed for the bottom edge
if (mcu_y + mcu_h <= max_y) win_h = mcu_h;
else win_h = min_h;
// copy pixels into a contiguous block
if (win_w != mcu_w)
{
uint16_t *cImg;
int p = 0;
cImg = pImg + win_w;
for (int h = 1; h < win_h; h++)
{
p += mcu_w;
for (int w = 0; w < win_w; w++)
{
*cImg = *(pImg + w + p);
cImg++;
}
}
}
// calculate how many pixels must be drawn
uint32_t mcu_pixels = win_w * win_h;
// draw image MCU block only if it will fit on the screen
if (( mcu_x + win_w ) <= tft.width() && ( mcu_y + win_h ) <= tft.height())
tft.pushImage(mcu_x, mcu_y, win_w, win_h, pImg);
else if ( (mcu_y + win_h) >= tft.height())
JpegDec.abort(); // Image has run off bottom of screen so abort decoding
}
tft.setSwapBytes(swapBytes);
showTime(millis() - drawTime); // These lines are for sketch testing only
}
//####################################################################################################
// Print image information to the serial port (optional)
//####################################################################################################
// JpegDec.decodeFile(...) or JpegDec.decodeArray(...) must be called before this info is available!
void jpegInfo() {
// Print information extracted from the JPEG file
Serial.println("JPEG image info");
Serial.println("===============");
Serial.print("Width :");
Serial.println(JpegDec.width);
Serial.print("Height :");
Serial.println(JpegDec.height);
Serial.print("Components :");
Serial.println(JpegDec.comps);
Serial.print("MCU / row :");
Serial.println(JpegDec.MCUSPerRow);
Serial.print("MCU / col :");
Serial.println(JpegDec.MCUSPerCol);
Serial.print("Scan type :");
Serial.println(JpegDec.scanType);
Serial.print("MCU width :");
Serial.println(JpegDec.MCUWidth);
Serial.print("MCU height :");
Serial.println(JpegDec.MCUHeight);
Serial.println("===============");
Serial.println("");
}
//####################################################################################################
// Show the execution time (optional)
//####################################################################################################
// WARNING: for UNO/AVR legacy reasons printing text to the screen with the Mega might not work for
// sketch sizes greater than ~70KBytes because 16-bit address pointers are used in some libraries.
// The Due will work fine with the HX8357_Due library.
void showTime(uint32_t msTime) {
//tft.setCursor(0, 0);
//tft.setTextFont(1);
//tft.setTextSize(2);
//tft.setTextColor(TFT_WHITE, TFT_BLACK);
//tft.print(F(" JPEG drawn in "));
//tft.print(msTime);
//tft.println(F(" ms "));
Serial.print(F(" JPEG drawn in "));
Serial.print(msTime);
Serial.println(F(" ms "));
}