ILI9341 TFT not drawing BMP from SD card

I'm running into an error where a derivative of the Adafruit_ImageReader library's ILI9341shield example refuses to draw bitmaps from the inserted SD card. Wiring is as follows:

SD card CS: 4
SD card MOSI/TFT MOSI: 11
SD card MISO/TFT MISO: 12
SD card CLK/TFT CLK: 13
TFT CS: 10
TFT RST: 9
TFT DC: 8

All pins are connected through voltage dividers to lower voltages to ~3.3V.

The code I am using is attached below. The SD card is a SanDisk Ultra Plus 64GB microSD card that has been manually reformatted to FAT32 using an external program, which is connected to the TFT (a red ILI9341 off EBay) through a SanDisk microSD adapter. The relevant .bmp file is the only file on the chip.

When turned on and with the sketch uploaded, the screen will turn blue (indicating successful communication between the TFT and Arduino) but will not load the subsequent image. The Serial Monitor reads the following:

Initializing filesystem...OK!
Loading pokedexscreen.bmp to screen...File not found.

// Adafruit_ImageReader test for Adafruit ILI9341 TFT Shield for Arduino.
// Demonstrates loading images from SD card or flash memory to the screen,
// to RAM, and how to query image file dimensions.
// Requires three BMP files in root directory of SD card:
// purple.bmp, parrot.bmp and wales.bmp.
// As written, this uses the microcontroller's SPI interface for the screen
// (not 'bitbang') and must be wired to specific pins (e.g. for Arduino Uno,
// MOSI = pin 11, MISO = 12, SCK = 13). Other pins are configurable below.

#include <Adafruit_GFX.h>         // Core graphics library
#include <Adafruit_ILI9341.h>     // Hardware-specific library
#include <SdFat.h>                // SD card & FAT filesystem library
#include <Adafruit_SPIFlash.h>    // SPI / QSPI flash library
#include <Adafruit_ImageReader.h> // Image-reading functions

// Comment out the next line to load from SPI/QSPI flash instead of SD card:
#define USE_SD_CARD

// TFT display and SD card share the hardware SPI interface, using
// 'select' pins for each to identify the active device on the bus.

#define SD_CS   4 // SD card select pin
#define TFT_CS 10 // TFT select pin
#define TFT_DC  8 // TFT display/command pin
#define TFT_RST 9 // TFT reset pin

#if defined(USE_SD_CARD)
  SdFat                SD;         // SD card filesystem
  Adafruit_ImageReader reader(SD); // Image-reader object, pass in SD filesys
#else
  // SPI or QSPI flash filesystem (i.e. CIRCUITPY drive)
  #if defined(__SAMD51__) || defined(NRF52840_XXAA)
    Adafruit_FlashTransport_QSPI flashTransport(PIN_QSPI_SCK, PIN_QSPI_CS,
      PIN_QSPI_IO0, PIN_QSPI_IO1, PIN_QSPI_IO2, PIN_QSPI_IO3);
  #else
    #if (SPI_INTERFACES_COUNT == 1)
      Adafruit_FlashTransport_SPI flashTransport(SS, &SPI);
    #else
      Adafruit_FlashTransport_SPI flashTransport(SS1, &SPI1);
    #endif
  #endif
  Adafruit_SPIFlash    flash(&flashTransport);
  FatFileSystem        filesys;
  Adafruit_ImageReader reader(filesys); // Image-reader, pass in flash filesys
#endif

Adafruit_ILI9341     tft    = Adafruit_ILI9341(TFT_CS, TFT_DC, TFT_RST);
Adafruit_Image       img;        // An image loaded into RAM
int32_t              width  = 0, // BMP image dimensions
                     height = 0;

