Display stays white because of Fill_Rectangle

Hi all,

I`m working on a fan controller for my pc and have some trouble with my TFT Display Shield.
It´s an ILI9486: Link

It is mounted to an Arduino Mega 2560 Board.

All Examples from its libary work fine - so does 99% of my code.
In the setup I`ve created a layout for the displayed information (picture).
In the loop I first do some calculations and then I use the function “anzeige()” to display the values.

The Problem within this function is the function “my_lcd.Fill_Rectangle” at the end of the code. If I comment these for and if statements out, my Display works fine. If I activate them again, the Display stays white.
There is no error message and if I place serial.print inside the for and if´s they also work.
So everything works except the “my_lcd.Fill_Rectangle” inside these statements.

My attempts so far:

  • Serial.printed all values to see if something isn´t right → values are good
  • A different function as seen in code → didnt work
  • Fill_Rectangle funtion(s) outside the for statement with different (lower) values → worked
#include <OneWire.h>                  // OneWire-Bibliothek einbinden
#include <DallasTemperature.h>        // DS18B20-Bibliothek einbinden
#include <LCDWIKI_GUI.h>              //Display Libary: Core graphics library
#include <LCDWIKI_KBV.h>              //Display Libary: Hardware-specific library

//Konstanten global
unsigned long previousMillis = 0;
const int fanpins [4] = {6, 7, 8, 9}; 
const int RGB [3] = {5, 3, 2}; //Rot grün blau
const int dpin_Temp [2] = {A3, A4}; 
const int apin_Temp [2] = {A1, A2};
const int threshold [4] = {160, 180, 150 , 50}; 
const int thresh_loud [4] = {215, 225, 200 , 150}; 
const int WasserTemp [3] = {25 - 10, 38 - 10, 42 - 10};
const int threshold_test [4] = {0, 0, 0 , 0};
//Variablen global

LCDWIKI_KBV my_lcd(ILI9486, 40, 38, 39, 44, 41); //model,cs,cd,wr,rd,reset

float Celsius [2]; //Feld für main
double oneTemp [2];
int fanspeed_wasser[2] = {};
int fanspeed_luft [2] = {};

//Variablen für LCD global
//Hälfte der Displaygröße
int16_t hhigh = my_lcd.Get_Display_Height() / 2 ;
int16_t hwide = my_lcd.Get_Display_Width() / 2 ;

//Koordinaten Grad Zeichen
uint16_t Grad [4] = {29, 193, 2, 3}; // X-Grad 1, X-Grad 2, delta x Grad, Grad [3] Grad

//Delta Koordiante Celsius
int8_t dxC = 8 ;

//Y-Koordinaten Texte
uint16_t yText [3] = {40, 75, 270}; //

//Delta Koordiante Temperatur Werte
int8_t dxZahl = 80;

//Rechteck-Koordinaten Fans
int16_t yy [2] = {247, 254};
int16_t tt [7] = {0, 56, 240 - 31, 356, 118, 240 + 31 , 418};

uint16_t stufen_neu [3] = {0, 0, 0};
uint16_t stufen_alt [3] = {0, 0, 0} ;

//define some colour values
#define BLACK   0x0000
#define BLUE    0x001F
#define RED     0xF800
#define GREEN   0x07E0
#define CYAN    0x07FF
#define MAGENTA 0xF81F
#define YELLOW  0xFFE0
#define WHITE   0xFFFF
#define COLD    0x31FF
#define GRAY    70, 70, 70

OneWire onewire0(dpin_Temp[0]);               // OneWire Referenz setzen
DallasTemperature sensors0(&onewire0);  // DS18B20 initialisieren
OneWire onewire1(dpin_Temp[1]);               // OneWire Referenz setzen
DallasTemperature sensors1(&onewire1);  // DS18B20 initialisieren

