Crash de l'ESP32 après lecture de la mémoire flash

Bonjour,

Dans la continuité de mon post sur le parcours récursif de la carte SD , je souhaite maintenant stocker l'arbre dans la mémoire flash.

je stocke la variable rootItem, elle a une taille de 68, j'arrive a lire la mémoire flash et stocker ça dans une variable rootItem2, qui a également une taille de 68 .... et ça plante.

void setup() {


  pinMode(pushButton_pin, INPUT);
  setCpuFrequencyMhz(160);
  tft.init();
  tft.setRotation(3);
  tft.fillScreen(TFT_BLACK);
  tft.setTextWrap(false);
        
    // Set microSD Card CS as OUTPUT and set HIGH
    pinMode(SD_CS, OUTPUT);      
    digitalWrite(SD_CS, HIGH); 
    
    // Initialize SPI bus for microSD Card
    SPI.begin(SPI_SCK, SPI_MISO, SPI_MOSI);
    
    // Start Serial Port
    Serial.begin(115200);
    delay(500);
    // Start microSD Card
    if(!SD.begin(SD_CS))
    {
      Serial.println("Error accessing microSD card!");
      while(true); 
    }

  Serial.println("initialization done.");
  Serial.println("Creating structure of files");
  File root = SD.open("/");
  FileItem rootItem;
  if (strlen(root.name()) != 0) strncpy(rootItem.name, root.name(), maxFileNameSize);
  else strncpy(rootItem.name, "/", maxFileNameSize);
  rootItem.name[maxFileNameSize] = '\0';
  rootItem.isDir = true;
  Serial.println("--start of Listing--");
  readSdCard(root, rootItem);
  //Fin du listing


      if (!EEPROM.begin(EEPROM_SIZE))
    {
      Serial.println("failed to initialise EEPROM"); delay(1000000);
    }
  Serial.print("Size of rootItem : ");
  Serial.println(sizeof(rootItem));
  EEPROM.put(0,rootItem);
  EEPROM.commit();

  FileItem rootItem2;
  EEPROM.get(0,rootItem2);
  Serial.print("Size of rootItem2 : ");
  Serial.println(sizeof(rootItem2));

}

voici ce que j'ai dans le moniteur série :

----end of listing ----

Size of rootItem : 68

Size of rootItem2 : 68

CORRUPT HEAP: Bad tail at 0x3ffb3e64. Expected 0xbaad5678 got 0x3ffb2c14

assert failed: multi_heap_free multi_heap_poisoning.c:259 (head != NULL)

Je ne comprends pas ce qui cloche :face_with_monocle:

Le code n'est pas complet. Tu ne donnes que le setup et il semble qu'il est exécuté jusqu'au bout.
L'erreur doit venir faire part

voici le code complet, mais pour moi l'erreur vient bien de la ligne avec le EEPROM.get, car quand je la passe en commentaire, tout va bien.

En continuant mes recherches, le CORRUPT HEAP: vient d'une mauvaise allocation de la mémoire.
Je suis en train de voir sur la doc espressif mais pour le moment, je ne comprends pas tout :confused:

 #define pushButton_pin   32
// Include required libraries
#include "Audio.h"
#include "SD.h"
#include "FS.h"
#include <Wire.h>
#include <TFT_eSPI.h> 
#include <SPI.h>


#include "EEPROM.h"
#define EEPROM_SIZE 128


// microSD Card Reader connections
#define SD_CS          5
#define SPI_MOSI      21 
#define SPI_MISO      19
#define SPI_SCK       18
 
// I2S Connections
#define I2S_DOUT      22
#define I2S_BCLK      26
#define I2S_LRC       25
 
 // Create Audio object
Audio audio;

TFT_eSPI tft = TFT_eSPI();  
//digitalWrite(TFT_BL, 1);

int maxIndex;
uint32_t Freq = 0;
int Val;
String Btn,SongName;
String Status="Idle";
bool state=true;
bool Current_State=state;
int Current_Millis;
int Previous_Millis;
int Debounce=175;
int Xindex=0;
int Yindex=0;

const uint8_t maxFileNameSize = 50;
struct FileItem {
  bool isDir;
  char name[maxFileNameSize+1];
  FileItem* upDir;
  std::vector<FileItem> elements;
};

File dir;
String CurrDir;
String NextDir;
String Song;
String Liste[50];


