This sample program demonstrates how to read JPEG files from an SD card and display them on a TFT LCD screen using an ESP32. It uses the TJpg_Decoder library to decode JPEG images, communicates with the SD card via the SPI bus, and renders the output on a 2.8-inch ILI9341 LCD screen.
This sample code is based on the ESP32-WROOM-32E microcontroller. It drives both the SD card and TFT LCD screen through the SPI interface to achieve JPEG image reading and display.
#include <TJpg_Decoder.h>
#define FS_NO_GLOBALS
#include <FS.h>
#ifdef ESP32
#include "SPIFFS.h"
#endif
#include "SPI.h"
#include <TFT_eSPI.h>
#define SD_CS 5
#define SD_SCK 18
#define SD_MISO 19
#define SD_MOSI 23
Defines the SPI pins used by the SD card:
ESP32-WROOM-32E Pin Connection Table:
| Function | Pin Number | ESP32 Pin |
|---|---|---|
| LCD_CS | 15 | GPIO 15 |
| LCD_DC/RS | 2 | GPIO 2 |
| LCD_RESET | - | EN |
| LCD_SDI/MOSI | 13 | GPIO 13 |
| LCD_SCK | 14 | GPIO 14 |
| LCD_SDO/MISO | 12 | GPIO 12 |
| LCD_BL | 21 | GPIO 21 |
| SD_CS | 5 | GPIO 5 |
| SD_SCK | 18 | GPIO 18 |
| SD_MISO | 19 | GPIO 19 |
| SD_MOSI | 23 | GPIO 23 |
TFT_eSPI tft = TFT_eSPI();
SPIClass MySPI(HSPI);
#define FILE_NUMBER 4
#define FILE_NAME_SIZE_MAX 20
char file_name[FILE_NUMBER][FILE_NAME_SIZE_MAX];
The setup() function completes serial initialization, file name configuration, SD card initialization, and TFT screen initialization. It runs only once when the Arduino is powered on or reset.
void setup()
{
Serial.begin(115200);
Serial.println("\n\n Testing TJpg_Decoder library");
strcpy(file_name[0],"/tulip.jpg");
strcpy(file_name[1],"/game.jpg");
strcpy(file_name[2],"/tree.jpg");
strcpy(file_name[3],"/flower.jpg");
pinMode(SD_CS, OUTPUT);
digitalWrite(SD_CS, HIGH);
MySPI.begin(SD_SCK, SD_MISO, SD_MOSI);
if (!SD.begin(SD_CS,MySPI)) {
Serial.println(F("SD.begin failed!"));
while (1) delay(0);
}
Serial.println("\r\nInitialisation done.");
tft.begin();
tft.setTextColor(0xFFFF, 0x0000);
tft.fillScreen(TFT_BLACK);
tft.setSwapBytes(true);
TJpgDec.setJpgScale(1);
TJpgDec.setCallback(tft_output);
}
Serial.begin(115200);
Serial.println("\n\n Testing TJpg_Decoder library");
Serial.begin(115200): Sets the serial baud rate to 115200 for debugging information outputstrcpy(file_name[0],"/tulip.jpg");
strcpy(file_name[1],"/game.jpg");
strcpy(file_name[2],"/tree.jpg");
strcpy(file_name[3],"/flower.jpg");
Uses strcpy() to copy 4 JPEG file paths into the file name array. These files must be copied to the SD card root directory in advance:
/tulip.jpg: Tulip image/game.jpg: Game image/tree.jpg: Tree image/flower.jpg: Flower imageNote: Before use, format the SD card as FAT16 or FAT32 and copy the corresponding JPEG files to the SD card root directory.
pinMode(SD_CS, OUTPUT);
digitalWrite(SD_CS, HIGH);
MySPI.begin(SD_SCK, SD_MISO, SD_MOSI);
if (!SD.begin(SD_CS, MySPI)) {
Serial.println(F("SD.begin failed!"));
while (1) delay(0);
}
Serial.println("\r\nInitialisation done.");
MySPI.begin(), specifying the SCK, MISO, and MOSI pinsSD.begin(SD_CS, MySPI) to initialize the SD card. If initialization fails, it prints an error message and enters an infinite loop (program stops running)tft.begin();
tft.setTextColor(0xFFFF, 0x0000);
tft.fillScreen(TFT_BLACK);
tft.setSwapBytes(true);
tft.begin(): Initializes the TFT screen, configuring SPI communication and display parameterstft.setTextColor(0xFFFF, 0x0000): Sets text color to white (0xFFFF) with black background (0x0000)tft.fillScreen(TFT_BLACK): Fills the entire screen with black, clearing any previous display contenttft.setSwapBytes(true): Enables byte swapping. This is necessary because the color byte order output by TJpg_Decoder differs from what TFT_eSPI expects, and swapping is required to display colors correctlyTJpgDec.setJpgScale(1);
TJpgDec.setCallback(tft_output);
TJpgDec.setJpgScale(1): Sets the JPEG image scaling factor to 1 (original size). Valid values are 1, 2, 4, or 8 — smaller values result in higher display resolutionTJpgDec.setCallback(tft_output): Registers the callback function tft_output, which the decoder calls during decoding to output pixel data to the TFT screenThe loop() function is the main loop of the Arduino program, which runs repeatedly after the setup() function is executed. It sequentially reads each JPEG file from the SD card and displays it on the screen.
void loop()
{
int i = 0;
uint32_t t = millis();
uint16_t w = 0, h = 0;
for(i=0;i<FILE_NUMBER;i++)
{
TJpgDec.getSdJpgSize(&w, &h, file_name[i]);
Serial.print("Width = "); Serial.print(w); Serial.print(", height = "); Serial.println(h);
TJpgDec.drawSdJpg(0, 0, file_name[i]);
t = millis() - t;
Serial.print(t); Serial.println(" ms");
delay(1500);
}
}
uint32_t t = millis();
Records the current timestamp for calculating the decoding and rendering time of each image. millis() returns the number of milliseconds elapsed since the Arduino was powered on.
for(i=0; i<FILE_NUMBER; i++)
Uses a for loop to iterate through all file names, processing each image in sequence.
uint16_t w = 0, h = 0;
TJpgDec.getSdJpgSize(&w, &h, file_name[i]);
Serial.print("Width = "); Serial.print(w); Serial.print(", height = "); Serial.println(h);
w (width) and h (height) to store the image dimensionsTJpgDec.getSdJpgSize() to obtain the pixel dimensions of the JPEG fileTJpgDec.drawSdJpg(0, 0, file_name[i]);
Calls the drawSdJpg() function to read the specified JPEG file from the SD card and draw it on the screen. The parameters (0, 0) indicate that the image starts displaying from the top-left corner (column 0, row 0) of the screen.
This function automatically performs the following:
tft_output()t = millis() - t;
Serial.print(t); Serial.println(" ms");
Calculates and prints the decoding and rendering time (in milliseconds) for the current image. This is useful for performance evaluation and optimization.
delay(1500);
Delays for 1500 milliseconds (1.5 seconds) to allow the user sufficient time to view the currently displayed image. The loop then proceeds to display the next image.
This is the critical function connecting the JPEG decoder to the TFT screen. The TJpg_Decoder library calls this function when decoding JPEG images to output the decoded pixel data to the display.
bool tft_output(int16_t x, int16_t y, uint16_t w, uint16_t h, uint16_t* bitmap)
{
if ( y >= tft.height() ) return 0;
tft.pushImage(x, y, w, h, bitmap);
return 1;
}
if ( y >= tft.height() ) return 0;
Checks whether the image block exceeds the bottom boundary of the screen. If the y coordinate is greater than or equal to the screen height, it returns 0 to notify the decoder to stop further decoding. This prevents rendering the image outside the screen boundaries.
tft.pushImage(x, y, w, h, bitmap);
Calls the pushImage() method to push the pixel data of the current image block to the specified position on the TFT screen. The TFT_eSPI library automatically handles the transmission and display of pixel data.
This sample program implements the functionality of reading JPEG images from an SD card and displaying them on a TFT screen through the following steps:
Initialization Phase:
Display Loop Phase:
The key functions used in the program include:
SD.begin(): Initialize the SD card file systemTJpgDec.setJpgScale(): Set the JPEG decoding scaling ratioTJpgDec.setCallback(): Register the image rendering callback functionTJpgDec.getSdJpgSize(): Get the dimensions of a JPEG file on the SD cardTJpgDec.drawSdJpg(): Read and draw a JPEG image from the SD cardtft.pushImage(): Push pixel data to the TFT screentft.setSwapBytes(): Set the color byte swap modemillis(): Get system uptime for performance timingIf you need to modify the code for different use cases, you can refer to the following aspects for adjustment:
Change Number of Displayed Images:
FILE_NUMBERfile_name arrayModify Image File Names:
strcpy() to point to your own JPEG filesChange Image Scaling Ratio:
TJpgDec.setJpgScale(1) (options: 1, 2, 4, 8)Change Display Delay:
delay(1500) to change the display time for each imageChange Display Position:
TJpgDec.drawSdJpg(0, 0, file_name[i])drawSdJpg(20, 40, file) positions the image at screen coordinates (20, 40)Add New Images:
file_name arrayFILE_NUMBERChange Background Color:
tft.fillScreen(TFT_BLACK)Change Text Color:
tft.setTextColor(0xFFFF, 0x0000)