void setup() {
  Serial.begin(9600);
  TCCR2B = TCCR2B & 0b11111000 | 0x01;    //Timer 2 auf 31.300 Hz
  TCCR4B = TCCR4B & 0b11111000 | 0x01;    //Timer 4 auf 31.300 Hz
  pinMode (RGB, OUTPUT);
  pinMode (fanpins, OUTPUT);
  pinMode (dpin_Temp, INPUT);
  pinMode (apin_Temp, INPUT);

  //LCD Display
  my_lcd.Init_LCD();
  my_lcd.Fill_Screen(BLACK);
  my_lcd.Set_Rotation(1);
  my_lcd.Set_Text_colour(WHITE);
  my_lcd.Set_Text_Size(3.5);
  my_lcd.Set_Text_Mode(1);
  my_lcd.Print_String("Temperaturen", CENTER, 3 );
  my_lcd.Print_String("Wasser", LEFT + 2, yText [0] );
  my_lcd.Print_String("Luft", LEFT + 2, yText [1] );

  //Wasser Temps Raster
  my_lcd.Set_Text_colour(COLD);
  my_lcd.Print_String("C", hhigh + Grad [0] + dxC, yText [0] ); //Erstes C
  my_lcd.Set_Draw_color(COLD);
  my_lcd.Draw_Circle (hhigh + Grad [0], yText [0] + Grad [2], Grad [3]);
  my_lcd.Set_Text_colour(RED);
  my_lcd.Print_String("C", hhigh + Grad [1] + dxC, yText [0] ); //Zweites C
  my_lcd.Set_Draw_color(RED);
  my_lcd.Draw_Circle (hhigh + Grad [1], yText [0] + Grad [2] , Grad [3]);

  //Lüfter Bezeichungnen
  my_lcd.Set_Text_Size(2);
  my_lcd.Print_String("Radiator", hhigh - 200, yText [2] );
  my_lcd.Print_String("unten", hhigh - 200 + 18, yText [2] + 20 );
  my_lcd.Print_String("Radiator", CENTER, yText [2] );
  my_lcd.Print_String("oben", CENTER, yText [2] + 20 );
  my_lcd.Print_String("Gehaeuse", hhigh + 100 , yText [2] );

  //Template Power Fans
  my_lcd.Set_Draw_color(GRAY);

  for (uint8_t t = 1 ; t < 4 ; t++)
  {
    uint8_t rr = 0;
    for (uint8_t r = 0 ; r < 10; r ++) {

      my_lcd.Draw_Rectangle ( tt[t], yy[0] - rr, tt[t + 3] , yy[1] - rr) ;
      rr = rr + 13;
    }
  }
}

void loop() {
  //timed interval
  uint16_t interval = 2000;
  unsigned long currentMillis = millis();
  if (currentMillis - previousMillis >= interval ) {
    // 1. Temperatures
    //....1.1 Water
    NTC_temperaturberechnung(Celsius);

    //....1.2Air via oneWire
    Luft_Temperaturen (oneTemp);
  }
  // 2. Fan controller
  //....2.1 Radiatorlüfter Geschwindigkeit
  WasserLuefter (fanspeed_wasser);
  analogWrite (fanpins [1], fanspeed_wasser [0]);
  analogWrite (fanpins [2], fanspeed_wasser [1]);
  //....2.2 Gehäuselüfter Geschwindigkeit
  GehaeuseLuefter (fanspeed_luft);
  analogWrite (fanpins [0], fanspeed_luft [0] );
  analogWrite (fanpins [3], fanspeed_luft [1] );
  //analogWrite (fanpins [4], fanspeed_luft [2] );

  //3. Display
  anzeige();
}