void setup(void) {

  ImageReturnCode stat; // Status from image-reading functions

  Serial.begin(9600);
#if !defined(ESP32)
  while(!Serial);       // Wait for Serial Monitor before continuing
#endif

  tft.begin();          // Initialize screen

  // The Adafruit_ImageReader constructor call (above, before setup())
  // accepts an uninitialized SdFat or FatFileSystem object. This MUST
  // BE INITIALIZED before using any of the image reader functions!
  Serial.print(F("Initializing filesystem..."));
#if defined(USE_SD_CARD)
  // SD card is pretty straightforward, a single call...
  if(!SD.begin(SD_CS, SD_SCK_MHZ(25))) { // ESP32 requires 25 MHz limit
    Serial.println(F("SD begin() failed"));
    for(;;); // Fatal error, do not continue
  }
#else
  // SPI or QSPI flash requires two steps, one to access the bare flash
  // memory itself, then the second to access the filesystem within...
  if(!flash.begin()) {
    Serial.println(F("flash begin() failed"));
    for(;;);
  }
  if(!filesys.begin(&flash)) {
    Serial.println(F("filesys begin() failed"));
    for(;;);
  }
#endif
  Serial.println(F("OK!"));

  // Fill screen blue. Not a required step, this just shows that we're
  // successfully communicating with the screen.
  tft.fillScreen(ILI9341_BLUE);
  tft.setRotation(3);
  // Load full-screen BMP file 'pokedexscreen.bmp' at position (0,0) (top left).
  // Notice the 'reader' object performs this, with 'tft' as an argument.
  Serial.print(F("Loading pokedexscreen.bmp to screen..."));
  stat = reader.drawBMP("/pokedexscreen.bmp", tft, 0, 0);
  reader.printStatus(stat);   // How'd we do?
  }

void loop() {
}

I am certain that the TFT works, as I was using it previously to manually draw graphics approximating this image to the screen. I am also certain that the SD card is not corrupted in any meaningful way, as inserting it into a USB adapter allows me to open, add or delete files without issue. However, something appears to be lost in transmission between the SD card and the Arduino - is there some simple error here I'm not seeing?

I realized that the filename 'pokedexscreen.bmp' is not in the approved 8.3 file format, and changed it to 's1.bmp'. However, this has had no effect on the output.

I then put in an SD_exists() function to check if it could read the file, with no success. The relevant section of code now looks like this:

 Serial.println(F("OK!"));

  // Fill screen blue. Not a required step, this just shows that we're
  // successfully communicating with the screen.
  tft.fillScreen(ILI9341_BLUE);
  tft.setRotation(3);
  // Load full-screen BMP file 's1.bmp' at position (0,0) (top left).
  // Notice the 'reader' object performs this, with 'tft' as an argument.
  Serial.println("File check for s1.bmp:");
  Serial.print(SD.exists("/s1.bmp"));
  Serial.println(F("Loading s1.bmp to screen..."));
  stat = reader.drawBMP("/s1.bmp", tft, 0, 0);
  reader.printStatus(stat);   // How'd we do?

and outputs this to the Serial Monitor:

Initializing filesystem...OK!
File check for s1.bmp:
0Loading s1.bmp to screen...
File not found.

The 0 in front of the Loading line is presumably the result of the SD.exists() command (I'd hoped to put it on the previous line, but that wasn't how the println() worked out and I'm too lazy to fix it since it's just debugging and will be deleted later). However, this does tell me that the SD card cannot find the file, despite it being the only one on the card.

I then tried deleting the / in front of the "/s1.bmp" in the call function, hoping that this would do something. No dice.

Lastly, I tried implementing the code used on this page to see if it could list the full directory. However, no difference to the Serial Monitor output was made with this code.

I've attached the bitmap file in question, just in case anyone can spot something wrong with it.

s1.zip (6.38 KB)

Hi elementcollector,

I downloaded your s1.bmp. Photoshop (version 6.0; running in Linux under Wine) wouldn't open it (error "cannot parse parse bmp").
I copied the file to a SD card and tried to open it on one of my own ILI9341 TFT screens (a 2.8 inch touchscreen shield on an Arduino Uno) and got a white flickering screen.

