Go Down

Topic: ESP32, ST7735 and TFT_eSPI / TFT libraries (Read 3417 times) previous topic - next topic

carguy

Jan 13, 2018, 01:02 am Last Edit: Jan 13, 2018, 01:08 am by carguy
Hello,

I am still trying out my new ESP32 which I would like to hook up to an ST7735 1.8'' TFT.

I have just installed the latest TFT_eSPI and TFT libraries from Bodmer on Github, but I can't get the code to compile.

Everything else compiles fine in my code, but when I put tft.initR(INITR_18GREENTAB); in my setup() section, I get this error:

Code: [Select]
D:\Arduino\HeadUnitESP32\HeadUnitESP32.ino: In function 'void setup()':

HeadUnitESP32:53: error: 'INITR_18GREENTAB' was not declared in this scope

   tft.initR(INITR_18GREENTAB);

             ^

exit status 1
'INITR_18GREENTAB' was not declared in this scope



How do I initialize the display using these libraries?


I've attached my code (with which I was simply going to test out the ESP32 together with the TFT a little bit) to this post in a zip file.

pert

Code: [Select]
tft.initR(INITR_18GREENTAB);
Where did you get the idea to use this code with those libraries? My search indicates INITR_18GREENTAB is from a different library:
https://github.com/adafruit/Adafruit-ST7735-Library

How do I initialize the display using these libraries?
Did you check the example sketches included with the libraries (find them in the File > Examples menu).

david_prentice

Configure User_setup.h in the TFT_eSPI library for your ESP32 and your ST7735

carguy

ok I have updated my sketch to correctly initialize the screen with these libraries. And I have changed the User_Setup.h in the TFT library.

That has somehow made everything much worse, and these are the errors I get now:

Code: [Select]
C:\Program Files\Arduino\libraries\TFT\src\utility\Adafruit_ST7735.cpp: In member function 'void Adafruit_ST7735::commonInit(const uint8_t*)':

C:\Program Files\Arduino\libraries\TFT\src\utility\Adafruit_ST7735.cpp:349:13: error: cannot convert 'volatile uint32_t* {aka volatile unsigned int*}' to 'volatile uint8_t* {aka volatile unsigned char*}' in assignment

   csport    = portOutputRegister(digitalPinToPort(_cs));

             ^

C:\Program Files\Arduino\libraries\TFT\src\utility\Adafruit_ST7735.cpp:351:13: error: cannot convert 'volatile uint32_t* {aka volatile unsigned int*}' to 'volatile uint8_t* {aka volatile unsigned char*}' in assignment

   rsport    = portOutputRegister(digitalPinToPort(_rs));

             ^

C:\Program Files\Arduino\libraries\TFT\src\utility\Adafruit_ST7735.cpp:370:17: error: cannot convert 'volatile uint32_t* {aka volatile unsigned int*}' to 'volatile uint8_t* {aka volatile unsigned char*}' in assignment

     clkport     = portOutputRegister(digitalPinToPort(_sclk));

                 ^

C:\Program Files\Arduino\libraries\TFT\src\utility\Adafruit_ST7735.cpp:372:17: error: cannot convert 'volatile uint32_t* {aka volatile unsigned int*}' to 'volatile uint8_t* {aka volatile unsigned char*}' in assignment

     dataport    = portOutputRegister(digitalPinToPort(_sid));

                 ^

exit status 1


I have attached the changed User_Setup.h and my updated sketch to this post.

david_prentice

It looks as if you have configured User_Setup.h for your specific hardware.
I would test it with the library examples e.g. TFT_flash_jpg to check the picture colours, quality etc.

Running library examples show how to use a particular library.   e.g. what header files to include,   what constructors to use, ...

Then I tried to build your sketch.
Why have you included TFT.h ?   That is an AVR specific library that is nothing to do with TFT_eSPI.h

When I removed the TFT.h line,   your sketch showed some icons that did not look very good on my display.

David.

bodmer

@carguy