void setup() {


  pinMode(pushButton_pin, INPUT);
  setCpuFrequencyMhz(160);
  tft.init();
  tft.setRotation(3);
  tft.fillScreen(TFT_BLACK);
  tft.setTextWrap(false);
        
    // Set microSD Card CS as OUTPUT and set HIGH
    pinMode(SD_CS, OUTPUT);      
    digitalWrite(SD_CS, HIGH); 
    
    // Initialize SPI bus for microSD Card
    SPI.begin(SPI_SCK, SPI_MISO, SPI_MOSI);
    
    // Start Serial Port
    Serial.begin(115200);
    delay(500);
    // Start microSD Card
    if(!SD.begin(SD_CS))
    {
      Serial.println("Error accessing microSD card!");
      while(true); 
    }

  Serial.println("initialization done.");
  Serial.println("Creating structure of files");
  File root = SD.open("/");
  FileItem rootItem;
  if (strlen(root.name()) != 0) strncpy(rootItem.name, root.name(), maxFileNameSize);
  else strncpy(rootItem.name, "/", maxFileNameSize);
  rootItem.name[maxFileNameSize] = '\0';
  rootItem.isDir = true;
  Serial.println("--start of Listing--");
  readSdCard(root, rootItem);
  //Fin du listing

 Serial.println ("----end of listing ----");

      if (!EEPROM.begin(EEPROM_SIZE))
    {
      Serial.println("failed to initialise EEPROM"); delay(1000000);
    }
  Serial.print("Size of rootItem : ");
  Serial.println(sizeof(rootItem));
  EEPROM.put(0,rootItem);
  EEPROM.commit();

  FileItem rootItem2;
  EEPROM.get(0,rootItem2);
  Serial.print("Size of rootItem2 : ");
  Serial.println(sizeof(rootItem2));
 Serial.println(rootItem2.elements[0].elements.at(1).elements[1].name);
}

void readSdCard(File dir, FileItem& parentItem) {
  while (true) {
    File entry = dir.openNextFile();
    if (!entry) {
      break;  // no more files
    }
    FileItem item;
    item.isDir = entry.isDirectory();
    strncpy(item.name,  entry.name(), maxFileNameSize); // https://cplusplus.com/reference/cstring/strncpy/
    item.name[maxFileNameSize] = '\0'; 
    //parentItem.elements.push_back(item);
   
    Serial.printf("Add %s to %s\n", item.name, parentItem.name);
    if (entry.isDirectory()) {
      readSdCard(entry, item);
    }
    parentItem.elements.push_back(item);
  }
}

void displaySdCardContent(FileItem& item, int numTabs) {
    for (int i = 0; i < numTabs; i++) {Serial.print("...");}
  if (item.isDir) {
    Serial.print ("name : ");
    Serial.println(item.name);
    Serial.print ("IsDir : ");
    Serial.println(item.isDir);
    Serial.print("size :");
    Serial.println(item.elements.size());
    for (FileItem& fileElement : item.elements) {
      displaySdCardContent(fileElement, numTabs + 1);
    }
  } else {
    Serial.println(item.name);
  }
}







void loop()
{
    Current_Millis=millis();
    audio.loop(); 
    
    if (Current_Millis - Previous_Millis>Debounce )
    {
      Get_Button(analogRead(pushButton_pin));
      Previous_Millis=millis();
      if (Current_State!=state )
      {
        
        Serial.println(Btn);
        Current_State= state;
          
      }
      
    }
    
     
}
void audio_id3data(const char *info){  //id3 metadata
    //Serial.print("id3data     ");Serial.println(info);
    String word3 = getValue(info,': ',0);
    //Serial.println(word3);
    if(word3=="Title:")
    {
      String bla =String(info);
      String titre = bla.substring(6,maxIndex+1);
      Serial.println(titre);
      tft.setCursor(-10, 0, 2);
      tft.setTextSize(1.9);
      //tft.println(titre);
      
    }
    
}

String getValue(String data, char separator, int index)
{
  int found = 0;
  int strIndex[] = {0, -1};
  maxIndex = data.length()-1;

  for(int i=0; i<=maxIndex && found<=index; i++){
    if(data.charAt(i)==separator || i==maxIndex){
        found++;
        strIndex[0] = strIndex[1]+1;
        strIndex[1] = (i == maxIndex) ? i+1 : i;
    }
  }

  return found>index ? data.substring(strIndex[0], strIndex[1]) : "";
}

vérifiez que cette chaîne d'appels est cohérente

at vérifie qu'on ne déborde pas mais génère une exception si l'index n'existe pas. essayez un truc du genre :

if (!rootItem2.elements.empty()) {
  const FileItem& premierElement = rootItem2.elements[0];

  if (!premierElement.elements.empty()) {
    const FileItem& deuxiemeElement = premierElement.elements.at(1);

    if (!deuxiemeElement.elements.empty()) {
      const FileItem& troisiemeElement = deuxiemeElement.elements[1];

      if (!troisiemeElement.elements.empty()) {
        const char* nomFichier = troisiemeElement.elements[1].name;
        Serial.println(nomFichier);
      } else {
        Serial.println("Le troisième niveau d'élément est vide.");
      }
    } else {
      Serial.println("Le deuxième niveau d'élément est vide.");
    }
  } else {
    Serial.println("Le premier niveau d'élément est vide.");
  }
} else {
  Serial.println("L'élément racine est vide.");
}

si j'utilise ce code, j'ai le retour suivant :

Le troisième niveau d'élément est vide.
CORRUPT HEAP: Bad tail at 0x3ffb3e64. Expected 0xbaad5678 got 0x3ffb2c14

Je ne comprends pas pourquoi il dit que le 3e niveau est vide car le code

Serial.println(rootItem2.elements[0].elements.at(1).elements[1].name);

Me retourne un nom de fichier MP3.

