Problème lecture ligne d'un fichier SD

Bonjour,
J'ai des fichiers dans la structure est immuable : 79 caractères + le retour à la ligne ('\n')

01/01/2035;Aube : 07:07:55;Lever : 07:47:26;Coucher : 15:52:37;Nuit : 16:32:08
02/01/2035;Aube : 07:07:54;Lever : 07:47:20;Coucher : 15:53:40;Nuit : 16:33:06
03/01/2035;Aube : 07:07:49;Lever : 07:47:12;Coucher : 15:54:45;Nuit : 16:34:07
04/01/2035;Aube : 07:07:42;Lever : 07:47:00;Coucher : 15:55:53;Nuit : 16:35:10

Cependant, la taille totale du fichier ne tombe pas rond, par exemple 29900 octets pour 365 jours.
pour info, ces fichiers sont créés en Python avec ce script. Soyez indulgents pour le bricolage du code, je connais pas Python et ephem ne semble retourner de datetime.
Qui soit, ce n'est pas le domaine ici et j'ai donc créé exprès un petit fichier txt tout propre, avec les même lignes que ci-dessus: 79 caractères plus un retour à la ligne, soit 240 octets pour 3 lignes.

Voici le code le lecture :

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

File myFile;
char buff[80]; // 79 + \n

void setup() {
  // Open serial communications and wait for port to open:
  Serial.begin(115200);
  Serial.print("Initializing SD card...");
  if (!SD.begin(4)) {
    Serial.println("initialization failed!");
    while (1);
  }
  Serial.println("initialization done.");

  if (SD.exists("2022.txt")) {
    Serial.println("2022.txt exists.");
  } else {
    Serial.println("2022.txt doesn't exist.");
  }
  myFile = SD.open("2022.txt", "r");
  //Serial.println(myFile.read(&buff,80));
  myFile.read(buff, 80);
  Serial.println(buff);
  myFile.close();
}
void loop() { }

Et aucun résultat : du vide.

Initializing SD card...initialization done.
2022.txt exists.

J'ai essayé des variantes comme tout bêtement Serial.println(myFile.read(&buff,80)); (en commentaire dans le code)

Je suis perdu ...
Merci de m'éclairer

Bonjour Renard32

Essaies:

 while (myFile.available()) 
 {
  String line = myFile.readStringUntil('\n');
  Serial.println(line);
 }

Cordialement
jpbbricole

Cette ligne lit le texte + le \n. Pas de chance, il va manquer le terminateur de chaine \0.
Ne pas oublier que les C-string doivent se terminer par \0. Sinon le print que tu fais derrière va lire la mémoire jusqu'à trouver un 0 et accessoirement se planter.

Oui, c'est ce qu'on trouve sur le WEB et qui ne fonctionne pas.
désolé, et merci pour l'effort

et juste comme cela:

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

File myFile;
char buff[81]; // 79 + \n + null

void setup() {
  // Open serial communications and wait for port to open:
  Serial.begin(115200);
  Serial.print("Initializing SD card...");
  if (!SD.begin(4)) {
    Serial.println("initialization failed!");
    while (1);
  }
  Serial.println("initialization done.");

  if (SD.exists("2022.txt")) {
    Serial.println("2022.txt exists.");
    buff[80] = '\0';
    myFile = SD.open("2022.txt", FILE_READ);
    myFile.read(buff, 80);
    myFile.close();  
    Serial.println(buff);
  } else {
    Serial.println("2022.txt doesn't exist.");
  }
}
void loop() { }

bien sûr il faut mettre le terminal série à 115200 bauds :innocent:

Bonsoir,
Oui, d'accord. un peu perplexe je suis, mais je vais modifier mon script py et je reviens .

Pas tout à fait. Ce que l'on trouve c'est ça:

Serial.write(myFile.read());

Et ça fonctionne parce que là on ne manipule pas des C-string mais des caractères isolés.
Par exemple ici:

Bonjour,
pas de bonnes nouvelles.
J'ai modifié le script Python en ajoutant \0 derrière le \n pour terminer la ligne myFile.write(sdt+"\n\0")