The TFT_eSPI library does not need the TFT library so that is probably the source of the error.

The TFT_eSPI library now has a function to draw FLASH bitmaps to the screen which will probably draw 8x faster than the sketch function, so you can also use that.

Here is a trimmed down version of your sketch (excluding your image header files) that works OK on my display:

Code: [Select]

#include <TFT_eSPI.h>

#include "header.h"
#include "frost.h"
#include "car.h"

TFT_eSPI tft = TFT_eSPI();

#define dimmerPin 33

void setup() {

  Serial.begin(115200);
  Serial.println("Serial connection OK.");

  tft.init();
  tft.setRotation(1);
  tft.fillScreen(0x20c0);

  ledcSetup(0, 5000, 8);
  ledcAttachPin(dimmerPin, 0);
  ledcWrite(0, 255);

  tft.pushImage(0,  0, 128, 33, header);
  tft.pushImage(0, 34, 128, 24, frost);
  tft.pushImage(0, 35, 128, 70, car);

  tft.fillRect(8, 130, 112, 2, 0xfa28);
}

void loop() {
  // put your main code here, to run repeatedly:

}
Formerly Rowboteer (now a broken user profile!)

carguy

ok, I can confirm that the flash images now display lightning fast.  :)  This will also help with my bespoke font libraries which I am creating at the moment. My first experiment with that is included in the attachments at the bottom of this post.

David: I've adjusted the User_Setup.h for my display, which turns out to be a "GREENTAB2" display. Now the colors are displayed correctly, except the rows of the flash images aren't right, and there is a margin of garbled pixels to the left:




This problem persists whether it's in "0" or "2" rotation mode.

In landscape mode (rotation set to "1"), the icons are displayed correctly, for whatever reason, but I've still got a garbled margin:



I would like the display to be in rotation mode "2", because the display of this trip computer will be mounted in the top of the center console, and the usual angle from which I will be looking at it (in an LHD car) will be from the top left, more or less. And on this display, the colors appear the most stable at such an angle when the rotation is set to "2".


I've made these icons with LCD Image Converter, and I'm not aware that I would have got that part wrong. I've included the three icons in a zip folder at the bottom of this post with everything else.


I'm not sure yet how to use my custom font libraries; the general idea will be to have a function like this:


void printMyText(String myText, String fontName, byte x_start, byte y_start)


Within this function, the text string will then be broken up into its individual characters and the characters will then be matched against the elements in const charData_SF19PtRed SF19PtRed[][]   at the bottom there, and then the correct character pixel information will be retrieved from the const uint16_t SF19PtRed[][] above and pushed onto the TFT character by character.

I'm not sure yet how to search for the correct character; do I have to loop through const charData_SF19PtRed[][] each time for each character? I don't know...

But I guess it's a good idea to first get the "garbled pixels" issues sorted.

david_prentice

I think that Bodmer offers you several choices for the initialisation.  e.g. ST7735_BLACKTAB, ST7735_GREENTAB, ...

I suggest that you try all the permutations.
One of them will give correct colours,  margins etc.

Bodmer also provides a diagnosis skectch to read the ID or at least determine whether you have a ST7735S.

The ESP32 (and ESP8266) work very nicely with SPI displays.

Rendering an anti-aliased Font should be pretty straightforward.   It just means using a 4-colour or 16-colour bitmap instead of the regular 2-colour monochrome Font bitmaps.

David.

carguy

ok I just figured out that my GREENTAB is actually a BLACKTAB  :)

It works perfectly now.

The next thing I will try tonight is to get my experimental font to display something on the screen.

carguy

#9
Jan 13, 2018, 10:53 pm Last Edit: Jan 13, 2018, 10:54 pm by carguy
So wth the initial niggles out of the way, I have now moved on to a font library and a function that puts writing into 16-bit RGB565 bitmaps.

This is not doing what it should at the moment and produces a number of compile errors, it will need a fair bit more work. But maybe you guys will have a few ideas:

