Aquisition de donnée en rapide sur carte sd / accéléromètre

Bonjour à tous,

Je suis actuellement sur un projet ayant pour but de retracer une trajectoire/ un mouvement grâce à la carte arduino. Nous utilisons un accéléromètre 3 axes sur une Gotronic Uno et un shield sd muni d’une pile pour faire horloge
ref :

GY-61 ADXL335 3-Axial Accelerometer
Shield datalogging GT1046
Carte UNO R3 UNO-V3

Actuellement je fais ±27 acquisition par seconde, directement enregistré sur la carte sd.
J’imprime sur la carte sd ceci : milliseconde ; axeX ; axeY ; axeZ

J’aimerais augmenter le nombre d’acquisition par seconde, ainsi qu’avoir la donnée précise du moment de l’acquisition de la donnée ( afin de pouvoir calculer un deltaT).

Si vous connaissez une manière de retracer la trajectoire efficacement ou/et d’autre capteur/module permettant de m’aider dans mon projet je suis tout ouïe.

Merci

Sylver 20 ans, étudiant en électromécanique.

code arduino

#include <SPI.h>
#include <SD.h>
#include <Wire.h>
#include <RTClib.h>

RTC_DS1307 RTC;
File monFichier;

boolean memo = 0,ire=0;
int bouton = 2;
const int xpin = A2; // axe x de l'accéléromètre
const int ypin = A1; // axe y
const int zpin = A0; // axe z (uniquement sur les modèles à 3 axes)

void setup(){
 
   pinMode (bouton, INPUT);
   pinMode (xpin, INPUT);
   pinMode (ypin, INPUT);
   pinMode (zpin, INPUT);
   pinMode (7, OUTPUT);
 
   Serial.begin(38400); //débute la communication avec le moniteur série
       if (!SD.begin(10)){//teste la communication avec la carte(pin 10 avec le shield)
       return; //stoppe le programme
   }; 
   
   Wire.begin();
   RTC.begin();
   RTC.adjust(DateTime(2020, 4, 10, 0, 0,0));// }
   
   if (SD.exists("mesure.csv")){
     SD.remove ("mesure.csv");
     Serial.println("fichier initialisé");
     }  
}


void loop() {
   DateTime now = RTC.now();
   
   boolean etatBouton=digitalRead(bouton);
   //Serial.println(etatBouton);
    
   int x = analogRead (xpin);
   delay (1);
   int y = analogRead (ypin);
   delay (1);
   int z = (analogRead (zpin)-10);
   delay (1);
  //Serial.print (now.hour(), DEC );
  // Serial.print(':');
   //Serial.print(now.minute(), DEC);
 //  Serial.print(':');
   Serial.print(millis());
   Serial.println (" ax = " + (String) x + " ay = " + (String) y + " az = " + (String) z  );

   
   
 if (etatBouton == 1){
    memo = !memo;
    ire = memo;
    delay(50);
         }
     
 if (ire == 1){
    digitalWrite(7, HIGH);
    if (!(monFichier = SD.open("mesure.csv",FILE_WRITE))){ //tente d'ouvrir le fichier
       return; //stoppe le programme
       Serial.println ("impossible d'ouvrir");
       }
       //Serial.println ("fichier ouvert");
       
     monFichier.print(millis(), DEC);
     monFichier.println(";" + (String) x + ";" + (String) y + ";" + (String) z  );  //fichier .csv est un fichier ouvrable sur excel, chaque donnée doit être séparée par un ";"
     Serial.println ("ecriture");
     monFichier.close();  
     //Serial.println ("fermer");
           }   
     
 else {
   digitalWrite(7, LOW);
 }
 delay(1);
}

lisez comment utiliser le forum (les posts épinglés en haut du forum comme “Bien éditer son post pour les nuls” , “Faire un Nouveau Sujet ou le corriger avec les Balises…” et “Règles du forum francophone”), et modifiez votre post pour faire un post “propre”

———————————-
Un bon article à lire par l’auteur de la bibliothèque SDFat

I am the author of SdFat, the base library for SD. Several people have asked me why SD is so slow in Arduino 22 when you use print() for numbers.

Here is the reason SD is so slow and a way to speed it up by a factor of 100.

Print does character at a time writes when it formats numbers. SD has been setup to do a flush after every write.

This means that println(n) will call flush six times for a four digit number, four times for the digits and two times for the new line.

SD cards can only be read or written in 512 byte blocks and SdFat has a single 512 byte buffer.

A flush causes the data block to be written, the directory block to be read, updated and written. The next write requires the data block to be read so it can be updated. This is 2048 bytes of I/O.

Therefore writing the four byte number and a new line requires 12,288 bytes of I/O to the SD card.

Pendant combien de temps voulez vous enregistrer ?

Quelques points à prendre en compte:

pour lire l’info de votre ADXL335:

  • de point de vue materiel il est configurable pour monter jusqu’à 1600 Hz sur les axes X et Y et 550 Hz sur l’axe Z.
  • Sur votre Arduino, il n’y a qu’un seul ADC donc vous devrez faire des lectures en séquence (donc vous n’avez pas les 3 mesures “au même instant”)
  • On ne peut pas passer d’une entrée à l’autre trop rapidement si les tensions sont fortement différentes (ce qui peut être le cas dans votre usage) vous aurez des lectures fausses. Souvent on fait 2 lectures par pins et on conserve la seconde
  • AnalogRead() est assez lent (plus de 0.1ms)

→ 6 lectures x 0.1ms = 0,6ms arrondissons à 1ms donc vous pourriez monter à 1000Hz ce qui est au de la de la spec du composant sur l’axe Z.

Ensuite faut regarder le volume de données:

  • millis c’est 4 octets (et on peut en garder que 2 vous n’enregistrez que de l’ordre de la minute)
  • votre ADC est sur 10 bits, donc 2 octets x 3 valeurs = 6 octets.
    donc on peut s’en sortir avec 12 octets par mesure - voire 10.

si vous montez disons à 100 échantillons par seconde (100 Hz) - ça prend quasiment 1ko de SRAM. Vous n’en avez que 2ko sur un UNO…

Il vaudrait mieux enregistrer en binaire les données plutôt que le forme ASCII qui sera plus longue et éviter de faire aussi des Serial.print (en plus avec des Strings) à 38400 bauds
Le facteur bloquant risque vite de devenir votre carte SD si vous ouvrez et fermez le fichier à chaque échantillon… -----> faudra être plus smart et gérer le buffer existant de 512 octets.

→ Avec la librairie SDFat on peut arriver à un débit de 300ko par seconde (lire ce post) mais c’est bien théorique et très loin de la pratique…

Disons qu’on tienne 50ko/s, Pour stocker 40 échantillons (40 x 12 = 480 octets) il faudra quand même ~10ms au moment de l’écriture, ce qui devient (si vous voulez conserver des fenêtres homogènes) le temps min entre deux échantillons → < 100Hz on est toujours dans la spec du capteur

→ si vous voulez tourner à 100Hz, à la louche on doit pouvoir faire ~1ms d’acquisition toutes les 10ms, plus ~10ms de sauvegarde toutes les 40 lectures (donc pratiquement toutes les 10ms occupées dans la fenêtre de 10ms)

→ au delà ça devient juste…

Donc 100Hz voire 150Hz devraient être jouables sans trop aller dans la manipulation des PORTs et du code un peu plus complexe.

Bonjour,

merci de votre réponse très complète!

Effectivement tourner autour des 100 échantillons secondes serais déjà énorme pour moi.

J’utilise désormais un convertisseur 16 bit sur lequel je branches mes 3 mesures X,Y,Z.

J’ai simplifier le code afin de ne plus ouvrir et fermer mon fichier à chaque mesure.

Je vais regarder à cette librairie car je n’ai aucune idée de comment procéder :slight_smile:

#include <Wire.h>
#include <Adafruit_ADS1015.h>
#include <SPI.h>
#include <SD.h>
#include <RTClib.h>

Adafruit_ADS1115 ads(0x48);
RTC_DS1307 RTC;
File monFichier;

boolean memo = 0,ire=0;
int bouton = 2;

void setup(void)
{
    pinMode (bouton, INPUT);
    pinMode (7, OUTPUT);
    
/*Serial.begin(9600);
Serial.println("Hello!");
 
Serial.println("Getting single-ended readings from AIN0..3");
Serial.println("ADC Range: +/- 6.144V (1 bit = 3mV/ADS1015, 0.1875mV/ADS1115)");*/

    if (!SD.begin(10)){//teste la communication avec la carte(pin 10 avec le shield)
      return; //stoppe le programme
    }
    
    Wire.begin();
    RTC.begin();
    RTC.adjust(DateTime(2020, 4, 10, 0, 0,0));// }
    
    if (SD.exists("mesure.csv")){
      SD.remove ("mesure.csv");
     // Serial.println("fichier initialisé");
    }  
     if (!(monFichier = SD.open("mesure.csv",FILE_WRITE))){ //tente d'ouvrir le fichier
        return; //stoppe le programme
       // Serial.println ("impossible d'ouvrir");
     }
       // Serial.println ("fichier ouvert");
 
ads.begin();
}

 
void loop(void)
{
DateTime now = RTC.now();
boolean etatBouton=digitalRead(bouton);
// Serial.println(etatBouton);
  if (etatBouton == 1){
     memo = !memo;
     ire = memo;
     delay(50);
  }
  
int16_t adc0, adc1, adc2, adc3;
 
adc0 = ads.readADC_SingleEnded(0);
adc1 = ads.readADC_SingleEnded(1);
adc2 = ads.readADC_SingleEnded(2);
adc3 = ads.readADC_SingleEnded(3);

/*Serial.println(millis());
Serial.print("AIN0: ");
Serial.println(adc0);
Serial.print("AIN1: ");
Serial.println(adc1);
Serial.print("AIN2: ");
Serial.println(adc2);
Serial.print("AIN3: ");
Serial.println(adc3);
Serial.println(" ");*/

 if (ire == 1){
   digitalWrite(7, HIGH);
   monFichier.print(millis(), DEC);
   monFichier.println(";" + (String) adc0 + ";" + (String) adc1 + ";" + (String) adc2  );  //fichier .csv est un fichier ouvrable sur excel, chaque donnée doit être séparée par un ";"
   monFichier.flush();
   //Serial.println ("ecriture");    
 }
       
 else {
    digitalWrite(7, LOW);
 }
delay(500);
}

J'ai un vieux bout de code que j'avais fait pour tester la perf de SDFat sur de l'acquisition de données sur voie analogique.

