Cause plantage?

Bonjour, j'ai une application avec un arduino UNO qui fonctionne 24h sur 24 , qui fonctionne bien ; sauf que parfois il apparait 2 problèmes:
1°) l'afficheur affiche des ''hiéroglyphes" , c'est un problème mineur sans conséquence à l'application qui ne nuit pas au déroulement du loop. J'ai pu contourner ce problème par un reset régulier ( l'affichage sert uniquement d'information)
2°) le plus grave , l'arduino se bloque .de temps en temps
malgré la présence su Wachdog reglé à 8
L'application fait un clignotement d'une led en permanence qui me sert de controle visuel sur le déroulement du loop

Cet arduino mesure des températures via le bus I2C et pilotent de électrovannes et circulateurs via une carte à relais du commerce
il affiche les mesures et actions sur un afficheur
Je ne pense pas à un bugg du programme puisque il peut se dérouler plusieurs mois sans plantage.
Auriez vous de pistes sur les causes de plantage ?
Mon alim est de 12v (15.8v à vide) donc dans les limites peut -elle être la cause d'un plantage très occasionnel ?
Quand il est planté, y a t-il quelque chose à faire pour mettre en évidence la cause ? :la seule chose que je fais c'est couper et remettre l'alim pour repartir.

Merci de vos conseils

Il ne se planterait pas 49 jours après la mise sous tension des fois? Voir comment est utilisée la fonction millis().

Si l'Arduino se plante, et que le WatchDog ne réinitialise pas, ce n'est pas normal. Il doit continuer à être appelé. Cela montrerait une erreur dans le code.

Le 15,8 est en dehors des limites. Ne sachant pas ce qui est alimenté, on ne peut pas se prononcer. Mais il est aussi possible que vu que Vin est élevé, que le régulateur ne joue plus son rôle, et que la tension descend trop pour garantir le fonctionnement. Cela dépend aussi fortement de ce qui est alimenté en 5V

dès que j'entends parler de relais, de bobines etc

se pose la question de l'alimentation, de la protection de l'arduino etc...

➜ un schéma précis et des détails sur les composants seraient utiles

un bug dans le code reste aussi possible si vous avez une lente fuite mémoire par exemple ➜ le code pourrait aussi être utile

Éventuellement :

Bonjour, merci pour ces pistes .
Je vais donc commencer alimenter mon Uno par un bloc alim de 9v.
Je joins le code complet pour un contrôle d'une éventuelle erreur de code .
Je ne comprend la chose suivante: si le micro "plante" pendant le loop et reste bloqué complet , pourquoi le wachdog le "reveillerait" ?
Que veut dire "se pose la question de la protection de l'arduino" ?
La carte relais est cablée classiquement ; résistance + led de signalisation , transistor et diode de roue libre.
Je vais aussi changer la carte relais par une carte relais avec optocoupleur fab seeit vendu par conrad,

Bonjour, merci pour ces pistes .
Je vais donc commencer alimenter mon Uno par un bloc alim de 9v.
Je joins le code complet pour un contrôle d'une éventuelle erreur de code .
Je ne comprend la chose suivante: si le micro "plante" pendant le loop et reste bloqué complet , pourquoi le wachdog le "reveillerait" ?
Que veut dire "se pose la question de la protection de l'arduino" ?
La carte relais est cablée classiquement ; résistance + led de signalisation , transistor et diode de roue libre.
Je vais aussi changer la carte relais par une carte relais avec optocoupleur fab seeit vendu par conrad, Est ce que cela suffira ?

Merci et à bientôt

#include <avr/wdt.h>
#include <Wire.h>
#include "Timer.h"
#include <LiquidCrystal.h>

// initialize the library with the numbers of the interface pins(RS,E,DB4,DB5,DB6,DB7)
//LiquidCrystal lcd(8, 9, 4, 5, 6, 7);  // dans le cas du shield arduino
LiquidCrystal lcd(6, 7, 5, 4, 3, 8);  // dans le cas du shield fabriqué PB( RS,E D4,D5,D6,D7) modifié 


// initialisation des variables
// float temp = 0.0; // pas besoin de cette variable globale
byte adress = 0x48;  // adresse de la sonde DS75 (les 4 bits)
int valeurP = 0;  // initialise la variable qui lira la valeur analogique  du potentiomètre
boolean present = 0;
boolean etat_led = 1;



