Lire une variable une fois à intervalle de temps régulier

J’avais un problème esthétique sur mon écran LCD 20x4. C’est à dire que lorsque j’affiche l’heure, pour que cette dernière se mette à jour (avec des caractères spéciaux), j’ai du faire un lcd.clear().
Justement, j’ai pensé alors à écrire des blocs vierges à la place du chiffre qui change au lieu de tout changer, ce qui rendrait le tout plus beau.

Voici alors un bout de mon code qui ne fonctionne pas, car je suppose que la variable oldmuni par exemple prend littéralement la valeur des minutes alors que je voudrais qu’elle ne prenne qu’une valeur, et ce à intervalle de 200ms.

int muni;

int oldmuni;

void setup()
{ Initialisation de l'écran, etc, etc ... (pas important)}

void loop()
{
DateTime now = rtc.now();
muni = now.minute() % 10;

switch (muni) {

if (oldmuni != muni){
  lcd.setCursor(15,1);
  lcd.print(" ");
  lcd.setCursor(16,1);
  lcd.print(" ");
  lcd.setCursor(17,1);
  lcd.print(" ");
  lcd.setCursor(15,2);
  lcd.print(" ");
  lcd.setCursor(16,2);
  lcd.print(" ");
  lcd.setCursor(17,2);
  lcd.print(" ");
  delay(200);
  oldmdiz = mdiz;
}   
    case 0:

  lcd.setCursor(15,1);
  lcd.printByte(7);
  lcd.setCursor(16,1);
  lcd.printByte(0);
  lcd.setCursor(17,1);
  lcd.printByte(1);
  lcd.setCursor(15,2);
  lcd.printByte(2);
  lcd.setCursor(16,2);
  lcd.printByte(3);
  lcd.setCursor(17,2);
  lcd.printByte(4);

      break;
      
   case 1:

  lcd.setCursor(15,1);
  lcd.printByte(0);
  lcd.setCursor(16,1);
  lcd.printByte(1);
  ////!VIDE!////lcd.setCursor(3,1);
  ////!VIDE!////lcd.printByte(0);
  lcd.setCursor(15,2);
  lcd.printByte(3);
  lcd.setCursor(16,2);
  lcd.printByte(6);
  lcd.setCursor(17,2);
  lcd.printByte(3);
   
      break;
      
   case 2:

  lcd.setCursor(15,1);
  lcd.printByte(5);
  lcd.setCursor(16,1);
  lcd.printByte(5);
  lcd.setCursor(17,1);
  lcd.printByte(1);
  lcd.setCursor(15,2);
  lcd.printByte(2);
  lcd.setCursor(16,2);
  lcd.printByte(3);
  lcd.setCursor(17,2);
  lcd.printByte(3);

Etc jusqu'à 9

Je pense que la raison est vraiment basique mais bon… j’ai pensé à définir une constante mais dans ce cas le téléversement ne fonctionne pas… Si quelqu’un a la solution, merci de m’aider :slight_smile:
Bonne soirée à tous

oldmdiz = mdiz;

il serait pas la le problème ?

Si, justement, mais je ne sais pas comment écrire ça ...

AH oui, il faut en effet le remplacer par oldmuni, donc ça donnerait:

int muni;

int oldmuni;

void setup()
{ Initialisation de l'écran, etc, etc ... (pas important)}

void loop()
{
DateTime now = rtc.now();
muni = now.minute() % 10;

switch (muni) {

if (oldmuni != muni){
  lcd.setCursor(15,1);
  lcd.print(" ");
  lcd.setCursor(16,1);
  lcd.print(" ");
  lcd.setCursor(17,1);
  lcd.print(" ");
  lcd.setCursor(15,2);
  lcd.print(" ");
  lcd.setCursor(16,2);
  lcd.print(" ");
  lcd.setCursor(17,2);
  lcd.print(" ");
  delay(200);
  oldmuni = muni;
}   
    case 0:

  lcd.setCursor(15,1);
  lcd.printByte(7);
  lcd.setCursor(16,1);
  lcd.printByte(0);
  lcd.setCursor(17,1);
  lcd.printByte(1);
  lcd.setCursor(15,2);
  lcd.printByte(2);
  lcd.setCursor(16,2);
  lcd.printByte(3);
  lcd.setCursor(17,2);
  lcd.printByte(4);

      break;
      
   case 1:

  lcd.setCursor(15,1);
  lcd.printByte(0);
  lcd.setCursor(16,1);
  lcd.printByte(1);
  ////!VIDE!////lcd.setCursor(3,1);
  ////!VIDE!////lcd.printByte(0);
  lcd.setCursor(15,2);
  lcd.printByte(3);
  lcd.setCursor(16,2);
  lcd.printByte(6);
  lcd.setCursor(17,2);
  lcd.printByte(3);
   
      break;
      
   case 2:

  lcd.setCursor(15,1);
  lcd.printByte(5);
  lcd.setCursor(16,1);
  lcd.printByte(5);
  lcd.setCursor(17,1);
  lcd.printByte(1);
  lcd.setCursor(15,2);
  lcd.printByte(2);
  lcd.setCursor(16,2);
  lcd.printByte(3);
  lcd.setCursor(17,2);
  lcd.printByte(3);

Etc jusqu'à 9

Mais justement, là ça devrais prendre la valeur en permanence alors que moi je veux que ça prenne la valeur à un instant t, et ce toutes les 200ms

Utilise la fonction millis() ce sera plus simple que d'utiliser ta RTC pour ça.

Oui on utilise millis() pour ce genre de chose si la boucle n'est pas trop longue. voici un exemple

unsigned long chrono;
const unsigned long periode = 200ul; // ul pour forcer unsigned long, ici pas important - juste une bonne habitude
int maVariable;

void setup()
{
  maVariable = 0;
  chrono = millis();
}

void loop()
{
  // est-ce le moment de mettre à jour la variable ?
  if (millis() - chrono >= periode) {
    maVariable++;
    chrono += periode;
  }
  // ici on fait autre chose

}

Bonjour

Pour l’actualisation des informations affichées sur un LCD, le plus efficace est de conserver, sous forme de variables globales en RAM, une image de ce qui est réellement affiché à l’écran (par exemple 4 lignes en char[20]).
Ton programme peut alors recalculer à haute fréquence ce qui devrait être théoriquement être affiché, et utiliser les variables globales pour n’envoyer au LCD que les caractères qui ont changé.

NB : le curseur avance “tout seul”, donc pas la peine de le repositionner pour afficher deux caractères consécutifs sur la même ligne.

D'accord, merci beaucoup, je vais donc utiliser la fonctione millis, qui ne ralentira pas mon programme. Mais vous n'avez pas répondu à mon principal problème: En fait, la variable par exemple muni Signifie l'unité des minutes, par exemple pour l'heure suivante: 15:34, l'unité est 4.
Et je voudrais faire une mesure unique toutes les "intervalles de temps ex: 200ms". Le problème, justement, c'est que si je vais un truc du genre variable = muni je vais avoir en sortie la même valeur tout le temps de muni .
Je sais pas trop si vous voyez.

Sinon, si vraiment je n'y arrive pas, je ferais un simple

lcd.clear();
delay(200);

. C'est vraiment pour un aspect tout bonnement esthétique. Merci beaucoup à vous dans tous les cas.

Je comprend pas trop.... tu veux rafraichir toutes les 200 ms la valeur de l'unité des minutes qui va changer toute les minutes ? pourquoi ne pas la rafraichir toutes les minutes ?

Ou alors la question n'est pas clairement posée.

Si je la rafraichit toutes les minutes, voici le problème:

Exemple: je téléverse mon programme à 15:45 et 50 secondes
Si c'est rafraichit toutes les minutes, sur mon écran, l'heure sera juste seulement à 15:46 et 50 secondes. A 15:46 et 10 sec par exemple, il sera affiché: 15:45

Bonjour

Pour l'actualisation des informations affichées sur un LCD, le plus efficace est de conserver, sous forme de variables globales en RAM, une image de ce qui est réellement affiché à l'écran (par exemple 4 lignes en char[20]).
Ton programme peut alors recalculer à haute fréquence ce qui devrait être théoriquement être affiché, et utiliser les variables globales pour n'envoyer au LCD que les caractères qui ont changé.

NB : le curseur avance "tout seul", donc pas la peine de le repositionner pour afficher deux caractères consécutifs sur la même ligne.

@Bricoleau ? Cela m’intéresserais de pouvoir faire ça, comment lire les données qui sont sur l'écran? Ca simplifierais énormément. Merci de ta réponse

De plus, je veux rafraîchir le caractère qu'il y a à rafraîchir SEULEMENT si ce dernier n'est plus juste sur l'écran. Ex: il est 15:46 et il est affiché 15:45, alors je vais devoir rafraîchir les unités minutes

Une ébauche d’exemple simple et vite fait pour te donner l’idée générale

//image des lignes du LCD
char image_ligne1[20];
char image_ligne2[20];
char image_ligne3[20];
char image_ligne4[20];
//peut aussi être mis sous forme de tableau à deux dimmensions

void initialiserLCD()
{
  LCD.begin(...);
  LCD.clear();
  for (uint8_t i=0;i<20;i++) {
    image_ligne1[i] = ' ';
    image_ligne2[i] = ' ';
    image_ligne3[i] = ' ';
    image_ligne4[i] = ' ';
  }//ou un bon vieux memset()
}

void actualiserLigneLCD(uint8_t num_ligne, char ligne_actuelle[], char ligne_nouvelle[])
{
  for (uint8_t i=0;i<20;i++) {
    if (ligne_actuelle[i] != ligne_nouvelle[i]) {
      LCD.setCursor(i, num_ligne); //optimisable si plusieurs print consecutifs
      LCD.print(ligne_nouvelle[i]);
      ligne_actuelle[i] = ligne_nouvelle[i];
    }
  }
}

void actualiserLCD()
{
  char l1[21]; //par sécurité un 21ème car réservé pour le 0 terminal si utilisation de fonctions comme sprintf()
  char l2[21];
  char l3[21];
  char l4[21];
  
  ... ici du code qui alimente l1 à l4
  
  actualiserLigneLCD(0, image_ligne1, l1);// ne fait rien si pas de changement
  actualiserLigneLCD(1, image_ligne2, l2);// ne fait rien si pas de changement  
  actualiserLigneLCD(2, image_ligne3, l3);// ne fait rien si pas de changement  
  actualiserLigneLCD(3, image_ligne4, l4);// ne fait rien si pas de changement  
}

void setup()
{
  ...
  initialiserLCD();
}

void loop()
{
  ...
  actualiserLCD();
}

Donc la question était mal formulée, tu voudrais que ta variable soit contrôlée toute les 200ms et que l'affichage soit changé uniquement en cas de changement de la valeur de la variable.

En gros avec millis() tu fais une boucle de contrôle toute les 200ms et ensuite tu fais une comparaison de tes valeurs, si cette dernière est différente tu reprends ton affichage avec les nouvelles valeurs.

Oui, exactement, mais ma question était: Comment faire cette boucle de contrôle ?

J-M-L t'a fait un exemple dans le message #6

Tu décompose en deux temps.

D’abord tu fais ta mesure toutes 200ms avec millis() :

Blink sans delay()

ensuite tu fais ta comparaison et si ça correspond à ce que tu veux tu affiches.

Edit : l’exemple de JML est plus simple :grin:

c’est quand même un peu l’artillerie lourde de faire un “frame buffer” de tout l’écran…

Le plus souvent on sait ce qu’on veut changer et il ne s’agit que de quelques caractères.

Par exemple si vous avez lu l’heure avec un

DateTime now = rtc.now();

et que vous vous en servez que pour afficher seulement HH:MM:SS, il suffit de conserver en mémoire HH, MM, SS qui sont affichés soit 3 octets seulement…

voilà un vieil exemple (que je crois avoir déjà posté quelque part) qui affiche l’heure en haut à droite d’un LCD en regardant ce qui a changé et en ne demandant pas l’heure à la RTC à tous les tours de boucle

#include <RTClib.h> // https://github.com/adafruit/RTClib
RTC_DS3231 rtc;

#include <LiquidCrystal_I2C.h> // https://github.com/fdebrabander/Arduino-LiquidCrystal-I2C-library/blob/master/LiquidCrystal_I2C.h
const uint8_t LCDI2CAddress = 0x27; // pour 20x4 lignes souvent 0x3F et pour 16x2 souvent 0x27
const uint8_t nbCols = 16;
const uint8_t nbLines = 2;
LiquidCrystal_I2C lcd( LCDI2CAddress, nbCols, nbLines);

int8_t hh = -1, mm = -1, ss = -1;

uint32_t chrono;
uint32_t periode = 200ul; // on va lire l'heure toutes les 5 fois par seconde

void afficheTemps2Digits(int8_t valeur, uint8_t col, uint8_t ligne, char remplissage = '0')
{
  // par acquis de conscience
  if (valeur > 59) valeur = 59;
  if (valeur < 0) valeur = 0;

  lcd.setCursor(col, ligne);
  if (valeur < 10) lcd.write(remplissage);
  lcd.print(valeur);
}

void setup()
{
  Serial.begin(115200);
  lcd.begin();
  lcd.setCursor(0, 0);

  if (! rtc.begin()) {
    Serial.println(F("PAS DE RTC"));
    while (true);
  }

  if (rtc.lostPower()) rtc.adjust(DateTime(F(__DATE__), F(__TIME__))); // RTC A INITIALISER, ON PREND L'HEURE DE COMPILATION

  lcd.setCursor(nbCols - 8, 0);
  lcd.print(F("HH:MM:SS"));

  chrono = 0;
}

void loop()
{
  int8_t h, m, s;

  if (millis() - chrono > periode) {
    // c'est l'heure de regarder si quelque chose a changé
    DateTime now = rtc.now(); 

    s = now.second();
    m = now.minute();
    h = now.hour();

    if (s != ss) { // les secondes ont changé
      ss = s;
      afficheTemps2Digits(s, nbCols - 2, 0);
    }

    if (m != mm) { // les minutes ont changé
      mm = m;
      afficheTemps2Digits(m, nbCols - 5, 0);
    }

    if (h != hh) { // les heures ont changé
      hh = h;
      afficheTemps2Digits(h, nbCols - 8, 0, ' ');
    }
    chrono += periode;
  }

  // ici on peut faire autre chose si c'est pas trop long

}

AH !! Voilà, c'est exactement ce que je cherchais ! Je vais faire le programme à ma sauce, je vous tiens au courant (ce soir, dans quelques heures) si ça fonctionne. Une dernière question: Pourquoi dans ce bout de code, on sait que l'heure à changé ? Je sais que hh = -1 mais je ne comprends pas:

if (h != hh) { // les heures ont changé
      hh = h;
      afficheTemps2Digits(h, nbCols - 8, 0, ' ');

Merci beaucoup ! Vous me sauvez !

iFrostizz67:
Pourquoi dans ce bout de code, on sait que l'heure à changé ?

j'initialise hh à -1 car je suis sûr que c'est une valeur impossible et donc à la première comparaison ça va mettre l'écran à jour. Ensuite hh prend la valeur de l'heure actuelle. tant que l'heure ne change pas, h vaudra la même chose donc que hh mais quand l'heure change alors hh contient l'ancienne valeur qui sera donc différente de h qu'on vient de lire sur la RTC et donc on met à jour l'écran et on se souvient dans hh de la nouvelle heure.

et on repart pour un tour

ça fait un truc du genre:

hh=-1 h=17 mise à jour de l'écran, hh passe à 17
hh=17 h=17 rien
hh=17 h=17 rien
hh=17 h=17 rien
hh=17 h=17 rien
hh=17 h=17 rien
...
hh=17 h=18 mise à jour de l'écran, hh passe à 18
hh=18 h=18 rien
hh=18 h=18 rien
...