Utilisation d'un écran TFT SPI 128x160

Bonjour @ tous,

Dans un projet en court de réalisation d'une horloge avec un esp32 et des bandes led rgb, je voudrai y ajouter un petit écran TFT SPI 128x160. J'ai un peu (même beaucoup) galéré pour le mettre en route, mais c'est bon, il fonctionne et je commence à comprendre son fonctionnement.
J'ai trouvé comment afficher un texte, mais c'est à partir des coordonnées (x et y) de départ avec "tft.setCursor(20, 45);" par exemple. Ce que je voudrai c'est centrer les différentes lignes.
Est-ce que quelqu'un aurait un lien où je pourrai trouver les différentes commandes possibles ou m'aider à trouver la solution, s'il vous plait?
Je vous met juste une partie du code que j'utilise car je suis parti d'un code avec plein de fonction (exemple d'affichage) que je n'utilise pas.

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

// These pins will also work for the 1.8" TFT shield

//ESP32-WROOM
#define TFT_DC 12 //A0
#define TFT_CS 13 //CS
#define TFT_MOSI 14 //SDA
#define TFT_CLK 27 //SCK
#define TFT_RST 0  
#define TFT_MISO 0 

Adafruit_ST7735 tft = Adafruit_ST7735(TFT_CS, TFT_DC, TFT_MOSI, TFT_CLK, TFT_RST);

  int jour = 25;
  int mois = 12;
  int annee = 2023;
  int heure = 20;
  int mn = 11; 
  int sec = 00;

void setup(void) 
{
  Serial.begin(115200);
  Serial.print("Hello! ST77xx TFT Test");

  // Use this initializer if you're using a 1.8" TFT
  tft.initR(INITR_BLACKTAB);   // initialize a ST7735S chip, black tab



  Serial.println("Initialized");

  uint16_t time = millis();
  tft.fillScreen(ST77XX_BLACK);
  time = millis() - time;

  Serial.println(time, DEC);
  delay(500);

  tft.setRotation(1);
  tft.fillScreen(ST77XX_BLACK);
}

void loop() {

  tftDateHeure();
  
}

void tftDateHeure() {
  
  tft.setCursor(40, 25);
  tft.setTextColor(ST77XX_YELLOW);
  tft.setTextSize(1);
  tft.println("Nous sommes le");
  tft.setCursor(20, 45);
  tft.setTextColor(ST77XX_GREEN, true);
  tft.setTextSize(2);
  tft.print(jour);
  tft.print("/");
  tft.print(mois);
  tft.print("/");
  tft.println(annee);
  tft.println("");
  tft.setCursor(65, 75);
  tft.setTextColor(ST77XX_YELLOW);
  tft.setTextSize(1);
  tft.println("Il est");
  tft.println("");
  tft.setCursor(35, 95);
  tft.setTextColor(ST77XX_GREEN, true);
  tft.setTextSize(2);
  tft.print(heure);
  tft.print(":");
  tft.print(mn);
  tft.print(":");
  tft.print(millis() / 1000);

}

Ce que je voudrai centrer c'est :" nous sommes le"
en dessous la date: jj/mm/annee
puis "il est"
et enfin l'heure: hh:mn:sec
Pour ce qui est de "nous sommes le" et "il est", comme c'est du texte fixe j'ai réussi à le centrer visuellement. Mais pour la date et l'heure, le texte n'est pas fixe car il n'affiche pas le 0 des dizaines pour les jour, mois, heure, minute et seconde.

J'avais trouvé, sur le net, la commande "tft.drawCentreString("texte", pos x centrale, y, font);" si je ne me trompe pas, mais quand je compile j'ai ce message d'erreur:
'class Adafruit_ST7735' has no member named 'drawCentreString'

Bon j'avoue je ne suis pas informaticien et si ça se trouve j'ai rien compris et je fais fausse route.

J'espère avoir était clair dans ma demande et je vous remercie par avance de vos réponses.

Amitiés @ tous

Vous écrivez le texte d’une ligne dans un buffer (c-string) ou une String et vous demandez à la bibliothèque quelle espace cela prend avec getTextBound()

Une fois que vous connaissez la largeur L du texte, vous calculez le x de départ pour center le texte par x = (largeur de l’écran - L) / 2

Bonjour et merci pour votre réponse.

Peut-être que je me trompe mais en utilisant getTextBound je vais connaître la largeur des textes qui sont fixes comme "Nous sommes le" et "il est", mais pour l'affichage de la date et l'heure la largeur va varier comme par exemple "3/5/2023" et "25/11/2023" pour la date ou "5:8:9" et "17:32:25" pour l'heure.
A moins que ce soit cette fonction qui permette de le faire automatiquement en fonction de ce qu'il y aura dans le String?
J'avoue que je ne comprend pas trop comment marche cette fonction. J'ai essayé de regarder, sans doute que je me trompe, mais j'ai l'impression qu'il faut que je renseigne tous les éléments qui composent cette fonction.
getTextBounds(const char *string, int16_t x, int16_t y, int16_t *x1, int16_t *y1, uint16_t *w, uint16_t *h);
const char *string -> ok int16_t x et int16_t y -> ok mais que mettre dans les autres?

Désolé mais vous devez penser que j'y connais rien et vous avez sans doute raison, mais j'aimerai bien comprendre.

A nouveau merci d'avance de votre réponse.

J'ai fait une fonction pour aligner, mais avec trois choix d'alignement, minimum, centré et maximum :wink:
Attendez que j'ai accès à mon ordinateur pour que je partage ce code.

Je ne connaissais pas à quoi servait cette fonction. Ni comment l'utiliser, mais merci.

Pas si vous fabriquez d’abord le message à afficher…

Non vous donnez juste la chaîne dont vous voulez connaître le rectangle englobant et le x,y qui est la position du curseur et vous récupérez par les 4 autres pointeurs le rectangle englobant

Cf dans le code

Fonction :

unsigned int alignement(byte typeAlignement, boolean parDefaut, unsigned int minimum, unsigned int maximum, unsigned int longueur) {
  //typeAlignement : 0 = minimum, 1 = centrer, 2 = maximum
  //parDefaut :  false = par excès, true = par défaut

  unsigned int valReturn;
  if (typeAlignement == 0) {
    valReturn = minimum;
  }
  else if (typeAlignement == 1) {
    if ((maximum - minimum - longueur % 2 == 0) or (maximum - minimum - longueur % 2 != 0 and parDefaut == true)) {
      valReturn = minimum + ((maximum - minimum) - longueur) / 2;
    }
    else {
      valReturn = minimum + ((maximum - minimum - longueur) / 2) + 1;
    }
  }
  else if (typeAlignement == 2) {
    valReturn = maximum - longueur;
  }
  return valReturn;
}

Définitions :

#define ALIGNEMENT_MINIMUM 0
#define ALIGNEMENT_CENTRER 1
#define ALIGNEMENT_MAXIMUM 2

#define PAR_DEFAUT true
#define PAR_EXCES false

Cette fonction ne sert pas qu'au texte.

Les définitions sont assez claires pour que n'explique pas.

  • Minimum : Le maximum pour l'alignement.
  • Maximum : Le maximum pour l'alignement.
  • Longueur : La longueur de la chose à afficher. Pas le nombre de caractères.

Image pour comprendre :

*Lg = Longueur

Bonjour @ tous,

@ J-M-L Jackson et techvij j'ai quelques difficultés à comprendre vos solutions mais bon j'ai réussi tout de même à faire quelque chose.
Le soucis que j'ai maintenant c'est que quand au niveau du comptage je vais passer de 59 secondes à 0, le texte étant plus petit, il reste des pixels de l'ancienne boucle qui restent allumés. J'ai bien vu qu'avec "tft.fillScreen(ST77XX_BLACK);" ça s'efface mais ça efface tout l'écran et ça fait une coupure d'affichage (pas très esthétique à mon gout).
Existerait-il une commande qui pourrait effacer juste la dernière commande d'affichage, svp?
Pour l'instant j'ai trouvé comme solution d'avoir une longueur de texte fixe en insérant des espaces si si les nombres sont inférieurs à 10.
A nouveau merci d'avance si vous pouvez trouver une autre solution.
Amitiés

Plusieurs solutions:

  • Pour écrire, tu fais calculer le rectangle englobant le texte et tu calcules la position du texte. Tu conserves ces informations d'une fois sur l'autre et tu t'en sers pour faire effacer la zone où tu écris
  • tu formates tout le temps les nombres avec 2 digits et donc la longueur du texte affiché ne changera pas. D'ailleurs, il est plus courant de voir 11:02:06 que 11:2:6. Pour les heures, on supprime généralement le 0 devant dans ce cas on insère un espace avant d'écrire l'heure.

Une façon de faire est de conserver l’ancienne valeur affichée en mémoire et lors d’une demande d’affichage on compare avec cette ancienne valeur. Si c’est la même on ne fait rien, Si c’est différent on écrit l’ancienne valeur en couleur inversée ce qui efface le texte puis on écrit la nouvelle valeur et on la mémorise pour la prochaine fois.

Cependant c’est souvent plus rapide de définir une zone rectangulaire dédiée à un affichage particulier (la température par exemple) et si la nouvelle valeur à afficher a changé alors on remplit tout le rectangle dans la couleur du fond et on récrit dessus la nouvelle valeur. Comme ça on n’efface qu’une petite partie de l’écran, ça fait moins clignotement

Bonjour et merci pour ta réponse.
"Pour écrire, tu fais calculer le rectangle englobant le texte et tu calcules la position du texte. Tu conserves ces informations d'une fois sur l'autre et tu t'en sers pour faire effacer la zone où tu écris" :roll_eyes: :wink:
Je pense que je vais partir sur la solution de longueur constante. Faut juste que je trouve le moyen d'insérer un "0" car même si je met 05 (par exemple) il ne s'affiche que 5.

Bonjour et à nouveau merci pour ta réponse.
Comme j'ai répondu à "fdufnews", je vais définir une même longueur quelque soit la date et l'heure à afficher. Juste à trouver pour insérer un "0" pour les nombres à juste l'unité, mais j'ai ma petite idée là dessus, même si ce sera pas la plus simple. Le principal c'est que je me comprenne dans ma logique... :smile:

Le plus simple

char buffer[10];
    sprintf(buffer, "%02d/%02d/%02d", heures, minutes, secondes);
    tft.print(buffer);
1 Like

La fonction itoa() doit permettre de convertir un nombre en caractère/chaîne de caractère. Pour avoir testé c'est PARFAIT.

Pour éviter l'effet scintillement j'ai une idée un peu stupide mais qui reste la plus compatible : pour chaque nouveau caractère à affiche, on fait une vérification pour chaque pixel si l'on a besoin de l'effacer. Et on ne touche qu'aux pixels qu'on a beosin de modifier la couleur.

itoa n'ajoute pas le 0 devant pour les nombres inférieurs à 10.

On n'aura qu'à faire une vérification pour ajouter le 0.

Si on travaille avec une chaine de longueur constante, alors il n'y a pas besoin d'effacer. La librairie Adafruit_GFX gère la couleur de fond si elle a été définie. Il faut utiliser la méthode
void setTextColor(uint16_t c, uint16_t bg)

Oui certes mais si cela se fait tout seul pourquoi s’embêter à le faire.

Dans un 1er essai j'avais mis un "espace" pour remplacer le 0

Je vais sans doute poser une question bête mais à quoi correspond c et bg dans setTextColor(uint16_t c, uint16_t bg) svp?