Programmateur horaire modifié

Bonjour , je me suis inspiré du Programmateur Horaire Multivoies de Julien Monterrin.
J'ai adapté son code pour un écran OLED I2c et un arduino MEGA afin de piloter 7 relais 12v commandés via des transistors. (6 relais en mode programmation et 1 relais commandé manuellement via 1 switch)
Mon programme fonctionne et les sorties sont bien pilotées aux heures prescrites, l'affichage de l'heure se fait correctement, par contre l'affichage de la voie active ne se fait que pendant 1 minutes alors que j'aimerais qu'il se fasse tout le temps que cette voie est active.
Où est le bug ?

#include <Wire.h>
#include <RTClib.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>
RTC_DS3231 rtc;

// display config
#define SCREEN_WIDTH 128
#define SCREEN_HEIGHT 64
Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, -1);

  const int buttonPin = 12;  // défini entrée switch Mode Manuel en D3
  const int relay1Pin = 10; // Pin out relais 1 pour le mode manuel

  int buttonState = 0;  // variable pour lire état status switch définie sur 0


 byte i;



  // Variables de la plage horaire multiples
  int TabEvt [6][4] =
            {
              {20,22,20,32},// 9h00 - 9h14 voie 1 vert
              {20,33,20,43},// 9h15 - 9h25 voie 2 bleu
              {20,44,20,54},// 9h26 - 9h36 voie 3 rouge
              {20,55,21,5},
              {21,6,21,16},
              {21,17,21,27},
              
            };
              
          byte PinSorties [6] = {5, 4, 7, 6, 9, 8};

           
            

void setup() {
  //Serial.begin (95200);


    pinMode(buttonPin, INPUT);// initialize the switch pin as an input en D3, R 1K entre D3 et +5v , autre côté du switch à GND
    pinMode(10,OUTPUT); // defini D8 pour sortie voie 1
   
  // initialisation pour 6 relais en D4,D5,D6,D7,D8,D9
  for ( i = 0; i < 6; i++ ) {pinMode(PinSorties[i], OUTPUT); digitalWrite (PinSorties[i], LOW);}

  if(!display.begin(SSD1306_SWITCHCAPVCC, 0x3C)) { // Adresse I2C 0x3C
    for(;;);
  }
  
  if (!rtc.begin()) {
    for(;;);
  }
  //rtc.adjust(DateTime(F(__DATE__), F(__TIME__))); // Décommenter pour régler l'heure
}

void loop() {
  DateTime now = rtc.now(); // Lecture de l'heure actuelle

  // --- Affichage display ---
            display.clearDisplay();
            display.setTextSize(2);
            display.setTextColor(WHITE);
            display.setCursor(10,0);
            //display.print("Heure: ");
            display.print(now.hour());
            display.print(":");
          if (now.minute() < 10) display.print("0"); // Affichage propre
            display.print(now.minute());
            display.print(":");
          if (now.second() < 10) display.print("0"); // Affichage propre
            display.print(now.second());

  // logique de commande pour switch mode manuel
    buttonState = digitalRead(buttonPin);// lit valeur état du switch en D5    
    
    if (buttonState == HIGH ) // si D10=high on est en mode MAN sinon on reste en AUTO
    
    {  
      
       display.setCursor(2, 50);
              display.print("EV MANUEL");
              digitalWrite(10,HIGH); //relais en D3 est actif 
              }
    else                     // dans le cas contraire D3 est LOW et on est en mode auto
     { 
       display.setCursor(2, 50);
       display.print("MODE AUTO");
       digitalWrite(10,LOW); //relais en D9 est inactif
       }

  
  // -- logique de commande pour relais multiples ----
  // Rechercher si heure actuelle est dans dans la ou les plages horaires 
        for ( i = 0; i < 6; i++ ) { 
          if (now.hour() == TabEvt [i][0] && now.minute() == TabEvt [i][1]) { // si H et Min spnt dans la plage horaire de la pos1 du tableau.
              digitalWrite (PinSorties[i], HIGH);

                display.setCursor(2, 24);
                display.print("Voie:");
                display.setCursor(62, 24);
                display.print(i+1);


          }
          if (now.hour() == TabEvt [i][2] && now.minute() == TabEvt [i][3]) {               
              digitalWrite (PinSorties[i], LOW);
                display.setCursor(62, 24); 
                display.print("-");
          }
        }
                 
                      
                          
             //digitalRead(PinSorties[i]);

             /*display.setCursor(2, 24);
              display.print("Voie:");
              display.setCursor(52, 24);

              if (digitalRead(PinSorties[i]) == HIGH) {
                //Serial.println (i);
                display.print(i); 
                              } 
              if (digitalRead(PinSorties[i]) == LOW) { 
                    
                  }   

              
                else {
                 display.setCursor(62, 24); 
                display.print("-"); 
                //display.display();
                //} 
                
           }*/
          
        

  
   display.display();

  delay(1000); // Mise à jour chaque sec
}

