Go Down

Topic: [Risolto] Lettura e scrittura di un record, come struttura dati, da e su SD  (Read 641 times) previous topic - next topic

crc57

Salve a tutti, vi sarei grato se mi poteste aiutare a risolvere un problema di lettura da una memoria SD.
Vorrei realizzare un sistema di identificazione RFID e/o impronta digitali memorizzando su una SD alcune informazioni che ho ordinato in una struttura dati.
Code: [Select]


File dbFile;                             // Classe File

struct StruRec {                      // Struttura del record di database
 char       DBTipo;                   // A=amministratore / U=utente / X=revocato
 uint16_t DbImp;                   // ID impronta
 uint32_t DbCard;                  // ID Card RFID
 char       DbNome[20];          // Nominativo utente
}
Record;



Per quanto riguarda la scrittura non ho problemi, scrivo i vari campi con delle semplici dbFile.write(Campo1) ecc. e dopo aver scritto tutti i campi eseguo dbFile.write(LF) come fine record.

Code: [Select]

dbFile.write('U');
dbFile.write(IdImp);
dbFile.write(IdRfid);
dbFile.write(NomUte);
dbFile.write(LF);
dbFile.flush();


Il problema è come popolare, in fase di lettura, la struttura dati?
I records non sono molti quindi anche una lettura sequenziale non dovrebbe creare problemi di prestazioni.
Avrei voluto fare una codifica tipo questa:
Code: [Select]

void LettMicro() {
   dbFile.seek(0);
   while (dbFile.available()) {
      int size = dbFile.readBytesUntil(LF, &Record, 8);

    }
    return;
}

ma naturalmente non funziona.

Grazie a tutti

Standardoil

attacco io, con alcune domande
ma la scrittura funziona bene bene?
intendo dire, hai provato a scrivere, chiudere il file, spegnere, portare la scheda SD sul PC e vedere se ha scritto giusto?
perchè io avrei dei dubbi, primo
e comunque, al di la dei miei dubbi, sei tu che devi essere sicuro che la scrittura vada, secondo
Prima legge di Nelson (che sono io): La risposta giusta si può ottenere solo dalla domanda giusta, domande sbagliate danno risposte inutili

Non bado a studenti, che copino altrove

Hai problema-Ti domando-Non rispondi: Non ti serve più

crc57

#2
May 04, 2019, 06:31 pm Last Edit: May 04, 2019, 07:12 pm by crc57
Di questo progetto sono ancora nella fase di scrittura del codice, di assemblare l'hardware è ancora presto, ma ho appena finito un braccio robotico con 6 servomotori in cui utilizzo

Code: [Select]

 int size = dbFile.readBytesUntil(LF, Record.SerPos, MaxMot);

per la lettura delle posizioni precedentemente memorizzate dove Record.SerPos è un array di 6 byte dove ho memorizzato ciclicamente gli angoli dei 6 servomotori.
In fase di lettura della SD il braccio robotico riesegue le movenze che avevo eseguito con i comandi manuali e memorizzato nella SD.
In quel caso però il record (la struttura) era costituito da un solo array ed ha funzionato perfettamente.

Code: [Select]

struct StruRec { // Struttura del record di database
 byte SerPos[MaxMot];
}
Record;


In questo caso la struttura è costituita da elementi di tipo diverso.

crc57

#3
May 04, 2019, 06:39 pm Last Edit: May 04, 2019, 06:40 pm by crc57
In verità avevo provato anche la libreria di database ma non sono riuscito a far funzionare neppure gli esempi, mi scriveva sulla SD sempre lo stesso record.
Sarebbe stata ottima  ;D

Standardoil

ma allora il tuo problema è la scrittura, non la lettura...
Prima legge di Nelson (che sono io): La risposta giusta si può ottenere solo dalla domanda giusta, domande sbagliate danno risposte inutili

Non bado a studenti, che copino altrove

Hai problema-Ti domando-Non rispondi: Non ti serve più

vlc0617

#5
May 05, 2019, 02:35 am Last Edit: May 05, 2019, 02:36 am by vlc0617
A me sembra che la funzione File.write(data) accetti come argomenty byte, char o char *

https://www.arduino.cc/en/Reference/FileWrite

Quindi

Code: [Select]
dbFile.write(IdImp);

