Ecriture EEPROM

Bonjour à tous,

Je souhaite mettre en mémoire toutes les 5 secondes les valeurs tension présentes sur une pin d’entrée (A5) dans l’EEPROM. puis pouvoir les relires.

je me suis inspiré du programme suivant : datalloger

mon programme est le suivant :

/*
 * Datalogger de tension sur borne entrée analogique.
 */

#include <EEPROM.h>


/* Broche du capteur analogique + declaration des variables */ 
const int monCapteur = A5;
int valeurLue;
float tension;


/* Constantes pour le mode lecture */
const String HELP_CMD = "HELP";
const String READ_CMD = "READ";
const String LOG_CMD = "LOG";
const String ERASE_CMD = "ZERO";
const unsigned long READ_CMD_TIMEOUT = 10000;

/* Constante pour le datalogging */
const unsigned long DELAY_BETWEEN_SAMPLE = 5000; // Enregistrement de la valeur mesurée toutes les 5 secondes


/* Buffer temporaire pour les données */
const unsigned int MAX_NB_SAMPLES = 256;
struct DataBuffer {
  unsigned int nb_samples;
  float samples[MAX_NB_SAMPLES];
};
DataBuffer data = { 0, { 0 } };



/** Terminal de commande */
void command() {
  
  /* Attends l'ordre de passage en mode lecture */
  Serial.setTimeout(READ_CMD_TIMEOUT);
  bool exit = false;
  while (!exit) {

    /* Lit la commande */
    Serial.print(F("CMD> "));
    String cmd = Serial.readStringUntil('\n');
    Serial.println(cmd);

    /* Interpréte la commande */
    if (cmd == HELP_CMD) {

      /* Menu aide */
      Serial.println(F("-- COMMAND LIST --"));
      Serial.println(F("HELP Display this help"));
      Serial.println(F("READ Read back data from memory"));
      Serial.println(F("LOG Starting logging data at last index"));
      Serial.println(F("ZERO Reset index and data values"));

    } else if (cmd == READ_CMD) {

      /* Affiche les données en mémoire */
      if (data.nb_samples) {
        Serial.println(F("N° ECHANTILON; Tension; "));
        for (unsigned int index = 0; index < data.nb_samples; ++index) {
          Serial.print(index);
          Serial.print(F("; "));
          Serial.println(data.samples[index]);    // on lit la structure mais pas la mémoire, pourquoi les valeurs alors restent-elles si coupure de l'alimentation ? rep : ds le setup EEPROM.get
                          }
                          
      } else {
        Serial.println(F("No data in memory."));
      }

    } else if (cmd == LOG_CMD) { 

      /* Sortie du mode terminal */
      exit = true;

    } else if (cmd == ERASE_CMD) {  

      /* Vidage de la mémoire */
      Serial.print(F("ERASING ... "));
      data.nb_samples = 0;                      // Pourquoi ne pas mettre aussi data.samples aussi à zero ?
      EEPROM.put(0, data);                   //Ecriture de la structure à l'adresse 0
      Serial.println(F("Memory erased."));        

    } else {
      
      /* Commande inconnue */
      Serial.println(F("ERROR (Bad command)"));
    }
  }
}

/** Fonction setup() */
void setup() {

  /* Initialise le port série */
  Serial.begin(9600);
  
 
  /* Load previous memory data */
  EEPROM.get(0, data);           // charge l'adresse 0 dans la structure data, ce qui permet la lecture "CMD affiche les données en mémoire" 
             }
  

