Problème de programme ou d'arduino mesure de capteurs

Bonjour,

Je me permet de solliciter votre aide car j’ai un petit soucis, premièrement pour expliquer j’ai un dispositif d’acquisition de mesure de rayonnement PAR ( du rayonnement solaire pour simplifier) avec donc 2 capteurs , une amplification de tension à l’aide de deux AD623 et enfin un enregistrement sur carte SD à l’aide d’une arduino mega + d’un shield SD + d’une horloge DS1307.

Le soucis est qu’au bout de plusieurs heures mon dispositif “plante” , c’est à dire que l’arduino est allumée mais n’enregistre plus de mesures, j’ai une centrale campbell scientifique cr1000 en parallèle pour prendre quand même les mesures et vérifier la tension de la batterie et aucun problème avec ça.

Je ne suis pas non plus un expert mais je me demande si ce n’est pas un soucis de mémoire. Par ailleurs il y a surement quelques optimisations à faire dans le programme donc si vous voyez quelque chose n’hésitez pas.

Merci

#include <Wire.h>
#include "DS1307.h"
#include <SPI.h> // Pour la communication SPI
#include <SD.h>  // Pour la communication avec la carte SD

/*Variables pour la carte SD et le fichier*/
const byte SDCARD_CS_PIN = 4; 
const char* OUTPUT_FILENAME = "PAR.csv";
File file;
/* Variables pour l'horloge*/
DS1307 clock;

const float coefbEtalon=594.87;
const float coefaEtalon=129.48;
const float coefaEtalonnageSolems=0.6113;
const float coefbEtalonnageSolems=602.94;
const float valeur_etalonage_etalon=4.7; //valeur en microvolt /mol/m²/s actuellement 4.7 d'après la notice constructeur
/* Variables liées à la mesure et au calcul de moyenne*/
int valeurluegrise; //Lit la valeur de l'entrée 0 comprise entre 0 et 1023 en comparant avec la référence (1023= 2.56v ; 0=0v)
int valeurluejaune;// Lire la valeur de l'entrée 1 donc capteur étalon
long totalgris=0;
long totaljaune=0;
float moyenne_tension_solems;
float moyenne_tension_etalon;
float moyenne_tension_etalon_calculee=0;
float PAR_Etalon;
float PAR_Solems;
/*Variables de fonction d'arret et de la LED*/
int etatBouton=0;
const int ledpin=13;

void setup() {

  analogReference(INTERNAL2V56); //Prend 2,56v comme réference
  /* Initialisation du port série (debug) */
  Serial.begin(115200);
  /* Initialisation des pins*/
  pinMode(ledpin,OUTPUT);
  pinMode(4, OUTPUT);
  pinMode(53, OUTPUT); // 
  pinMode(10,INPUT);
  digitalWrite(4,HIGH);
  clock.begin();
  
  /*Initialisation de l'heure  
  /*clock.fillByYMD(2020,06,22);//Jan 19,2013
  clock.fillByHMS(13,1,00);//15:28 30"
  clock.fillDayOfWeek(MON);//Saturday
  clock.setTime();//write time to the RTC chip*/

  
  /* Initialisation de la carte SD */
  Serial.println(F("Initialisation de la carte SD ... "));
  if (!SD.begin(4)) {
    Serial.println(F("Erreur : Impossible d'initialiser la carte SD"));
    Serial.println(F("Verifiez la carte SD et appuyez sur le bouton RESET"));
    return; // 
  }
  /* Ouvre le fichier de sortie en écriture */
  Serial.println(F("Ouverture du fichier de sortie ... "));
  file = SD.open(OUTPUT_FILENAME, FILE_WRITE);
  if (!file) {
    Serial.println(F("Erreur : Impossible d'ouvrir le fichier de sortie"));
    Serial.println(F("Verifiez la carte SD et appuyez sur le bouton RESET"));
    for (;;); // Attend appui sur bouton RESET
  }
  /* Ajoute l'entête CSV si le fichier est vide */
  if (file.size() == 0) {
        Serial.println(F("Ecriture de l'entete CSV ..."));
        file.println(F("Date;Moyennetensionsolems;ParSOLEMS;moyenne_tension_etalon;moyenne_tension_etalon_calculée;PAR_etalon"));
        file.flush();
      }
  }