Toutes les navigations dans l'arbre que j'ai testé pour le moment, n'ont pas posées de problème (quand l'élement existe) quand je viens de faire le listing en début de code.
Mais quand j'essaye de récupérer ça depuis la mémoire flash, sans meme chercher a naviguer dans l'abre ... l'ESP de met en Corrupt heap

j'avais pas vu

  EEPROM.put(0,rootItem);

votre type FileItem n'est pas sérialisable par EEPROM.put. D'une part vous avez un pointeur (upDir) qui est une adresse absolue et donc ne voudra rien dire à la relecture et d'autre part votre type n'est pas trivial et put ne saura pas gérer cette récursivité.

si vous voulez mettre cela en flash, il faut pouvoir sérialiser et deserialiser les données

Ça peut aussi venir des paramètres mémoire de l'ESP, indiqués dans l'IDE

Oups, je n'avais pas vu la réponse de @J-M-L

ok merci, je vais chercher un peu de lecture sur la serialisation/deserialisation

dit plus simplement put et get ne suivent pas les pointeurs, ça stocke le contenu du pointeur.

ok, mais il faut que ce contenu soit "lisible". Si j'ai bien compris ce que j'ai lu pour le moment de la sérialisation/déserialisation, il faut que je découpe ma variable rootItem en élément qui plus simple (typiquement qui peuvent s'ecrire dans le moniteur série) et que je fasse le processus inverse quand je veux récupérer les données.

ça ce n'est pas obligé, ça peut être du binaire, mais ça doit être "flat" et indépendant de la mémoire ➜ donc vous ne pouvez pas stocker un pointeur et vous ne pouvez pas stocker un objet complexe comme un vecteur d'instances d'un truc compliqué...

➜ vous pourriez ranger les données dans un tableau et garder des index relatifs au lieu de pointeurs

un truc du genre

enum TypeElement : byte {FICHIER, REPERTOIRE};
size_t tailleNomMax = 20;
size_t nombreEntreesMax = 100;
const uint16_t idInconnu = 0xFFFF;

struct __attribute__((packed))  Element {
  char nom[tailleNomMax];
  TypeElement type;
  uint16_t parentID;
  uint16_t suivantID;
  uint16_t precedentID;
  uint16_t enfantID;
};

Element elements[nombreEntreesMax];
uint16_t prochainIndexElementDispo = 0;

le parcours initial ferait l'insertion dans ce tableau elements (avec comme convention qu'un ID est mis à idInconnu pour dire qu'on ne pointe sur rien)

l'insertion dans le tableau se fait sur prochainIndexElementDispo et vous mettez à jour les Index (ID) des membres de l'Element

ensuite la tableau étant "à plat", vous pourriez l'écrire avec put() ou le relire avec get()

Je ne comprends pas trop ce que vous entendez par "index relatifs".
Ce que j'arrive a faire en modifiant un peu la fonction readSdcard, c'est avoir un index de chaque chanson qui commence a 0 a chaque changement d'album, un incrément pour chaque dossier, ainsi que le chemin complet de chaque item. Chose indispensable pour lancer mes mp3.
Est ce que je peux faire quelque chose avec ça ?

Je n'ai jamais testé mais d'après ce que j'ai lu je dois pouvoir recréer l'aborescence dans le SPIFFS, mais je ne sais pas si ça sera vraiment utile car initialement je pourcourais la carte SD à la volée et ça coupait le son le temps du listing du dossier a afficher. J'imagine que ça sera là meme chose avec la mémoire SPIFFS, a moins que ce soit vraiment plus rapide que la lecture de la carte SD

Parce pour avoir suivantID, precedentID et enfantID, il faut que je change un peu plus en profondeur la fonction, et dans un précédent post sur ce forum je n'avais pas réussi a établir tous ces liens.

Je veux dire par la qu’on n’a pas un pointeur sur une position absolue en mémoire vers l’autre structure mais l’index par rapport au début du tableau de structure (le N° de case du tableau).

Ainsi si vous dupliquez tout le tableau ou le déplacez de RAM en FLASH les valeurs d’index restent valables

Quel est l’objectif réel de cette duplication de la table des fichiers en mémoire ?

c'est dans le cadre d'un lecteur MP3.
La musique est sur une carte SD.
l'objectif est d'afficher la liste des MP3 à l'écran pour selectionner la chanson a lancer.
Actuellement je parcours la carte SD à la volée, c'est a dire, je sais dans quel dossier je suis, si je selectionne un dossier, j'ai une fonctione qui va faire OpenNextFile tant qu'il y a quelque chose a parcourir dans le dossier.
Ca fonctionne bien, mais le souci, c'est que si j'ai une lecture en cours, le fait de lister les fichier d'un dossier va bloquer la musique le temps de le faire.
L'idée est donc d'avoir déja fait le boulot en amont pour avoir quelque chose de plus rapide et ne plus avoir ce petit "blanc" durant la lecture, le stocker pour ne pas avoir a le refaire a chaque extinction de l'appareil.

This topic was automatically closed 180 days after the last reply. New replies are no longer allowed.