Code: [Select]
void printMyText(String myText, String fontName, byte x_start, byte y_start) {

  int textLength = myText.length();
  char textCopy[50];
  myText.toCharArray(textCopy, 50);

  uint16_t imageData;

  byte startX = x_start;
  byte startY = y_start;
  byte x_width;
  byte y_height;


  boolean matchFound = false;

  for (int i = 0; i <= textLength; i++) {

    int characterTableLength =  sizeof(NG20ptLightRed_coords) / sizeof(NG20ptLightRed_coords[0]);

    for (int j = 0; j <= characterTableLength; j++) {

      //Looking up matching character in character table "NG20ptLightRed_coords[][]

      char currentChar[1];
      currentChar[0] =  NG20ptLightRed_coords[j][0];

      if (textCopy[i] == currentChar[0] ) {

        // if match found...

        matchFound = true;
        imageData = NG20ptLightRed[i];

        x_width = NG20ptLightRed_coords[j][1];
        y_height = NG20ptLightRed_coords[j][2];

        // ... print the character out on the screen at the desired pixel coordinates

        tft.setAddrWindow(startX, startY, startX + y_width - 1, startY + y_height - 1);

        int count = 0;

        for (uint16_t k = 0; k < height; k++) {
          for (uint16_t l = 0; l < width; l++) {

            tft.pushColor(pgm_read_word(&NG20ptLightRed[j][count++]));
          }
        }

        startX += (x_width + 1);
        startY += (y_height + 1);
      }

      // If a character is not found in coordinates table, print a blank character

      if (textCopy[i] && j == characterTableLength && !matchFound) {

        tft.fillRect(startX, startY, x_width, y_height, 0x20c0);
        matchFound = false;
      }
    }
  }
}




This is a rudimentary function for now; later when it is finished, I want it to be able to use a number of different fonts.


The font library .h file that is used in this sketch is included in the zip file below.

I take it I can't just use tft.pushImage() in this function, as I would have no array name to reference for a particular character in my function arguments the way my font library is set up at the moment.

But see for yourselves in the attachment.

david_prentice

Please use consistent names for your ZIP file(s).

Where did you get your "Font" from?
Where did you get your "converter" program from?

As a general rule,   fonts in embedded systems consist of a table that lists number of letters and general flags and tables for width, height, glyph_width, glyph_height, glyph_offset, bitmap_position for each letter.

Several schemes are used for minimising the storage size of the bitmaps.   e.g just storing the glyph rather than the whole rectangle reserved for the letter.

To render a letter,  you look up in the tables to find the address of the bitmap, dimensions etc.
And draw the bitmap in the required place on your screen.

It is pretty straightforward but gets a little fiddly if some tables are in SRAM,  others in Flash.  e.g. on an AVR.

Provide some links.   Then we can show you what to do.

But the general strategy is the same.  i.e. look up the letter.   Find the memory address of the bitmap.   Drawing a monochrome bitmap or a 16-colour bitmap is trivial.

David.

carguy

As it says in the header information at the top of the file "NG20ptLightRed", the font was created using LCD Image Converter, which is a software project by "riuson", who is also a regular on this forum.

http://www.riuson.com/lcd-image-converter

It allows you to create your own templates to format the information in your .h output file, and that's what I did.

Instead of having one single continuous pane of RGB565 pixels and then accessing the characters by their offset position, I have tried out the approach of letting the array with all the pixel information be a two-dimensional array, with every character occupying one sub-array. That's what you see at the beginning of the .h file in the array const uint16_t NG20ptLightRed[69][323] .

And then there's a struct/array at the very bottom of that file that lists all the characters in my character set (I will be working with a "reduced" character set, which will only contain the characters +,-./0123456789:;ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz). This array also contains the height and width of each character as the second and third respective sub-array element.

The basic idea is to go through the string that I want to print out as bitmap characters by matching every letter of my string against the characters as found in the subarrays of const charData_NG20ptLightRed NG20ptLightRed_coords[69][3] .

