Hulp gezocht bij inlezen txt file

Hoi,

Ik ben al een tijd aan het stoeien en puzzelen met het inlezen van een .txt file die ik op een SD kaart heb staan.
Het inlezen gaat op zich goed, en met STRTOK() is het me zelf gelukt om de waardes tussen de comma’s van elkaar te scheiden.
Maar, het probleem zit hem er in dat ik de waardes die ik heb ingelezen later wil kunnen gebruiken. En het lukt me niet om de waardes in een herbruikbare variabele te krijgen.

Dit is de sketch die ik gebruik. Een bewerk ReadLn voorbeeld.

#include <tinyFAT.h>
#include <avr/pgmspace.h>

byte res;
word result;
char textBuffer[81];
char *str;
char *p;
char* mashString[20];
int I;

const int  MAX_STRING_LEN = 60; // set this to the largest string
                                // you will process
char stringBuffer[MAX_STRING_LEN+1]; // a static buffer for computation and output

char *verboseError(byte err)
{
	switch (err)
	{
	case ERROR_MBR_READ_ERROR:
		return "Error reading MBR";
		break;
	case ERROR_MBR_SIGNATURE:
		return "MBR Signature error";
		break;
	case ERROR_MBR_INVALID_FS:
		return "Unsupported filesystem";
		break;
	case ERROR_BOOTSEC_READ_ERROR:
		return "Error reading Boot Sector";
		break;
	case ERROR_BOOTSEC_SIGNATURE:
		return "Boot Sector Signature error";
		break;
	default:
		return "Unknown error";
		break;
	}
}

void setup() {
  // Initialize serial communication at 115200 baud
  Serial.begin(115200);
  Serial.println();
  // Initialize tinyFAT 
  // You might need to select a lower speed than the default SPISPEED_HIGH
  res=file.initFAT(); 
  if (res!=NO_ERROR)
  {
    Serial.print("***** ERROR: ");
    Serial.println(verboseError(res));
    while (true) {};
  }

//  Serial.println("This demo will display a textfile called 'maisch.txt'");
//  Serial.println();
//  Serial.println("***** Send any character to start *****");
//  while (!Serial.available()) {};
  Serial.flush();
  Serial.println();
  Serial.println();
  
  if (file.exists("maisch.txt"))
  {  
    I=0;
    res=file.openFile("maisch.txt", FILEMODE_TEXT_READ);
    if (res==NO_ERROR)
    {
      result=0;
      while ((result!=EOF) and (result!=FILE_IS_EMPTY))
      {
        result=file.readLn(textBuffer, 80);
        if (result!=FILE_IS_EMPTY)
        {
          if (result==BUFFER_OVERFLOW)
            Serial.print(textBuffer);
          else
//           Serial.println(textBuffer);
           strncpy(stringBuffer, textBuffer, MAX_STRING_LEN); // copy source string
           Serial.println(stringBuffer);                      // show the source string
  
           for( str = strtok_r(stringBuffer, ",", &p);        // split using comma
           str;                                          // loop while str is not null
           str = strtok_r(NULL, ",", &p)                 // get subsequent tokens
           )
         {
          mashString[I] = str;
//          Serial.print(I);
          Serial.print("Name: ");
          Serial.print(" :  ");
          Serial.println(mashString[I]);
//          Serial.println(str);          
          ++I;
          }           
        }
        else
          Serial.println("** ERROR: File is empty...");
      }
      Serial.println();
      file.closeFile();
    }
    else
    {
      switch(res)
      {
        case ERROR_ANOTHER_FILE_OPEN:
          Serial.println("** ERROR: Another file is already open...");
          break;
        default:
          Serial.print("** ERROR: ");
          Serial.println(res, HEX);
          break;
      }
    }
  }
  else
    Serial.println("** ERROR: 'TEXTFILE.TXT' does not exist...");
    
  Serial.println();
  Serial.println();
  Serial.println("***** All done... *****");
  
}  

void loop() 
{
}

En dit is het textbestand dat ik wil inlezen. Het aantal regels in het bestand kan varieren, en de lengte van de regels ook.
Het bestand is als volgt opgebouwd: naam, temperatuur, tijd, temperatuur, tijd,…etc.
Standaard is de naam op de eerste positie, en daarna 1 of meerdere combo’s van temperatuur en tijd.

eenstaps,66,60
tweestaps,50,10,60,20,68,5
driestaps,45,15,50,10,60,20,68,5

De output van de sketch is nu

eenstaps,66,60
Name:  :  eenstaps
Name:  :  66
Name:  :  60
tweestaps,50,10,60,20,68,5
Name:  :  tweestaps
Name:  :  50
Name:  :  10
Name:  :  60
Name:  :  20
Name:  :  68
Name:  :  5
driestaps,45,15,50,10,60,20,68,5
Name:  :  driestaps
Name:  :  45
Name:  :  15
Name:  :  50
Name:  :  10
Name:  :  60
Name:  :  20
Name:  :  68
Name:  :  5




***** All done... *****

Ik zou graag de waardes herkenbaar opgeslagen hebben. Bijvoorbeeld als ‘maischXY’. Waarbij maisch de variabele is (geen idee wat voor type), X het regelnummer aangeeft en Y de positie.
Geen idee of iemand me nog begrijpt, of kan helpen. Maar niet geschoten is altijd mis.

Alvast bedankt.

Edwin.

Hoi Edwin.

Je hebt gelijk, ik heb geen idee wat je wil.
In eerste instantie dacht ik dat je een waarde wil kunnen verwerken, maar dat je verkeerde resultaten krijgt.
Als dat zo is, dan komt dat omdat je tekens inleest en geen waardes.
Je moet dan dus eerst die hele reeks binnenhalen (alles wat er tussen de twee komma’s staat), en vervolgens een variabele van maken.
Maar het laatste stukje wijst er op dat je iets ander wil, alleen raak ik je daar kwijt.

Hi MAS3,

Ik was er al bang voor dat het niet goed over zou komen. Wat ik wilde bereiken is dat het txt bestand wordt ingelezen, en ik dan per regel de waarden in variabelen zou krijgen. Als voorbeeld, na het inlezen wil ik graag dat ik de volgende variabelen heb gevuld: Regel1Var1=eenstaps Regel1Var2=66 Regel1Var3=60 Regel2Var1=tweestaps Enzovoorts. Zoals gezegd, het bestand varieert kwa lengte, en de regel lengte is ook variabel.

Hopelijk verduidelijkt dit wat.

eschraven Waar je volgens mij eens goed moet over nadenken is: hoe ga ik die waarden die ik inlees opslaan in het geheugen. Wat jij volgens mij nodig hebt om te doen wat je hier omschrijft is een linked list. Maar in arduino wereld (weinig geheugen en slecht geheugen management) lijkt me dat geen goed idee. Wat wil je eigenlijk doen met de "gelezen nformatie"? Is het nodig om die steeds in het geheugen te hebben? Is het niet makkelijker om de informatie te lezen als je ze nodig hebt?

Met vriendelijke groet Jantje

Ok, je wil dus toch waardes binnenhalen. In het eerste deel van je bericht heb je het ook over herbruikbare variabelen. Zoals Jantje al vertelde, moet je ook weten wat je er dan mee gaat doen. Het geheugen is schaars en daar moet je dus slim mee om gaan. Je kunt namelijk ook redeneren dat de data altijd beschikbaar is, want die SD kaart zit er nog steeds in (toch ?).

Maar goed. Ik heb een jaartje geleden eens een hele routine bedacht om te doen wat jij nu wil bereiken. Dat was leerzaam en ook leuk (omdat me dat gelukt is..), het ging daarbij om hexadecimale waarden in een string, met komma's gescheiden en die via een seriële verbinding binnen kwam. Maar om te doen wat jij wil, moet je meer weten over de opbouw van jouw data. Als je precies weet hoe die data is opgebouwd, kun je daar dus mee werken. De komma's zijn al een hele hulp. Die helpen je onderscheid te maken tussen de verschillende waardes. De naam van de regel is het volgende hulpmiddel. Als er een nieuwe regel begint, dan heeft die een e, t of d als eerste karakter. Daarmee weet je dan dus om welke regel het gaat, en dan weet je dus ook hoeveel waardes er gaan komen. Ik neem aan dat elke regel telkens hetzelfde aantal waardes heeft. Nu moet je nog weten wat voor waardes je gaat binnen krijgen. In jouw voorbeeld blijven ze allemaal binnen de 8 bits, en das erg mooi vanwege de beperkingen van het beschikbare geheugen.

Het nadeel van jouw aanpak tot nog toe lijkt mij te zijn dat je een voorbeeld hebt genomen en die niet helemaal begrijpt. Daarvan kun je wel leren maar als je niet naar dit voorbeeld hebt toegewerkt door ander sketches te bekijken en aan te passen dan word het wel lastig. Dus hebt je dat al eens gedaan, andere sketches bekeken en aangepast ?

Volgens mij is er een commando om een string om te zetten naar een variabele, maar ik kan 'm zo snel ook niet vinden. Met een string bedoel ik dus een rijtje ASCII tekens.

Ik heb nu geen tijd meer, andere zaken te doen. Maar je moet nog wat zaken bekijken (of als je dat al gedaan hebt hier noemen). Zoals hoe groot de waardes zijn (kleiner dan 256, of 65535 of niet, kunnen er ook negatieve waardes voorkomen ?). Hoeveel waardes wil je opslaan, heb je die ook allemaal nodig ? Word elke regel met een CR (Carriage Return) en/of LF (Line Feed) beëindigd ? (CR/LF is wat er gebeurt als je in een tekstverwerker op enter drukt).

Als je precies weet hoe de regels zijn opgebouwd, kun je dat gebruiken.

Hoi,

De waarden in het txt bestand zijn per regel relevant. Per regel is het de input voor een maisschema. Het schema heeft een naam, en vervolgens steeds paartjes van 2 getallen. Het eerste getal is een temperatuur, en komt dus nooit boven 100, het tweede getal is een tijdsduur in minuten. Deze kan dus wel boven de 100 komen, maar blijft onder de 256. Wat ik nu wil bereiken is dat ik op basis van de naam een regel kan selecteren, en vervolgens met de temperaturen en tijden die daar achter staan verder in mijn programma kan werken. Ze worden gebruikt om te checken of de respectievelijke temperaturen en tijden gehaald zijn of overschreden worden.

Hopelijk voegt dit wat toe?

Bedankt voor het meedenken.

Jazeker voegt dat wat toe. Het betekent dat je altijd tussen 0 en 255 blijft, en dus ook nooit een negatieve waarde zult hebben. Daarmee kun je dus elke waarde opslaan als een byte, en dat beperkt de opslagruimte die er voor nodig is aanzienlijk. Je hebt voor de temperaturen dan zelfs nog wat bits over die je in een later stadium nog zou kunnen gebruiken, maar das wel een beetje tricky en zou ik niet doen als het niet per se nodig is.

Het is inmiddels al weer tijd om te gaan werken, dus heb ik geen tijd om hier nu verder op in te gaan. Maar ik zal later nog ff kijken of ik dat stukje code wat ik in elkaar gebakken had kan laten zien. Dan moet het eerst aangepast worden voor dit doel, want ik had er natuurlijk een heel andere toepassing voor.

Overigens zul je wellicht deze thread (kilk !) ook wel interessant vinden, met 194 postings wel ff doorbijten.

Nou, ik ben er achter welk commando het ook al weer was dat een string kan omzetten naar een waarde (ik zei eerder variabele, maar das natuurlijk niet waar).

Het gaat hierbij om StringToInt (klik !). Die maakt dus een integer uit een cijferreeks. Een integer is 2 bytes, en dus tussen 0 en 65535. Via de link vind je ook een voorbeeld, maar ik kan me voorstellen dat die lastig te begrijpen is. Maar kijk daar in ieder geval eens naar.

Daar ik niet zo Stringerig ben :) gebruik ik meestal atoi() (standaard c functie. Via strtok() kun je de separators vinden.

Dank je wel Nico. Daarmee kan ik vermoedelijk de sketch die ik eerder gebouwd heb efficiënter maken. Want daar heb ik met de mij bekende middelen bepaalde data die binnenkomt via een seriële verbinding on the fly uit elkaar getrokken en tegelijk de hexadecimale waarden daarin omgezet naar decimale waarden. Dat was overigens ook weer erg leerzaam en daarom zeker niet voor niets. Maar met deze twee methodes die je net hebt geopperd zal de code er een stuk compacter op worden en vermoedelijk ook sneller werken. Dus ik ga daar ook maar eens mee aan de gang binnenkort.

Hoi,

Het heeft even geduurd, maar ik ben al een heel stuk verder. Ik kan het bestand lezen, een selectie maken op basis van de namen in het bestand en de waarden die daar bij horen in een ander deel van de software gebruiken.
Enige vreemde dat ik nu nog heb is dat ik meer regels lees dan er in werkelijkheid in het bestand staan.

In de terminal krijg ik het volgende te zien:

Maak een keuze:

  1. eenstaps
  2. tweestaps
  3. driestaps
  4. vierstaps

Maar er zitten echt maar 4 regels in het bestand. Geen lege regels of spaties die ik over het hoofd heb gezien.
Waar ga ik de fout in? Ik staar me er op dit moment een beetje blind op.
Hieronder de code. Niet het mooist geprogrameerd maar hij doet het voor het grootste deel.

#include <tinyFAT.h>
#include <avr/pgmspace.h>

byte res;
word result;
char textBuffer[81];
char *str;
char p;
char
mashString[20];
int curline, curcol;
int maxcol[20];
String Mashname[20];
String Mash2DArray[20][20];
String teststring;

const int MAX_STRING_LEN = 60; // set this to the largest string
// you will process
char stringBuffer[MAX_STRING_LEN+1]; // a static buffer for computation and output

char *verboseError(byte err)
{
switch (err)
{
case ERROR_MBR_READ_ERROR:
return “Error reading MBR”;
break;
case ERROR_MBR_SIGNATURE:
return “MBR Signature error”;
break;
case ERROR_MBR_INVALID_FS:
return “Unsupported filesystem”;
break;
case ERROR_BOOTSEC_READ_ERROR:
return “Error reading Boot Sector”;
break;
case ERROR_BOOTSEC_SIGNATURE:
return “Boot Sector Signature error”;
break;
default:
return “Unknown error”;
break;
}
}

void setup() {
// Initialize serial communication at 115200 baud
Serial.begin(115200);
Serial.println();
// Initialize tinyFAT
// You might need to select a lower speed than the default SPISPEED_HIGH
res=file.initFAT();
if (res!=NO_ERROR)
{
Serial.print("***** ERROR: ");
Serial.println(verboseError(res));
while (true) {};
}

Serial.flush();

if (file.exists(“maisch.txt”))
{
curline=1;
res=file.openFile(“maisch.txt”, FILEMODE_TEXT_READ);
if (res==NO_ERROR)
{
result=0;
while ((result!=EOF) and (result!=FILE_IS_EMPTY))
{
curcol=1;
result=file.readLn(textBuffer, 80);
if (result!=FILE_IS_EMPTY)
{
if (result==BUFFER_OVERFLOW)
Serial.print(textBuffer);
else
strncpy(stringBuffer, textBuffer, MAX_STRING_LEN); // copy source string

for( str = strtok_r(stringBuffer, “,”, &p); // split using comma
str; // loop while str is not null
str = strtok_r(NULL, “,”, &p) // get subsequent tokens
)
{
Mash2DArray[curline][curcol]= str;
maxcol[curline]= curcol;
++curcol;
}
++curline;
}
else
Serial.println("** ERROR: File is empty…");
}
Serial.println();
file.closeFile();
}
else
{
switch(res)
{
case ERROR_ANOTHER_FILE_OPEN:
Serial.println("** ERROR: Another file is already open…");
break;
default:
Serial.print("** ERROR: “);
Serial.println(res, HEX);
break;
}
}
}
else
Serial.println(”** ERROR: ‘maisch.txt’ does not exist…");

Serial.println(“Maak een keuze:”);
for (int tmpln = 1; tmpln <= curline; tmpln++) // Display name, first position of each line
{
Serial.print(tmpln);
Serial.print(". ");
Serial.println(Mash2DArray[tmpln][1]);
}

}

void loop()
{
int MashSelect;
while (Serial.available() > 0)
{
int inChar = Serial.read();
MashSelect = (inChar - ‘0’);
Serial.print(“gemaakte keuze :”);
Serial.print(MashSelect);
Serial.print(". ");
Serial.print(Mash2DArray[MashSelect][1]);
Serial.println();
for (int tmpcol = 2; tmpcol <= maxcol[MashSelect]; tmpcol++) // skip name
{
if ( (tmpcol % 2) == 0)
{
Serial.print("Temp.: ");
}
else
{
Serial.print("Tijd : ");
}
Serial.println(Mash2DArray[MashSelect][tmpcol]);
}
}
}

Deze code geeft met het volgende resultaat als ik een getal invoer in de terminal:

gemaakte keuze :2. tweestaps
Temp.: 50
Tijd : 10
Temp.: 60
Tijd : 20

Dus, het lijkt er op dat het werkt. Alleen die extra regels nog weg zien te krijgen.
Als iemand een ide heeft, ik houdt me aanbevolen.

Groeten,
Edwin.

Ik heb de code niet gelezen, maar ik denk dat je jezelf wel kunt helpen om hierover meer uit te vinden. Daarvoor pas je je bestand dan aan. Wat gebeurt er als je bestand maar 2 of 3 regels heeft, en wat als er 6 regels zijn ? Heb je dan nog steeds 3 regels extra, of verandert dat ook ?

Ik heb het vermoeden dat het dan dus ook verandert.

Helaas. Ik zoek me rot maar kom er niet uit.
De onderstaande code leest een bestand op de SD kaart, maar probeert steevast 2 regels meer te lezen dan er op de kaart staan.
De variabele curline houdt bij hoeveel regels er in het bestand staan. Maar als er 4 staan, dan probeert hij toch 6 regels (curline=6) te lezen, als er 2 regels staan dan loopt curline op tot 4 enzovoorts.

Ik had het gebruikt van ++var en var++ door elkaar gehaald en dacht dat het daar aan kon liggen, maar dat is het ook niet.
Enige optie die ik zelf zie is bij het laten zien van de keuze opties curline-2 aan te houden als maximaal aantal regels in het bestand op de SD kaart. Maar dat voelt een beetje gekunsteld.

#include <tinyFAT.h>
#include <avr/pgmspace.h>

byte res;
word result;
char textBuffer[81];
char *str;
char *p;
char* mashString[20];
int curline, curcol;
int maxcol[20];
String Mashname[20];
String Mash2DArray[20][20];


const int  MAX_STRING_LEN = 60; // set this to the largest string
                                // you will process
char stringBuffer[MAX_STRING_LEN+1]; // a static buffer for computation and output

char *verboseError(byte err)
{
	switch (err)
	{
	case ERROR_MBR_READ_ERROR:
		return "Error reading MBR";
		break;
	case ERROR_MBR_SIGNATURE:
		return "MBR Signature error";
		break;
	case ERROR_MBR_INVALID_FS:
		return "Unsupported filesystem";
		break;
	case ERROR_BOOTSEC_READ_ERROR:
		return "Error reading Boot Sector";
		break;
	case ERROR_BOOTSEC_SIGNATURE:
		return "Boot Sector Signature error";
		break;
	default:
		return "Unknown error";
		break;
	}
}

void setup() {
  // Initialize serial communication at 115200 baud
  Serial.begin(115200);
  Serial.println();
  // Initialize tinyFAT 
  // You might need to select a lower speed than the default SPISPEED_HIGH
  res=file.initFAT(); 
  if (res!=NO_ERROR)
  {
    Serial.print("***** ERROR: ");
    Serial.println(verboseError(res));
    while (true) {};
  }

  Serial.flush();
  
  if (file.exists("maisch.txt"))
  {  
    curline=1;
    res=file.openFile("maisch.txt", FILEMODE_TEXT_READ);
    if (res==NO_ERROR)
    {
      result=0;
      while ((result!=EOF) and (result!=FILE_IS_EMPTY))
      {
        curcol=1;
        result=file.readLn(textBuffer, 80);
            Serial.println(textBuffer);        
        if (result!=FILE_IS_EMPTY)
        {
          if (result==BUFFER_OVERFLOW)
            Serial.print(textBuffer);
          else
           strncpy(stringBuffer, textBuffer, MAX_STRING_LEN); // copy source string
           for( str = strtok_r(stringBuffer, ",", &p);        // split using comma
           str;                                          // loop while str is not null
           str = strtok_r(NULL, ",", &p)                 // get subsequent tokens
           )
         {
          Mash2DArray[curline][curcol]= str;
          maxcol[curline]= curcol;
          ++curcol;
          }
          ++curline;
        }
        else
          Serial.println("** ERROR: File is empty...");
      }
      Serial.println();
      file.closeFile();
    }
    else
    {
      switch(res)
      {
        case ERROR_ANOTHER_FILE_OPEN:
          Serial.println("** ERROR: Another file is already open...");
          break;
        default:
          Serial.print("** ERROR: ");
          Serial.println(res, HEX);
          break;
      }
    }
  }
  else
    Serial.println("** ERROR: 'maisch.txt' does not exist...");
 


  Serial.println("Maak een keuze:");
  for (int tmpln = 1; tmpln <= curline; ++tmpln) // Display name, first position of each line
  { 
//    Serial.println(curline);
    Serial.print(tmpln);
    Serial.print(". ");
    Serial.println(Mash2DArray[tmpln][1]);    
  }
  
}  

void loop() 
{
  int MashSelect;
   while (Serial.available() > 0) 
   {
    int inChar = Serial.read();
    MashSelect = (inChar - '0');
    Serial.print("gemaakte keuze :");
    Serial.print(MashSelect);
    Serial.print(". ");
    Serial.print(Mash2DArray[MashSelect][1]);
    Serial.println();
    for (int tmpcol = 2; tmpcol <= maxcol[MashSelect]; ++tmpcol) // skip name
    {
      if ( (tmpcol % 2) == 0) 
      { 
        Serial.print("Temp.: "); 
      }
      else
      {
        Serial.print("Tijd : ");
      }
      Serial.println(Mash2DArray[MashSelect][tmpcol]);    
    }
   }
}

Ik heb geen idee.
Wat zie ik nog over het hoofd?

Edwin.

Hoi.

Ik heb 2 vragen.
De eerste is dat je begint te tellen bij 1.
Het is gangbaar om te beginnen bij nul.
Zit daar niet de eerste offset toevallig ?

En dan je teller.
Dat probleem zit niet in curline, maar in tmpln, en das ook waar je mee constateert dat je te hoog uitkomt.
Je laat 'm namelijk 1 lijn teveel tellen omdat je een kleiner of gelijk doet bij het ophogen van tmpln.
Als je alleen kleiner dan doet, dan kom je beter uit.
Ik heb het dus over deze regel

  for (int tmpln = 1; tmpln <= curline; ++tmpln) // Display name, first position of each line

Verander die dan dus in:

  for (int tmpln = 1; tmpln < curline; ++tmpln) // Display name, first position of each line

Ik heb dit gelijk misbruikt om te oefenen in C++, ben net begonnen om ook pc programmaatjes te leren maken.
Daarmee heb ik zojuist gecontroleerd of het tweede puntje correct is en ik ben er nu zeker van dat je daarmee in ieder geval 1 van je niet bestaande regels wegpoetst.

[edit]
Kijk ook even naar maxcol

Ow, en nog iets.
Variabelen zijn net iets beter te volgen als je er ook hoofdletters in gebruikt.
Bij maxcol zou ik liever maxCol gebruiken, en zo ook curLine en tmpLn.
En waarom eerst line vol uitschrijven en later afkorten als ln.
Houd zoveel mogelijk dezelfde norm aan.
Moet je wel zelf weten, maar als je later je sketches nog eens gaat bekijken zul je zien dat deze twee dingen je echt helpen om je code te lezen.
[/edit]

Volgens mij zie ik zojuist nog iets.
Je hoogt in regel 88 curline op als je klaar bent met het verwerken ervan daarvoor (ergens tussen regels 74 en 89).
Daarna ga je in regel 116 toch nog een keer aan de slag met de zojuist opgehoogde waarde.
Daarmee kom je dus ook 1 curline te hoog uit.

Kun je dat ophogen niet doen voor je 'm gaat verwerken vanaf regel 74, in plaats van erna ?
Ik denk dat je dan beter uit gaat komen.
Als dat niet gaat, kun je doen wat je eerder al opperde;

  for (int tmpln = 1; tmpln <= (curline -1); ++tmpln) // Display name, first position of each line

Maar dat zou ik wel wat jammer vinden, moet eigenlijk wel eleganter kunnen.

Het verplaatsen van de ophoging van curline naar regel 74, samen met het veranderen van kleiner dan in kleiner heeft het probleem verholpen. Geweldig bedankt voor het meedenken.

Ik ga op naar de volgende uitdaging :)