Coucou enregistrement donnée sur carte sd

Bonjour voila je viens ici car je ne comprend pas certains points d’un projet que je souhaite réaliser et j’espère que vous pourrez m’éclairer.

Voila je souhaite enregistré un signal analogique de 0-5V avec une fréquence échantillonnage de quelque kilo hertz le signal que je souhaite enregistré varie de 0 à 1khz donc ma Fe min doit être de 2Khz je pensais que la carte arduino uno avec le can 10bits en été capable jai donc acheter cette carte + une seeed studuio SDcard pour sauvegardé mes donnée.

je réalise le code suivant et je me rend compte que ma carte ne suit pas au niveau acquisition
Le signal dans le fichier mis en forme ne correspond pas a mon signal générer par mon gbf.
est-ce du au la vitesse d’écriture du Spi ou au port serie ?

pouvez vous m’aider ?

/* Dépendances */
#include <SPI.h> // Pour la communication SPI
#include <SD.h>  // Pour la communication avec la carte SD
int T;

/** Broche CS de la carte SD */
const byte SDCARD_CS_PIN = 4; // A remplacer suivant votre shield SD

/** Nom du fichier de sortie */
const char* OUTPUT_FILENAME = "data.csv";

/** Delai entre deux prise de mesures */
const unsigned long DELAY_BETWEEN_MEASURES = 0;


/** Fichier de sortie avec les mesures */
File file;