/** Fonction loop() */
void loop() {

  /* Réalise la mesure */
String date= "";
clock.getTime();
date=date+(clock.year+2000)+"/"+clock.month+"/"+clock.dayOfMonth+" "+clock.hour+":"+clock.minute+":"+clock.second+"";
totalgris=0;
totaljaune=0;
    
  for (int mesures=0; mesures < 60; mesures++){
    digitalWrite(ledpin,HIGH);
    etatBouton=digitalRead(10);
   if(etatBouton == LOW) {
     valeurluegrise=analogRead(A0);
     valeurluejaune=analogRead(A1);
     totalgris=totalgris+valeurluegrise;
     totaljaune=totaljaune+valeurluejaune;
     Serial.println(valeurluejaune);
     delay(2000);
     digitalWrite(ledpin,LOW);
   }
   else{
     digitalWrite(ledpin,HIGH);
     for(;;);
   }
  delay(3000);
  //Serial.println(valeurluejaune);
    }
 moyenne_tension_solems=(((float)totalgris/60)*((float)2560/1023));
 moyenne_tension_etalon=(((float)totaljaune/60)*((float)2560/1023));
 moyenne_tension_etalon_calculee=moyenne_tension_etalon-coefbEtalon;
 moyenne_tension_etalon_calculee=(float)moyenne_tension_etalon_calculee/(coefaEtalon);
 PAR_Etalon=moyenne_tension_etalon_calculee*((float)1000/valeur_etalonage_etalon);
 PAR_Solems=(moyenne_tension_solems-coefbEtalonnageSolems)/coefaEtalonnageSolems;
 
  /* Enregistre les données sur la carte SD */
  file.print(date);
  file.print(F("; "));
  file.print(moyenne_tension_solems);
  file.print(F("; "));
  file.print(PAR_Solems);
  file.print(F("; "));
  file.print(moyenne_tension_etalon);
  file.print(F("; "));
  file.print(moyenne_tension_etalon_calculee);
  file.print(F("; "));
  file.println(PAR_Etalon);
  file.flush();
  }

Essaie dans un premier temps de remplacer String date par char date[taille], et utilise strftime().
Bien entendu la chaîne doit être correctement dimensionnée pour contenir les données.

Encore toi aha , merci j'essaie ça, tu pense que cela peut venir de là ?

char date[20];
clock.getTime();
date=strftime(date,20,"%Y/%m/%d %H:%M:%S", ?);

Pour le dernier paramètre de la fonction strftime comment indiquer le dS1307 ?

j'ai bien vu l'exemple présent sur ton lien

time_t rawtime;
  struct tm * timeinfo;
  char buffer [80];

  time (&rawtime);
  timeinfo = localtime (&rawtime);

  strftime (buffer,80,"Now it's %I:%M%p.",timeinfo);

Mais je ne comprend pas si ce code est "universel" ou si je dois indiquer quelque part que mon repère est l'horloge ds1307.

Merci.

Sans savoir de quelle librairie DS1307 il s'agit, difficile de répondre.

Si la librairie n'est pas capable de fournir une struct tm, tu peux utiliser sprintf().

sprintf(date, "%04d/%02d/%02d %02d:%02d:%02d", clock.year+2000, clock.month, clock.dayOfMonth, clock.hour, clock.minute, clock.second);

D'accord merci, lorsque je vérifie le programme j'obtient :

DS1307 clock' redeclared as different kind of symbol

après avoir intégré la bibliothèque time.h pour strftime.

Dans time.h clock() est une fonction.
Renomme ton objet DS1307.

  1. l'utilisation de la librairie standard GCC AVR suppose deux choses :
  • mettre la librairie à l'heure à l'aide de set_system_time()
  • mise à jour de l'heure périodique (toutes les secondes) avec un appel à system_tick()
  • appeler localtime() pour récupérer l'heure
  1. s'il s'agit du projet logger :
    set_system_time() n'existe pas, system_tick() non plus.
    Mais il y a des équivalents.

Si tu utilises directement les infos du DS1307 et sprintf() cela passe dans les deux cas.

Merci pour ta réponse , oui c'est bien pour le datalogger et du coup oui j'ai utilisé directement les infos du DS1307 et sprintf().

Tu pense que le problème de "crash" venait de ça ?