Fortunately The Gimp was able to open the bitmap file. s1.bmp is not corrupt. However, it is in 32bit BMP format which is as far as I know not supported by the Arduino library

I converted the s1.bmp to 24 bit BMP and saved as s1_new. bmp. That worked on the TFT (and in Photoshop). To get a color correct image I had to ionvert the colors. This is the case in s1_inv.bmp

-original s1 and converted s1's attached as ZIP - s1_bmps.zip

have fun, stay safe!

s1_bmps.zip (16.8 KB)

Hmm. Opening my original .bmp file via Windows displays it just fine (after the obligatory 30 seconds of waiting for the Photos program to start up, that is). I replaced the bitmap in my SD card with your uninverted new version, and attempted to run the same program. It still wouldn't display, but I did get a different result from the Serial Monitor when implementing the root directory call I linked to earlier:

Initializing filesystem...use getName()/
use getName() 12
use getName() 76
use getName() 230522
OK!
Loading s1.bmp to screen...
Not a supported BMP variant.

However, removing this code restores the original result of 'file not found':

Initializing filesystem...OK!
Loading s1.bmp to screen...
File not found.

This is reversible, and I'm not sure why this particular code manages to find the file and mark it as an unsupported format when the display code can't seem to find it at all. It appears the program thinks there are three files in the card - one with a size of 12 (can't find units, but I'm assuming bytes?), another with a size of 76, and a third with a size of 230 kB, approximately matching s1_new.bmp's size. Either way, it clearly finds the file in question, even if it can't list the name for some reason.

Are you using the same code as I am to display the image? If not, can you post what you're using to compare differences?

I installed all the Adafruit ImageReader dependent libraries

Then ran the ShieldILI9341.ino example on a M0 Pro Arduino. (adding the TFT_RST argument to TFT constructor)

I then copied the s1a_inv.bmp to the SD card.
Ran your sketch from #0. (with my CS, DC, RST pins and "/s1a_inv.bmp" )

The colour scheme for s1_new.bmp is pretty horrible. But s1a_inv.bmp is pretty horrible too.

There is little doubt that the Adafruit examples work.
On the other hand, I don't like the multiple dependent libraries.

I am happier with the regular "show BMP format images from SD card" examples.

What Arduino target are you using?
Note that Red Chinese ILI9341 boards require 3.3V logic.

David.

Arduino target is Uno/Genuino Uno.

As I stated in my first post, all communication pins are run through voltage dividers to get down to around 3.3V.

If you can display both bitmap images, the file clearly isn't a problem, which is what I had suspected.

Maybe the SD card isn't quite formatted correctly? I had it reformatted to FAT32 from exFAT, but it was through a third-party service and might not have gone as well as it claimed. That's the only thing I can think of - the file's fine, the TFT, Arduino and code all work...

EDIT: I was able to find a FAT32-formatted microSD off my 3D printer. This... almost worked. The file now 'successfully' loads, but the bitmap as-displayed appears to be heavily corrupted. I'm not entirely sure why it displays in this manner, but attached is a photo of the display screen (apologies for the lighting):

Looking at it closely, it appears the colors of the bitmap file made it to the image - the random lines and pixels are composed of the correct shades of green, yellow and blue. However, they are not in the correct positions and appear to be spaced out almost at random. In addition, the screen 'scrolls' through around 4 different and equally random configurations of pixels before settling on this one. There is a pause on the Serial Monitor during this time (in between 'Loading...' and 'Success!'), indicating all four screens are considered part of the same bitmap (at least, as far as I can interpret). Why they display in sequence, I'm not sure.

Initializing filesystem...OK!
use getName()/
use getName() 12
use getName() 76
use getName() 230522
Loading s1.bmp to screen...
Success!