byte TemperatureMSB = 0;
byte TemperatureLSB = 0;
byte index_affichage = 0;
byte pospotar = 0;
byte oldpospotar = 0;
float Tcapteur = 0.0;
float Tballon1 = 0.0;
float Tballon2 = 0.0;
float Tarrivee = 0.0;
float T_CE   = 0.0;
//float Tretour = 0.0;
float Tresist = 0.0;

int duree = 1000; //utilisé pour le clignotement de la led

unsigned long frequence_affichage = 3500; //3000 utilisé pour la fréquence d'affichage toutes les 3 sec
unsigned long frequence_acquisition = 60000; //60000 utilisé pour la  fréquence d'acquisition toutes les mn
unsigned long frequence_reset = 3600000; //3600000 utilisé pour la  fréquence de reset toutes les heures
unsigned long last_led = millis(); //utilisé pour le clignotement
unsigned long last_acquisition = millis(); //utilisé pour la  fréquence d'acquisition
unsigned long last_affichage = millis(); //utilisé pour l'affichage des valeurs
unsigned long last_reset = millis(); //utilisé pour la réinitialisaton
boolean gel;
// initialisation des constantes (n° sonde)
const byte Scapteur = 0;
const byte Sarrivee = 1;
const byte Sballon1 = 3;
const byte Sballon2 = 7;
const byte S_CE = 6;
//const byte Sretour=  4;
const byte Sresist= 2;
// initialisation des constantes pins
const byte vanne1 = 11;
const byte vanne2 = 10;
const byte resistance = 12;
const byte circ = 9;       // ATTENTION  3 SI ESSAI AVEC SHIELD AARDUINO  SInon AVEC LA PLATINE PB C'EST 9
const byte led = 13;
const byte capteurdebit = 2;  // le capteur est raccordé sur la pin 2 pour utilisé l'interruption INT0

// declarations des variables liées au capteur de débit et à la fonction de l'interruption
unsigned long  debut_sensor; 
volatile int impulsion = 0 ;
volatile unsigned long precedentPulse = millis();  
float debit = 0;  
boolean marcheR = 0;    // à 1 qd la résistance est sous tension

// declarations des variables liées à la communicarion avec le Pi
boolean auto_resistance = 0; //var boolean si autorisation de gestion de la résistance
String message;              // message reçu de la liaison série avec le pi


/////////////////////    SETUP      ////////////////////////////////////
void setup() {
  Serial.begin(9600);          // initialisation du port série
  lcd.begin(16, 2);             // initialisation de l'afficheur LCD
  // Print a message to the LCD.
  lcd.print(" Progr solaire 5 ");
  lcd.setCursor(0, 1) ; // positionne à 1ière colonne , 2 ième ligne
  lcd.print(" du 19 sept 21");
  delay(2000);
  lcd.setCursor(0, 0) ; // positionne à 1ière colonne , 1 ième ligne
  lcd.print("fichier");
  lcd.setCursor(0, 1) ; // positionne à 1ière colonne , 1 ième ligne
  lcd.print("solaire_5_com");
  
  // configurer les 8 sondes possibles
  for (byte i = 0; i < 8; i++) {
    Wire.begin();
    Wire.beginTransmission(adress + i);  // début communication sur chaque sonde
    Wire.write(0x01);                  // Accès au registre de configuration
    Wire.write(0x20);                  // Ecriture dans le registre de configuration avec résolution 10  bits (pour 11 bits c'est 0x40) et pour 9 bits c'est 0x00
    Wire.endTransmission();
  }
  // initialize digital pin  as an output.
  pinMode(led, OUTPUT);
  pinMode(vanne2, OUTPUT);
  pinMode(vanne1, OUTPUT);
  pinMode(circ, OUTPUT);
  pinMode(resistance, OUTPUT);
  pinMode(capteurdebit, INPUT);
  digitalWrite(led, HIGH) ;  // allumage led témoin
  delay(2000);

  // initialisation de l'interruption pour la mesure de débit
  attachInterrupt(0, pulsecounter, FALLING);  // attache l'interruption n°0 (qui correspond à la pin 2) à la fonction pulsecounter
  
  // Serial.printledln(last_affichage);
  acquisitions();  // premières acquisitions
  if (pospotar == 0)  {
    actions();  // et tout de suite action pour ne pas attendre le temps de la fréquence d'acquisitions
  }
  last_reset = millis(); // réinitialisaton du reset

debut_sensor = 0;   //initialisation pour la pemiere periode de 1 sec pour définir un débit
wdt_enable(WDTO_8S);    // initialisation du wachdog sur 8 s

}

////////////////////     FIN DE SETUP     ////////////////////////////////