/** Fonction loop() */
void loop() {
  static unsigned long previousMillis = 0;
  unsigned long currentMillis = millis();



  /* Mesure toutes les N millisecondes */
  if (currentMillis - previousMillis >= DELAY_BETWEEN_SAMPLE) 
  {
    previousMillis = currentMillis;

    /* Arret en cas de fin de mémoire */
    if (data.nb_samples == MAX_NB_SAMPLES) {

      /* Empéche la surécriture des données */
      Serial.println(F("LOGGING STOPPED (Out of memory)"));
      for (;;);
                                           }

    /* Mesure la tension */
    
 valeurLue = analogRead(monCapteur);  // lit l'entrée analogique A5
 tension = map(valeurLue, 0, 1023, 0, 5000); //conversion de la valeur lue en tension en mV
tension = tension / 1000; //conversion des mV en V

    data.samples[data.nb_samples] = tension;

    /* Debug */
    Serial.print(F("ECHANTILLON "));
    Serial.print(data.nb_samples);
    Serial.print(F(": "));
    Serial.println(data.samples[data.nb_samples]);
   
    
    /* Sauvegarde */
    data.nb_samples += 1; // on rajoute +1 à nb_samples
    EEPROM.put(0, data); //Ecriture de la structure à l'adresse 0
  }
  
  /* Active le mode commande si l'utilisateur passe le caractère '

Cela fonctionne assez bien mais j’ai quelques soucis et des questions sur son fonctionnement :

  1. Lorsque j’efface la mémoire, je commence bien à l’adresse (data.nb_samples). par contre si je fait un reset de la carte arduino je commence tjs à 30. (même si je n’ai enregistré que 5 valeurs).

Avez vous une idée du pourquoi ?

  1. Dans ce paragraphe
    /* Vidage de la mémoire */
    Serial.print(F("ERASING … "));
    data.nb_samples = 0;
    EEPROM.put(0, data); //Ecriture de la structure à l’adresse 0
    Serial.println(F(“Memory erased.”));

Pourquoi ne pas mettre aussi data.samples aussi à zero ?

Comment toutes les valeurs tiennent à l’adresse 0 ? je pensais que chaque valeur étaient écrite dans une adresse ? non ?

Merci pour vos réponses, comme je vous l’indiquais plus haut le programme fonctionne mais cela me permettra de me coucher moins bête ce soir !!

sur le port série */
  if (Serial.available() && Serial.read() == ’


Cela fonctionne assez bien mais j'ai quelques soucis et des questions sur son fonctionnement :

1) Lorsque j'efface la mémoire, je commence bien à l’adresse (data.nb_samples). par contre si je fait un reset de la carte arduino je commence tjs à 30. (même si je n'ai enregistré que 5 valeurs).

Avez vous une idée du pourquoi ?

2) Dans ce paragraphe 
/* Vidage de la mémoire */
Serial.print(F("ERASING ... "));
data.nb_samples = 0; 
EEPROM.put(0, data); //Ecriture de la structure à l'adresse 0
Serial.println(F("Memory erased.")); 

Pourquoi ne pas mettre aussi data.samples aussi à zero ?

Comment toutes les valeurs tiennent à l'adresse 0 ? je pensais que chaque valeur étaient écrite dans une adresse ? non ?

Merci pour vos réponses, comme je vous l'indiquais plus haut le programme fonctionne mais cela me permettra de me coucher moins bête ce soir !!

) {
    command();
    Serial.println(F("RESTART LOGGING ..."));
            } 
}

Cela fonctionne assez bien mais j’ai quelques soucis et des questions sur son fonctionnement :

  1. Lorsque j’efface la mémoire, je commence bien à l’adresse (data.nb_samples). par contre si je fait un reset de la carte arduino je commence tjs à 30. (même si je n’ai enregistré que 5 valeurs).

Avez vous une idée du pourquoi ?

  1. Dans ce paragraphe
    /* Vidage de la mémoire */
    Serial.print(F("ERASING … "));
    data.nb_samples = 0;
    EEPROM.put(0, data); //Ecriture de la structure à l’adresse 0
    Serial.println(F(“Memory erased.”));

Pourquoi ne pas mettre aussi data.samples aussi à zero ?

Comment toutes les valeurs tiennent à l’adresse 0 ? je pensais que chaque valeur étaient écrite dans une adresse ? non ?

