diff --git a/examples/Generic/ESP32_SDcard_jpeg/ESP32_SDcard_jpeg.ino b/examples/Generic/ESP32_SDcard_jpeg/ESP32_SDcard_jpeg.ino new file mode 100644 index 0000000..9cc8b02 --- /dev/null +++ b/examples/Generic/ESP32_SDcard_jpeg/ESP32_SDcard_jpeg.ino @@ -0,0 +1,268 @@ +// This sketch if for an ESP32, it draws Jpeg images pulled from an SD Card +// onto the TFT. + +// As well as the TFT_eSPI library you will need the JPEG Decoder library. +// A copy can be downloaded here, it is based on the library by Makoto Kurauchi. +// https://github.com/Bodmer/JPEGDecoder + +// Images on SD Card must be put in the root folder (top level) to be found +// Use the SD library examples to verify your SD Card interface works! + +// The example images used to test this sketch can be found in the library +// JPEGDecoder/extras folder +//---------------------------------------------------------------------------------------------------- + +#include + +#include +#include + +#include +TFT_eSPI tft = TFT_eSPI(); + +// JPEG decoder library +#include + +//#################################################################################################### +// 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) + + tft.begin(); + + if (!SD.begin()) { + Serial.println("Card Mount Failed"); + return; + } + uint8_t cardType = SD.cardType(); + + if (cardType == CARD_NONE) { + Serial.println("No SD card attached"); + return; + } + + 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."); +} + +//#################################################################################################### +// Main loop +//#################################################################################################### +void loop() { + + tft.setRotation(2); // portrait + tft.fillScreen(random(0xFFFF)); + + // The image is 300 x 300 pixels so we do some sums to position image in the middle of the screen! + // Doing this by reading the image width and height from the jpeg info is left as an exercise! + int x = (tft.width() - 300) / 2 - 1; + int y = (tft.height() - 300) / 2 - 1; + + drawSdJpeg("/EagleEye.jpg", x, y); // This draws a jpeg pulled off the SD Card + delay(2000); + + tft.setRotation(2); // portrait + tft.fillScreen(random(0xFFFF)); + drawSdJpeg("/Baboon40.jpg", 0, 0); // This draws a jpeg pulled off the SD Card + delay(2000); + + tft.setRotation(2); // portrait + tft.fillScreen(random(0xFFFF)); + drawSdJpeg("/lena20k.jpg", 0, 0); // This draws a jpeg pulled off the SD Card + delay(2000); + + tft.setRotation(1); // landscape + tft.fillScreen(random(0xFFFF)); + drawSdJpeg("/Mouse480.jpg", 0, 0); // This draws a jpeg pulled off the SD Card + + delay(2000); + + while(1); // Wait here +} + +//#################################################################################################### +// 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: + boolean decoded = JpegDec.decodeSdFile(jpegFile); // Pass the SD file handle to the decoder, + //boolean 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 = min(mcu_w, max_x % mcu_w); + uint32_t min_h = 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 ")); +} +