To check if something happened to the file while in transit, I re-inserted the microSD into the USB adapter and opened the file. This was completely successful, meaning the file is correctly stored on the SD card and uncorrupted.

The very last thing I wanted to check was if I was inserting the adapter/SD card correctly. The TFT board I'm using does not appear to have a 'latch' that the SD briefly pushes down on before clicking into place - instead, I push it into the SD slot until it can go no further. Is this correct?

Attach the actual BMP file. (you probably have to put it in a ZIP file like photoncatcher did)

david_prentice:
Attach the actual BMP file. (you probably have to put it in a ZIP file like photoncatcher did)

And as I did, earlier in the thread. This was ripped directly from the SD card, so it should be the same.

s1_new.zip (5.68 KB)

Thiswas my library and memory usage:

Using library Adafruit_GFX_Library at version 1.7.5 in folder: C:\Users\David\Documents\Arduino\libraries\Adafruit_GFX_Library 
Using library Adafruit_ILI9341 at version 1.5.4 in folder: C:\Users\David\Documents\Arduino\libraries\Adafruit_ILI9341 
Using library SPI at version 1.0 in folder: C:\Users\David\AppData\Local\Arduino15\packages\arduino\hardware\samd\1.8.3\libraries\SPI 
Using library arduino_617813 at version 1.2.3 in folder: C:\Users\David\Documents\Arduino\libraries\arduino_617813 
Using library Adafruit_SPIFlash at version 3.1.4 in folder: C:\Users\David\Documents\Arduino\libraries\Adafruit_SPIFlash 
Using library Adafruit_ImageReader_Library at version 2.3.5 in folder: C:\Users\David\Documents\Arduino\libraries\Adafruit_ImageReader_Library 
Using library Adafruit_EPD at version 2.5.2 in folder: C:\Users\David\Documents\Arduino\libraries\Adafruit_EPD 
"C:\\Users\\David\\AppData\\Local\\Arduino15\\packages\\arduino\\tools\\arm-none-eabi-gcc\\7-2017q4/bin/arm-none-eabi-size" -A "C:\\Users\\David\\AppData\\Local\\Temp\\arduino_build_674054/elementcollector1_imagereader.ino.elf"
Sketch uses 28380 bytes (10%) of program storage space. Maximum is 262144 bytes.

If you say what Arduino, I could replicate your project.
I guess that arduino_617813 at version 1.2.3 is the Adafruit hack of SdFat

I am always a little suspicious of Adafruit libraries that undergo frequent version changes.
Which is why I suggest it is worth posting (just) those lines from your verbose build report.

David.

elementcollector1:
Arduino target is Uno/Genuino Uno.

Here's my verbose output for library/memory usage - the library versions used are identical to yours, save for memory used. Maybe the .bmp is stretching memory demands?

Using library Adafruit_GFX_Library at version 1.7.5 in folder: C:\Users\Jeremy\Documents\Arduino\libraries\Adafruit_GFX_Library
Using library Adafruit_ILI9341 at version 1.5.4 in folder: C:\Users\Jeremy\Documents\Arduino\libraries\Adafruit_ILI9341
Using library SPI at version 1.0 in folder: C:\Program Files
Using library SdFat_-Adafruit_Fork at version 1.2.3 in folder: C:\Users\Jeremy\Documents\Arduino\libraries\SdFat-_Adafruit_Fork
Using library Adafruit_SPIFlash at version 3.1.4 in folder: C:\Users\Jeremy\Documents\Arduino\libraries\Adafruit_SPIFlash
Using library Adafruit_ImageReader_Library at version 2.3.5 in folder: C:\Users\Jeremy\Documents\Arduino\libraries\Adafruit_ImageReader_Library
Using library Adafruit_EPD at version 2.5.2 in folder: C:\Users\Jeremy\Documents\Arduino\libraries\Adafruit_EPD
"C:\Program Files (x86)\Arduino\hardware\tools\avr/bin/avr-size" -A "C:\Users\Jeremy\AppData\Local\Temp\arduino_build_321862/pokedexgraphicsbmptest.ino.elf"
Sketch uses 22702 bytes (70%) of program storage space. Maximum is 32256 bytes.

