How to overwrite over old text on an lcd tft screen?

I have a 3.5 inch tft display for my arduino mega that I want to use for my airsoft bomb. So I have to display the time it has before exploding, but I can't write over the old value of min and sec without writing over, thus making it unreadable. What I tried was to make a box over the time to make it the same colour as the background and then print the values, but this way the time flickers making it a bit hard to read in the heat of the moment. This code is to set the time for the bomb before the game even starts and store it to the EEPROM using a key switch.

This is the code I am using:

#include <EEPROM.h>
#include <Keypad.h>
#include <LCDWIKI_GUI.h>
#include <LCDWIKI_KBV.h>

LCDWIKI_KBV mylcd(ILI9486, 40, 38, 39, -1, 41);

#define BLACK 0x0000
#define BLUE 0x001F
#define RED 0xF800
#define GREEN 0x07E0
#define CYAN 0x07FF
#define MAGENTA 0xF81F
#define YELLOW 0xFFE0
#define WHITE 0xFFFF

const byte ROWS = 4;
const byte COLS = 3;
char Keys[ROWS][COLS] = {
  { '3', '2', '1' },
  { '4', '5', '6' },
  { '7', '8', '9' },
  { '*', '0', '#' }
};
byte rowPins[ROWS] = { 3, 8, 7, 5 };
byte colPins[COLS] = { 4, 2, 6 };

Keypad keypad = Keypad(makeKeymap(Keys), rowPins, colPins, ROWS, COLS);

int min, sec, newMin, newSec, i = 0, numKey = 0, numKey2 = 0;
String inputStr;

void setup() {
  Serial.begin(9600);
  mylcd.Init_LCD();
  mylcd.Fill_Screen(BLACK);
  pinMode(13, INPUT_PULLUP);
  sec = EEPROM.read(0);
  min = EEPROM.read(1);
}

void loop() {
  char keypressed = keypad.getKey();
  int but = digitalRead(13);
  mylcd.Set_Rotation(1);
  mylcd.Set_Text_Mode(1);
  //mylcd.Set_Text_Back_colour(BLACK);
  if (but == HIGH) {
    if (keypressed != NO_KEY) {
      inputStr += keypressed;
      numKey++;
      if (i == 0) {
        if (numKey == 2) {
          newSec = inputStr.toInt();
          i = 1;
          inputStr = "";
          numKey = 0;
        }
      } else if (i == 1) {
        numKey2++;
        if (numKey == 2) {
          newMin = inputStr.toInt();
          min = newMin;
          sec = newSec;
          if (sec > 59) {
            sec = sec - 60;
            min++;
            numKey2 = 0;
          }
          EEPROM.write(0, sec);
          EEPROM.write(1, min);
        }
      }
    }
    showmod();
  } else if (but == LOW) {
    showtime();
    mylcd.Fill_Rect(175, 65, 125, 35, BLACK);
  }
}

void showtime() {
  mylcd.Set_Text_colour(BLUE);
  mylcd.Set_Text_Back_colour(BLACK);
  mylcd.Set_Text_Size(5);
  if (min > 10) {
    mylcd.Print_Number_Int(min, 175, 25, 1, 0, 10);
  } else if (min < 10) {
    mylcd.Print_Number_Int(0, 175, 25, 1, 0, 10);
    mylcd.Print_Number_Int(min, 205, 25, 1, 0, 10);
  }
  mylcd.Print(":", 225, 25);
  if (sec > 10) {
    mylcd.Print_Number_Int(sec, 245, 25, 1, 0, 10);
  } else if (sec < 10) {
    mylcd.Print_Number_Int(0, 245, 25, 1, 0, 10);
    mylcd.Print_Number_Int(sec, 275, 25, 1, 0, 10);
  }
}

void showmod() {
  mylcd.Set_Text_colour(BLUE);
  mylcd.Set_Text_Back_colour(BLACK);
  mylcd.Set_Text_Size(5);
  if (min > 10) {
    mylcd.Print_Number_Int(min, 175, 25, 1, 0, 10);
  } else if (min < 10) {
    mylcd.Print_Number_Int(0, 175, 25, 1, 0, 10);
    mylcd.Print_Number_Int(min, 205, 25, 1, 0, 10);
  }
  mylcd.Print(":", 225, 25);
  if (sec > 10) {
    mylcd.Print_Number_Int(sec, 245, 25, 1, 0, 10);
  } else if (sec < 10) {
    mylcd.Print_Number_Int(0, 245, 25, 1, 0, 10);
    mylcd.Print_Number_Int(sec, 275, 25, 1, 0, 10);
  }

  mylcd.Set_Text_colour(RED);
  mylcd.Set_Text_Size(5);
  if (newMin > 10) {
    mylcd.Print_Number_Int(newMin, 175, 65, 1, 0, 10);
  } else if (newMin < 10) {
    mylcd.Print_Number_Int(0, 175, 65, 1, 0, 10);
    mylcd.Print_Number_Int(newMin, 205, 65, 1, 0, 10);
  }
  mylcd.Print(":", 225, 65);
  if (newSec > 10) {
    mylcd.Print_Number_Int(newSec, 245, 65, 1, 0, 10);
  } else if (newSec < 10) {
    mylcd.Print_Number_Int(0, 245, 65, 1, 0, 10);
    mylcd.Print_Number_Int(newSec, 275, 65, 1, 0, 10);
  }
}