J'ai modifié la structure t_echantillon pour avoir 3 attributs comme dans votre cas.
Un échantillon aura donc l'heure relative d'acquisition (par rapport au début de la séquence d'acquisition) et 3 valeurs lues sur A0, A1 et A2.

  • Je génère un fichier en binaire pour être optimal.
  • ce fichier commence par "une sentinelle", un code qui permet d'identifier mon fichier de log. (0xDEADBEEF)
  • puis ensuite j'ai n enregistrements de ma structure pour chaque échantillon.

il y a deux constantes que vous pouvez modifier

const uint32_t periodeAcquisition = 20;    // en ms -> une acquisition toutes les periodeAcquisition ms
const uint32_t dureeAcquisition = 10000;   // en ms

la première (periodeAcquisition) est comme son nom l'indique la période d'acquisition en ms. donc la fréquence d'échantillonnage c'est 1000/periodeAcquisition, ici avec 20 on est à 50Hz

La seconde valeur est la durée d'enregistrement que vous souhaitez obtenir.

J'ai fait tourner cela avec une carte SD pas super rapide à 50Hz, 100Hz, 200Hz et 500Hz (une 2Go chinoise à 1€) et j'ai fait un petit graphe des moment d'acquisition de chaque échantillon (abscisse = N° échantillon, Ordonnée = temps). Si tout se passe bien on devrait avoir un droite dont la pente est inversement proportionnelle à la fréquence d'échantillonnage). Si on voit une rupture c'est que l'on n'a pas tenu la cadence
graphe.pdf (225 KB)

--> On voit donc qu'à 100Hz on est à peu près limite il y aura une petite latence lors du flush (écriture du buffer sur la carte SD) qui va décaler les prochains échantillons. Au dessus de 100Hz c'est de plus en plus visible.

Le code dans le post suivant car ça ne tient pas ici

graphe.pdf (225 KB)

Voici le code, il n’est pas super optimisé (utilisation des ports, pousser au max la communication avec la carte SD, meilleure carte SD, format de la carte, …)

La console sur votre PC doit être mise à 500,000 bauds. (parce que sinon c’est long de dumper le fichier à la fin).

/*
    Copyright (c) 2020 J-M-L  https://forum.arduino.cc/index.php?action=profile;u=438300
    Author        :   J-M-L
    Create Time   :   April 2020
    Change Log    :

    The MIT License (MIT)
    Permission is hereby granted, free of charge, to any person obtaining a copy
    of this software and associated documentation files (the "Software"), to deal
    in the Software without restriction, including without limitation the rights
    to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
    copies of the Software, and to permit persons to whom the Software is
    furnished to do so, subject to the following conditions:
    The above copyright notice and this permission notice shall be included in
    all copies or substantial portions of the Software.
    THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
    IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
    FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
    AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
    LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
    OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
    THE SOFTWARE.
*/

#include <inttypes.h>       // formats pour sprintf, sscanf

#include <SPI.h>
#include "SdFat.h"
SdFat SD;
File fichierLog;
const char* nomFichierLog = "mesures.log";
const uint32_t signature = 0xDEADBEEF;

struct __attribute__ ((packed)) t_echantillon  {
  uint32_t t;
  uint16_t v0;
  uint16_t v1;
  uint16_t v2;
} unEchantillon;

const uint8_t tailleEchantillon = sizeof(t_echantillon); // le nombre d'octets dans une mesure
const uint8_t nbEchantillonsParBuffer = 512 / tailleEchantillon; // la carte SD a un buffer de 512 octets

const uint8_t pinV0 = A0;
const uint8_t pinV1 = A1;
const uint8_t pinV2 = A2;

const uint32_t periodeAcquisition = 20;    // en ms -> une acquisition toutes les periodeAcquisition ms
const uint32_t dureeAcquisition = 10000;   // en ms
const uint32_t nombreTotalEchantillons = dureeAcquisition / periodeAcquisition;
uint32_t nombreEchantillonsAcquis = 0;

uint32_t topDepart = 0, tempsTotal = 0, tempsFlush = 0;

void relectureFichier()
{
  char ligne[100];
  uint8_t sentinelle[sizeof(signature)]; // va-t-on retrouver la signature ?

  Serial.println(F("---- CONTENU DU FICHIER ----"));
  fichierLog = SD.open(nomFichierLog, O_READ);
  if (! fichierLog) {
    Serial.println(F("erreur ouverture fichier Log en lecture"));
    return;
  }

  fichierLog.read((uint8_t*) &sentinelle, sizeof(signature));
  if (memcmp(sentinelle, &signature, sizeof(signature)) == 0) { // la signature est conforme, on lit le fichier
    uint32_t nombreEchantillonsLus = 0;
    Serial.println(F("échantillon\t T en ms \t V0 \t V1 \t V2"));
    while (fichierLog.available()) {
      if (fichierLog.read((uint8_t*) &unEchantillon, tailleEchantillon) != tailleEchantillon) {
        Serial.print(F("erreur lecture échantillon #"));
        Serial.println(nombreEchantillonsLus);
      } else {       // imprimer l'échantillon
        nombreEchantillonsLus++;
        sprintf(ligne, "%-9" PRIu32 "\t%9"  PRIu32 "\t%4"  PRIu16 "\t%4"  PRIu16 "\t%4" PRIu16, nombreEchantillonsLus, unEchantillon.t, unEchantillon.v0, unEchantillon.v1, unEchantillon.v2);
        Serial.println(ligne);
      }
    }
    if (nombreEchantillonsLus != nombreEchantillonsAcquis) {
      Serial.println(F("Erreur sur le nombre d'échantillons acquis versus relus"));
    } else {
      Serial.println(F("Le compte est bon !"));
    }
  } else {
    Serial.println(F("Signature incorrecte, Fichier de LOG non reconnu."));
  }
  fichierLog.close();
}