e

Code: [Select]
dbFile.write(IdRfid);

probabilmente non scriveranno correttamente IdImp e IdRfid nel file.

Prova

Code: [Select]
dbFile.write((byte *)&IdImp, sizeof(IdImp));

e

Code: [Select]
dbFile.write((byte *)&IdRfid, sizeof(IdRfid));

Inoltre, con questa istruzione

Code: [Select]
      int size = dbFile.readBytesUntil(LF, &Record, 8);


leggi al massimo 8 byte, ma sizeof(Record) > 8, quindi devi usare

Code: [Select]
      int size = dbFile.readBytesUntil(LF, &Record, sizeof(Record));

Standardoil

Infatti gli avevo ben chiesto se si ritrovava con quello che aveva scritto...
Prima legge di Nelson (che sono io): La risposta giusta si può ottenere solo dalla domanda giusta, domande sbagliate danno risposte inutili

Non bado a studenti, che copino altrove

Hai problema-Ti domando-Non rispondi: Non ti serve più

crc57

Grazie Standardoil per le dritte, faccio un po' di prove e ti faccio sapere.

Federico66

Premesso che il C lo trovo leggermente ostico, ma credo ci sia un po' di confusione, e vi prego di correggermi se sbaglio.
L'OP usa una struttura, ma poi cerca di scrivere su file i dati non strutturati ed inserendo un LF per indicare il fine record, il che naturalmente (IMHO) comporta non poche complicazioni in fase di lettura.
Ho come l'impressione che ci sia confusione anche sulla modalità di scrittura, binaria/testuale.


Al momento non saprei tradurlo in C, ma nel caso di scrittura binaria, ipotizzo qualcosa del genere (probabilmente va effettuata una conversione della variabile data)
Code: [Select]

//Scrittura
StruRec data;
dbFile.write(data, sizeof(data));

//Lettura
dbFile.read(data, sizeof(data));

in questo modo, data contiene l'intero record, data.DBTipo e così via.

Ripeto, probabilmente ho scritto una castronata in C, ma mi interessa capire se la logica è corretta.

In alternativa, si potrebbe scrivere un file di testo con separatore, ma anche il quel caso non è necessario usare un LF, se si una dbFile.println("a;b;c;d")


TIA
Federico
"La logica vi porterà da A a B. L'immaginazione vi porterà dappertutto." A. Einstein

nid69ita

#9
May 06, 2019, 11:17 am Last Edit: May 06, 2019, 11:18 am by nid69ita
@federico, direi di si.
Un file testo ha righe a dimensione diversa, quindi un fine linea fa capire quando finisce una riga.
Un file binario ci scrivi dei byte e quindi, ad esempio, una struct a dimensione fissa. Quindi se lo struct è di 100 byte ogni 100 byte c'e' un record.
Ovviamente se il file contiene dati solo binari non è da usare un separatore tipo nuova riga e inoltre il file non è ben leggibile con un editor. Se contiene stringhe almeno una parte è leggibile, ma le variabili numeriche sono memorizzare come in memoria RAM/SRAM quindi non facili da leggere (un long sono 4 byte quindi spezzato nelle 4 parti).   
Un "database" di solito è binario e non testuale.
my name is IGOR, not AIGOR

torn24

#10
May 06, 2019, 11:57 am Last Edit: May 06, 2019, 11:58 am by torn24
@Federico66 quello che dici il linguaggio C lo permette di fare su file, con la libreria standar e su un computer.

Adesso la classe FILE non fa parte della libreria standar del C, mette a disposizione dei metodi che non permettono di salvare struct su SD.
Per cui quello che hanno fatto o cercano di fare, è perché l'unica cosa consentita dai metodi forniti.
Da quello che ho letto il massimo che si può fare con FILE.write, è scrivere un array di char.
Secondo me non è neanche indispensabile organizzare i dati in struct, visto che poi dovrai leggere e scrivere i singoli campi.

Federico66

@nid69ita: scusa, ma nelle mie premesse ho dimenticato di dire per lavoro progetto e sviluppo sistemi di gestione dati (da quasi 30 anni!) :)
I miei dubbi sono solo sulle modalità di scrittura/lettura dei dati strutturati da parte dell'OP, visto che, non avendo esperienza di C, (senza studiare), non saprei come scrivere (se si puo', ma penso di si) una struct su file binario.