Possible. String provoque une fragmentation de la RAM.
Seul un test sur la durée permettra de le dire.

Remarque : avec un ESP32 et sa mémoire beaucoup plus importante le problème ne se pose pas.

D'accord merci, oui c'est vrai après ce qui m'étonne c'est que ça arrive après parfois une centaine de mesures et donc de point enregistrés dans le fichier. Je vais faire un test

l'ESP32 semble vraiment être un bon choix dans mon cas. Par ailleur sur l'arduino j'ai du amplifier la tension pour la mesurer correctement car la tension du capteur est comprise entre 1 et 8mv , tu pense que c'est aussi nécessaire avec l'ESP 32 ?

Au sujet de String : arduino-la-fragmentation-memoire

L'amplification sera nécessaire aussi.
Par contre L'ESP32 a un ADC 12 bits, au lieu de 10 pour l'ARDUINO.

Merci , cela ne résous pas le problème , pas grave tout façon je vais passer sur un ESP32 comme tu me l'a conseillé.

Juste pour être sur je peux connecter mon ds1307 à l'ESP32 pour avoir l'heure ? car il peut y avoir des coupure de courant donc le NTP n'est peut être pas la meilleure solution.

L'ADC 12 bits aura donc une meilleure précision que celui de l'arduino, c'est super

ESP 32 j'ai trouvé ce lien pour l'ESP32 c'est bien le bon que tu m'avais indiqué avec une mémoire de 1,4mo pour pouvoir stocker mon fichier csv ?

Juste pour être sur je peux connecter mon ds1307 à l'ESP32 pour avoir l'heure ?

Oui. Le DS3231 est cependant plus précis.

Il y a beaucoup de versions de module ESP32, celui-ci est un peu cher mais devrait convenir.
En général j'achète sur AliExpress, mais le délai est plus long : 20 jours minimum.

  else{
     digitalWrite(ledpin,HIGH);
     for(;;);
   }

Tu es sur de ne pas entrer dans ce cas ?

D'accord merci , par contre je suis sur une batterie 12V donc il faut un régulateur 3.3v pour l'esp32 ?

Normalement oui , si je ne me trompe je ne suis dans cette situation que si le bouton est enfoncé hors je n'y touche pas .

En plus la led continue de clignoter indiquant que le programme fonctionne , j'ai plus l'impression que le programme continue de tourner mais que ça n'enregistre pas sur la carte SD

Le problème est que tu ouvres le fichier dans setup() et que tu ne le fermes jamais.
Dans ces conditions le retrait de la carte SD pour contrôle est problématique.
Il faudrait un bouton pour fermer le fichier et pouvoir retirer la SD en toute sécurité, ou un bouton pour visualiser le fichier sur la console.
J'avoue que la SD sur ARDUINO ne m'a jamais vraiment convaincu :confused:

Oui effectivement il vaudrait mieux un régulateur 3.3V, à découpage de préférence (MP1584, LM2596, etc.).

Oui c'est vrai je suis d'accord qu'il serait plus propre de réaliser file.close , j'avoue avoir déja eu pas mal de problème avec la carte SD ce n'est pas trés pratique.

Peut être que le problème vient de garder ouvert le fichier si je faisais un file.close à chaque fin de boucle puis file.open ça réglerait le problème je ne sais pas si le fait de garder le fichier ouvert peut "saturer" la mémoire.

Pour te montrer en pratique ce que cela donne.

Là on voit qu'il y a un soucis puisque j'ai retiré la carte SD a 16h , il manque donc énormément de points.

Parfois ça enregistre pendant 8/10h puis des fois ça coupe au bout de 1h30 ou 2h c'est totalement aléatoire et à chaque fois que j'arrive devant le dispositif la led de fonctionnement du programme clignote bien , je pense donc que la carte SD c'est vraiment pas une bonne solution puisque pour moi le programme fonctionne toujours, c'est uniquement un problème d'enregistrement.

Pour essayer de trouver le problème j'ai supprimé tous les calculs du programme et les variables, j'ai juste laissé l'heure et ça a fonctionné pendant 24h avant que je l’arrête , ensuite j'ai juste ajouté l'enregistrement des analog.read des 2 capteurs sans aucun calcul et même problème coupure vers 23h après 90 points environ.

Je ne sais pas d'ou viens le problème du coup