Normally you can write a blank line over the area in question.

1 Like

it seems to me that you messed up the order of the lines.

Isn't it necessary to first erase the old text, and then display a new one?

1 Like

One way is to write the old value in the background colour then immediately afterwards write the new value. To do this you have to keep the old value from the previous write, write the old value in the background colour, write the new value in the text colour then save the new value as the old value ready for next time.

Drawing a rectangle with background color will be faster.

When you figure it out, let me know. I've tried a couple of techniques that should work, but they still produce visible flicker with some of the larger fonts. I'm going down the path of of only redrawing the parts that need to be redrawn. i.e., instead of updating the whole number, only update the digits that changed.

The real answer is probably to rewrite the driver, but just can't be bothered :frowning:

It is not the driver, it caused by fact that the used fonts are transparent.
Fonts, used for TFT , usally has no background

Compare the blue link text in the centre of the images:


(images itself are not related to the topic, used only as illustration)

that line is for when the key switch is off, thus exiting the "admin mode" and to cover the second line which is visible only in admin mode.

Doing so the text will flicker, I have already tried it.

Do not update the text if it was not changed
You print the time every run of loop(), with interval of range milliseconds. But your time is changed only one time in a second!
Your display is constantly redrawn and this because it flicker

By the way, where from you get the time - hour, mins and sec ?
I don't see a RTC in your code.

This code is made to set the time for the game. After I finish making it I will implement it to the main code.

It would be better do not draw it when the program not in admin mode rather than always draw and almost always hide. It can cause an additional flicker

So you need redraw time on the screen ONLY if an user changed it by knobs. Why do you draw full screen with each call of loop ?

Hey there, I managed to do something, I had to make another variable for min and sec called oldMin and oldSec, and pretty much did the x,y,aux code. These are the changes:

if (keypressed != NO_KEY) {
      inputStr += keypressed;
      numKey++;
      if (i == 0) {
        if (numKey == 2) {
          newSec = inputStr.toInt();
          i = 1;
          inputStr = "";
          numKey = 0;
        }
      } else if (i == 1) {
        numKey2++;
        if (numKey == 2) {
          newMin = inputStr.toInt();
          oldMin = min;
          oldSec = sec;
          min = newMin;
          sec = newSec;
          if (sec > 59) {
            sec = sec - 60;
            min++;
          }
          EEPROM.write(0, sec);
          EEPROM.write(1, min);
        }
      }
    }
void showmod() {
  mylcd.Set_Text_colour(RED);
  mylcd.Set_Text_Size(5);
  if (oldMin == newMin) {
    if (newMin > 10) {
      mylcd.Print_Number_Int(newMin, 175, 65, 1, 0, 10);
    } else if (newMin < 10) {
      mylcd.Print_Number_Int(0, 175, 65, 1, 0, 10);
      mylcd.Print_Number_Int(newMin, 205, 65, 1, 0, 10);
    }
  } else if (oldMin != newMin) {
    mylcd.Fill_Rect(175, 65, 60, 35, BLACK);
    if (newMin > 10) {
      mylcd.Print_Number_Int(newMin, 175, 65, 1, 0, 10);
    } else if (newMin < 10) {
      mylcd.Print_Number_Int(0, 175, 65, 1, 0, 10);
      mylcd.Print_Number_Int(newMin, 205, 65, 1, 0, 10);
    }
    oldMin = newMin;
  }
  mylcd.Print(":", 225, 65);


  if (oldSec == newSec) {
    if (newSec > 10) {
      mylcd.Print_Number_Int(newSec, 245, 65, 1, 0, 10);
    } else if (newSec < 10) {
      mylcd.Print_Number_Int(0, 245, 65, 1, 0, 10);
      mylcd.Print_Number_Int(newSec, 275, 65, 1, 0, 10);
    }
  } else if (oldSec != newSec) {
    mylcd.Fill_Rect(245, 65, 60, 35, BLACK);
    if (newSec > 10) {
      mylcd.Print_Number_Int(newSec, 245, 65, 1, 0, 10);
    } else if (newSec < 10) {
      mylcd.Print_Number_Int(0, 245, 65, 1, 0, 10);
      mylcd.Print_Number_Int(newSec, 275, 65, 1, 0, 10);
    }
    oldSec = newSec;
  }
  modTime();
}

Please explain, why do you print secs again, if it was not changed?

if you are talking about the else if, that's in case the number has only 1 digit, so it prints a 0 before the seconds to look like "08" for esthetic purposes.

No, I talking about the fact, that you should not print the value at all, if it not changed.
Regardless whether it started from 0 or not :slight_smile:

Did a bit more research into this and found out that I can update the canvas in the background then draw it to the screen and that helps. At least for the smaller fonts, it's not horrible. Using larger fonts you can see it paint the screen but it's better than the flicker was.