Un file testo ha righe a dimensione diversa, quindi un fine linea fa capire quando finisce una riga.
Certo, ma se i dati li scrivo in formato testo usando println, il fine riga viene già aggiunto, quindi non serve aggiungere un LF, se invece li scrivo con write, allora ho una dimensione fissa per i bytes, quindi quando li leggo, mi serve solo sapere quanti bytes devo leggere per avere un record, e quindi il LF non serve comunque.

In definitiva non capisco l'utilità di queste righe, in quanto penso siano inutili
Code: [Select]

dbFile.write(LF);
int size = dbFile.readBytesUntil(LF, &Record, 8);


Sbaglio?

F

PS
Sono fresco di Arduino, ma l'argomento mi ha incuriosito. Nei giorni scorsi mi è arrivato un modulo SD, quindi studierò :-)
"La logica vi porterà da A a B. L'immaginazione vi porterà dappertutto." A. Einstein

Standardoil

Boh, io sono dell'idea che per un neofita ogni cosa vale per semplificare il lavoro
Quindi voto per scrivere su file di testo, pur se la struttura permetterebbe usare file binari
Dopo scritto di va su un pc per avere conferma
Solo dopo si fa la lettura, che essendo di testo forse è  un pochino più semplice
Magari dopo butto giù  un abbozzo, ma non sarà tanto presto, stasera ho traffico. ..
Prima legge di Nelson (che sono io): La risposta giusta si può ottenere solo dalla domanda giusta, domande sbagliate danno risposte inutili

Non bado a studenti, che copino altrove

Hai problema-Ti domando-Non rispondi: Non ti serve più

nid69ita

#13
May 06, 2019, 02:25 pm Last Edit: May 06, 2019, 02:28 pm by nid69ita
@Federico66 quello che dici il linguaggio C lo permette di fare su file, con la libreria standar e su un computer.
Per cui quello che hanno fatto o cercano di fare, è perché l'unica cosa consentita dai metodi forniti.
Da quello che ho letto il massimo che si può fare con FILE.write, è scrivere un array di char.
Secondo me non è neanche indispensabile organizzare i dati in struct, visto che poi dovrai leggere e scrivere i singoli campi.
Basta usare un cast e la struct viene vista come un array di byte. E' una cosa normale che fai su PC in C ma anche su Arduino lo fai tranquillamente.   
https://www.programiz.com/c-programming/c-file-input-output#example-write
Che il comando a disposizione sia lo standard fwrite o la write della lib, sempre un puntatore a qualcosa devi dargli e quanti byte scrivere
fwrite(&num, sizeof(struct threeNum), 1, fptr);
my name is IGOR, not AIGOR

vlc0617

In realta' il modo usato da crc57 per scrivere il record funziona a 2 condizioni

1) che il compilatore non inserisca nella struttura byte di padding https://en.wikipedia.org/wiki/Data_structure_alignment

2) che i dati della struttura non contengano al loro interno il carattere usato come terminatore (LF)

Mentre la 1) probabilmente e' verificata per un processore a 8 bit, la 2) e' piu' problematica perche' la struttura contiene 2 interi (per un totale di 6 bytes) che possono avere qualunque valore (assumiamo che il campo nome utente non contenga LF).

Se almeno un byte di uno dei due interi e' uguale al codice ASCII del LF, la lettura del record si fermera' nel punto sbagliato.

Tra parentesi, anche la scrittura del campo nome utente potrebbe non funzionare, perche' write(char *) mi pare che non scriva il terminatore di stringa (lo '\0' finale), e quindi in fase di lettura non si sapra' quanti caratteri sono validi dei 20 che costituiscono il campo.

Io personalmente avrei usato questo codice per scrivere il record


Code: [Select]
Record.DBTipo = 'U';
Record.DbImp = IdImp;
Record.DbCard = IdRfid;

if (strlen(NomUte) < 20)
strcpy(Record.DbNome,NomUte);
else
// gestire l'errore di stringa troppo lunga

dbFile.write((byte *)&Record, sizeof(Record));
dbFile.flush();


e per la lettura, invece di readBytesUntil

Code: [Select]
dbFile.read(&Record,sizeof(Record));

Go Up