void anzeige () // LCD settings
{ uint16_t d;
  uint16_t pwm [3] = {}; //0:none 1:SP  2:EK  3:Fractal
  uint16_t prozent [3] = {};
  uint16_t fanspeeds [3];
  uint8_t s = 0;
  uint8_t a = 0;

  //water temp
  my_lcd.Set_Text_Back_colour (BLACK);
  my_lcd.Set_Text_Size(3);
  my_lcd.Set_Text_Mode(0);
  my_lcd.Set_Text_colour(COLD);
  //my_lcd.Set_Text_Cousur(hhigh + Grad [0] - dxZahl, yText [0]);
  my_lcd.Print_Number_Float(Celsius [0] , 1, hhigh + Grad [0] - dxZahl, yText [0], '.',  1, ' ');
  my_lcd.Set_Text_colour(RED);
  my_lcd.Set_Text_Cousur(hhigh + Grad [1] - dxZahl , yText [0]);
  my_lcd.Print_Number_Float(Celsius [1], 1 , hhigh + Grad [1] - dxZahl , yText [0], '.',  1, ' ');

  //Air temp
  my_lcd.Set_Text_colour(WHITE);
  my_lcd.Print_Number_Float( oneTemp [0] , 1, hhigh + Grad [0] - dxZahl, yText [1], '.',  1, ' ');
  my_lcd.Print_Number_Float( oneTemp [1], 1 , hhigh + Grad [1] - dxZahl , yText [1], '.',  1, ' ');

  //Power Fans

  // my_lcd.Fill_Rectangle (209, 247, 256, 260); //### works here
  // my_lcd.Fill_Rect (209, 247, 62, 5, RED); //### works here
  fanspeeds [0] = fanspeed_wasser [0];
  fanspeeds [1] = fanspeed_wasser [1];
  fanspeeds [2] = fanspeed_luft [1];

  if (0 > 1) {  // ### This IF is just for testing purposes to enable oder disable the corrupt part
    a = 0;
    for ( a = 0; a < 3 ; a++ )
    {
      my_lcd.Set_Draw_color(70, 70, 70);
      my_lcd.Fill_Rectangle (100, 100, 110, 110);
      prozent[a] = map (fanspeeds[a], threshold_test[a + 1], 255, 0, 100);
      stufen_neu [a] = round(prozent[a] / 10.0);
      if (stufen_neu [a] != stufen_alt [a]) {
        my_lcd.Set_Draw_color(70, 70, 70);
        my_lcd.Fill_Rectangle (100 + 10, 100 + 10, 110 + 10, 110 + 10);
        d = 0;
        stufen_alt [a] = stufen_neu [a];
        for (s = 0 ; s < stufen_neu [a] ; s++)
        {
          my_lcd.Set_Draw_color(70, 70, 70);
          my_lcd.Fill_Rectangle (  tt[a], yy[0] - d, tt[a + 3] , yy[1] - d) ; //!! makes the display sta white
          //my_lcd.Fill_Rect (tt[a], yy[0] - d, 62, 7, WHITE); //also doesn´t work
          d = d + 13;
        }
      }
      else {
        Serial.print("No temperature change in sensor");
        Serial.print(a);
        Serial.print("\n ");
        continue;
      }
    }
  }
}

No one in their right mind would use Badly Spelled LCDWIKI library(s).

I strongly advise you to use Bodmer's TFT_HX8357.h library which uses regular GFX methods. Most Arduino TFT projects use GFX-style libraries. And we can always help you port existing projects.

Your Shield is also supported by UTFT which has some unusual graphics methods but at least they are spelled ok.
There are a lot of UTFT projects but not as common as Adafruit_GFX projects.

David.

Redesign the function

void anzeige ()

I imagine you want to get an effect of vertical bars that adapt to the value of the readings.

To obtain that effect you are looking for, convert the value of the reading into a percentage. Draw a rectangle from bottom to top considering the percentage value. The other rectangle is drawn from top to bottom with the value (100-percent).

david_prentice:
I strongly advise you to use Bodmer’s TFT_HX8357.h library which uses regular GFX methods. Most Arduino TFT projects use GFX-style libraries. And we can always help you port existing projects.

Ok thank you for the advice. I will change the library. Tbh I am quite new to LCDs and had no idea^^
How is this porting done? I would have changed the code manually.

TFTLCDCyg:
I imagine you want to get an effect of vertical bars that adapt to the value of the readings.

To obtain that effect you are looking for, convert the value of the reading into a percentage. Draw a rectangle from bottom to top considering the percentage value. The other rectangle is drawn from top to bottom with the value (100-percent).

Thats right (just horizontal ones because of the rotation). I actually do a conversion to percentage to draw that many filled rectangles into the blank ones picture from bottom to top. Is that what you mean or could you give me an example?

How is this porting done? I would have changed the code manually.

You just change the library include file, constructor, and possibly the tft.begin() argument for most programs.
Occasionally there is a hardware-specific item.

Adafruit_GFX graphics methods are used by 90% of public Arduino programs.
So most programs will build and run quite easily.

It is always wise to run all the TFT_HX8357 library examples first.
It shows you what things are possible. Gives you ideas.

Gain experience with the examples before writing your own programs.

David.

You should familiarize yourself with the way the library draws primitives.

It is not only copy and paste code and hope it works, you must imagine what the program should do and write on paper step by step, so that it is reflected in the TFT

The distribution of the pixels on the screen is not equal to the XY plane that we learned in school, in the TFT the upper left corner is 0.0

To complicate things we want to represent graphics on the TFT that follow the opposite logic, in which the lower left corner should be 0,0

In the part that you control the reading interval of the sensors:

   uint16_t interval = 2000;
   unsigned long currentMillis = millis ();
   if (currentMillis - previousMillis> = interval) {

You should have this:

  uint16_t interval = 2000;
   unsigned long currentMillis = millis ();
   if (currentMillis - previousMillis> = interval) {
   previousMillis = currentMillis;


Teensy 4 + ILI9488 (I adapted your sketch to the ILI9488_t3 library)

To convert the reading into a proportion, we can use the map function, to obtain the segment to which the reading corresponds, once the scale we want to use is assigned

N2 = map (sensor2, 0, 100, 0, 9);

T4x_test02_base.zip (4.15 KB)

Sometimes the map function behaves not the way we want it to. To better narrow the data into the appropriate segment, it is best to perform interval comparisons, for example:

    if ((sensor0>=0) && (sensor0<=10))
    {
      N0 = 0;
    }

    if ((sensor0>=11) && (sensor0<=20))
    {
      N0 = 1;
    }

    if ((sensor0>=21) && (sensor0<=30))
    {
      N0 = 2;
    }    

    if ((sensor0>=31) && (sensor0<=40))
    {
      N0 = 3;
    }    

    if ((sensor0>=41) && (sensor0<=50))
    {
      N0 = 4;
    }
    
    if ((sensor0>=51) && (sensor0<=60))
    {
      N0 = 5;
    }    

    if ((sensor0>=61) && (sensor0<=70))
    {
      N0 = 6;
    }
    
    if ((sensor0>=71) && (sensor0<=80))
    {
      N0 = 7;
    }
    
    if ((sensor0>=81) && (sensor0<=90))
    {
      N0 = 8;
    }    

    if ((sensor0>=91) && (sensor0<=100))
    {
      N0 = 9;
    }

This approximation will greatly improve the behavior of the segment chart. However, it will require more support from the MCU. It is possible that in AVR we are practically demanding too much with this approach, resulting in a slow response in the TFT.

An extra adjustment, to improve the behavior of the bars, eliminating the flicker in each one

T4x_test04.zip (5.43 KB)

Confession: I have not read your code.

Looking at the photo in #6 you want a 3 digit number and 0-10 bars displayed.

As always, use some intelligence e.g. remember previous value.
At worst you have to update 3 digits and add / remove one bar.
In practice you only alter one digit and occasionally alter one bar.
But mostly there is no change at all.

The "intelligence" overhead is trivial. The performance improvement is dramatic.

I might run this on a 16MHz Uno. Teensy4 is much too fast for a realistic comparison.

David.

Certainly, the code was like a kind of performance scan in a borderline case. What the sketch does is present a sequence from 0 to 100 and then from 100 to 0, with a sensing interval of 0 millis and changes in temperature of 1 unit.

In actual terms of use, the reading present on each sensor will not change as drastically. To do some quick test, I connected an i2C MAX6697 sensor that can monitor up to 6 external temperature sensors, plus a local one, installed on the chip itself. Here the result:

TFT ILI9488

Thank you TFTLCDCyg for the awesome work. The setup of your video is what I´m aiming for.

ATM I´m working through your code and change the code to the tft library.
So 3 x 10 If-Statement are more reliable but need a bit more processing power?
But I will have to use the map function because the bars represent the power of the fans which is calaculated from the PWM value. And it ranges, depending on the fan, from e.g. 215 to 255 or 50 to 255.

@David: Also thank you for the advice. There will be even more: 4 3-digit values (temperatures) and 3 x 0-10 bars for the fans.
I will implement more “intelligence” - this should be a simple If-statement if I´m correct?

-Maxi

Be careful in the code, it is designed for primitives with the adafruit GFX style. For UTFT you have to use the instructions to draw primitives corresponding to the exercise.

PS: how pretentious it would be to suggest that you upgrade your project to a teensy 4.1 with TFT SPI-ILI9488.

I like to look at the TFTs that are in the memory drawer from time to time, it helps me to dust off ideas and refresh my thoughts

Happy new year to you :slight_smile:
Little Update: I`ve change the library and re-wrote the code for displaying stuff. There is no error when uploading and all other examples from the TFT_HX8357 library work. BUT I still get the same result as before: The display stays white :-/

My first thought was, that I made a mistake in the setup file of the library, but the examples work, so I´m not sure about this.
2nd guess was my wires, but since the LCD is a shield, same white display.

I uploaded the library and the .ino to my google drive ,if you want to check these.

-Maxi

From #0. You have an ILI9486
From your Google Drive. You seem to have configured User_Setup.h for the ILI9486 correctly.

I would expect all of Bodmer's TFT_HX8357.h examples to work out of the box.
Please confirm. Or quote a problem example by name.

So it is simply a question of writing a sketch using GFX methods.
Note that Bodmer's text drawing methods are not the same as Adafruit_GFX but regular graphics like Circles, Rects, Triangles, Lines, ... should be the same.

I would guess that you have plenty of GPIO pins available for external electronics.
Make sure that you don't have any duplicates.

David.

All examples work. Some of them even have the fillRect command, so it's wierd that mine doesn't work.
I use very simple and only few functions like drawString, drawCircle, drawRect, drawFloat, ...

Talking pins: The shield uses 2 separate pins - apart from the digital pins - that plug into 3.3V and reset. Do these also need to be configured in the Setup.h or TFT_HX8357.h ? What pin number would they be?

No, you don’t need anything more than to specify ILI9486.
You have a regular Mega2560 Shield. All the wiring is fixed. You just plug it in. Most signals are present on the 18x2.

3.3V and RESET are not on the 18x2. Hence the lonely 2-pin male.

I don’t have your hardware. But the general rule is to insert a Serial.println("end of setup()"); while(1); at the end of setup(). Observe whether the initial screen is drawn correctly.

If so, move the progress statements to different points in the loop() function.
e.g. Serial.print() your location. while(1) provides a “stop when you get here”

This will show where things start to go wrong.
If you are worried about fillRect() statements, simply add some Serial.print() lines to show the arguments.

Quite honestly, fillRect() should always test for legal arguments e.g. x, y should be >= 0 and x+w, y+h should be < width and < height.
Without studying Bodmer’s library code I would be confident that he does these checks.
But likewise, you should never present illegal arguments in the first place.

David.

Ok this is a very effective methode. And I found something, but can't say what it means.

If this Serial.println() and while(1) ist placed like this (around line 140)

  if (currentMillis - previousMillis >= interval ) {
    Serial.println("end of if ()");
    while (1);
    previousMillis = currentMillis;

the screen shows the correct screen but then flashes white like every 200ms (estimated, no value from code is similar). And the serial.print doesn't work, too. It says:
' ⸮t⸮t⸮t⸮t⸮t⸮t⸮t⸮t⸮t⸮tstst⸮t⸮tst⸮t⸮t⸮t⸮t⸮t⸮t⸮t⸮t⸮t⸮tst⸮t⸮t⸮tst⸮tst⸮t;t⸮t⸮t⸮t⸮t⸮t⸮t⸮t⸮t⸮t{t⸮t⸮t⸮t⸮t⸮tst{t⸮t ' and adds more characters every cycle.

Your code snippet will only show "end of if" and then stop.
A more accurate message would be "if is true"

Any other output would come from a different Serial.print()

I suggest that you place two debug lines e.g.

  if (currentMillis - previousMillis >= interval ) {
    Serial.println("if (true)");
    while (1);
    previousMillis = currentMillis;

    // 1. Temperaturen
    //....1.1 WasserTemperatur bestimmen
    NTC_temperaturberechnung(Celsius);

    //....1.2Lufttemperaturen über oneWire
    Luft_Temperaturen (oneTemp);
    state = 1;
  }
  else {
    Serial.println("if (false)");
    while (1);
    state = 0;
  }

Obviously you choose strategic places to stop i.e. insert while(1);

The White screen sounds like your sketch is crashing and re-starting.

David.

I changed the while in else for a delay(3000) to continue the loop. The serial monitor only says if (false) and the display now blinks every 3 sec. I have no idea why that could be... is my arduino faulty?

  if (currentMillis - previousMillis >= interval ) {
    Serial.println("if (true)");
    while (1);
    previousMillis = currentMillis;
    // 1. Temperaturen
    //....1.1 WasserTemperatur bestimmen
    NTC_temperaturberechnung(Celsius);

    //....1.2Lufttemperaturen über oneWire
    Luft_Temperaturen (oneTemp);
    state = 1;
  }
  else {
    Serial.println("if (false)");
    delay(3000);
    //while (1);
    state = 0;
  }

I am sure that your Arduino is fine. You just need to do a little more debugging.

e.g. comment WasserLuefter() or GehaeuseLuefter()

You can either start with nothing and add sequences until it fails.
Or start with complex and remove sequences until better.

Personally, I think the first is better strategy.
Keep setup() entire.
Comment the whole body of the loop() function.
Add small sequences to loop() until failure.

These are just standard debugging procedures. You don't need much more than the Serial Terminal.

David.

Ok sounds good to me - I will do this.
Thank you
-Maxi