Un fichier de 365 jours fait 29.565 octets, soit tout pile des lignes de 81 octets. Du coup, j'ai utilisé un éditeur hexadécimal et je vois que toutes les lignes se terminent par 0d 0a 00. Je ne pensais pas que le '\n' générait le CR et le LF...
J'ai donc supprimé le \n et le fichier de 365 jours fait maintenant 28.835 octets, soit des lignes de 79 octets. Bien entendu la lecture humaine est moins simple, mais ce n'est pas l'objectif.

Et cela ne fonctionne toujours pas :

Initializing SD card...initialization done.
2022.txt exists.
0

Le code n'a presque pas changé

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

File myFile;
char buff[80] ; //
String sBuff ;
void setup() {
  // Open serial communications and wait for port to open:
  Serial.begin(115200);
  Serial.print("Initializing SD card...");
  if (!SD.begin(4)) {
    Serial.println("initialization failed!");
    while (1);
  }
  Serial.println("initialization done.");

  if (SD.exists("2022.txt")) {
    Serial.println("2022.txt exists.");
  } else {
    Serial.println("2022.txt doesn't exist.");
  }

  myFile = SD.open("2022.txt", "r");
  //buff[80]='\0';
  Serial.println(myFile.read(buff, 79));
  Serial.println(buff);

  sBuff = String(buff);

  Serial.println(sBuff);
  myFile.close();
}
void loop() { }

À vous lire,
un renard désespéré.

Bonjour Renard32

Pourquoi ne pas garder l'ancien format avec chaque ligne se terminant avec \n?
Tu peux les lire ainsi:

  if (myFile) {
	 while (myFile.available())
	 {
		String line = myFile.readStringUntil('\n');
		 Serial.println(line);
	 }    
    myFile.close();
  }

Ainsi la longueur de la ligne n'a plus d'importance et le fichier est aisément lisible.

Cordialement
jpbbricole

Ce n'est pas du tout ce dont parlait fdufnews
Il ne sert à rien d'ajouter ce '\0'.

Le fichier doit être lu jusqu'à rencontrer '\n', en éliminant les '\r'.
Ensuite un '\0' doit être ajouté en fin de chaîne.
Tu peux t'inspirer de ce qui est fait dans la librairie SDFat :

Voir la fonction :
int FatFile::fgets(char* str, int num, char* delim)
Autrement, si tu utilisais la librairie SDFat au lieu de SD ?

Bon, j'ai trouvé une solution qui fonctionne :

  myFile = SD.open("2022.txt"); // à noter et important : sans mode 'r' !
  sBuff = "";
  for (int i = 0; i < 79; i++) {  // \0 y compris dans ce cas-ci !
    sBuff += (char)myFile.read();
  }
  Serial.println(sBuff);
  myFile.close();

J'ai compris pour le \0, je dois l'ajouter en fin de chaine. Je l'avais appris, mais oublié.

Je l'ai essayé et il ne fonctionne pas.


Je peux reprendre mon ancien format de fichier (\n) pour le rendre + humain, calculer le seek et ce sera bon.
Là, j'ai un objet String que je peux manipuler facilement, tout baigne :smiley:

Merci à vous tous pour vos efforts

Sauf que tu risques la fragmentation mémoire, surtout si tu utilise d'autres String par ailleurs.
Je te conseille de réserver un espace pour ta chaîne.
sBuff.reserve(80);

Je ne connaissais pas cette méthode. Merci. (mais elle ne fonctionne pas)


À titre d'information, j'ai décidé de mettre le null dans le fichier suivi du \n pour sa lisibilité.
En incluant le null dans le fichier, il est "naturellement" ajouté à la String lors de la lecture, et cela ne mange pas de pain.

Alors voilà le croquis de lecture de ces éphémérides. Va s'y ajouter des affichages sur une matrice LED de 12 éléments, ou, dans un autre projet, commander l'ouverture/fermeture d'une porte de poulailler (aube et nuit)
Mais d'abord le résultat pour ce 9 janvier 2022 (en UTC 0) :
09/01/2022;Aube : 07:06:23;Lever : 07:45:13;Coucher : 16:02:17;Nuit : 16:41:07
Le croquis


#include <SPI.h>
#include <SD.h>
#include <Wire.h> //include Wire.h library
// RTC
#include <TimeLib.h>
#include <DS1307RTC.h>
tmElements_t tm;
File myFile;


byte mDay = 0;
int nrJ = 0;
String sBuff;
//sBuff.reserve(80);
unsigned long long secondeMillis = 0 ;

void setup() {
  // Open serial communications and wait for port to open:
  Serial.begin(115200);
  delay(50);

  Serial.print("Initialisation carte SD ...");
  if (!SD.begin(4)) {
    Serial.println("Initialisation en défaut!");
  } else {
    Serial.println("Initialisation OK.");
  }
  secondeMillis = superMillis(); // top départ
}


void loop() {
  if (superMillis() >= secondeMillis)
  { // nouvelle seconde
    secondeMillis +=  1000; // pour seconde suivante
    RTC.read(tm); // lecture horloge
    if (mDay != (tm.Day))  { // nouveau jour
      mDay = tm.Day;
      String fileName;
      int mYear = tmYearToCalendar(tm.Year);
      fileName = String(mYear) + ".txt";
      nbrJoursAnnee();
      myFile = SD.open(fileName); // sans le mode 'r'
      if (myFile) {
        /*
           une ligne fait 78 caractères utiles + NULL et + 2 à cause du '\n' (CR et LF) qui ne servent qu'à rendre le fichier "humain"
           Lire les 79 caractères, null ('\0') compris
        */
        myFile.seek(81 * (nrJ - 1) ); // positionnement à partir de 0
        sBuff = "";
        for (int i = 0; i < 79; i++) {
          sBuff += (char)myFile.read();
        }
        Serial.println(sBuff);

      } else {
        Serial.println("error opening " + fileName );
      }
    }
  }
}


void nbrJoursAnnee() {
  nrJ = 0;
  switch (tm.Month)
  {
    case  2 : nrJ =  31; break;
    case  3 : nrJ =  59; break;
    case  4 : nrJ =  90; break;
    case  5 : nrJ = 120; break;
    case  6 : nrJ = 151; break;
    case  7 : nrJ = 181; break;
    case  8 : nrJ = 212; break;
    case  9 : nrJ = 243; break;
    case 10 : nrJ = 273; break;
    case 11 : nrJ = 304; break;
    case 12 : nrJ = 334; break;
  }
  nrJ += tm.Day ;
  if (tm.Year % 4 == 0 && tm.Month > 2) {
    // bissextile
    nrJ += 1;
  }
}

unsigned long long superMillis() {
  /**
     Retourne le nombre de millisecondes depuis le démarrage du programme
     sous la forme d'un nombre entier sur 64 bits (unsigned long long).
     anti bug de l'overflow des 32 bits qui repasse à zéro tous les ~50 jours
  */
  static unsigned long nbRollover = 0;
  static unsigned long previousMillis = 0;
  unsigned long currentMillis = millis();
  if (currentMillis < previousMillis) {
    nbRollover++;
  }
  previousMillis = currentMillis;
  unsigned long long finalMillis = nbRollover;
  finalMillis <<= 32;
  finalMillis +=  currentMillis;
  return finalMillis;
}

Bonne continuation !

ça n'a aucun sens de faire des print() de '\0'. la fonction print s'arrête juste au moment où elle trouve le \0 justement...

ça n'aide pas beaucoup quand vous dites que ça ne fonctionne pas....
Qu'est-ce qui ne fonctionne pas ?

Rien de visible dans le buffer

essayez le code posté en #5 (Problème lecture ligne d'un fichier SD - #5 by J-M-L)

String sBuff;
sBuff.reserve(80);

et

Test_fichier_ephemerides:15:1: error: 'sBuff' does not name a type
 sBuff.reserve(80);
 ^~~~~
exit status 1
'sBuff' does not name a type

Où as-tu vu que l'on pouvait en C écrire des lignes de code en dehors des fonctions ?