inline bool acquisition()
{
  static uint32_t tempsDerniereAcquisition = 0xFF000000; // pour forcer une acquisition immédiate
  if (millis() - tempsDerniereAcquisition >= periodeAcquisition) { // est-ce le moment de faire une acquisition
    unEchantillon.t  = millis() - topDepart;
    analogRead(pinV0); // on en fait une à blanc pour laisser la tension se stabiliser sur l'ADC
    unEchantillon.v0  = analogRead(pinV0);
    analogRead(pinV1); // on en fait une à blanc pour laisser la tension se stabiliser sur l'ADC
    unEchantillon.v1  = analogRead(pinV1);
    analogRead(pinV2); // on en fait une à blanc pour laisser la tension se stabiliser sur l'ADC
    unEchantillon.v2  = analogRead(pinV2);
    nombreEchantillonsAcquis++;
    tempsDerniereAcquisition = millis();
    return true;
  }
  return false;
}

void setup()
{
  Serial.begin(500000);

  pinMode(pinV0, INPUT);
  pinMode(pinV1, INPUT);
  pinMode(pinV2, INPUT);

  if (!SD.begin(SS)) {
    Serial.println(F("erreur SD.begin !"));
    while (true);
  }

  Serial.print(F("\nAcquisition de "));
  Serial.print(nombreTotalEchantillons);
  Serial.print(F(" échantillons, pendant environ "));
  Serial.print(dureeAcquisition / 1000.0, 1);
  Serial.println(F(" s."));
  Serial.print(F("Fréquence d'échantillonnage = "));
  Serial.print(1000.0 / periodeAcquisition);
  Serial.println(F(" Hz."));
  Serial.println(F("tapez return pour Commencer l'acquisition."));
  while (Serial.read() != '\n'); // on attend 'entrée'

  SD.remove(nomFichierLog);
  fichierLog = SD.open(nomFichierLog, O_WRITE | O_CREAT);
  if (!fichierLog) {
    Serial.println(F("erreur ouverture fichier Log en écriture"));
    while (true);
  }

  // sentinelle en début de fichier pour reconnaitre notre format
  // permet aussi d'initialiser les buffers etc
  fichierLog.write(&signature, sizeof(signature));
  fichierLog.flush();

  topDepart = millis();
}

void loop()
{
  static uint32_t chronoImpressionAttente = millis();

  if (acquisition()) { // si on a lu un nouvel échantillon, on le sauve

    if (fichierLog.write(&unEchantillon, tailleEchantillon) != tailleEchantillon) {
      Serial.print(F("erreur ecriture échantillon #"));
      Serial.println(nombreEchantillonsAcquis);
    }

    if (((nombreEchantillonsAcquis % nbEchantillonsParBuffer) == 0) || (nombreEchantillonsAcquis >= nombreTotalEchantillons)) {
      uint32_t t = millis();
      fichierLog.flush(); // on écrit physiquement sur la SD
      tempsFlush += millis() - t;
    }

    if (nombreEchantillonsAcquis >= nombreTotalEchantillons) {    // on a terminé
      tempsTotal = millis() - topDepart;
      fichierLog.close();

      Serial.print(F("Acquisition de "));
      Serial.print(nombreEchantillonsAcquis);
      Serial.print(F(" échantillons en "));
      Serial.print(tempsTotal / 1000.0, 1);
      Serial.println(F("s."));
      Serial.print(F("Débit d'écriture SD = "));
      Serial.print(1000.0 * (nombreEchantillonsAcquis * tailleEchantillon) / tempsFlush, 0);
      Serial.println(F(" octets/s."));
      Serial.println(F("tapez return pour lire le fichier."));
      while (Serial.read() != '\n'); // on attend 'entrée'
      relectureFichier();
      while (true);
    }
  }

  if (millis() - chronoImpressionAttente >= 1000) {
    static uint32_t compteur = 0;
    compteur++;
    if (compteur % 5 == 0) Serial.print(compteur);
    else Serial.write('.');
    if (compteur % 40 == 0) Serial.println();
    chronoImpressionAttente = millis();
  }
}

Merci pour ce code j'ai du passer la journée à l'analyse et le comprendre et j'ai beaucoup appris.

Je n'ai pas besoin d'une interface sur pc, grâce à mon ancien programme j'appuyait sur un bouton physique pour commencer la mesure et une LED témoin m'indiquait la prise de mesure.

J'ai vraiment besoin de pouvoir exploiter mes donnée sur excels donc j'ai mis en CSV Malheureusement que se soit en .log ou .csv ; j'obtient quelque chose comme ceci

D*Ÿ"R#YG*œ"O#¨F*œ"K#øC* "O#H?ž"N#˜Ež"O#çG*Ÿ"N#6E*ž"R#…E*ž"M#ÔG*ž"M#$ B*¢"O#t H*"N#Ä A* "Q#=š"O#bGœ"J#±A*¢"P# B*—"P#P J*¢"N# B*™"J#ð E*š"P#?

--> totalement inexploitable ....

cependant cela fonctionne dans le terminal :

échantillon T en ms V0 V1 V2 1 0 10820 8865 9037 2 80 10823 8868 9038 3 160 10818 8852 9041 4 240 10822 8868 9038 5 320 10824 8857 9041 6 399 10818 8858 9037 7 478 10825 8868 9040 8 557 10820 8858 9038 9 636 10823 8867 9040

Mais j'ai une mesure toute les 0.080 sec....

Je n'ai pas réussi à mettre a 5.000.000 bauds car le max dans mon moniteur est de 2.000.000

J'aurais besoin d'un fichier me donnant quelque chose comme ceci 15947;10825;8890;9052 16612;10823;8863;9033 16794;10820;8861;9028 16975;10815;8864;9032 17158;10823;8863;9033 17773;10814;8863;9046 18327;10821;8862;9031 18508;10824;8858;9032 19062;10824;8857;9030

Merci de votre réponse !

Comme je l'ai dit plus haut, le fichier est en binaire la valeur d'un capteur par exemple 1023 est codée en hexadécimal sur 2 octets 0x3FF et donc dans le fichier vous aurez deux octets écrits: 0x03 et 0xFF. ça prend 2.5x moins de temps que si je devais écrire 1023 (4 caractères) plus une virgule ensuite.. sans parler du fait que un print déclenche un flush() etc --> lisez le lien donné plus haut pour plus de détails mais en gros dites vous que si vous voulez aller assez vite, vous ne pourrez pas générer un fichier texte.

Donc comme le fichier est en binaire, on ne peut pas le lire avec excel ou un éditeur de texte. Il faut le convertir - mais si vous voulez 100Hz, vous ne pouvez pas faire cela pendant l'acquisition.

Si vous regardez la fonction relectureFichier(), elle lit le binaire et re-fabrique un truc en ASCII à l'écran dans la console série. soit vous faites un copier coller dans excel de ce que la console affiche (c'est comme ça que j'ai fabriqué les courbes ci dessus), soit vous modifiez cette fonction pour qu'au lieu d'écrire sur la console elle écrive dans un fichier .csv que vous ouvrirez ensuite depuis la carte SD

Pour les bauds, c'est 500,000 pas 5,000,000 :)

ok merci ce n'est pas une solution envisageable malheureusement :)

pensez vous que ce serais possible d'avoir une acquisition rapide avec une autre carte compatible arduino; comme par exemple la teensy 4.0 qui est plus performante?

En gros es ce mon matériel qui me limite ou pas?

Je ne comprends pas pourquoi vous ne pourriez pas générer un CSV après coup une fois l’acquisition terminée

Pendant combien de temps devez vous faire l’acquisition ? Pourriez vous les conserver en RAM ?

ok merci ce n'est pas une solution envisageable malheureusement :)

pensez vous que ce serais possible d'avoir une acquisition rapide avec une autre carte compatible arduino; comme par exemple la teensy 4.0 qui est plus performante?

En gros es ce mon matériel qui me limite ou pas?

sylverb7:
ok merci ce n’est pas une solution envisageable malheureusement :slight_smile:

Figure-toi qu’on aimerait bien savoir pourquoi.
Ou pourquoi tu ne pourrais pas transcrire ton fichier binaire en ASCII sur le PC, après avoir transféré la carte SD (programme de quelques lignes à faire sur PC)

« On ne peut pas » n’est pas suffisant en ingénierie il faut rajouter ensuite un « car xxx » et il faut xxx soit une raison valable :)

sylverb7: En gros es ce mon matériel qui me limite ou pas?

Ta 1ère limite vient de toi, tu veux absolument créer directement un fichier .CSV. Cette contrainte te coûte un facteur 2.5, comme brillamment démontré par J-M-L

Donc, non, ce n'est pas ton matériel qui te limite.

en gros pour être précis il vous faudra sans doute de l'ordre de N secondes pour générer le fichier CSV à partir du fichier binaire si vous avez enregistré pendant N secondes.

Le code pourrait ressembler à cela (par rapport à mon exemple ci dessus)

/*
    Copyright (c) 2020 J-M-L  https://forum.arduino.cc/index.php?action=profile;u=438300
    Author        :   J-M-L
    Create Time   :   April 2020
    Change Log    :

    The MIT License (MIT)
    Permission is hereby granted, free of charge, to any person obtaining a copy
    of this software and associated documentation files (the "Software"), to deal
    in the Software without restriction, including without limitation the rights
    to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
    copies of the Software, and to permit persons to whom the Software is
    furnished to do so, subject to the following conditions:
    The above copyright notice and this permission notice shall be included in
    all copies or substantial portions of the Software.
    THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
    IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
    FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
    AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
    LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
    OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
    THE SOFTWARE.
*/

#include <inttypes.h>       // formats pour sprintf, sscanf

#include <SPI.h>
#include "SdFat.h"
SdFat SD;
File fichierLog;
const char* nomFichierLog = "mesures.log";
const char* nomFichierCSV = "mesures.csv";
const uint32_t signature = 0xDEADBEEF;

struct __attribute__ ((packed)) t_echantillon  {
  uint32_t t;
  uint16_t v0;
  uint16_t v1;
  uint16_t v2;
} unEchantillon;

const uint8_t tailleEchantillon = sizeof(t_echantillon); // le nombre d'octets dans une mesure
const uint8_t nbEchantillonsParBuffer = 512 / tailleEchantillon; // la carte SD a un buffer de 512 octets

const uint8_t pinV0 = A0;
const uint8_t pinV1 = A1;
const uint8_t pinV2 = A2;

const uint32_t periodeAcquisition = 10;    // en ms -> une acquisition toutes les periodeAcquisition ms
const uint32_t dureeAcquisition = 10000;   // en ms
const uint32_t nombreTotalEchantillons = dureeAcquisition / periodeAcquisition;
uint32_t nombreEchantillonsAcquis = 0;

uint32_t topDepart = 0, tempsTotal = 0, tempsFlush = 0;