I built for Uno. (it is a 3.3V switchable Uno clone from Open-Smart)

Using library Adafruit_GFX_Library at version 1.7.5 in folder: C:\Users\David\Documents\Arduino\libraries\Adafruit_GFX_Library 
Using library Adafruit_ILI9341 at version 1.5.4 in folder: C:\Users\David\Documents\Arduino\libraries\Adafruit_ILI9341 
Using library SPI at version 1.0 in folder: C:\Program Files\Arduino-1.8.12\hardware\arduino\avr\libraries\SPI 
Using library arduino_617813 at version 1.2.3 in folder: C:\Users\David\Documents\Arduino\libraries\arduino_617813 
Using library Adafruit_SPIFlash at version 3.1.4 in folder: C:\Users\David\Documents\Arduino\libraries\Adafruit_SPIFlash 
Using library Adafruit_ImageReader_Library at version 2.3.5 in folder: C:\Users\David\Documents\Arduino\libraries\Adafruit_ImageReader_Library 
Using library Adafruit_EPD at version 2.5.2 in folder: C:\Users\David\Documents\Arduino\libraries\Adafruit_EPD 
"C:\\Users\\David\\AppData\\Local\\Arduino15\\packages\\arduino\\tools\\avr-gcc\\7.3.0-atmel3.6.1-arduino5/bin/avr-size" -A "C:\\Users\\David\\AppData\\Local\\Temp\\arduino_build_674054/elementcollector1_imagereader.ino.elf"
Sketch uses 21770 bytes (67%) of program storage space. Maximum is 32256 bytes.
Global variables use 1165 bytes (56%) of dynamic memory, leaving 883 bytes for local variables. Maximum is 2048 bytes.

And it runs ok. Just a bit slower than the M0 Pro.
The Transcend 2GB microSD is in a SD adaper.

I can try it with a 8GB microSD. I don't have a 64GB microSD.

Arduino SD library works ok with FAT-16 and FAT-32. I would expect the SdFat fork to work ok with big SD sizes.
"SD disk" libraries are sometimes fussy. e.g. Teensy SD.h does not like 2GB FAT-16

I suggest that you try regular SD example programs with your 64GB i.e. SD.h that comes with your Arduino IDE.
Then try the Adafruit SdFat examples with your 64GB.

If you can find a 2GB or 8GB, try them too.

David.

Here are the results of running CardInfo in the SD library on my 8 GB card:

Initializing SD card...Wiring is correct and a card is present.

Card type: SDHC
Clusters: 245504
Blocks x Cluster: 64
Total Blocks: 15712256

Volume type is: FAT32
Volume size (Kb): 7856128
Volume size (Mb): 7672
Volume size (Gb): 7.49

Files found on the card (name, date and size in bytes):
SYSTEM~1/ 2018-10-04 20:15:28
WPSETT~1.DAT 2018-10-04 20:15:30 12
INDEXE~1 2018-10-04 20:15:30 76
S1_NEW.BMP 2020-04-11 12:52:34 230522

Here is the same sketch's Monitor output with the 64GB card:

Initializing SD card...Wiring is correct and a card is present.

Card type: SDHC
Could not find FAT16/FAT32 partition.
Make sure you've formatted the card

Clearly the FAT32 formatting didn't go so well. This explains why the system can't find the file on this card, but what I'm more interested in is the corruption on the bitmap when displaying.

I downloaded 'purple.bmp' from this Adafruit page. As an approved bitmap file that was specifically published for test use, it should have no problems. However, it suffers a similar fate as s1.bmp:

This tells me that the file is not the problem. The Arduino's SD card library can recognize the files on the card, but can't seem to draw them for some reason - I'm going to go back to the original version of the ImageReader sketch and see if I can make purple.bmp work there now that I have it. If not, something would appear to be wrong with the drawing function for this Arduino, and I'm unsure what the cause or solution would be.

If I had a 2GB SD card, I'd try that for consistency - did you have any luck with an 8GB card on your end?

Update: I found the issue and I feel silly.

The wire leading to pin 10 on the Arduino had gotten out of its spot, leaving TFT_CS unable to transmit data. This is why the images looked so garbled.

It's always the little things that get you... Regardless, this will hopefully serve as instructions to future users with this problem.

EDIT: Incidentally, why is the program so slow to draw a new image? Is there any way to make it faster (different Arduino, way to improve Arduino's memory capacity/transfer rate, etc.)? I'm hoping to eventually use a Nano for this project.

Sorry for the topic bump, but I figured it was better than starting a new one if only to show the history of my issues.

I recently switched boards on this project to an Arduino Nano, as well as two 5V<->3.3V level shifters from China. After wiring them correctly, I was able to confirm that the ILI9341 communicates just fine with the Arduino Nano and is able to draw graphics manually.

However, the SD card has once again run into problems. This time, it refuses to initialize, giving the following error on the SDInfo sketch in the SDFat (Adafruit fork) library:

SdFat version: 10100

Disabling SPI device on pin 10

Assuming the SD chip select pin is: 4
Edit SD_CHIP_SELECT to change the SD chip select pin.

type any character to start
error: cardBegin failed
SD errorCode: 0X43,0XFF

type any character to start

0xFF, from the limited resources I was able to find, is a data error code that indicates MISO is successfully transmitting data. However, I have no idea about 0x43 and was unable to find anything indicating what it was. I've triple-checked every wire to make sure it's wired correctly and fully inserted this time, so I'm sure it isn't that. Running the SDInfo program with no SD card inserted instead gives an error of 0x20,0xFF.

General information is below as a refresher:

Board: Arduino Nano on COM5 w/ ATMega328P (Old Bootloader)
Screen: ILI9341 w/SD card adapter, red board from China
SD card: 32 GB, Fat32 format microSD with Polaroid microSD to SD adapter

Pinouts:
CS (TFT) - D10
RST - D9
DC - D8
MOSI (both) - D11
SCK (both) - D13
MISO (both) - D12
CS (SD) - D4
HV (level shifters) - 5V
LV (level shifters) - 3v3

I think I may have some insight for you. I have the same exact parts (Uno, TFT, SD reader module). Ran into the same issues. Swapped the generic china SD reader module for an adafruit, but some better results but still failed. I resolved with the following:

  1. better wiring - I used different bread board and went to straight 22 gauge wire, made wires as short as reasonable (longest wire less than 4 inch)
  2. used main stream file format (24bit .bmp 320x240)
  3. used adafruit imagereader library (was just easier)
  4. added 0.1pf cap between vcc and gnd as close to the IC as possible (just in front of module connections)
  5. used separate power source for everything except Uno (used bench ps going to adjustable buck). Gave me two things; protection for my low voltage devices using buck because could set to voltage desired and max amperage. Also took load off Uno that can not deliver that much amps via pins

Now all works every time and very reliably. So much, that I can leave it run for days and it keeps working. Can also stop and restart very reliably. Even added a camera with motion to it (and ensured image res and color bit matched above) and that works great too.

Last note: I used resistors as voltage dividers, but later changed to logic level converters. No change in either case but just wanted to state that for those that use each. Resistors needed to be checked as new devices added but still worked just like the logic level converters.

May seem like a lot but did all changed in one sitting, so not as bad as you may think. For more reliability, suggest solder wire connections as much as possible and then check wire resistance for tolerance.

I am still a newbe so if more expert minds see where this could be better, please feel free to correct the above. Just because I got it working, does not mean it optimal or best practice.