My problems still with this approach are that I don't quite understand how I can match single letters of the text that I would like to print out against that array.

Here's my printMyText() function in its current form:

Code: [Select]
void printMyText(String myText, String fontName, byte x_start, byte y_start) {

  int textLength = myText.length();
  byte startX = x_start;
  byte startY = y_start;
  byte x_width;
  byte y_height;
  boolean matchFound = false;

  for (int i = 0; i <= textLength; i++) {

    int characterTableLength =  sizeof(NG20ptLightRed_coords) / sizeof(NG20ptLightRed_coords[0]);

    for (int j = 0; j <= characterTableLength; j++) {

      //Looking up matching character in character table "NG20ptLightRed_coords[][]
      if (myText.charAt(i) == NG20ptLightRed_coords[j][0]) {

        // if match found...
        matchFound = true;
        x_width = NG20ptLightRed_coords[j][1];
        y_height = NG20ptLightRed_coords[j][2];

        // ... print the character out on the screen at the desired pixel coordinates
        tft.setAddrWindow(startX, startY, startX + x_width - 1, startY + y_height - 1);

        int count = 0;

        for (uint16_t k = 0; k < y_height; k++) {
          for (uint16_t l = 0; l < x_width; l++) {

            tft.pushColor(pgm_read_word(&NG20ptLightRed[j][count++]));
          }
        }

        startX += (x_width + 1);
        startY += (y_height + 1);
      }

      // If a character is not found in coordinates table, print a blank character
      if (j == characterTableLength && !matchFound) {

        tft.fillRect(startX, startY, x_width, y_height, 0x20c0);
      }
      matchFound = false;
    }
  }
}




And these are the error messages I am getting:

Code: [Select]
D:\Arduino\HeadUnitESP32\HeadUnitESP32.ino: In function 'void printMyText(String, String, byte, byte)':