// fonction pour la lecture
float mesure(byte sonde) {  // qui retourne une valeur
  //float s = 1.0;
  int MSB = 0;
  int LSB = 0;
  float temp_mesuree = 0.0;
  present = 0;
  Wire.beginTransmission(sonde);    // début communication sur la sonde 0 en mode écriture
  Wire.write(0x00);                  // Pointer sur le registre de temperature
  Wire.endTransmission();
  Wire.requestFrom(sonde, 2u);
  if (2 <= Wire.available()) { // si deux octets disponibles
    MSB = Wire.read();  // lire l'octet de poids fort
    LSB = Wire.read();  // lire l'octet de poids faible
    present = 1;        // la sonde est présente car il y a les 2 octets
  } else {
    present = 0; // iil n'y a pas les 2 octets  donc la sonde n'est pas présente
  }
  Wire.endTransmission();
  //Serial.print(" MSB  =    ");
  //Serial.print(MSB);
  // Serial.print("     LSB  =    ");
  //Serial.println(LSB);
  if (present) {
    if (MSB >= 128) {  // si le bit 7 du MSB est à 1 c'est négatif

      MSB = MSB - 256;   // inverse les bits

      temp_mesuree =  (0.5 * ((LSB & 0x80) >> 7) +  0.25 * ((LSB & 0x40) >> 6)) - 1 + (float)MSB  ;
      //    Serial.print("   nouveau MSB apres  -255  =    ");
      //    Serial.println(MSB);
    }
    else {
      temp_mesuree = 0.5 * ((LSB & 0x80) >> 7) + 0.25 * ((LSB & 0x40) >> 6) + (float)MSB;
      // 0x80 = 10000000  et le & signifie je selectionne uniquement le bit 7 et je le decale à droite de 7  http://www.locoduino.org/spip.php?article70
      // 0x40 = 01000000 et le & signifie  je selectionne uniquement le bit 6  et le le decale à droite de 6
      // temp=0.5*((LSB&0x80)>>7) + 0.25*((LSB&0x40)>>6) + 0.125*((LSB&0x20)>>5) + 0.0625*((LSB&0x10)>>4) + (float)MSB;  pour une résolution plus fine
    }
  }
  return temp_mesuree;
}


// fonction d'acquiitions
void acquisitions() {
  Tballon1 = 0.0;
  Tballon2 = 0.0;
  Tcapteur = 0.0;
  Tarrivee = 0.0;
  // T_CE = 0.0;                  
  //T_resist= 0.0;
  Tballon1 = mesure(adress + Sballon1);
  affichage("Mesure ","Ballon 1",Tballon1);
  delay(1000);
  Tcapteur = mesure(adress + Scapteur);
  affichage("Mesure ","Capteur",Tcapteur);
  delay(1000);
  Tballon2 = mesure(adress + Sballon2);
  affichage("Mesure ","Ballon 2",Tballon2);
  delay(1000);
  Tarrivee = mesure(adress + Sarrivee);
  affichage("Mesure ","Arrivée",Tarrivee);
  //delay(1000);
  //Tretour = mesure(adress + Sretour);
  // affichage("Mesure ","Retour",Tretour);
  //delay(1000);
  T_CE = mesure(adress + S_CE);
  affichage("Mesure ","C.E",T_CE);
  Tresist = mesure(adress + Sresist);
  affichage("Mesure ","Resistan",Tresist);
}



// fonction pour les actions
void actions() {
  if (Tballon1 > Tballon2) {
    if ((Tcapteur > Tballon2 + 4 ) || (Tarrivee > Tballon2 + 4 ))
    {
      digitalWrite(vanne2, HIGH);
      digitalWrite(vanne1, LOW);
      digitalWrite(circ, HIGH);
    }
    else {
      digitalWrite(vanne2, LOW);
      digitalWrite(vanne1, LOW);
      digitalWrite(circ, LOW);
    }
  }
  else  if ((Tcapteur > Tballon1 + 4 ) || (Tarrivee > Tballon1 + 4))
  {
    digitalWrite(vanne1, HIGH);
    digitalWrite(vanne2, LOW);
    digitalWrite(circ, HIGH);
  } else {
    digitalWrite(vanne1, LOW);
    digitalWrite(vanne2, LOW);
    digitalWrite(circ, LOW);
  }


  // mise en circulation en cas de  risque de gel
  if (Tcapteur <= 2 ) {
    gel = 1;
    if (Tballon1 >= Tballon2) {
      digitalWrite(vanne1, HIGH);
      digitalWrite(circ, HIGH);
    }  else {
      digitalWrite(vanne2, HIGH);
      digitalWrite(circ, HIGH);
    }
  }
  // le risque de gel disparait
  if ((gel == 1) && (Tcapteur > 2  ) )    {
    digitalWrite(vanne2, LOW);
    digitalWrite(vanne1, LOW);
    digitalWrite(circ, LOW);
    gel = 0;
  }
}


