Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

ILI9341 & ESP32 - Unable to open JPEGs from SD Card - File Not Found #87

Open
cmcooper1971 opened this issue Jun 20, 2024 · 3 comments
Open

Comments

@cmcooper1971
Copy link

cmcooper1971 commented Jun 20, 2024

I'm using an ESP32, ILI9341, which I can control with fill back grounds, draw lines etc. I can read text files and display them from the SD card using Serial.print, but no matter what I try for JPEGs, it says "Jpeg file not found"

I reverted back to the Arduino 1.8 IDE, removed all the libraries and reinstalled. This has made no difference.

These are my includes:

// Includes

#include "globalConfig.h"
#include "tft_Control.h"

#include <Arduino.h>
#include <SPI.h>
#include <Wire.h>

#include <SD.h>

#include <Adafruit_ILI9341.h>
#include <Adafruit_ST7735.h>
#include <Adafruit_GFX.h>

#include <JPEGDecoder.h>

#include <Adafruit_MCP23X08.h>

My TFT declaration:

// Class Declarations and Definitions

Adafruit_ILI9341 TFT1 = Adafruit_ILI9341(TFT1_CS_DUMMY, TFT1_DC, TFT1_RST);

Within the setup, I initialise the displays, set various background colours, all in sequence, then I call the function to draw the JPEG as follows:

	/*-------------------------------------------*/

	listDir(SD, "/", 0);

	readFile(SD, "/test.txt");

	// Enable A-BUS Screen 5

	enable_Tft_5_ABus();
	drawJpeg("/house.jpg", 0, 0);

	// Disable screen chip select

	disableAllTftAllBus();

	/*-------------------------------------------*/

As you can see, I perform a little test to ensure the SD card is addressable and working, which it is.

Here is the drawJpeg function:

void drawJpeg(const char* filename, int xpos, int ypos) {

	Serial.println("===========================");
	Serial.print("Drawing file: "); Serial.println(filename);
	Serial.println("===========================");

	// Open the named file (the Jpeg decoder library will close it after rendering image)
	// fs::File jpegFile = SPIFFS.open(filename, "r");    // File handle reference for SPIFFS
	File jpegFile = SD.open(filename);  // or, file handle reference for SD library

	if (!jpegFile) {
		Serial.print("ERROR: File \""); Serial.print(filename); Serial.println("\" not found!");
		return;
	}

	// Use one of the three following methods to initialise the decoder:
	//boolean decoded = JpegDec.decodeFsFile(jpegFile); // Pass a SPIFFS file handle to the decoder,
	boolean decoded = JpegDec.decodeSdFile(jpegFile); // or pass the SD file handle to the decoder,
	//boolean decoded = JpegDec.decodeFsFile(filename);  // or pass the filename (leading / distinguishes SPIFFS files)
	// Note: the filename can be a String or character array type
	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!");
	}
}

It fails to open the listed file and I do not understand why? I have used FILE_READ as well as it is currently. The SD card works and therefore all GPIOs are correct. All TFTs are operational as I can control them independently, currently 10 displays, increasing to 25 assuming I can solve this issue.

To get the following line working:

boolean decoded = JpegDec.decodeSdFile(jpegFile);

I had to edit the JPEGDecoder.h file as follows:

  #ifdef ESP32  // SDFAT library not compatible with ESP32
    #define LOAD_SD_LIBRARY
    //#undef LOAD_SDFAT_LIBRARY
  #endif

All I can say is... HELP!

@cmcooper1971
Copy link
Author

cmcooper1971 commented Jun 20, 2024

I have written this function to test the SD card before I even try to draw the JPEG and it fails to open the file:

void openAndReadJPEG(fs::FS& SD, const char* path) {
	Serial.printf("Opening JPEG file: %s\n", path);

	File file = SD.open(path);
	if (!file) {
		Serial.print("ERROR: File \""); Serial.print(path); Serial.println("\" not found!");
		return;
	}

	Serial.println("JPEG file opened successfully!");

	// You can add code here to process the JPEG file
	// For example, reading data and printing out the first few bytes

	Serial.println("Reading first 10 bytes of the JPEG file:");
	for (int i = 0; i < 10 && file.available(); i++) {
		Serial.printf("0x%02X ", file.read());
	}
	Serial.println();

	file.close();
}

So I simply do not understand why I can open TFT files and print them, but I cannot open JPEG files!