Merci pour vos réponses, comme je vous l’indiquais plus haut le programme fonctionne mais cela me permettra de me coucher moins bête ce soir !!

Bonjour

lpg79:
Comment toutes les valeurs tiennent à l'adresse 0 ? je pensais que chaque valeur étaient écrite dans une adresse ? non ?

Avec l'ordre EEPROM.put(0, data);, la structure est écrite à l'adresse 0 et va s'étendre sur le bon nombre d'octets ce qui présente sans doute un problème puisque la structure est grande

Mais c'est louche votre truc... 4 octet pour un float x 256 dans le tableau = 1kilo octet + 2 octets pour le premier int = 1026 octets or le ATmega328P n'a que 1024 octets d'EEPROM...

Notez aussi que une case mémoire d'EEPROM est limitée à 100,000 cycles d'écriture/effacement. Je n'ai pas lu tout votre programme mais si vous écrivez dans l'EEPROM toutes les 5 secondes --> en 500,000 secondes vous avez cramé vos cellules. 500,000 secondes c'est moins de 6 jours...

Bonjour,

Ton tableau data est plus grand (de deux octets) que la taille de l'EEPROM, c'est peut être ça qui cause la reprise à l'octet 30 (peut être que l'écriture reboucle sur l'adresse 0).
Diminues la taille du tableau MAX_NB_SAMPLES = 255

+1 avec J-M-L, l'EEPROM va vieillir prématurément.

Au choix :

  • Une Eeprom externe (SPI ou I2C)
  • Une carte SD

Mais pas d’utilisation de l’Eeprom interne au microcontrôleur a de telle fréquence.

PS : même la flash à des limites d’utilisation.

Bonjour,