//fonction d'affichage
void affichage(String origine, String sonde, float temperature) {  //  origine c'est  soit mesure à la suite de l'acquisitrion , soit temp pour les affichages permanents
  lcd.clear();
  lcd.setCursor(0, 0) ; // 1ème col - 1ème ligne - positionne le curseur à l'endroit voulu (colonne, ligne) (1ère=0 !)
  lcd.print(origine) ;
  lcd.print(sonde) ;
  lcd.setCursor(0, 1) ; // 1ème col - 2ème ligne - positionne le curseur à l'endroit voulu (colonne, ligne) (1ère=0 !)
  lcd.print(temperature) ;
  lcd.print(" ") ;
  lcd.print((char)223);  // c'est °
  lcd.print('C');
  if (auto_resistance) {
      lcd.setCursor(15,0);  // 16ème col - 1ème ligne   pour indiquer gestion resistance en auto
      lcd.print('A');}  
  if (marcheR) {  
      lcd.setCursor(12,1);  // 13ème col - 2ème ligne    pour indiquer resistance sous tension
      lcd.print('R');} 
  if (debit > 0) {
      lcd.setCursor(9, 1) ; // 10ème col - 2ème ligne - positionne le curseur à l'endroit voulu (colonne, ligne) (1ère=0 !)
      lcd.print(debit) ;             
  }
  }
     

// fonction de l'interruption INT0 occasionnée par le pulse du flow sensor ************************
void pulsecounter(){
  impulsion++;
  precedentPulse = millis();
  }

///////////////////////////////////////////////////////////////////////////////////
//////           PROGRAMME PRINCIPAL              /////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////