/** Fonction setup() */
void setup() {
int T=0;
  /* Initialisation du port série (debug) */
  Serial.begin(2000000);

  /* Initialisation du port SPI */
  pinMode(10, OUTPUT); // Arduino UNO
 
  /* Initialisation de la carte SD */
  Serial.println(F("Initialisation de la carte SD ... "));
  if (!SD.begin(SDCARD_CS_PIN)) {
    Serial.println(F("Erreur : Impossible d'initialiser la carte SD"));
    Serial.println(F("Verifiez la carte SD et appuyez sur le bouton RESET"));
    for (;;); // Attend appui sur bouton RESET
  }

  /* 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.flush();
  }
}


/** Fonction loop() */
void loop() {
  // Temps de la précédente mesure et actuel
  static unsigned long previousMillis = 0;
  unsigned long currentMillis = millis();

  /* Réalise une prise de mesure toutes les DELAY_BETWEEN_MEASURES millisecondes */
  if (currentMillis - previousMillis >= DELAY_BETWEEN_MEASURES) {
    previousMillis = currentMillis;
    measure();
    
  }
}


/** Fonction de mesure - à personnaliser selon ses besoins */
void measure() {
  
  /* Réalise la mesure */
  float v_1 = analogRead(A0) * 5.0 / 1023;
  /* Affiche les données sur le port série pour debug */ 
  T=T+1; 
  Serial.print(v_1);
  Serial.print(F(";"));
  Serial.print("A ");
  Serial.println(T);
  file.print(F(";"));
  file.println(T);
  file.flush();
  
  
}

Bonjour,

Je ne vois pas ou tu écris ta variable v_1 sur la carte sd

oui toute mes excuses j’ai effacé une ligne par accident ::

/*Exemple de code Arduino pour un datalogger basique avec stockage sur carte SD.
 */

/* Dépendances */
#include <SPI.h> // Pour la communication SPI
#include <SD.h>  // Pour la communication avec la carte SD
int T;

/** Broche CS de la carte SD */
const byte SDCARD_CS_PIN = 4; // A remplacer suivant votre shield SD

/** Nom du fichier de sortie */
const char* OUTPUT_FILENAME = "data.csv";

/** Delai entre deux prise de mesures */
const unsigned long DELAY_BETWEEN_MEASURES = 0;


/** Fichier de sortie avec les mesures */
File file;


/** Fonction setup() */
void setup() {
int T=0;
  /* Initialisation du port série (debug) */
  Serial.begin(2000000);

  /* Initialisation du port SPI */
  pinMode(10, OUTPUT); // Arduino UNO
 
  /* Initialisation de la carte SD */
  Serial.println(F("Initialisation de la carte SD ... "));
  if (!SD.begin(SDCARD_CS_PIN)) {
    Serial.println(F("Erreur : Impossible d'initialiser la carte SD"));
    Serial.println(F("Verifiez la carte SD et appuyez sur le bouton RESET"));
    for (;;); // Attend appui sur bouton RESET
  }

  /* 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.flush();
  }
}


/** Fonction loop() */
void loop() {
  // Temps de la précédente mesure et actuel
  static unsigned long previousMillis = 0;
  unsigned long currentMillis = millis();

  /* Réalise une prise de mesure toutes les DELAY_BETWEEN_MEASURES millisecondes */
  if (currentMillis - previousMillis >= DELAY_BETWEEN_MEASURES) {
    previousMillis = currentMillis;
    measure();
    
  }
}


/** Fonction de mesure - à personnaliser selon ses besoins */
void measure() {
  
  /* Réalise la mesure */
  float v_1 = analogRead(A0) * 5.0 / 1023;
  /* Affiche les données sur le port série pour debug */ 
  T=T+1; 
  Serial.print(v_1);
  Serial.print(F(";"));
  Serial.print("A ");
  Serial.println(T);
  file.print(v_1);
  file.print(F(";"));
  file.println(T);
  file.flush();
  
  
}

Ton T n’est pas représentatif du temps. Il dépend de la vitesse d’exécution.

Bon,
vous mettez quelques caractères; T peut avoir 5 caractère; un float une dizaine: mettons 20 cars par mesure...
à 2E6 bauds, (i.e 2E5 cars/seconde, une écriture sur la ligne série vous prend autant de temps qu'une conversion bloquante)
Faute de pouvoir optimiser la carte SD, je vois quelques pistes pour accélérer la cadence d'acquisition

  • faire des impressions plus concises (en passant, une multiplication par un float est un peu gourmande, sans parler du passage en ASCII: pourquoi ne pas afficher -voire stocker- la valeur brute?
  • peut être (mais c'est ennuyeux et non portable sur d'autres familles), rendre l'ADC non bloquant (armer le convertisseur; vaquer à ses occuppations; au bout d'un certain temps, verifier qu'il est prêt et ramener la mesure): j'avais joué avec ça pour un oscilloscope il y a 3 ans, ayant vu l'idée dans Hackable Magazine; un lien parfait -beaux dessins, anglais , analyse systématique ou presque- est https://www.gammon.com.au/adcJ

En passant, pourquoi utilisez vous millis(), qui vous donne une granularité d'1E-3 seconde, alors que micros() doit faire l'affaire?

hbachetti:
Ton T n'est pas représentatif du temps. Il dépend de la vitesse d'exécution.

oui je sais c'est pour avoir une idée du nombre échantillon acquis !

mon problème c'est de pouvoir vraiment reconstituer ma sinusoïde a 500hz je pensé que le can avait une fréquence de 200khz et une fréquence fe effective d'environ 15khz mais apparemment ca n'est pas le cas ....

et ca m’embête vraiment car mon signal temporel a reconstituer vas du continue à 500hz et je pensais vraiment la arduino capable de gerer ca ....

Bonjour,

A la place du Serial.println(T); tu mets un Serial.println(millis()); comme ça tu verras le temps entre chaque mesure.
Ensuite tu mets en commentaire l'écriture sur la carte sd et tu verras si effectivement c'est l'écriture sur la carte sd qui ralenti.

Je disais ça parce que j’ai vu :

const unsigned long DELAY_BETWEEN_MEASURES = 0;

Si tu veux accélérer l’ADC :

arduino-les-performances-de-ladc

Voir : 3. Augmenter la vitesse

Ensuite si c’est l’écriture sur SD qui ralentit … :confused:

je mis mets je vous donne les résulats demain merci pour votre aide qui est la bienvenu !

j'ai pensé a une chose mais je sais pas si ca sera efficace je voudrais stocker mes donnée dans la flash sous forme de tableau mesure de 15/20s a 1ou khz en fe et une fois la mesure réalisé uploadé les valeurs dans la carte sd ca me permettrait d’évité le goulot d'étranglement de l’écriture sd si c'est vraiment ça qui bloque.

seulement je crois que la flash de l'arduino uno est très limité...

jmingargiola:
...

Voila je souhaite enregistré un signal analogique de 0-5V avec une fréquence échantillonnage de quelque kilo hertz le signal que je souhaite enregistré varie de 0 à 1khz donc ma Fe min doit être de 2Khz je pensais que la carte arduino uno avec le can 10bits en été capable jai donc acheter cette carte + une seeed studuio SDcard pour sauvegardé mes donnée.

je réalise le code suivant et je me rend compte que ma carte ne suit pas au niveau acquisition
Le signal dans le fichier mis en forme ne correspond pas a mon signal générer par mon gbf.
est-ce du au la vitesse d'écriture du Spi ou au port serie ?

pouvez vous m'aider ?

Bonsoir
Regarde l'exemple AnalogBinLogger de cette Lib
Il faut faire attention pour bien logger sur les cartes SD lorsque l'on utilise le mode SPI

Tu devrais supprimer le file.flush() qui écrit à chaque fois sur la carte sd au lieu d'écrire uniquement lorsque le buffer est plein, ce qui doit ralentir considérablement l'enregistrement.
Par contre pour garantir que les dernières mesures seront écrites, il faudra fermer le fichier, par exemple au bout d'un nombre d'enregistrements donné ou appui sur un bouton.

vous pensez que jai mal choisie ma carte ? une autres carte arduino pourrait elle être plus adapté ?

comme la léonardo la zéro ou la Due ?

je vais déjà commencer par mieux coder et mieux comprendre se qui se passe et si il faut changer je changerais mais il faut que je garde la même compatibilité au niveau de l’emboîtage des cartes.

Non.
Votre stockage seriel est redondant avec les impressions de debugging (qui sont illisibles par un hêtre humain normal)
Vous avez deux solutions, qui ne nécessitent aucun investissement materiel
a) utiliser le PC (avec teraterm en mode capture, pyserial, processing...) comme stockeur.... il doit savoir faire.
Là, la carte Sd devient inutile.... cacher (#ifdef cache .... #endif) donc ce code qui peut ralentir

b) cacher les impressions de debugging qui ne servent à rien (si vous ne voulez pas stocker sur PC) ralentissent l'acquisition , si vous souhaitez une cadence infernale). Voyez si votre carte stocke (et le flush toutes les écritures , comme l'a dit kamill, est un excès de précautions et ralentit -pour info, les xxPi font des flush generaux tous les 1000 secondes à peu près).

alors j’ai entrepris d’amélioré le code car je crée un système non connecté a un pc j’ai supprimé toutes les lignes"serial.print" j’ai souhaité a minima un échantillonnage à 1Khz donc j’ai mis la consigne “DELAY_BETWEEN_MEASURES” égale à ‘1’

et j’ai déclaré une variable T qui enregistre le temps entre chaque échantillon résultat je suis à 11/12ms

avec le code suivant :

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


/** Broche CS de la carte SD */
const byte SDCARD_CS_PIN = 4; // A remplacer suivant votre shield SD

/** Nom du fichier de sortie */
const char* OUTPUT_FILENAME = "data.csv";

/** Delai entre deux prise de mesures */
const unsigned long DELAY_BETWEEN_MEASURES = 1;
unsigned long T;

/** Fichier de sortie avec les mesures */
File file;


/** Fonction setup() */
void setup() {
T=0;
 /* Initialisation du port série (debug) */


 /* Initialisation du port SPI */
 pinMode(10, OUTPUT); // Arduino UNO

 /* Initialisation de la carte SD */

 if (!SD.begin(SDCARD_CS_PIN)) {

   for (;;); // Attend appui sur bouton RESET
 }

 /* Ouvre le fichier de sortie en écriture */
 file = SD.open(OUTPUT_FILENAME, FILE_WRITE);
 if (!file) {

   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.flush();
 }
}


/** Fonction loop() */
void loop() {
 // Temps de la précédente mesure et actuel
 static unsigned long previousMillis = 0;
 unsigned long currentMillis = millis();

 /* Réalise une prise de mesure toutes les DELAY_BETWEEN_MEASURES millisecondes */
 if (currentMillis - previousMillis >= DELAY_BETWEEN_MEASURES) {
   previousMillis = currentMillis;
   T=currentMillis;
   measure();
   
 }
}


/** Fonction de mesure - à personnaliser selon ses besoins */
void measure() {
 
 /* Réalise la mesure */
 float v_1 = analogRead(A0) * 5.0 / 1023;
 /* Affiche les données sur le port série pour debug */ 
 file.print(v_1);
 file.print(F(";"));
 file.println(T);
 file.flush();
}

échantillon des donnée reçu a 10hz sur 100 point :

0.16	125
3.76	172
3.06	183
1.78	195
0.53	206
0.00	219
0.41	230
1.58	241
2.91	253
3.71	265
3.59	276
2.59	288
1.23	300
0.21	311
0.04	323
0.82	334
2.14	347
3.33	358
3.79	369
3.27	381
2.05	393
0.74	404
0.02	416
0.25	428
1.32	439
2.67	451
3.63	462
3.69	475
2.83	486
1.50	497
0.35	509
0.00	521
0.60	532
1.86	544
3.14	556
3.77	567
3.45	579
2.32	590
0.98	603
0.10	614
0.14	625
1.07	637
2.41	649
3.50	660
3.76	672
3.06	684
1.77	695
0.53	707
0.00	718
0.42	730
1.59	742
2.92	753
3.78	769
3.37	780
2.19	792
0.86	803
0.05	816
0.20	827
1.23	838
2.60	850
3.59	862
3.71	874
2.88	885
1.53	898
0.37	909
0.00	921
0.60	932
1.88	945
3.16	956
3.78	968
3.41	979
2.26	992
0.91	1003
0.07	1015
0.18	1027
1.18	1039
2.55	1050
3.58	1062
3.72	1074
2.90	1086
1.56	1097
0.38	1110
0.00	1121
0.59	1133
1.87	1144
3.15	1157
3.78	1168
3.42	1180
2.25	1191
1.29	1240
2.66	1251
3.63	1263
3.69	1274
2.81	1287
1.45	1298
0.32	1310
0.00	1321
0.67	1334
1.98	1345
3.24	1357

je vais essayer de mieux comprendre la commande flush histoire de l’utilisé comme il faut et je vais revenir vers vous pour plus de précision si besoin.

Re j’ai ecris ca mais ca marche pas du tout :slight_smile: je vais faire une pause de 20 minutes j’ai un mal de crane…
ps: jai mi des variable train mais c’est pour le délire :stuck_out_tongue:

j’ai essayé de limiter le flunch et j’ai une question est t’il possible d’incrémenté et de faire une création de fichier genre toto1.txt toto2.txt … ect !

#include <SPI.h> // Pour la communication SPI
#include <SD.h>  // Pour la communication avec la carte SD
const byte  SDCARD_CS =4;
const char* output_data = "TRAIN.TXT";
int compteurtrain;                                  //
byte FLAGTrain ;                                     // une entrée type WD ou interruption qui declenche l'acquisition quand c'est a 1
/* compter le temps entre chaque mesure*/
const unsigned long DBM = 1;
unsigned long Temps;
/* je sais pas a quoi sa sert */ 
int D2 =2;

File file; 
void setup()
{   
    Temps=0;
    compteurtrain=0;
    FLAGTrain=0;                                      // mise a Zero du flag
    /*Initialisation il y a un train*/
    pinMode(0, INPUT);                                // entrée  de déclenchement enregistrement coordonnée avec FLAG 
    FLAGTrain=digitalRead(D2);
    /*Initialisation SPI*/
    pinMode(10, OUTPUT);                              // Arduino UNO
    /*test initialisation SDCARD*/
     if (!SD.begin(SDCARD_CS))
        {
        for (;;);                                     // attend sdcard et RESET.
        }
        file = SD.open(output_data, FILE_WRITE);
        if (!file) 
        {
        for (;;); // Attend appui sur bouton RESET
        } 
  }
        


/*fonction de boucle*/

void loop()

{


if (FLAGTrain==HIGH)
{
    file.println("");
    file.print("****************TRAIN N°");
    file.print(compteurtrain);
    file.println("****************");
    file.println("");
    while(FLAGTrain==HIGH)
    {
     static unsigned long previousMillis = 0;
     unsigned long currentMillis = millis();
    if (currentMillis - previousMillis >= DBM) 
    {
    previousMillis = currentMillis;
    Temps=currentMillis;
    
    int ADCTension = analogRead(A0);                                // déclaration de "ADCTension" qui prend la valeur de A0.
    float Tension = ADCTension*(5.0/1023.0);                        // déclaration de   "Tension"  avec convertion en Volt.
    file.print(Tension);
    file.print(";");                                                
    file.println(Temps);
    }    }
    file.flush();
    compteurtrain=compteurtrain++;
}

}

Commencez par faire simple, comme dans vitre post 13; faites un test à la fois;
par exemple,
a) verifiez que vous pouvez ouvrir un fichier , disons TRAIN001.TXT, le remplir avec 12345 valeurs, et le fermer; (n’oubliez pas de le fermer!)
b) verifiez que le bout de code du genre suivant, à mettre dans le setup, après le demarrage de Serial, fait bien des fichiers numerotes (vous pourriez, si vous avez une hologe, les dater avec le même proncipe; SDfat supporte des noms de fichier longs

#include <stdio.h> // le mettre éventuellement (nécessaire sur PC)
int main() {
char output_data[28] ; // je choisis de mettre des noms assez longs
for (int i=1; i<10;i++) {
  snprintf(output_data, 12, "TRAIN%03d.TXT", i);
 // printf(" %s ", output_data); //PC
Serial.println(output_data);

vous génère des noms defichiers numerotés…
c) trouvez une façon un peu moins primitive que la mienne de numeroter les fichiers;

d): en passant, si vous avez une LED, voius devrierz la faire clignoter si la carte SD est mal enfichée.

e) au vu de aà c (permet de numéroter les fichiers, ce que vous siuhaitez) et d (confort à terme), faire trop de modifs d’un seul coup est un peu bordelique; il vaut mieux partir d’un état qui “marche”, faire 3 ou 4 lignes, et rechercher l’horreur dans ce qui a été rajouté… s’il y en a.

