Problems with Bitmaps on TFT Display using GFX Library

Hey guys hopefully this is the right place to ask this, but im having some problems trying to draw a bitmap on my 1.8" TFT display via the Adafruit GFX library. Ive been following THIS video guide on how to generate the bitmap code and ive used both the Image2Code Java Utility as well as LCD Image Converter and neither have worked properly. When I try to draw the bitmap all I get is a jumbled mess of pixels on my LCD. Ive tried making the image smaller, repositioning it, rotating it, defining the drawBitmap void in my own sketch and not using the library, nothing has worked. If anybody has any idea why this isnt working that would be great, thanks! Pic and relevant code below. FYI the Yellow and White Characters are drawn afterwards so ignore them.

EDIT: Also THIS is the link to the actual image im trying to draw.

#include "LowPower.h"
#include <Adafruit_GFX.h>    // Core graphics library
#include <Adafruit_ST7735.h> // Hardware-specific library
#include <SPI.h>


//TFT Display pins, LED to 3.3v VCC to 5V
#define TFT_CS     10   //CS pin to D10
#define TFT_RST    9    //RESET to P9
#define TFT_DC     8    //A0 to D8
Adafruit_ST7735 tft = Adafruit_ST7735(TFT_CS,  TFT_DC, TFT_RST);
#define TFT_SCLK 13     //SCK to D13
#define TFT_MOSI 11     //SDA to D11


//Main bitmap images stored in Bitmaps.c
extern uint8_t MainTFTDisplayBitmap[];  //Main Background Image


tft.initR(INITR_BLACKTAB);    //TFT Display init
  uint16_t time = millis();
  tft.fillScreen(ST7735_BLACK);
  time = millis() - time;
  
  tft.setTextWrap(false);
  tft.setRotation(1);
  delay(500);

  //Draw the main background bitmap, doesnt change so only needs to be called once
  tft.drawBitmap(0,0,MainTFTDisplayBitmap,160,128,ST7735_BLUE);
  
  delay(100);

HERE is a link to the bitmap stored in my Bitmaps.c file in PROGMEM

You have declared the array as in SRAM

//Main bitmap images stored in Bitmaps.c
extern uint8_t MainTFTDisplayBitmap[];  //Main Background Image

add the const qualifier and it will use the correct overloaded drawBimap() method:

    //bitmap in PROGMEM
    drawBitmap(int16_t x, int16_t y, const uint8_t bitmap[],
      int16_t w, int16_t h, uint16_t color),
    //bitmap in SRAM
    drawBitmap(int16_t x, int16_t y, uint8_t *bitmap,
      int16_t w, int16_t h, uint16_t color),

David.

Edit. Your bitmap uses 2560 bytes which is not too expensive.
However you could probably draw the lines and print the text with regular GFX graphics methods. It depends on whether you are using that particular font elsewhere.

david_prentice:
You have declared the array as in SRAM

//Main bitmap images stored in Bitmaps.c

extern uint8_t MainTFTDisplayBitmap;  //Main Background Image



add the const qualifier and it will use the correct overloaded drawBimap() method:


//bitmap in PROGMEM
   drawBitmap(int16_t x, int16_t y, const uint8_t bitmap,
     int16_t w, int16_t h, uint16_t color),
   //bitmap in SRAM
   drawBitmap(int16_t x, int16_t y, uint8_t *bitmap,
     int16_t w, int16_t h, uint16_t color),




David.

Edit. Your bitmap uses 2560 bytes which is not too expensive.
However you could probably draw the lines and print the text with regular GFX graphics methods. It depends on whether you are using that particular font elsewhere.

That was it! Thanks so much I actually tried defining it as a const at one point but it didnt help, idk I guess I did it wrong lol. Again thank you so much for the quick reply! Below is the updated code portion.

extern const uint8_t MainTFTDisplayBitmap[];  //Main Background Image

It is easier to include as a H file. Then the INO file knows exactly what it is dealing with.

Yes, I know that H files are not supposed to have data. But it is fairly common practice to put fonts and images in H files.

If the sketch does not reference the font or image, the linker discards it anyway.

David.

david_prentice:
But it is fairly common practice to put fonts and images in H files.

May be in Arduino's world its common practice. Bit it is a bad practice anyway.
As a result, you can get multiple duplication of large data sets.

Yes, it is bad practice to put executable code in H files.

Fonts and images tend to be inanimate data tables.