void generationCSV(bool sortieSerie = false)
{
  char ligne[100];
  uint8_t sentinelle[sizeof(signature)]; // va-t-on retrouver la signature ?
  const char* enTeteCSV = "T en ms\tV0\tV1\tV2\r\n";
  uint32_t chrono;

  chrono = millis();

  fichierLog = SD.open(nomFichierLog, O_READ);
  if (! fichierLog) {
    Serial.println(F("erreur ouverture fichier Log en lecture"));
    return;
  }

  SD.remove(nomFichierCSV);
  File fichierCSV = SD.open(nomFichierCSV, O_WRITE | O_CREAT);
  if (! fichierCSV) {
    fichierLog.close();
    Serial.println(F("erreur ouverture fichier CSV en écriture"));
    return;
  }

  if (sortieSerie) Serial.println(F("---- CONTENU DU FICHIER ----"));

  fichierLog.read((uint8_t*) &sentinelle, sizeof(signature));
  if (memcmp(sentinelle, &signature, sizeof(signature)) == 0) { // la signature est conforme, on lit le fichier
    uint32_t nombreEchantillonsLus = 0;
    if (sortieSerie) Serial.print(enTeteCSV);
    fichierCSV.write(enTeteCSV, strlen(enTeteCSV));
    while (fichierLog.available()) {
      if (fichierLog.read((uint8_t*) &unEchantillon, tailleEchantillon) != tailleEchantillon) {
        Serial.print(F("erreur lecture échantillon #"));
        Serial.println(nombreEchantillonsLus);
      } else {       // imprimer l'échantillon
        nombreEchantillonsLus++;
        sprintf(ligne, "\t%9"  PRIu32 "\t%4"  PRIu16 "\t%4"  PRIu16 "\t%4" PRIu16 "\r\n", unEchantillon.t, unEchantillon.v0, unEchantillon.v1, unEchantillon.v2);
        if (sortieSerie) Serial.print(ligne);
        fichierCSV.write(ligne, strlen(ligne));
      }
    }
    if (nombreEchantillonsLus != nombreEchantillonsAcquis) {
      Serial.println(F("Erreur sur le nombre d'échantillons acquis versus relus"));
    } else {
      Serial.println(F("Le compte est bon !"));
    }
  } else {
    Serial.println(F("Signature incorrecte, Fichier de LOG non reconnu."));
  }

  fichierCSV.close();
  fichierLog.close();
  chrono = millis() - chrono;

  Serial.print(F("\n\nGénération du fichier en "));
  Serial.print(chrono / 1000.0, 1);
  Serial.print(F(" s."));
}

inline bool acquisition()
{
  static uint32_t tempsDerniereAcquisition = 0xFF000000; // pour forcer une acquisition immédiate
  if (millis() - tempsDerniereAcquisition >= periodeAcquisition) { // est-ce le moment de faire une acquisition
    unEchantillon.t  = millis() - topDepart;
    analogRead(pinV0); // on en fait une à blanc pour laisser la tension se stabiliser sur l'ADC
    unEchantillon.v0  = analogRead(pinV0);
    analogRead(pinV1); // on en fait une à blanc pour laisser la tension se stabiliser sur l'ADC
    unEchantillon.v1  = analogRead(pinV1);
    analogRead(pinV2); // on en fait une à blanc pour laisser la tension se stabiliser sur l'ADC
    unEchantillon.v2  = analogRead(pinV2);
    nombreEchantillonsAcquis++;
    tempsDerniereAcquisition = millis();
    return true;
  }
  return false;
}

void setup()
{
  Serial.begin(500000);

  pinMode(pinV0, INPUT);
  pinMode(pinV1, INPUT);
  pinMode(pinV2, INPUT);

  if (!SD.begin(SS)) {
    Serial.println(F("erreur SD.begin !"));
    while (true);
  }

  Serial.print(F("\nAcquisition de "));
  Serial.print(nombreTotalEchantillons);
  Serial.print(F(" échantillons, pendant environ "));
  Serial.print(dureeAcquisition / 1000.0, 1);
  Serial.println(F(" s."));
  Serial.print(F("Fréquence d'échantillonnage = "));
  Serial.print(1000.0 / periodeAcquisition);
  Serial.println(F(" Hz."));
  Serial.println(F("tapez return pour Commencer l'acquisition."));
  while (Serial.read() != '\n'); // on attend 'entrée'

  SD.remove(nomFichierLog);
  fichierLog = SD.open(nomFichierLog, O_WRITE | O_CREAT);
  if (!fichierLog) {
    Serial.println(F("erreur ouverture fichier Log en écriture"));
    while (true);
  }

  // sentinelle en début de fichier pour reconnaitre notre format
  // permet aussi d'initialiser les buffers etc
  fichierLog.write(&signature, sizeof(signature));
  fichierLog.flush();

  topDepart = millis();
}

void loop()
{
  static uint32_t chronoImpressionAttente = millis();

  if (acquisition()) { // si on a lu un nouvel échantillon, on le sauve

    if (fichierLog.write(&unEchantillon, tailleEchantillon) != tailleEchantillon) {
      Serial.print(F("erreur ecriture échantillon #"));
      Serial.println(nombreEchantillonsAcquis);
    }

    if (((nombreEchantillonsAcquis % nbEchantillonsParBuffer) == 0) || (nombreEchantillonsAcquis >= nombreTotalEchantillons)) {
      uint32_t t = millis();
      fichierLog.flush(); // on écrit physiquement sur la SD
      tempsFlush += millis() - t;
    }

    if (nombreEchantillonsAcquis >= nombreTotalEchantillons) {    // on a terminé
      tempsTotal = millis() - topDepart;
      fichierLog.close();

      Serial.print(F("\n\nAcquisition de "));
      Serial.print(nombreEchantillonsAcquis);
      Serial.print(F(" échantillons en "));
      Serial.print(tempsTotal / 1000.0, 1);
      Serial.println(F("s."));
      Serial.print(F("Débit d'écriture SD = "));
      Serial.print(1000.0 * (nombreEchantillonsAcquis * tailleEchantillon) / tempsFlush, 0);
      Serial.println(F(" octets/s."));
      Serial.println(F("tapez return pour générer le fichier CSV"));
      while (Serial.read() != '\n'); // on attend 'entrée'
      generationCSV();
      while (true);
    }
  }

  if (millis() - chronoImpressionAttente >= 1000) {
    static uint32_t compteur = 0;
    compteur++;
    if (compteur % 5 == 0) Serial.print(compteur);
    else Serial.write('.');
    if (compteur % 40 == 0) Serial.println();
    chronoImpressionAttente = millis();
  }
}

le moniteur série () 500,000 bauds) affichera

[color=purple]
Acquisition de 1000 échantillons, pendant environ 10.0 s.
Fréquence d'échantillonnage = 100.00 Hz.
tapez return pour Commencer l'acquisition.
....5....10

[color=red]Acquisition de 1000 échantillons en 10.1s[/color].
Débit d'écriture SD = 48077 octets/s.
tapez return pour générer le fichier CSV
Le compte est bon !
[color=red]Génération du fichier en 8.1 s.[/color][/color]

et sur la carte SD j'ai bien maintenant un fichier CSV lisible

J’ai testé sur un TEENSY 3.6 avec un code légèrement modifié pour prendre la librairie SD de teensy

J’ai obtenu pour 200Hz

[color=purple]
TEST CARTE sd SUR TEENSY 3.6

Acquisition de 2000 échantillons, pendant environ 10.0 s.
Fréquence d'échantillonnage = 200.00 Hz.
tapez return pour Commencer l'acquisition.
....5....10

Acquisition de 2000 échantillons en 10.1s.
Débit d'écriture sd = 62305 octets/s.
tapez return pour générer le fichier CSV
Le compte est bon !


Génération du fichier en 11.2 s.
[/color]

et il doit y avoir moyen d’aller bcp plus vite en prenant SdFatSdioEX

voila j’ai repris votre code, je dois lire les données depuis les pin 4 et 5 analog car j’utilise désormais un convertiseur 16bit qui va m’aider avec la précision des mesures (le ADS1115)

j’obtient ceci

Acquisition de 1000 échantillons, pendant environ 10.0 s.
Fréquence d’échantillonnage = 100.00 Hz.
tapez return pour Commencer l’acquisition.
…5…10…15…20…25…30…35…40
…45…50…55…60…65.Acquisition de 1000 échantillons en 69.5s.
Débit d’écriture SD = 41667 octets/s.
tapez return pour lire le fichier.
Le compte est bon !

Génération du fichier en 11.9 s.

Lorsque j’essaie sans le convertisseur 16 bit ca marche !!! merci beaucoup
Mais je pense que la précision me sera plus utile que la rapidité si je dois choisir

voici le code

#include <inttypes.h>       // formats pour sprintf, sscanf
#include <SPI.h>
#include "SdFat.h"
#include <Wire.h>                 //ADD
#include <Adafruit_ADS1015.h>     //ADD

Adafruit_ADS1115 ads(0x48);
SdFat SD;
File fichierLog;
const char* nomFichierCSV = "mesres.csv";
const char* nomFichierLog = "mesures.log";
const uint32_t signature = 0xDEADBEEF;

struct __attribute__ ((packed)) t_echantillon  {
  uint32_t t;
  uint16_t v0;
  uint16_t v1;
  uint16_t v2;
} unEchantillon;

const uint8_t tailleEchantillon = sizeof(t_echantillon); // le nombre d'octets dans une mesure
const uint8_t nbEchantillonsParBuffer = 512 / tailleEchantillon; // la carte SD a un buffer de 512 octets

//const uint8_t pinSDA = A4;
//const uint8_t pinSCL = A5;
///const uint8_t pinV2 = A2;

const uint32_t periodeAcquisition = 10;    // en ms -> une acquisition toutes les periodeAcquisition ms
const uint32_t dureeAcquisition = 10000;   // en ms
const uint32_t nombreTotalEchantillons = dureeAcquisition / periodeAcquisition;
uint32_t nombreEchantillonsAcquis = 0;

uint32_t topDepart = 0, tempsTotal = 0, tempsFlush = 0;

void generationCSV(bool sortieSerie = false)
{
  char ligne[100];
  uint8_t sentinelle[sizeof(signature)]; // va-t-on retrouver la signature ?
  const char* enTeteCSV = "T en ms\tV0\tV1\tV2\r\n";
  uint32_t chrono;

  chrono = millis();

  fichierLog = SD.open(nomFichierLog, O_READ);
  if (! fichierLog) {
    Serial.println(F("erreur ouverture fichier Log en lecture"));
    return;
  }

  SD.remove(nomFichierCSV);
  File fichierCSV = SD.open(nomFichierCSV, O_WRITE | O_CREAT);
  if (! fichierCSV) {
    fichierLog.close();
    Serial.println(F("erreur ouverture fichier CSV en écriture"));
    return;
  }

  if (sortieSerie) Serial.println(F("---- CONTENU DU FICHIER ----"));

  fichierLog.read((uint8_t*) &sentinelle, sizeof(signature));
  if (memcmp(sentinelle, &signature, sizeof(signature)) == 0) { // la signature est conforme, on lit le fichier
    uint32_t nombreEchantillonsLus = 0;
    if (sortieSerie) Serial.print(enTeteCSV);
    fichierCSV.write(enTeteCSV, strlen(enTeteCSV));
    while (fichierLog.available()) {
      if (fichierLog.read((uint8_t*) &unEchantillon, tailleEchantillon) != tailleEchantillon) {
        Serial.print(F("erreur lecture échantillon #"));
        Serial.println(nombreEchantillonsLus);
      } else {       // imprimer l'échantillon
        nombreEchantillonsLus++;
        sprintf(ligne, "\t%9"  PRIu32 "\t%4"  PRIu16 "\t%4"  PRIu16 "\t%4" PRIu16 "\r\n", unEchantillon.t, unEchantillon.v0, unEchantillon.v1, unEchantillon.v2);
        if (sortieSerie) Serial.print(ligne);
        fichierCSV.write(ligne, strlen(ligne));
      }
    }
    if (nombreEchantillonsLus != nombreEchantillonsAcquis) {
      Serial.println(F("Erreur sur le nombre d'échantillons acquis versus relus"));
    } else {
      Serial.println(F("Le compte est bon !"));
    }
  } else {
    Serial.println(F("Signature incorrecte, Fichier de LOG non reconnu."));
  }

  fichierCSV.close();
  fichierLog.close();
  chrono = millis() - chrono;

  Serial.print(F("\n\nGénération du fichier en "));
  Serial.print(chrono / 1000.0, 1);
  Serial.print(F(" s."));
}

inline bool acquisition()
{
  static uint32_t tempsDerniereAcquisition = 0xFF000000; // pour forcer une acquisition immédiate
  if (millis() - tempsDerniereAcquisition >= periodeAcquisition) { // est-ce le moment de faire une acquisition
    unEchantillon.t  = millis() - topDepart;
    ads.readADC_SingleEnded(0); // on en fait une à blanc pour laisser la tension se stabiliser sur l'ADC
    unEchantillon.v0  = ads.readADC_SingleEnded(0);
    ads.readADC_SingleEnded(1); // on en fait une à blanca pour laisser la tension se stabiliser sur l'ADC
    unEchantillon.v1  = ads.readADC_SingleEnded(1);
    ads.readADC_SingleEnded(2); // on en fait une à blanc pour laisser la tension se stabiliser sur l'ADC
    unEchantillon.v2  = ads.readADC_SingleEnded(2);
    nombreEchantillonsAcquis++;
    tempsDerniereAcquisition = millis();
    return true;
  }
  return false;
}

void setup()
{
  Serial.begin(500000);
  ads.begin();
  Wire.begin();
  
  ///pinMode(pinV0, INPUT);
  ///pinMode(pinV1, INPUT);
  ///pinMode(pinV2, INPUT);

  if (!SD.begin(SS)) {   // SS à la place de 10 normalement
    Serial.println(F("erreur SD.begin !"));
    while (true);
  }

  Serial.print(F("\nAcquisition de "));
  Serial.print(nombreTotalEchantillons);
  Serial.print(F(" échantillons, pendant environ "));
  Serial.print(dureeAcquisition / 1000.0, 1);
  Serial.println(F(" s."));
  Serial.print(F("Fréquence d'échantillonnage = "));
  Serial.print(1000.0 / periodeAcquisition);
  Serial.println(F(" Hz."));
  Serial.println(F("tapez return pour Commencer l'acquisition."));
  while (Serial.read() != '\n'); // on attend 'entrée'

  SD.remove(nomFichierLog);
  fichierLog = SD.open(nomFichierLog, O_WRITE | O_CREAT);
  if (!fichierLog) {
    Serial.println(F("erreur ouverture fichier Log en écriture"));
    while (true);
  }

  // sentinelle en début de fichier pour reconnaitre notre format
  // permet aussi d'initialiser les buffers etc
  fichierLog.write(&signature, sizeof(signature));
  fichierLog.flush();

  topDepart = millis();
}

void loop()
{
  static uint32_t chronoImpressionAttente = millis();

  if (acquisition()) { // si on a lu un nouvel échantillon, on le sauve

    if (fichierLog.write(&unEchantillon, tailleEchantillon) != tailleEchantillon) {
      Serial.print(F("erreur ecriture échantillon #"));
      Serial.println(nombreEchantillonsAcquis);
    }

    if (((nombreEchantillonsAcquis % nbEchantillonsParBuffer) == 0) || (nombreEchantillonsAcquis >= nombreTotalEchantillons)) {
      uint32_t t = millis();
      fichierLog.flush(); // on écrit physiquement sur la SD
      tempsFlush += millis() - t;
    }

    if (nombreEchantillonsAcquis >= nombreTotalEchantillons) {    // on a terminé
      tempsTotal = millis() - topDepart;
      fichierLog.close();

      Serial.print(F("Acquisition de "));
      Serial.print(nombreEchantillonsAcquis);
      Serial.print(F(" échantillons en "));
      Serial.print(tempsTotal / 1000.0, 1);
      Serial.println(F("s."));
      Serial.print(F("Débit d'écriture SD = "));
      Serial.print(1000.0 * (nombreEchantillonsAcquis * tailleEchantillon) / tempsFlush, 0);
      Serial.println(F(" octets/s."));
      Serial.println(F("tapez return pour lire le fichier."));
      while (Serial.read() != '\n'); // on attend 'entrée'
      generationCSV();
      while (true);
    }
  }

  if (millis() - chronoImpressionAttente >= 1000) {
    static uint32_t compteur = 0;
    compteur++;
    if (compteur % 5 == 0) Serial.print(compteur);
    else Serial.write('.');
    if (compteur % 40 == 0) Serial.println();
    chronoImpressionAttente = millis();
  }
}