Re coucou les Amis je continue dans ma galère ... j'ai lu plein plein de lien et les indictation que vous m'avez donnée

au point ou j'en suis je vais faire un reset pour essayer d'y voir plus claire.

objectif :

réalisé un programme qui comme un dataloger enregistre les information délivré par un accéléromètre une voie Analogique sur "A0"
mais qui enregistre seulement quand j'ai une interruption a 1 sur une voie numérique.

mon enregistrement doit duré 10s et acquérir des fréquence de 0 à 500hz
pour les stocké dans un fichier que l'on appel 1 2...3 ... 4 ect qui se trouve sur une carte sd de type seeedstudio 4.3

je pense avoir compris que lorsque je fais un sample et je regarde la fréquence échantillonnage avec la commande analogRead(A0) jai 8.9Khz soit 8900point par seconde se qui semble presque 9 fois supérieur a mon min prérequis pour une Fe.

seulement quand je vire toutes les commande serial j'acquiere mon fichier texte de donnée je le passe dans excel pour voir les courbes elles sont malheureusement foireuse au dessus de 40hz. je voie aussi que toute les 1s ou 800mS jai un élément qui arrête l'acquisition donc une discontinuité dans l'acquisition.
il semble que le problème vienne du Spi et puisse être contourné par un buffer je vais donc avoir besoin de vous .

Alors par quoi je peux commencé pour réaliser mon programme .... si je m'attaque à l'adc sur interruption et au buffer c'est deja une bonne chose mais j'aurais besoin d'aide car je suis limité.

c'est pour ca que je re up ce poste

Vous voulez une frequence d’échantillonnage de 500 par seconde (I.e un echantillon toutes les 2 millisecondes, soit toutes les 2000 microsecondes), et ceci sur une durée de 10 secondes (5000 lignes dans votre fichier)?
Une solution simple est

  • creer un fichier SD et l’ouvrir; vous savez le faire
uint32_t lDepart = millis() , cDepart=micros(); (mettre dans le set up) 
for (int nbSamples = 0; nbSamples < 5000; nbSamples++) {
    // lire l'echantillon;  le stocker tel quel
    // ecrire l'echantillon dans la carte; je suppose que ces deux operations ont un temps constant, inferieur à 2 ms
    while ((micros() - cDepart) < 2000) {} // bloquage tant qu'il ne s'est pas écoulé 2 ms depuis le demarrage de l'échantillonnage précédent; sur les 70 premières secondes, il n'y aura pas de problèmes
    cDepart = micros(); // je suppose que copier 4 octets est très rapide, et à temps constant
}
// fermer le fichier

uint32_t lDepart c'est quoi ? c'est équivalent a unsigned long ?
micros(); et millis(); je connais c'est des registres qui compte ....