merci pour vos réponses, ( :wink:

Pour le nombre d’écriture limité à 100 000, je savais...le programme n'est pas fait pour fonctionner en permanence mais pour mettre en mémoire des valeurs donnés par un capteur de pression qui va dans l'eau (j'aurai d'ailleurs des questions sur ce capteur mais je ferai un autre poste...récupération données port I2C!) et juste vérifier son bon fonctionnement, donc peu de mesure au final.

Je n'ai par contre pas toujours compris pourquoi je partais de 30 lors d'un RAZ. (j'ai changé MAX_NB_SAMPLES = 128)

Le seul truc c'est que lorsque que je rajoute ces lignes, cela fonctionne :

/* Lance le terminal de commande si la broche n'est pas activée */
  pinMode(LOG_NOW_PIN, INPUT_PULLUP);
  if (digitalRead(LOG_NOW_PIN) == HIGH) {
    command();
  }
  Serial.println(F("START LOGGING ..."));

Cela permet de ne pas lancer toutes de suite les acquisitions dès le téléversement. Donc on dirait que je fais 30 enregistrement juste après le téléversement ou un RAZ mais je ne vois pas pourquoi ?

C'est pas très grave, cela fonctionne, mais cela me chiffonne un peu :disappointed_relieved:

Merci encore.

dans votre code d’origine, vous lisez la structure en mémoire depuis l’EEPROM dans le setup.

  EEPROM.get(0, data);           // charge l'adresse 0 dans la structure data, ce qui permet la lecture "CMD affiche les données en mémoire"

le commentaire est incorrect. Ce que vous faites c’est que la fonction que vous appelez va regarder la taille mémoire occupée par la structure de votre paramètre data, si vous avez mis le tableau à 128 ça veut dire que la fonction va lire les 2 octets de l’unsigned int nb_samples puis 4x128 (un float = 4 octets) = 512 octets depuis l’EEPROM pour initialiser toute la structure data. ça a donc pour effet d’initialiser data.nb_samples avec le contenu des 2 premiers octets de l’EEPROM.

→ la première fois que vous avez fait cela, avez vous d’abord fait un petit programme qui garantissait que l’EEPROM était à 0? sinon les 2 premiers octets lus vont servir à initialiser data.nb_samples et c’est ce que vous utiliserez ensuite

dans ce code

    /* Arret en cas de fin de mémoire */
    if (data.nb_samples == MAX_NB_SAMPLES) {

la bonne pratique est plutôt de tester avec >= plutôt que == car si vous avez dépassé pour une raison inconnue (bug ailleurs dans le code) vous allez jamais arrêter la collecte

Vous vous rendez compte que dans la loop, toutes les 5 secondes vous lisez 1 valeur analogique (au passage c’est ballot de la transformer en volt sur 4 octets alors que vous pourriez uniquement stocker en EEPROM la valeur entre 0 et 1023 que vous avez lu et qui tient sur 2 octets…), l’ajoutez dans votre tableau à la position disponible suivante, puis écrivez tout le tableau en EEPROM (les 2 octets de data.nb_samples mais aussi les MAX_NB_SAMPLES nombres flottants… c’est un peu dommage de tout écrire alors qu’une grande majorité du tableau ne change pas et que finalement vous ne faites que rajouter un élément…

MerciJ-M-L

oui je vidais la mémoire, enfin je crois (je suis sur de rien, d'ou mon premier message et mes commentaires à coté du programme !!) . Je vide de cette façon : (bon ou pas ? :blush: )

 /* Vidage de la mémoire */
      Serial.print(F("ERASING ... "));
      data.nb_samples = 0;                      // Pourquoi ne pas mettre aussi data.samples aussi à zero ?
      EEPROM.put(0, data);                   //Ecriture de la structure à l'adresse 0
      Serial.println(F("Memory erased."));

Concernant la mise en mémoire, je suis d'accord avec toi j'aurai pu laisser 0-1023 plutôt que de convertir, mais j'apprend en même temps...

Pour la réécriture totale, je croyais que EEPROM.put avait la fonction update et ne réécrivait que si la valeur avait changée...je me trompe aussi ? :roll_eyes:

Merci en tout cas pour ton aide

Pour la réécriture totale, je croyais que EEPROM.put avait la fonction update et ne réécrivait que si la valeur avait changée...je me trompe aussi ? :roll_eyes:

oui tout a fait, la fonction utilise update —>je me suis mal exprimé - ça n'écrit pas tout à nouveau - mais ça relit chacune des 512 cellules pour voir si le contenu a changé donc c'est lent puisqu'on sait qu'il n'ya que 6 octets qui ont changé (les 2 premiers pour le compte et la dernière valeur ajoutée)

Oui votre code remet bien à zéro le compte (il suffirait d'écrire 0 à l'adresse 0 et 1, la encore pas besoin de tout remettre à 0 sur l'ensemble des octets)

Concernant la mise en mémoire, je suis d'accord avec toi j'aurai pu laisser 0-1023 plutôt que de convertir, mais j'apprend en même temps...

bien sûr, c'est juste une critique constructive. ce faisant vous occupez 2 fois moins de mémoire (EEPROM et RAM), conservez la précision d'origine, et divisez par 2 le temps de lecture ou d'écriture du tableau.

bien sûr, c'est juste une critique constructive

Pas de pb, c'est comme ça qu'on progresse et je t'en remercie

(il suffirait d'écrire 0 à l'adresse 0 et 1, la encore pas besoin de tout remettre à 0 sur l'ensemble des octets)

Euh la je comprends pas, tu peux stp m'expliquer pourquoi? les autres adresses (2,3...) peuvent aussi contenir qq choses ? non ?

Désolé de mes questions un peu basiques :blush:

Oui mais votre code utilise uniquement la valeur des 2 premiers octets pour décider combien de valeurs sont valides dans le tableau donc si vous dites 0 - on sait que ce qui est dans l'EEPROM n'est pas valide

Ok, Ok, va falloir que je creuse un peu la notion de structure…je crois que c’est cela que je n’est pas bien compris !

Merci de ta réponse rapide

Un structure c'est juste des variables regroupées en mémoire