Le problème vient du fait que vous testez une égalité stricte entre l’heure courante et un instant précis ➜ vous activez et affichez la voie uniquement quand heure + minute correspond exactement au début de la plage. En dehors de cette minute exacte, la condition est fausse, donc l’affichage disparaît.

Vous devez tester une appartenance à un intervalle, pas une égalité. Par exemple en convertissant l’heure en minutes et en vérifiant si la valeur courante est comprise entre début et fin.

un approche typique si 'est un horaire journalier est de compter le nombre de secondes ou minutes depuis minuit comme ça c'est facile de faire des comparaison.

Pour rendre le code lisible vous pourriez ainsi avoir une structure (ou une classe si ça ne vous fait pas peur) Moment qui décrit une heure et minute et un structure Evenement qui décrit 2 Moments (début et fin) et la broche d'action associée.

La structure Moment pourrait embarquer une fonction qui donne le nombre de minutes depuis minuit et la structure Évènement une fonction qui dit si un Moment donné est dans l'interval et deux fonctions pour activer et désactiver la broche.

struct Moment {
  byte heure;
  byte minute;
  uint16_t enMinutes() const {return (uint16_t)heure * 60u + minute;}
};

struct Evenement {
  Moment debut;
  Moment fin;
  byte pin;

  bool contient(const Moment& m) const {
    uint16_t t = m.enMinutes();
    return t >= debut.enMinutes() && t < fin.enMinutes();
  }
  void activer() const {digitalWrite(pin, HIGH);}
  void desactiver() const {digitalWrite(pin, LOW);}
};

ça permettrait de définir simplement vos actions.

Evenement evenements[] = {
  {{20, 22}, {20, 32}, 5}, // de 20h22 à 20h32 sur la broche 5
  {{20, 33}, {20, 43}, 4},
  {{20, 44}, {20, 54}, 7},
  {{20, 55}, {21,  5}, 6},
  {{21,  6}, {21, 16}, 9},
  {{21, 17}, {21, 27}, 8},
};
const byte nombreEvenements = sizeof evenements / sizeof *evenements;

vous pourriez alors avoir des fonctions lisibles

dans le setup vous pourriez appeler

void initialiserSorties() {
  for (byte i = 0; i < nombreEvenements; i++) {
    pinMode(evenements[i].pin, OUTPUT);
    evenements[i].desactiver();
  }
}

ou en C++ plus moderne

void initialiserSorties() {
  for (auto & evt: evenements) { // pour chaque evenement
    pinMode(evt.pin, OUTPUT);
    evt.desactiver();
  }
}

et dans la loop vous pourriez tester le Moment présent pour ajuster les sorties

void ajusterEtat(const Moment& maintenant) {
  for (byte i = 0; i < nombreEvenements; i++) {
    if (evenements[i].contient(maintenant)) {
      evenements[i].activer();
    } else {
      evenements[i].desactiver();
    }
  }
}

ou en C++ plus moderne et compressé

void ajusterEtat(const Moment& maintenant) {
  for (auto& evt : evenements) // pour chaque evenement
    if (evt.contient(maintenant)) evt.activer(); else evt.desactiver();
}

etc

Il y a d'autres challenges dans le code avec le câblage en dur du nombre d'évènements (le calculer comme ci dessus avec sizeof pour nombreEvenements), la possibilité de n'afficher qu'une seule voie active alors que techniquement le code permet d'en avoir plusieurs en même temps, des affichages répétitifs qui ne sont pas optimaux (on affiche uniquement ce qui change pour que ça clignote moins, ....)

Merci J-M-L pour ta réponse;
Je n'ai pas appliqué ta solution mais en examinant attentivement mon code, j'ai trouvé ce qui clochait.
Une ou 2 accolade mal placées et quelques lignes déplacées et dès lors maintant ça fonctionne voici mon code

#include <Wire.h>
#include <RTClib.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>
RTC_DS3231 rtc;