uint32_t est l'equivalent d'un unsigned long pour l'arduino (du moins avec avr....), d'un unsigned int pour une machine 32 bits: ça a 4 octets, avec tous les bits ; je préfère des types portables, qui permettent de tester sur PC/RPi (et si vous décidez de changer un arduino 8 bits an une autre carte -moins chère, plus mignonne- passer de 32 bits -mon PC- ou 64 bits -PC plus neufs - à 8 bits -un avr- peut s'avérer désagréaable)
J'ai déclaré deux variables 32 bits non signées pour pouvoir gérer le temps sur les plus grandes périodes possibles, sans débordement.

cDepart sert à se synchroniser, et à avoir des débuts d'échantillonnage (à peu près) réguliers (au temps de comparaison et de soustraction de 32 bits près: ce temps, quoique très petit, n'a pas de raison d'être constant -mais il est petit-)
lDepart ne sert à rien (mais permet, si on veut, de verifir lors du debugging que la capture a duré 10 secondes...) Iµl servira plus tard...
Il convient -c'est le seul piège- de faire attention aux opérations (dans le cas que je vous ai donné, une comparaison et une soustraction) lors du débordement (au bout de 232 == 4 * 230 c a 4 millions de microsecondes, soit 4 secondes). Mais on n'en est pas encore là. (édité : heureusement: 2**10 vaut environ 1000; il fallait lire 4 milliards d emicrosecondes, environ une heure!)