Lettura tabella da SD card

Buongiorno e buon anno a tutti e tutte,
Ho un dubbio: si può leggere un file excel da arduino se questo è caricato su di una schedina SD? Poiché avrei bisogno di leggere dei dati come se stessi leggendo un array di arduino (però i valori sono troppi e devo per forza immagazzinarli in una memoria esterna), in modo da leggere i valori per esempio fornendo ad arduino le coordinate della cella excel da leggere.
I valori che devo leggere sono del genere dei seguenti:

0 4 0 -4 0 3 0
0 0 7 0 -3 0 0
0 0 4 0 -3 0 0
-7 14 0 -7 0 0 0
4 -4 0 0 0 0 0
0 0 7 4 0 0 0

Però al momento ne ho altre 120 righe che devo dare in pasto ad arduino. Avevo pensato di inserirli in un file .txt e leggerli con la funzione seek() ma anche solo la presenza del segno - ne complica la procedura e infine la presenza di valori a doppia cifra (p.es. 14) me lo rende quasi impossibile. C'è quindi un modo per leggere questa serie di dati? Magari esiste un tipo di file che li contenga già come numeri e non caratteri, oppure un array leggibili sulla SD sempre in un file .ino. Non so minimamente come procedere nonostante abbia già fatto parecchie ricerche online.
Grazie del vostro aiuto in anticipo

Un file excel XLS non lo puoi leggere ha un formato proprietario di Microsoft.
Però una tabella excel si può salvare in modalità CSV,
che poi in realtà non è altro che un file testo, con la prima riga i titoli e poi i dati riga per riga,
e le colonne (ovvero ogni dato/cella) è separato di norma da ;
A volte i dati sono racchiusi tra doppi apici.
Prova a prendere il file excel e salvarlo come CSV, puoi riaprire il file con excel oppure con un editor tipo notepad e vedi come è fatto.
Esempio dei tuoi dati, ho anche messo nome colonne:

col1;col2;col3;col4;col5;col6;col7
0;4;0;-4;0;3;0
0;0;7;0;-3;0;0
0;0;4;0;-3;0;0
-7;14;0;-7;0;0;0
4;-4;0;0;0;0;0
0;0;7;4;0;0;0

Ah, grazie mille. Non ne sapevo l'esistenza.
Per leggere un dato come posso fare? Per esempio se devo leggere il dato nella colonna 2 riga 4? Pensavo di utilizzare la funzione seek(); ma quella ti sposta per carattere come se stessi leggendo il file da ciò che ho letto. Il problema di base è che non c'è un criterio per la quale si può definire la posizione di un dato per la funzione seek();. Pensavo per esempio un
mfFile.seek((nRighecaratteriPerRiga)+2colonna);
In questo modo se ogni valore fosse composto da una sola cifra allora sarebbe leggibile ma anche solo il segno lo rende inutilizzabile.
Grazie ancora

Come suggerito da @nid69ita esporta il file excel in csv e poi usa il carattere ';' come separatore di colonne ed il carattere di fine riga '\n' come separatore per le righe.

Il tuo algoritmo dovrà per forza scorrere e contare ognuno di questi caratteri per individuare la cella necessaria.

Se la velocità è un fattore determinante, allora bisogna provare a salvare una tabella con dimensioni fisse per ogni cella. In tal caso sarebbe possibile usare seek()

Grazie mille,
Diciamo che non mi serve una alta velocità ma preferisco imparare ad utilizzare il metodo seek();
La prima domanda che mi viene in mente è come formattare il file in modo da avere ogni cella con dimensioni fisse. Ho esportato la tabella in .csv e ottengo un file che se aperto con blocco note mi restituisce questi valori:

0;4;0;-4;0;3;0
0;0;7;0;-3;0;0
0;0;4;0;-3;0;0
-7;14;0;-7;0;0;0

Sono corretti però si può gia notare che non sono contenuti in cellule a dimensione fissa. Provo a cercare un po' su internet.
Invece per leggere pensavo di utilizzare uno script fatto in questo modo:

int ricercaValore(byte a, byte b){
  document.seek(valoriPerCella*((a*cellePerRiga)+b);
  return document.read();
}

Ora necessito di un modo per formattare a celle di uguali dimensioni partendo da un file excel

Devi guardare nelle impostazioni della finestra di esportazione di excel.
Io uso LibreOffice invece di Excel e nlla finestra ho questa opzione


che produce un csv così

          0          4          0         -4          0          3          0
          0          0          7          0         -3          0          0
          0          0          4          0         -3          0          0
         -7         14          0         -7          0          0          0

ciao fratt e tutti,
nella tabella che hai postato non vedo il ';' come separatore e vedo ben incollonati i valori...non è che sostituisce il ';' con il '\t' ?

magari sbaglio ma lui ha sempre 7 posizioni delimitate dal ';' e quindi la tabella è ben definita...poi un numero può avere una, due, dieci cifre e con segno...ma sempre divise dal ';'...quindi per sapere che valore c'è alla riga 2 posizione 3 dovrò contare un "a capo" e leggere cosa c'è tra la seconda e la terza ';'...oppure dopo un "a capo" passare l'intera stringa in un charArray giustamente lungo...se i valori vanno da -999 a 999 basta un array di (("valore cella" + separatorre)*posizion)+"a capo"+terminatore...((4+1)*7)+2+1 = 38... quindi "verificare" i vari token della stringa...

ciao @andreaber, il csv che ho postato io è incolonnato utiizzando degli spazi (lo ha fatto in automatico LibreOffice).
per il resto... è una questione di approccio, c'è chi si trova meglio col separatore e chi con la lughezza fissa...
io userei un approccio ancora diverso, ma @jamoncerrano chiedeva un modo per poter utilizzare la seek().

...ahhh...ok chiaro.

si ok...

Grazie a tutti,
In risposta a @fratt: ho provato anche io ad esportarlo e ora riesco ad esportarlo come te. Mi blocco quando devo cercare i valori e cercarli. Inoltre non saprei come dare il segno al valore e nel caso ci fossero più cifre... Però con un po' di ingegno ci posso arrivare.
In risposta a @andreaber: ragionamento molto interessante. Pensando a come realizzarlo dovrei immagazzinare tutto il file di testo in un array molto più grande e con un ciclo for() trovare i '\r' e poi contare? Oppure c'è una funzione per gli array o addirittura per la SD che permette ciò in modo più semplice?

La funzione seek() la volevo utilizzare perché non mi venivano in mente altri modi per procedere, ma ora ho trovato un'altra cosa altrettanto interessante:
a questo link immagazzina in un file .json sulla SD dei parametri e poi li immagazzina e legge con arduino. Non so assolutamente niente dei file .json e di questa libreria, quindi per me è una cosa campata mooolto per aria :sweat_smile:
Vi chiedo come sia meglio procedere e quale sia più semplice come metodo.
Grazie ancora

Con la seek ti posizioni sul primo carattere del "blocco" che ti interessa.
Copi l'intero blocco in un array di char di lunghezza adeguata, scartando gli spazi ed eventuali caratteri non numerici.
Poi converti in numero con le apposite funzioni del C.

Il json per certi versi ti semplifica le cose, ma non credo si possa esportare un json da excel...
Poi c'è anche la possibilità che la libreria carichi in memoria tutto il file, quindi devi vedere quanta memoria ti porta via...
Su questo però non sono ferratissimo, quindi melio se aspetti altri pareri.

Grazie mille!
Adesso mi cimento un po' e provo a ricavare il dato con il metodo che mi hai fornito!
Vi tengo aggiornati :slight_smile:

Allora...
Ho scritto un breve codice per inserire la riga in un array. Il codice è il seguente:

#include <SD.h>
File document;

const int chipSelect = 10;
byte cols;
byte rows;
byte n;
char riga[100];
bool read;

void setup() {
  Serial.begin(9600);
  if(!SD.begin(chipSelect)){
    Serial.println("errore 1");
  }
  rows = 3;

}

void loop() {
  document = SD.open("font.txt", FILE_WRITE);
  document.seek(0);
  if(document){
    n = 0;
    while(n != rows - 1){
      char c = document.read();
      if(c == '\n'){
        n++;
      }
      Serial.println("ricavo posizione riga");
      Serial.println(c);
    }
    Serial.println("riga ricavata");
    read = 1;
    int index = 0;
    while(read == 1){
      char c = document.read();
      if(c != '\n'){
        riga[index] = c;
        index++;
      }else{
        read == 0;
      }
      Serial.println("lettura riga");
    }
  }
  Serial.println(riga);
}

Inizializza la scheda SD e crea le variabili, successivamente apre il file font.txt e se aperto ci lavora su. Inizia ponendo il "cursore" all'inizio e la variabile n=0 che consiste nel numero di volte che incontra il carattere di fine riga nella lettura. Successivamente inizia un ciclo while() che si ripete finché non si giunge alla riga giusta (n = rigaVoluta-1) poiché si cerca di leggere gli a capo che sono il numero di righe meno 1. Un if() permette l'uscita dal ciclo. Imposto la variabile read = 1 per permettere il secondo ciclo while() e la sua uscita. In questo ciclo leggo i valori e li inserisco in un array (di dimensione casuale per ora) fino al ripresentarsi di un nuovo carattere di fine riga.
Sfortunatamente si blocca nel "ricavo posizione riga"... Non riesco a capire come mai...
Il file che ho caricato è il seguente:
FONT.TXT (53 Byte)
Devo ammettere che non mi piace lavorare con i cicli while();. Ora provo a rifarlo magari con uno switch-case..

Hai provato quello sketch?
Ho il sospetto che non funzioni come ti aspetti...

Esatto... Non capisco perché... :frowning:
Eppure logicamente dovrebbe andare :thinking:
Dove ho sbagliato?
Ho provato con questo sketch che dovrebbe essere simile ma niente:

#include <SD.h>
File document;

int chipSelect = 10;
byte cols;
byte rows;
byte n;
char riga[100];
byte fase = 0;
byte index;
char c;

void setup() {
  Serial.begin(9600);
  if(!SD.begin(chipSelect)){
    Serial.println("errore 1");
    while(1);
  }else{
    Serial.println("connesso");
  }
  rows = 3;
  n = 0;
}

void loop() {
  document = SD.open("font.txt", FILE_WRITE);
  if(document){
    switch(fase){
      case 0:
        c = document.read();
        if(c == '\n'){
          n++;
        }
        Serial.println("ricavo posizione riga");
        Serial.println(n);
        if(n == rows-1){
          fase++;
          index = 0;
        }
      break;
      case 1:
        c = document.read();
        if(c != '\n'){
          riga[index] = c;
          index++;
        }
        Serial.println("lettura riga");
      break;
    }
    //Serial.print(document.read());
    document.close();
    Serial.println(riga);
  }
}

Il primo errore è qui...

Però hai cambiato il formato del file (a larghezza fissa) per usare la seek e poi fai tutto sto giro con cicli e controcicli?
Tra l'altro la versione senza while mi pare più contorta della precedente...

1 Like

ho fatto due prove...ho creato un file ".ods" con LibreOfice di 10 colonne (salti di 100 tra colonna) per 1099 righe (salti di 1 tra righe)...insomma circa 11.000 celle con valori tra -999 a 999...poi ho salvato la tabella in 2 csv...uno con le ';' di separazione e l'altro con lo spazio fisso tra le colonne (come da esempio di fratt).
le dimensioni dei due csv sono 48K quello con ';' e 98K quello con larghezza fissa.
ho fatto due prove...uno per entrambi i file dove conto i fine riga ed alla riga che mi interessa leggo la riga stessa...risultato per la riga 1000...1885 ms primo file e 3998 ms il secondo file; ho fatto test con la seek() (solo per il secondo file) sempre alla riga 1000 con tempo di 160 ms.
non c'è dubbio che la seek() è nettamente più perfomante ma richiede, molto probabilmente, un file più grande in dimensioni.
spero di essere stato chiaro.

PS: dimenticavo...ho usato una UNO R3 e ho fatto qualche stampa qua e la a 9600 per verifica.

2 Likes

Ah, quindi il valore fisso rimane per ogni cella?
Mi spiego meglio, in un file .csv tutte le celle hanno dimensione uguale. Ciò che non mi va giù è che quando apri il file come .txt non risulta tutto allineato, alcune righe sono più lunghe di altre e io immagino che siano anche di dimensioni diverse. Se però hanno tutte la stessa dimensione indipendentemente dal file aperto come .txt allora è tutto più semplice. Se ho capito bene ogni cella del file che hai salvato te è di 10 caratteri?

Si, diciamo che il mio cervello era un po' cotto a quell'ora. Adesso ci riprovo con più attenzione :rofl:

LibreOffice ha generato lui, io non ho selezionato nulla, 9 caratteri per cella + i due caratteri di NL e CR...quindi per me con 10 colonne leggo l'intera riga con un array di 91 char (non considerando i deu fine riga).
quindi con la seek() fai:

quanteColonne =10;
caratteriColonna = 9;
rigaDaLeggere = 1000;
bytesSeek  = ((quanteColonne*caratteriColonna)+2)*(rigaDaLeggere-1);
myFila.seek(bytesSeek);

da qui è facile estrarre il numero della colonna che ti interessa usando la strtok()