The Linker decides whether to actually put it in the HEX file i.e. only if it is actually used.

Placing in an H file means that the data table(s) are compiled in the same style as the main C or C++ file.
If you put in a C file, you must specify the appropriate external linkage. Not difficult but beyond the normal Arduino user.

YMMV.

David.

1 Like

Not only code, but placing DATA in header is also a bad idea.
You can use same style, but data may be duplicated.
For example, take attention to TEXT section in compiler’s output below.

Header file with data:

#ifndef SAMPLE_DATA_H_
#define SAMPLE_DATA_H_

#include <stdint.h>

static const uint8_t const data_array[10240] = { 1, 2, 3 };


#endif /* SAMPLE_DATA_H_ */

Data file not used anywhere and excluded by compiler:

Invoking: Cross ARM GNU Print Size
arm-none-eabi-size --format=berkeley "test-f4.elf"
   text   data    bss    dec    hex filename
  13864    476   1588  15928   3e38 test-f4.elf
Finished building: test-f4.siz

Header file included in only one C-file:

#include "sample-data.h"
#include "stm32f4xx_hal.h"

void method1(void) {
 uint8_t b = 0;
 for (uint32_t i = 0; i < sizeof(data_array); i++) {
 if (data_array[i] != 0) {
 b = 1;
 }
 }
 if (b == 1) {
 __HAL_RCC_GPIOB_CLK_ENABLE();
 }
}
Invoking: Cross ARM GNU Print Size
arm-none-eabi-size --format=berkeley "test-f4.elf"
   text   data    bss    dec    hex filename
  24192    476   1588  26256   6690 test-f4.elf
Finished building: test-f4.siz
 

22:10:51 Build Finished (took 1s.432ms)

Header file also included to second C-file:

#include "sample-data.h"
#include "stm32f4xx_hal.h"

void method2(void) {
 uint8_t b = 0;
 for (uint32_t i = 0; i < sizeof(data_array); i++) {
 if (data_array[i] != 0) {
 b = 1;
 }
 }
 if (b == 1) {
 __HAL_RCC_GPIOB_CLK_ENABLE();
 }
}
Invoking: Cross ARM GNU Print Size
arm-none-eabi-size --format=berkeley "test-f4.elf"
   text	   data	    bss	    dec	    hex	filename
  34520	    476	   1588	  36584	   8ee8	test-f4.elf
Finished building: test-f4.siz
 

22:13:04 Build Finished (took 1s.487ms)

And now only data’s declaration in header:

#ifndef SAMPLE_DATA_H_
#define SAMPLE_DATA_H_

#include <stdint.h>

extern const uint8_t const data_array[10240];

#endif /* SAMPLE_DATA_H_ */

and data’s definition in C source file:

#include "sample-data.h"

const uint8_t const data_array[10240] = { 1, 2, 3 };

Using this declaration in only one C file (same code) give next output:

Invoking: Cross ARM GNU Print Size
arm-none-eabi-size --format=berkeley "test-f4.elf"
   text   data    bss    dec    hex filename
  24192    476   1588  26256   6690 test-f4.elf
Finished building: test-f4.siz
 

22:21:10 Build Finished (took 1s.395ms)

Also include in second C source file:

Invoking: Cross ARM GNU Print Size
arm-none-eabi-size --format=berkeley "test-f4.elf"
   text   data    bss    dec    hex filename
  24280    476   1588  26344   66e8 test-f4.elf
Finished building: test-f4.siz
 

22:22:19 Build Finished (took 1s.268ms)

And now imagine for a minute that the data structure is a font size of 200 KB …

Does the Arduino’s compiler solve this duplication problem automatically?

I attempted to draw a mono bitmap from the single example on your website. As it stands, the C file is attempting to store the data in SRAM. A Uno does not have enough SRAM.

I edited the butterfly2.c file to declare the tImage and to place the data in PROGMEM:

/*******************************************************************************
* image
* filename: /home/vladimir/workspace/lcd-image-converter-wiki/examples/image-butterfly2.xml
* name: butterfly2
*
* preset name: Monochrome
* data block size: 8 bit(s), uint8_t
* RLE compression enabled: no
* conversion type: Monochrome, Diffuse Dither 128
* bits per pixel: 1
*
* preprocess:
*  main scan direction: top to bottom
*  line scan direction: backward
*  inverse: no
*******************************************************************************/

#include <stdint.h>
#include <avr/pgmspace.h>

 typedef struct {
     const uint8_t *data;
     uint16_t width;
     uint16_t height;
 } tImage;



static const uint8_t PROGMEM image_data_butterfly2[9600] = {
...
};
const tImage butterfly2 = { image_data_butterfly2, 320, 240};

and displayed on a TFT with:

#include "MCUFRIEND_kbv.h"
MCUFRIEND_kbv tft;

typedef struct {
    const uint8_t *data;
    uint16_t width;
    uint16_t height;
} tImage;

extern const tImage butterfly2;

void setup()
{
    uint16_t ID = tft.readID();
    tft.begin(ID);
    tft.setRotation(1);
}

void loop(void)
{
    tft.fillScreen(TFT_BLACK);
    tft.drawBitmap(0, 0, butterfly2.data, butterfly2.width, butterfly2.height, TFT_BLUE);
    delay(5000);
}

The butterfly does not look very good in mono but it does get displayed.
There does not seem to be any way to load a JPG, PNG, BMP, … from my PC and convert to the C array.

There are online programs e.g. http://skaarhoj.com/FreeStuff/GraphicDisplayImageConverter.php that can create a suitable array from a regular JPG on the PC (resizing if necessary).

Oh, you can store the data in a C file if you make the array global.

David.

I attempted to draw a mono bitmap from the single example on your website. As it stands, the C file is attempting to store the data in SRAM. A Uno does not have enough SRAM.

I'm not using RAM to store const data. ARM GCC places it in a TEXT section at program memory flash.
It is rather a question to AVR compiler and its differences from the ARM.

There does not seem to be any way to load a JPG, PNG, BMP, ... from my PC and convert to the C array.

I can convert any image to raw data for display controller's format. This eliminates the need for using jpg/png.
Also I can store binary file at file system or directly embed file to firmware with other tools of gcc.
So... Saving jpg, png and other files in the C array is not at all an important task to consider. This is also not the best way to solve this problem.

There are online programs e.g

It's great that you have enough of this limited tool.

So what about increasing the size of the firmware when duplicating data from the header files? Is this problem present in Arduino/AVR like the ARM GCC? Or completely solved by the developers of the compiler?

I have installed Arduino IDE to check this.

Sketch with
only header

#ifndef SAMPLE_DATA_H
#define SAMPLE_DATA_H

#include <stdint.h>
#include <avr/pgmspace.h>

static const uint8_t PROGMEM data_array[1024] = { 1, 2, 3 };

#endif // SAMPLE_DATA_H

gives me following output:

Sketch uses 2554 bytes (7%) of program storage space. Maximum is 32256 bytes.
Global variables use 10 bytes (0%) of dynamic memory, leaving 2038 bytes for local variables. Maximum is 2048 bytes.


But with using of
header

#ifndef SAMPLE_DATA_H
#define SAMPLE_DATA_H

#include <stdint.h>
#include <avr/pgmspace.h>

extern const uint8_t PROGMEM data_array[1024];

#endif // SAMPLE_DATA_H

and
source

#include "sample_data.h"

const uint8_t PROGMEM data_array[1024] = { 1, 2, 3 };

gives me other output:

Sketch uses 1528 bytes (4%) of program storage space. Maximum is 32256 bytes.
Global variables use 10 bytes (0%) of dynamic memory, leaving 2038 bytes for local variables. Maximum is 2048 bytes.

Hence the obvious conclusion: your common practice (data in header files) is a bad way.

sketch_may19a-001-common-practice.zip (1.63 KB)

sketch_may19a-002-right-way.zip (1.85 KB)

You often want an image or logo displayed on a monochrome LCD or colour TFT.
If I was Michaelangelo I could draw it freehand in your PC program.

However I prefer to copy an existing professional graphic. Hence my preference to use existing PC Tools to edit, resize, recolour, ... Then create a C array suitable for the Arduino program.

Monochrome bitmaps can fit in AVR Flash memory. Colour Palette bitmaps can just fit in ARM Flash.
Several JPG, GIF, ... images can fit in AVR or ARM Flash. But they take more processing power to decode them.
You can store a massive number of full Colour BMP images on SD card and display with an AVR. You trade off processing power against SD card memory.

Yes, I agree that data arrays should be in C or C++ files. But I am not going to lose any sleep over it.

David.

david_prentice:
If I was Michaelangelo I could draw it freehand in your PC program.

You are absolutely didn’t understand goals and possibilities of my application. But I’m here not to advertise them to you.

david_prentice:
Yes, I agree that data arrays should be in C or C++ files. But I am not going to lose any sleep over it.

Well, yes, it does not matter to you. But you teach beginners the same bad practices. And then they put fonts, hundreds of kilobytes in size, into the header files, which makes the application incredibly fat.
Its a disservice.

A tree doesn't mind if a dog lifts his leg. :slight_smile:

The Arduino builds a project from INO, C++, C, S source files.

It parses the main INO file to determine any library header files and locations.
It adds "Arduino.h", inserts any forward declarations and concatenates all INO files into a regular C++ file.
The original C++, C, S project files are unchanged.

The process of finding headers and library files makes it much more user friendly than a conventional IDE where the user has to manually add Include paths and library paths.

Personally, I think that the forward declarations is BAD practice. But the paths are very effective. Yes, a computer science student should understand the significance. An Arduino punter does not need to know.

There is a certain amount of snobbery about Arduino. It hides the difficult subjects of paths and forward declarations. But it means a project just needs to have a single INO file with appropriate #include "library.h" statements.

Existing Arduino Graphics libraries and programs follow this "style" i.e. #include "font.h"
e.g. Adafruit_ILI9341, ILI9341_due, UTFT, TFT_eSPI, ...

Yes, of course it would be better C practice to add H, C pairs of font files for every local font in an Arduino project.

I know that the Linker only adds data that is actually referenced.
I know that including an unused "library.h" does not add the library code.
Yes, it is important for computer science students to understand this.

A regular Arduino punter just wants to "wash and go".

You are absolutely didn't understand goals and possibilities of my application. But I'm here not to advertise them to you.

I want to use existing artwork. I am not Michaelangelo. Your program only seems to support freehand drawing.

David.

david_prentice:
I know that the Linker only adds data that is actually referenced.
I know that including an unused "library.h" does not add the library code.
Yes, it is important for computer science students to understand this.

It was shown above that with such use of header files, these features do not work.

david_prentice:
Yes, a computer science student should understand the significance. An Arduino punter does not need to know.

What do Arduino's punter, when the program ceases to fit in memory? As I understand it, in this case, the common practice will lead to the connection of a new Arduino board, instead of optimization?

david_prentice:
A regular Arduino punter just wants to "wash and go".

Oh, that was a good idea. But because of such common practices (in a slipshod way), snobbery against Arduino does not decrease. Do not you want to change this situation, being an advanced punter of Arduino? At least, to warn newcomers that this common practice has bad consequences, but there are more correct ways, although a bit more complicated.

david_prentice:
I want to use existing artwork. I am not Michaelangelo. Your program only seems to support freehand drawing.

Obvious misconception.
Should I repeat that you absolutely do not understand the goals and capabilities of my application? Who cares, he will find out. And who does not want, because of the superficial glance, he will not see anything useful and pass by. I do not get any profit out of it to worry.

Go on. This thread started with someone wanting to display a bitmap on his 128x160 TFT. It was solved by #2.

Yes, ideally his project could be organised as project.ino, bitmaps.h bitmaps.c
bitmaps.h could ensure correct types and linkage

The OP started with PNG and used a PC tool to create the appropriate C array. I do not know which tool.

I would be interested to know whether your application is able to perform this common task.
You do not have to tell me. You can keep it a secret.

David.

Edit. My apologies. It looks as if you can "import" a graphic and "convert" it to a C file according to a "image.tmpl"

So it is possible to create C files with suitable format for Arduino projects. C files from your default template do not compile.

david_prentice:
Yes, ideally his project could be organised as project.ino, bitmaps.h bitmaps.c
bitmaps.h could ensure correct types and linkage

Definitely yes. And this is the reason why I wrote a message in this topic. Not for advertising anything.
Earlier here I saw the topic, where the Arduino's punter, following the common practice, placed a huge font in the header file. And then included it in those source code files where the font was required.

david_prentice:
I would be interested to know whether your application is able to perform this common task.
You do not have to tell me. You can keep it a secret.

It's also possible.
This secret is published on home page of app.

david_prentice:
Edit. My apologies. It looks as if you can "import" a graphic and "convert" it to a C file according to a "image.tmpl"

So it is possible to create C files with suitable format for Arduino projects. C files from your default template do not compile.

You are right.