// display config
#define SCREEN_WIDTH 128
#define SCREEN_HEIGHT 64
Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, -1);

  const int buttonPin = 12;  // défini entrée switch Mode Manuel en D3
  const int relay1Pin = 2; // Pin out relais 1 pour le mode manuel

  int buttonState = 0;  // variable pour lire état status switch définie sur 0

 byte i;

  // Variables de la plage horaire multiples
  int TabEvt [6][4] =
            {
              {10,0,10,15},// 9h00 - 9h14 voie 1 vert
              {10,16,10,31},// 9h15 - 9h25 voie 2 bleu
              {10,32,10,47},// 9h26 - 9h36 voie 3 rouge
              {10,48,11,3},
              {11,4,11,19},
              {11,20,11,35},             
            };
              
          byte PinSorties [6] = {5, 4, 7, 6, 9, 8};
        
void setup() {
  //Serial.begin (95200);
    pinMode(buttonPin, INPUT);// initialize the switch pin as an input en D3, R 1K entre D3 et +5v , autre côté du switch à GND
    pinMode(2,OUTPUT); // defini D8 pour sortie voie 1
   
  // initialisation pour 6 relais en D4,D5,D6,D7,D8,D9
  for ( i = 0; i < 6; i++ ) {pinMode(PinSorties[i], OUTPUT); digitalWrite (PinSorties[i], LOW);}

  if(!display.begin(SSD1306_SWITCHCAPVCC, 0x3C)) { // Adresse I2C 0x3C
    for(;;);
  }
  
  if (!rtc.begin()) {
    for(;;);
  }
  //rtc.adjust(DateTime(F(__DATE__), F(__TIME__))); // Décommenter pour régler l'heure
}

void loop() {
  DateTime now = rtc.now(); // Lecture de l'heure actuelle

  // --- Affichage display heure---
            display.clearDisplay();
            display.setTextSize(2);
            display.setTextColor(WHITE);
            display.setCursor(10,0);            
            display.print(now.hour());
            display.print(":");
          if (now.minute() < 10) display.print("0"); // Affichage propre
            display.print(now.minute());
            display.print(":");
          if (now.second() < 10) display.print("0"); // Affichage propre
            display.print(now.second());
   // -- Affichage display voie --
            display.setCursor(2, 24);
            display.print("Voie:");

  // logique de commande pour switch mode manuel
    buttonState = digitalRead(buttonPin);// lit valeur état du switch en D12    
    
    if (buttonState == HIGH ) // si D12=high on est en mode MAN sinon on reste en AUTO
        {  
      
       display.setCursor(2, 50);
              display.print("EV MANUEL");
              digitalWrite(2,HIGH); //relais en D3 est actif 
              }
       else                     // dans le cas contraire D3 est LOW et on est en mode auto
        { 
       display.setCursor(2, 50);
       display.print("MODE AUTO");
       digitalWrite(2,LOW); //relais en D2 est inactif
       }

  
  // -- logique de commande pour relais multiples ----
  // Rechercher si heure actuelle est dans dans la ou les plages horaires 
        for ( i = 0; i < 6; i++ ) { 
          if (now.hour() == TabEvt [i][0] && now.minute() == TabEvt [i][1]) { // si H et Min spnt dans la plage horaire de la pos1 du tableau.
              digitalWrite (PinSorties[i], HIGH); // sortie activée
          }               
                if (digitalRead(PinSorties[i])== HIGH) 
                {
                display.setCursor(62, 24);
                display.print(i+1);
                }
                else {
                display.setCursor(62, 24);           
                display.print("  ");
            }               
          
          if (now.hour() == TabEvt [i][2] && now.minute() == TabEvt [i][3]) {               
              digitalWrite (PinSorties[i], LOW);
                
          }
        }
    
   display.display();

  delay(1000); // Mise à jour chaque 1/2sec
}

OK. Dans l’état actuel, vous redessinez toute l’interface OLED à chaque seconde, même pour des éléments qui ne changent pas. La bonne approche consiste à séparer deux choses : ce qui change souvent (heure, état des relais) et ce qui change pas ou rarement (structure de l’écran).

Pour gérer le rafraîchissement il faudrait conserver l'état actuel en mémoire et quand un nouvel état se présente pour affichage, vous comparez et si ça a changé alors vous affichez.

Si visuellement ça n'a pas l'air de clignoter toutes les secondes quand vous faites le rafraîchissement, ne vous ennuyez pas avec ça.

Merci J-M-L pour les précisions, vous avez raison, je ne programme arduino que occasionnellement et ne connais pas toutes les subtilités.
Pour l'instant ça ne clignote pas, mais si celà arrive j'y penserai.