Here is the debug output:

Listing directory: /
  DIR : System Volume Information
  FILE: house.jpg	SIZE: 76098
  FILE: bobble240.jpg	SIZE: 31821
  FILE: candj240.jpg	SIZE: 60284
  FILE: Chris240.jpg	SIZE: 15548
  FILE: dad320x240.jpg	SIZE: 43857
  FILE: greenbobble.jpg	SIZE: 32757
  FILE: island.jpg	SIZE: 50228
  FILE: knight.jpg	SIZE: 51497
  FILE: mum.jpg	SIZE: 55118
  FILE: mumanddad.jpg	SIZE: 67378
  FILE: test.txt	SIZE: 30
Reading file: /test.txt
- read from file:
Did this file open and read? YES...

Opening JPEG file: /house.jpg
ERROR: File "/house.jpg" not found!

@cmcooper1971
Copy link
Author

cmcooper1971 commented Jun 20, 2024

Further information, out of interest, I have changed the sketch to use LittleFS, leaving everything the same, which works...

In the setup:

	// Enable A-BUS Screen 5

	enable_Tft_3_ABus();

	drawJpeg("/island.jpg", 0, 0);

	// Disable screen chip select

	disableAllTftAllBus();

Draw function:

void drawJpeg(const char* filename, int xpos, int ypos) {

	Serial.println("===========================");
	Serial.print("Drawing file: "); Serial.println(filename);
	Serial.println("===========================");

	// Open the named file (the Jpeg decoder library will close it after rendering image)
	fs::File jpegFile = LittleFS.open(filename, "r");    // File handle reference for SPIFFS
	//File jpegFile = SD.open(filename);  // or, file handle reference for SD library

	if (!jpegFile) {
		Serial.print("ERROR: File \""); Serial.print(filename); Serial.println("\" not found!");
		return;
	}

	// Use one of the three following methods to initialise the decoder:
	boolean decoded = JpegDec.decodeFsFile(jpegFile); // Pass a SPIFFS file handle to the decoder,
	//boolean decoded = JpegDec.decodeSdFile(jpegFile); // or pass the SD file handle to the decoder,
	//boolean decoded = JpegDec.decodeFsFile(filename);  // or pass the filename (leading / distinguishes SPIFFS files)
	// Note: the filename can be a String or character array type
	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!");
	}
}

Render function:

void jpegRender(int xpos, int ypos) {

	// retrieve infomration about the image
	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;

	// 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 = minimum(mcu_w, max_x % mcu_w);
	uint32_t min_h = minimum(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;

	// read each MCU block until there are no more
	while (JpegDec.read()) {

		// save a pointer to the image block
		pImg = JpegDec.pImage;

		// calculate where the image block should be drawn on the screen
		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)
		{
			for (int h = 1; h < win_h - 1; h++)
			{
				memcpy(pImg + h * win_w, pImg + (h + 1) * mcu_w, win_w << 1);
			}
		}


		// draw image MCU block only if it will fit on the screen
		if ((mcu_x + win_w) <= TFT1.width() && (mcu_y + win_h) <= TFT1.height())
		{
			TFT1.drawRGBBitmap(mcu_x, mcu_y, pImg, win_w, win_h);
		}

		// Stop drawing blocks if the bottom of the screen has been reached,
		// the abort function will close the file
		else if ((mcu_y + win_h) >= TFT1.height()) JpegDec.abort();

}

	// calculate how long it took to draw the image
	drawTime = millis() - drawTime;

	// print the results to the serial port
	Serial.print("Total render time was    : "); Serial.print(drawTime); Serial.println(" ms");
	Serial.println("=====================================");

}

@cmcooper1971
Copy link
Author

So, this was a huge red herring, as it was an SD card problem. I am driving multiple SPI devices using FET switches and the BUS was getting upset.

However, although I fixed the SD card access and I did manage to get a part of a JPEG displayed from SD, there was a memory issue, as a third of a way through each image, the screen corrupted with colour blocks, which I didn't experience at all using LittleFS.

Apologies if you have read this far… But all is not lost.

I therefore wrote a function to copy the JPEG files into LittleFS and then used the "void jpegRender(int xpos, int ypos)" to display the JPEG and this worked well, although drawing speed is slightly delayed.

If anyone reads this in Star Date 48000, any feedback on why the SD card image doesn't fully display, would be appreciated.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant