leggere file .csv da sd card

Ciao a tutti, è da un po' che non traffico con la libreria sd card.
Mi chiedevo se fosse semplice leggere un file csv, una riga dopo l'altra, come farebbe processing quando invia i file ad arduino, e poi vengono divisi con serial.parseInt.
Gli esempi che ho trovato nella libreria sono su file .txt e non c'era molto "parsing".
Vi ringrazio.

ciao

i metodi base ti consentono di aprire un file e di leggerlo carattere per carattere.
dovresti quindi:

  • leggere il carattere e salvartelo in un char[]
  • se il carattere è ; hai finito di leggere il campo attuale
  • se il carattere è \n (o \r\n se il file è DOS) hai finito di leggere la riga

Ti ringrazio, mi sembra fattibile.
Il metodo parseInt su arduino è applicabile solo alla seriale quindi?

Ciao,
molte librerie per SD ti permettono di aprire un file e leggerlo riga per riga.
Per ogni riga, con la funzione strtok_r ne fai il parse in una sequenza di token, separati da un carattere (nel tuo caso ";")
Quindi riesci ad ottenere in modo veloce i vari valori.

parseInt è un metodo applicabile ad ogni oggetto Stream:

SdFile però non è di tale tipo.

pitusso:
Ciao,
molte librerie per SD ti permettono di aprire un file e leggerlo riga per riga.
Per ogni riga, con la funzione strtok_r ne fai il parse in una sequenza di token, separati da un carattere (nel tuo caso ";")
Quindi riesci ad ottenere in modo veloce i vari valori.

Quindi ci sono altre librerie oltre la SD ufficiale?

Quindi ci sono altre librerie oltre la SD ufficiale?

:stuck_out_tongue_closed_eyes: tu quale stai usando?

Gli esempi che ho guardato sono quelli compresi nell'IDE.

usa la fscanf!

esempio se vuoi leggere un intero, poi un float e poi una stringa

int i;
float f;
char s[10];
scanf("%d %f %s", &i, &f, s);

Dove il numero di caratteri preceduto da "%" è il numero di variabili che ci si aspetta per riga e che vanno a modificare le variaili precedute da "&"?

Più che riga per riga viene letto un byte alla volta, poi quello cha fa la libreria o lo sketch col parsing è un altro discorso.
il buffer che ottieni dalla SD non è diverso da quello della ethernet shield, dalla seriale, da un hard disk, da un cdrom.
tutto dipende dall' archiaviazione dei dati sul supporto, più semplice sarà il criterio di scrittura, più semplice e veloce sarà il recupero.
Ci lavoro da un anno con archivi di dati su SD che tipo di dati vuoi estrarre dal file? o meglio come sono composte queste linee? quanti dati ci sono su una linea prima di incontrare "/n"

ciao

Per esempio un csv con linee composte da quattro variabili.
Per esempio:
1,0,11500,6430
1,0,11550,6435
1,0,20000,6700
Mi servirebbe per una specie di cnc, in modo da archiviare una specie di gcode semplificato, in realtà un csv, sulla sd card invece di riceverlo da processing via seriale.
Per non impiegare ad ogni costo un computer.
Ogni riga conterrebbe delle coordinate e dei comandi, espressi comunque da numeri predefiniti in certi campi.

ok ho capito, tu cosa hai di già fatto per leggere i char del file da SD?
hai qualcosa o ti manca tutto?

inoltre i dati che devi prelevare ti servono sequenziali o ti servono dei salti all'interno del file?
in caso ti servissero dei salti dovresti far numerare le righe dal programma che genera il file

se mi dai queste info ti faccio un esempio veloce già fatto

Dovresti fare una cosa simile se hai già la parte che ti inizializza la sd, controllo errori ecc

void read_sd_config()
{ 
  String ReadLine;
  char charBuf[40];
    
  if(file.open(&root, "file.csv", O_READ))  
  {
   int16_t c;
       while ((c = file.read())>0)
       {
         ReadLine += (char)c;         
           if((char)c=='\n') 
           {                 
                 //--------------------------------------- LETTURA LINEE FILE-------------------------------                           
                 Serial.print(ReadLine);//debug                
                 //-----------------------------------------------------------------------------------------  
                             
                ReadLine.toCharArray(charBuf, 40);                        
                get_data(charBuf);     
                 ReadLine=""; 
           }         
       }
   }

    else 
    {
       Serial << F("File inesistente o errore SD ");            
    }       
}

void get_data(char *charBuffer)
{  
  int temp_dato[]= {0,0,0,0}; //<<<<< (int) se usi numeri oltre il 255 altrimenti byte 
  char *p = charBuffer;  
  byte i = 0;

  while (*p != '\0') 
  {
    if (*p == ',') { ++i;  ++p;  continue;} // separatore usa quello che ti pare
    if (isdigit(*p)) //questo controlla che siano solo numeri, se c'e' una lettera o uno spazio vuoto la salta
    {
         temp_dato[i] *= 10; 
         temp_dato[i] += (*p - '0');            
    }    
    ++p;
  }  
    
//--------------------test di lettura dell'array verifica se e' corretta la scrittura 
  for (byte ii = 0; ii < 4; ii++)
  {
      Serial.println((String)temp_dato[ii]+" ");//debug
  }
//---------------------------------------------------------------------
}

Per leggere l'ultima riga io di solito piazzo un EOF (end of file) alla fine, altrimenti non trova l'ultimo /n
Il char buffer l'ho impostato a 40, se la tua riga supera tale valore aumentalo, ma non esagerare

ciao

secondsky:
Dove il numero di caratteri preceduto da "%" è il numero di variabili che ci si aspetta per riga e che vanno a modificare le variaili precedute da "&"?

il carattere che segue il % vuol dire il tipo di dato che vuoi leggere. %d è un intero con segno, %f un float. sono gli stessi identici che usa il printf: Insomma la spataffiata di codice di pablos in realtà esiste già :slight_smile: http://www.cplusplus.com/reference/cstdio/printf/

vanno a modificare le variaili precedute da "&"?

esatto, la & commerciale usa l'indirizzo della variabile. Notare che l'array non ha bisogno di & in quanto se usato senza [] allora è già un indirizzo

spataffiata di codice di pablos in realtà esiste già

infatti io ho preso un pezzo di una libreria che fa solo quello che a lui serve, il suo csv non è un misto di formati, tu invece gli dai un mattone di libreria fregandotene a metà di quello che fa.

però Lesto tu sei molto bravo a scrivere libri di testo, ma non vedo mai un esempio pratico.
io prima di pubblicare uno sketch, prendo l'alimentatore, lo collego ad arduino, apro l'ide e lo scrivo, compilo piu e piu volte finchè non fa quello che deve fare, quando sono certo che funziona lo pubblico. Nessuno mi dice ... guarda che non va.

prendi una serie di char tipo "1,0,11500,6430" e facci vedere dalla A alla Z come si fa perdi il tuo tempo a scriverlo e compilarlo come facciamo tutti noi, poi spieghi cosa hai fatto, questo è il professore modello

tu invece gli dai un mattone di libreria fregandotene a metà di quello che fa.

infatti non funziona nemmeno, il puntatore a file della fscanf e quelli della SD sono differenti e non compatibili :grin:

per rimediare ecco il codice compilato MA NON TESTATO (niente SD sorry), spero che si autocommenti, sono partito dall'esempio della SD :slight_smile:

/*
SD card read/write

This example shows how to read and write data to and from an SD card file
The circuit:

  • SD card attached to SPI bus as follows:
    ** MOSI - pin 11
    ** MISO - pin 12
    ** CLK - pin 13
    ** CS - pin 4

created Nov 2010
by David A. Mellis
modified 9 Apr 2012
by Tom Igoe
modified 10 Gen 2013
by Lesto

This example code is in the public domain.

*/

#include <SD.h>

File myFile;

void setup()
{
// Open serial communications and wait for port to open:
Serial.begin(9600);
while (!Serial) {
; // wait for serial port to connect. Needed for Leonardo only
}

Serial.print("Initializing SD card...");
// On the Ethernet Shield, CS is pin 4. It's set as an output by default.
// Note that even if it's not used as the CS pin, the hardware SS pin
// (10 on most Arduino boards, 53 on the Mega) must be left as an output
// or the SD library functions will not work.
pinMode(10, OUTPUT);

if (!SD.begin(4)) {
Serial.println("initialization failed!");
return;
}
Serial.println("initialization done.");

// re-open the file for reading:
myFile = SD.open("test.txt");
if (myFile) {
Serial.println("test.txt:");

// read from the file until there's nothing else in it:
int numero = 0;
while (myFile.available()) {//finchè ci sono dati
char tmp = myFile.read(); //leggo una lettera
switch (tmp){
//se è una vigola o uno '\n' interpreto il valore letto come intero, se è \n avviso della fine riga
case '\n':
case ',':
Serial.print("letto un intero:");
Serial.println( numero ); //stampo il numero
numero=0; //lo azzero per leggere il prossimo
if (tmp == '\n'){
Serial.print("riga è completa");
}else{
Serial.print("altri dati nella riga");
}
break;
//in tutti gli altri casi
default:
if (tmp >= '0' && tmp <='9'){ //se è una cifra e non manda il tutto in overflow
numero = (numero*10); //scifta il numero a sinistra in base 10
numero += tmp - '0'; //salva nello spazio vuoto la nuova unità, convertendola da carattere a numero
}else{ //altrimenti c'è un errore!
Serial.print("Errore di interpretazione del file, carattere errato ignorato (in esadecimale):");
Serial.println(tmp, HEX);
}
break;
}
}
// close the file:
myFile.close();
} else {
// if the file didn't open, print an error:
Serial.println("error opening test.txt");
}
}

void loop()
{
// nothing happens after setup
}

pablos, senza offesa ma se segui il link in firma o fai una ricerca sul forum e vedrai che i miei bei esperimenti me li son fatti anche io :slight_smile:
Purtopppo lavoro fino alle 18.30/19.00, e anche se sono attivo sul forum capirai ce non posso tirare fuori l'arduino/arduino IDE e fare esprerimenti
poi quando arrivo a casa non ho certo voglia di scrivere e testare codice per terze parti, salvo che non sia stimolante, ovvero la questione non sia stata affrontata centinaia di volte su questo forum.

I motivi per cui ho dato della "spatafiata" al tuo codice sono 2, e come puoi vedere lamia soluzione non è molto differente dalla tua:

  1. il mio prof di informatica chiamava così i nostri codici, e mi fa piacere ricordarlo;
  2. devi ammettere che fai dei salti moltali.
    leggi un'intera riga in char, la metti nella classe String (per l'allocazione di ram dinamica, ovvero concatenare più facilmente i dati. ma tu sai che 1 intero è di al massimo 6 cifre, ovvero -32768, * 4 perchè sono 4 interi, +3 per le virgole e +1 perlo \n, etvoilà la dimensione massima della riga), poi da String ritorni ad array di char (che non è allocato dinamicamente, quindi rendi inutile iol concetto dell'uso di String!).
    Quì avstri potuto evitare di ritornare ad array di char, tanto anche la string può essere usata con [] ([] - Arduino Reference)
    Bella l'idea di scorrere l'array con un puntatore, ma forse scelta infelice a scopi didattici.
    Avresti potuto felicemente usare atoi() (che inserita nel codice mio di prima porta da 13.994 bytes a 14.154), ma a questo punto nascondevi la parte "interessante" di conversione vera e propria, altro punto dolente di chi fa queste richieste ( e quindi usa poco il tasto search ]:smiley: ).
    la
    Serial.println((String)temp_dato[ii]+" ");//debug
    è tranquillamente
    Serial.println( temp_dato[ii] );//debug

ps. anche tu non hai compilato... Serial << F("File inesistente o errore SD "); non mi risulta sai ]:slight_smile:

Come puoi vedere non c'e' nemmeno il loop e il setup proprio perchè ho chiesto se quelle parti di inizializzazioni sd e compagnia bella già le ha, ho messo solo le 2 funzioni.

L'atoi purtroppo mi ha sempre dato problemi su cifre di 5-6 numeri.
Ti ho detto quelle cose per stimolarti ahahahahah, hai molta conoscienza, conosci una montagna di teoria e al forum me compreso servono più dettagli di quello che dici. Io purtroppo tempo fa ho scelto il ramo elettronica ed elettrotecnica, l' informatica non veniva abbinata a questi corsi nei professionali, quindi non ho conoscenze approfondite, fresche, manualità come le tue, certi trucchi purtroppo mi mancano :frowning:

Grazie per aver postato il programma sarà fonte di ispirazione:)

ciao

alla fine Lesto hai ridotto il tutto a questo

 if (tmp >= '0' && tmp <='9'){ //se è una cifra e non manda il tutto in overflow
              numero = (numero*10); //scifta il numero a sinistra in base 10
              numero += tmp - '0'; //salva nello spazio vuoto la nuova unità, convertendola da carattere a numero

Io ho fatto una cosa un po' diversa perchè a me sembrava che questi numeri lui li volesse non solo leggere ma separare e usare singolarmente con delle variabili, ecco perchè avevo creato un array temporaneo per fare questo.

-prende la riga carattere per carattere sporchi

  • quando trova lo /n lo metto in chararray e lo passo alla funzione dedicata a pulire e dividere
    -divide i 4 numeri rilevando il separatore ","
    -prende un numero alla volta e li archivia in un array
    -li usa come meglio crede prelevandoli dall'array (esempio del serial.print array)
    -torna sul file a leggere una nuova linea

se deve solo visualizzarle sul serial pulendo le righe allora avevo compreso male la richiesta

ciao

alla fine Lesto hai ridotto il tutto a questo

e come puoi vedere lamia soluzione non è molto differente dalla tua

certo, difficile trovare un sistema più semplice e funzionale.

anche se volesse leggere e usare più valori, è inutile memorizzare tutta la riga in formato char, basta salvare il dato in int nelle righe

numero=0; //lo azzero per leggere il prossimo
if (tmp == '\n'){
              Serial.print("riga è completa");
            }else{
              Serial.print("altri dati nella riga");
            }

in

if (tmp == '\n'){
              arrayDati[indice] = numero;
              analizzaArrayDati(arrayDati, indice);
              indice = 0;
              Serial.print("riga è completa");
            }else{
              arrayDati[indice] = numero;
              indice++;
              Serial.print("altri dati nella riga");
            }
numero=0; //lo azzero per leggere il prossimo