void loop() {
   // clignotement de la led *************************
  if ((millis() - last_led ) >= duree) {
      if (etat_led == 1) {digitalWrite(led, LOW);       // c'était allumé donc on éteint la led
                          etat_led = LOW;                // la led est éteinte
                          duree = 1000; }                 // pour une durée de 1 sec       }
      else {                            // c'était éteint donc on l'allume
          digitalWrite(led, HIGH);      //  allumage de la led
          etat_led = HIGH;              // la led est allumée
          if ( auto_resistance == 1) {
              duree = 1000;           //  pour la durée d'allumage de 200ms ou de 1000ms si autorisation de lecture su capteur de débit
         } else {
               duree = 200;
               }
          }
      last_led = millis();
      }
  delay(10);
  
  // lire état du potentiometre   *******************************************
  valeurP = analogRead(A0); // test du potentiomètre
  if (valeurP >= 900) {
       pospotar = 3; // défini la position du potentiometre
     } else if (valeurP > 500) {
           pospotar = 2;
     } else if (valeurP > 100) {
           pospotar = 1;
     } else {
           pospotar = 0;
            }
            
  //  gestion du potentiometre car sa position  a changé *******************
  if (pospotar != oldpospotar) {
    if (pospotar > 2 ) {        //  c'est donc à 3
      digitalWrite(vanne1, LOW);  // forcage vanne 2 et circ
      digitalWrite(vanne2, HIGH);
      digitalWrite(circ, HIGH);
      auto_resistance = 0;
    }
    else if  (pospotar > 1)  {   //  c'est donc à 2
      digitalWrite(vanne1, HIGH);  // forcage vanne 1 et circ
      digitalWrite(vanne2, LOW);
      digitalWrite(circ, HIGH);
      auto_resistance = 0;
    }
    else {                     //  c'est donc à 0 ou 1
      digitalWrite(vanne2, LOW);                                                                              // tout arrêt
      digitalWrite(vanne1, LOW);
      digitalWrite(circ, LOW);
      if (pospotar == 1 )  {    // c'est donc à 1 , on autorise l'interruption pour lire le débimetre
         auto_resistance = 1;  }
      else  {auto_resistance = 0;}
        }
    oldpospotar = pospotar;
  }
  
  // acquisitions suivant timer   **********************************************
  if ((millis() - last_acquisition ) >= frequence_acquisition ) {
    acquisitions() ;
    if (pospotar <= 1)  {
      actions();
    }
    last_acquisition = millis();
  }       else  {
    lcd.setCursor(12, 1) ;
    lcd.print("  ");   // efface les digits 13 et 14 de la seconde ligne pas les 15 ni  16
    lcd.setCursor(14, 1) ; // 15ème col - 2ème ligne - positionne le curseur à l'endroit voulu (colonne, ligne) (1ère=0 !)
    lcd.print((last_acquisition + frequence_acquisition - millis()) / 1000);  // affiche les econdes restantes avant la nouvelle acquisitions
  }
  delay(10);
  
  // affichages suivant timer  ************************************************
  if ((millis() - last_affichage ) >= frequence_affichage ) {
    if (index_affichage > 4) {
      affichage("Temp  ","Arrivee", Tarrivee) ;
    } else if (index_affichage > 3) {
      affichage("Temp  ","Ballon 2", Tballon2) ;
    } else if (index_affichage > 2) {
      affichage("Temp  ","Capteur", Tcapteur) ;
    } else if (index_affichage > 1){
      affichage("Temp  ","Ballon 1 ", Tballon1) ;
    } else if (index_affichage > 0){
      affichage("Temp  ","CE ", T_CE) ;
    }  else { affichage("Temp  ","Resistance",Tresist);}
    index_affichage++;
    if (index_affichage == 6) {
      index_affichage = 0;
    }
    last_affichage = millis();
  }
  delay(20);
  
  // pour faire un reset régulièrement   ****************************************
  if ((millis() - last_reset) >= frequence_reset) {
    setup();
  }
  delay(20);


 // définir le débit pour l'affichage  basé sur le comtage des impulsions en 1 sec  *************************
  if ((millis()- debut_sensor )>= 1000) {      // à chaque seconde écoulée
       debit= impulsion/8.90;  //    déhit sur 1 sec
       debut_sensor=millis();  // pour une nouvelle péridoe d'observation de 1 sec
       impulsion = 0;}  

          
// gestion de la résistance      ***********************************************
// debuter le comptage de temps si un débit mini de 1 litre/mn s'écoule  càd dès que 2 pulses sont vus qui permettent de définir un débit
  if (auto_resistance){
      if (marcheR) {   // si resistance en marche
           if ((millis() - precedentPulse) > 1000) {   // 1 sec sans pulse du capteur donc plus de débit
               marcheR = 0;                   
               digitalWrite(resistance, LOW); } //arrête la resistance
                  }
          else {     // la résistance n'est pas en marche
             if (((millis() - precedentPulse) < 100)&& (Tballon1 < 48)) {       // il y a u n débit de au moins 1 litre /mn 
                marcheR = 1;
                digitalWrite(resistance, HIGH); }             //marche la resistance  
              }
              }      
              
              
  // verifier si message reçu du pi et le traiter  et renvoyer les données   ***************************
  message = "";
  if (Serial.available() > 0) {
      message = Serial.readString();
      if (message == "R") {
          auto_resistance = 1;
                           }
      if  (message == "S") {
          auto_resistance = 0;
          if (marcheR== 1){   // dans le cas où Resistance était en matche
               digitalWrite(resistance, LOW);  //arrête la resistance
               marcheR = 0;}
                           }               
    // ensuite envoi les donnnées sur le pi
      Serial.print(auto_resistance);  // correspond à  0 ou 1
      Serial.print(";");
      Serial.print(Tcapteur);
      Serial.print(";");
      Serial.print(Tarrivee);      
      Serial.print(";");
      Serial.print(Tballon2);
      Serial.print(";");
      Serial.print(Tballon1);
      Serial.print(";");
      Serial.print(T_CE);
      Serial.print(";");
      Serial.print(Tresist);
    
    }

  wdt_reset();   // reset du wachdog à chaque tour de loop
}    // fin du loop

C'est précisément le but du watchdog. C'est un circuit qui réinitialise le µP si on ne lui envoie pas de signal régulièrement.

Je ne pense pas que cela apporte quoi que ce soit.

J'ai eu aussi ce problème, résolu par l'adoption d'un circuit RC sur la charge (une vanne motorisée). Une varistance marche aussi.
Voir #4

En supposant que c'est bien du secteur qui est commuté, quitte à investir, j'investirais plutôt dans des relais statiques avec commutation sur le zéro de tension.
Dans ce genre par exemple:

Sachant que l'on peut en trouver des moins cher en ligne.
Je conviens que c'est plus cher mais tu n'a plus le problème de l'usure des contacts, des parasites et éventuellement du claquement à la commutation (si les relais sont dans une pièce à vivre)