HeadUnitESP32:29: error: no match for 'operator==' (operand types are 'char' and 'const charData_NG20ptLightRed')

       if (myText.charAt(i) == NG20ptLightRed_coords[j][0]) {

                            ^

HeadUnitESP32:33: error: cannot convert 'const charData_NG20ptLightRed' to 'byte {aka unsigned char}' in assignment

         x_width = NG20ptLightRed_coords[j][1];

                 ^

HeadUnitESP32:34: error: cannot convert 'const charData_NG20ptLightRed' to 'byte {aka unsigned char}' in assignment

         y_height = NG20ptLightRed_coords[j][2];

                  ^

D:\Arduino\HeadUnitESP32\HeadUnitESP32.ino: In function 'void setup()':

HeadUnitESP32:92: error: variable or field 'printMyText' declared void

   void printMyText("Frost", "NG20ptLightRed", 40, 28);

                   ^

exit status 1
no match for 'operator==' (operand types are 'char' and 'const charData_NG20ptLightRed')



I'm not entirely sure how to convert the variable types so that I can actually compare and match them.


I've included all the files in a (consistently named) zip file again.


david_prentice

#12
Jan 14, 2018, 02:00 pm Last Edit: Jan 14, 2018, 02:03 pm by david_prentice
I looked at your "Font" and changed some weirdos.  e.g.
Code: [Select]

typedef struct {

    char* character_NG20ptLightRed;
//    uint16_t charData_NG20ptLightRed;   //your inits do not have this field
    byte height_NG20ptLightRed;
    byte width_NG20ptLightRed;
} charData_NG20ptLightRed;

// you had a multi-dimensional array of structs
const charData_NG20ptLightRed NG20ptLightRed_coords[69] = {
    {"+", 19, 17}, {",", 19, 4}, {"-", 19, 7}, {".", 19, 4},
    ...


And then I amended your sketch to :
Code: [Select]


#include <TFT_eSPI.h>

#include "NG20ptLightRed.h"

#include "header.h"
#include "frost.h"
#include "car.h"

TFT_eSPI tft = TFT_eSPI();

byte printLetter(char c, byte startX, byte startY)
{
    byte idx;
    // look up the character.  Find index of the bitmap etc.
    // most fonts have sequences e.g. ascii ' ' to ascii '}'
    // this means you can calculate the index immediately
    for (idx = 0; idx < 69; idx++) {
        if (NG20ptLightRed_coords[idx].character_NG20ptLightRed[0] == c)
            break;
    }
    byte x_width = NG20ptLightRed_coords[idx].width_NG20ptLightRed;
    byte y_height = NG20ptLightRed_coords[idx].height_NG20ptLightRed;
    uint16_t *imageData = (uint16_t*)NG20ptLightRed[idx];
    tft.setAddrWindow(startX, startY, startX + x_width - 1, startY + y_height - 1);
    tft.pushColors(imageData, x_width * y_height);
    return x_width;
}

byte printString(char *p, byte x_start, byte y_start)
{
    char c;
    while (c = *p++) {
        x_start += printLetter(c, x_start, y_start);
    }
}

#define dimmerPin 33
uint16_t buf[128 * 70];   //so we can build bitmaps in the correct endian

void pushImage(uint32_t x0, uint32_t y0, uint32_t w, uint32_t h, uint16_t *data)
{
    for (uint32_t i = 0; i < w * h; i++) {
        uint16_t x = data[i];
        buf[i] = (x >> 8) | (x << 8);
    }
    tft.pushRect(x0, y0, w, h, buf);

}

void setup()
{
    Serial.begin(115200);
    Serial.println("Serial connection OK.");

    tft.init();
    tft.setRotation(2);
    tft.fillScreen(0x20c0);

    ledcSetup(0, 5000, 8);
    ledcAttachPin(dimmerPin, 0);
    ledcWrite(0, 255);

    delay(2000);
    // your bitmaps are in the wrong endian
    tft.pushRect(0,  0, 128, 33, (uint16_t*)header);
    //    tft.pushRect(0, 34, 128, 24, (uint16_t*)frost);
    tft.pushRect(0, 58, 128, 70, (uint16_t*)car);

    tft.fillRect(8, 130, 112, 2, 0xfa28);

    printString("Frost", 40, 28);
    delay(5000);

    // call helper function to change endian
    pushImage(0,  0, 128, 33, (uint16_t*)header);
    pushImage(0, 58, 128, 70, (uint16_t*)car);

}

void loop()
{
}


I know that the ESP32 has got oodles of Flash.   But your "Font" is unbelievably inefficient for storage.
Oh,  and the text rendering is not that impressive.

Yes,  I could tidy up the Font access and make it understand AVR PROGMEM.

David.

bodmer

Mmm... well there were a lot of logical, flow and syntax errors but I have probably fixed most of them!

You can use pushImage, see modded code, as it just needs a pointer to the FLASH, an array name is an address pointer.  To point to an array with [index] it is a case of converting it to an address e.g. &name[25]
Formerly Rowboteer (now a broken user profile!)

carguy

#14
Jan 14, 2018, 05:20 pm Last Edit: Jan 14, 2018, 05:21 pm by carguy
Thanks everybody so far,

I will have a more detailed look at your posts and your code as soon as I get home (I'm at another computer right now where I don't have access to my Arduino stuff).

I will also contact riuson again, the guy who made the LCD Image Converter, and ask him if there is a way I can put offset information for all the characters in my struct array.

Technically, it's not difficult at all using a custom template file within LCD Image Converter to have the RGB565 pixel information put out any way you like, meaning it's also possible to put it in one continuous pane within a one-dimensional array.

What I wasn't quite able to figure out is how to then put the pixel offset information for the individual characters/glyphs in the second array. So I am going to have to ask riuson what I have to state in my template file to achieve that.

This would probably shrink down the effective memory size of my font arrays, won't it?


Also, LCD Image Converter lets you choose freely between big-endian and little-endian, so I would just have to toggle that setting and then generate a new file with the correct-endian pixel definitions.

Go Up