Go Down

Topic: [Risolto] Gestire errore di scrittura su micro SD. (Read 493 times) previous topic - next topic

fabpolli

il bacherozzo non riesco a capirlo, mi sembra di passare tutto su array "riempiti" tramite sprintf (uso di oggetti stringa?? perdona la mia ignoranza)
Standardoil si riferisce al tipo della funzione dati
Code: [Select]

String dati() {

Che appunto usa la classe String.
Per farti tornare il valore su un array di char hai svariate possibilità, ad esempio passare l'array come parametro per riferimento, far tornare il suo puntatore dalla funzione ecc.. La cosa più semplice è definirlo globale come hai fatto per gli altri e trasformare la funzione in modo che sia di tipo void

fabpolli

#16
Jul 12, 2018, 12:40 pm Last Edit: Jul 12, 2018, 12:40 pm by fabpolli
grazie a tutti

per quanto riguarda il reset automaizzato andrò in cerca di informazioni al riguardo anche se la soluzione di docdoc non mi dispiaceva affatto.
Quella soluzione non funziona
Una soluzione percorribile (anche se è bene risolvere il problema e non aggiraglo così come ti ho già indicato io e @gbp01) può essere questa
Code: [Select]

#include <avr/wdt.h>

nel setup per sicurezza
Code: [Select]

wdt_disable();

Quando vuoi resettare usi
Code: [Select]

wdt_enable(WDTO_8S);
for(;;){}

Standardoil

Tu (lo OP) vuoi leggere i dati solo al minuto 0 e al minuto30 di ogni ora?
Stasera arrivò a casa tardi, e dal furbofono è dura scrivere
Però  appena posso ti do due dritte, oltre a quella di fabpolli, che è  ottima
Si tratta di capire se è  il nome file o la SD a dar problemi
Prima legge di Nelson (che sono io):
A parità di risultato maggiore è il pensiero, minore il lavoro.
Quindi prima di fare pensa!

docdoc

purtroppo come docdoc ipotizzava il progetto vive lontano dal pc, quindi quando il led è acceso vuol dire che si è piantata la scrittura e quindi devo premere il reset di arduino
Come ti hanno fatto notare, la classe String va evitata come la morte. Tanto più se come in questo caso ne fai un valore di ritorno di una funzione che viene richiamata continuamente nel loop. Ed io credo/temo che il blocco non sia della scrittura su SD ma proprio dell'intero programma...

Usa una variabile globale (fuori da setup() e loop() per intenderci) di tipo char* e appoggia lì la stringa da scrivere usando una funzione , es. "leggiDati()". Non è particolarmente "elegante" ma funziona, ed in queste piccole MCU risparmiare memoria e cicli è sempre utile.. ;)

D'altronde già hai una "buf[60]" per cui potresti per esempio fare semplicemente questo, portando buf[] come globale:
Code: [Select]
char buf[80];
...
    if (dataFile) {                                      // se tutto a posto
      leggiDati();
      dataFile.println(buf);                        // aggiungo al file i dati richiamati dalla funzione dati()
      dataFile.close();                            // chiudo il file
      Serial.println(buf);                      // scrivo su seriale i dati aggiunti
      scritto=true;                                //porto a vero la condizione scritto
...
void scriviDati() {
  myDHT22.readData();         //aggiorno dati temperatura e umidità
  DateTime now = rtc.now();  // aggiorno orologio

  int x = map(analogRead(luce),0,1024,100,0);  //aggiorno e scalo valore luminosità

  sprintf(buf,"%02d/%02d/%02d; %02d:%02d ; %d; %hi.%01hi; %i.%01i",now.year(),now.month(),now.day(),
  now.hour(),now.minute(),x,myDHT22.getTemperatureCInt()/10, abs(myDHT22.getTemperatureCInt()%10),
  myDHT22.getHumidityInt()/10, myDHT22.getHumidityInt()%10);           // formatto i dati da scrivere e li inserisco in buf
}


(anche se è bene risolvere il problema e non aggiraglo così come ti ho già indicato io e @gbp01)
Beh, io ho dato una soluzione "al volo" ma ho anche scritto: "seppure siano comunque da investigare le cause ;)

Alex "docdoc" - ** se ti sono stato d'aiuto, un punto karma sarà gradito, clicca su "add" qui a sinistra, vicino al mio nome ;) **

Standardoil

#19
Jul 12, 2018, 01:10 pm Last Edit: Jul 12, 2018, 01:12 pm by Standardoil
Non credo che il programma si impianti,  almeno non li, all'apertura  del file fallisce, ma poi, nel ramo else, accende il led, quindi almeno alla else ci arriva.
Quindi io voto per un problema sulla SD, che sia hw, oppure sw o magari solo sbagliato il filename
Sarebbe magari utile far "lampeggiare" il led, che ci indicherebbe programma ancora in servizio
Oppure trovare la maniera di vedere il nome file, collegando il pc ad una softserial, per non resettare arduino
Ancora, spegnere il led se un tentativo successivo va a buon fine
Comunque certamente cambiare  sd con una formattata di fresco
E aggiungo: provare a fare scritture frequenti,adessocon una scrittura ogni 30 minuti è  dura aspettare l'errore
Prima legge di Nelson (che sono io):
A parità di risultato maggiore è il pensiero, minore il lavoro.
Quindi prima di fare pensa!

Silente

C'é una vosa che non ho capito:
Anche se il progetto non prevede computer non funziona come si vorrebbe. Quinfi cosa impedisce di fare prove in un circuito simile attaccato al pc, fare li tutto il debug che si vuole, e poi a codice completo e funzionante testare il circuito vero e proprio?
In questo modo si vede anche se il probkema é hw o sw (se rimane é hw)

docdoc

#21
Jul 12, 2018, 02:01 pm Last Edit: Jul 12, 2018, 02:02 pm by docdoc
Non credo che il programma si impianti,  almeno non li, all'apertura  del file fallisce, ma poi, nel ramo else, accende il led, quindi almeno alla else ci arriva.
Non è detto, perché se è la chiamata a dati() che restituisce la famigerata String, è possibile che non riesca più a chiamare la funzione perché il processore si è "impantanato" con la gestione della memoria per le tante chiamate a oggetti String che poi dovrebbe buttare essendo il valore di ritorno della funzione (e dato che non c'è un GC, ad ogni chiamata temo vada ad allocare quei 60 byte ad invocazione...).

Non escludo che possa essere un problema della SD o del circuito di gestione, però questa mi pare la più probabile.

cosa impedisce di fare prove in un circuito simile attaccato al pc, fare li tutto il debug che si vuole, e poi a codice completo e funzionante testare il circuito vero e proprio?
Me lo chiedevo anche io (e l'ho chiesto all'OP) ma nel listato vedo che legge e scrive dalla seriale per cui non penso possa farlo, a meno di non modificare almeno quella parte.

Alex "docdoc" - ** se ti sono stato d'aiuto, un punto karma sarà gradito, clicca su "add" qui a sinistra, vicino al mio nome ;) **

r69ts

la scheda in uso è una arduino nano

le scritture vengono fatte ogni 30 minuti quindi anche le letture vengono fatte ogni 30 minuti e mi sembra di aver letto da qualche parte che "tutto" quello che sta all'interno delle funzione in qusto caso dati(), una volta usciti dalla funzione la memoria viene liberata (in materia di risorse occupate)
quindi secondo me (da profano e ignorante) credevo fosse + "economico" usare le funzioni e chiamarle solo al momento del bisogno piuttosto che lavorarle ad ogni ciclo.

ora mi sono accorto della chiamata quasi simultanea della funazione dati() la prima per la scrittura del dato e la seconda 2 righe sotto che mi serviva per il debug

comunque tanto per fare il punto della situazione provvederò a inserire delle sequenze di lampeggio al led (per ora non sono capace di lavorare in eeprom) in modo tale da avere diversi lampeggi a seconda di dove il programma si ferma (in inizializzazione, apertura, scrittura o chiusura file)

solo adesso rileggendo il programma mi sono reso conto che riscrivere la variabile nomefile ad ogni ciclo sia una stupidaggine grande come un palazzo, quindi l'ho spostata all'interno del if per scrivere nel file

da così

Code: [Select]

void loop () {

  DateTime now = rtc.now();    //aggiorno orologio

  sprintf(nomefile,"%02d%02d.txt",now.year(),now.month());   // creo nome file da anno e mese forniti da orologio
 
  if (inizio==false && now.minute() <30 ){       // aggiorno periodo a qualsiasi ora metto in funzione il programma
    periodo=30;
.........



a così

Code: [Select]

  void loop () {

  DateTime now = rtc.now();    //aggiorno orologio

 .......

  if (scritto==false && now.minute() == periodo ){      //verifico condizione per scrivere su file
    sprintf(nomefile,"%02d%02d.txt",now.year(),now.month());   // creo nome file da anno e mese forniti da orologio
    File dataFile = SD.open(nomefile , FILE_WRITE);    // apro in scrittura il file nome file creato con sprintf
    // if the file is available, write to it:              // se non esiste lo crea in automatico
.....

fabpolli

#23
Jul 12, 2018, 02:16 pm Last Edit: Jul 12, 2018, 02:16 pm by fabpolli
Usare le funzioni è corretto, così come usare gli array di char che come dici tu all'uscita della funzione il loro "spazio" viene liberato dalla memoria.
Invece la classe String del c++ che usi come tipo della funzione dati su un microcontrollore può generare svariati problemi e risultati poco prevedibili in quanto, contrariamente a ciò che avviene ad esempio su PC, non esisste un garbage collecor che va a "pulire" la memoria da oggetti non più utilizzati portanto a volte a blocchi e/o risultati non facilmente debuggabili.
Quindi segui il consiglio che ti abbiamo fornito ovvero trasforma la funzione dati da String a void, porta la definizione del vettore buf e poi richiami la funzione e usi direttamente buf, ovvero quello che ti ha suggerito pari pari @docdoc.
Il fatto di ricalcolare il nome del file ad ogni ciclo di loop effettivamente era uno spreco di risorse ma non è quello a farti piantare il tutto, comunque ben vengano le ottimizzazioni quindi potresti anche pensare di farlo una volta al mese! con una cosa del genere:
Code: [Select]

byte mesePrecedente = 13;
...
void loop()
{
 ..
  if (scritto==false && now.minute() == periodo ){      //verifico condizione per scrivere su file
    if(mesePrecedente != now.month())
    {
       sprintf(nomefile,"%02d%02d.txt",now.year(),now.month());
       mesePrecedente = now.month();
    }
}

docdoc

"tutto" quello che sta all'interno delle funzione in qusto caso dati(), una volta usciti dalla funzione la memoria viene liberata (in materia di risorse occupate)
Si ma creare e distruggere oggetti, soprattutto gli String, sono cose "pesanti" come gestione perché rischiano una frammentazione della RAM, per cui avrai magari metà RAM ancora disponibile ma nessun segmento da almeno 60 byte contigui, ma che, per le altre variabili "statiche", continuerà a funzionare. Arduino non ha un GC (Garbage Collector) "intelligente" come altri linguaggi di alto livello.

Quote
comunque tanto per fare il punto della situazione provvederò a inserire delle sequenze di lampeggio al led
No, come PRIMA cosa elimina quella funzione che ritorna String e sostituiscila come ti ho suggerito, questo ti permette di escludere la gestione della RAM. Se in quel modo funziona, era questo il problema.

Quote
solo adesso rileggendo il programma mi sono reso conto che riscrivere la variabile nomefile ad ogni ciclo sia una stupidaggine grande come un palazzo, quindi l'ho spostata all'interno del if per scrivere nel file
Questa cosa invece non ha grossi problemi perché non è una "String", ma un array statico di 15 byte che vai a riscrivere. D'accordo che riscriverlo ad ogni loop è assolutamente inutile quindi hai fatto bene, però non può causare problemi.
Alex "docdoc" - ** se ti sono stato d'aiuto, un punto karma sarà gradito, clicca su "add" qui a sinistra, vicino al mio nome ;) **

fabpolli

Visto che stiamo parlando di ottimizzare il codice cambia tutti i tipi della variabili int in byte per tutti quei valori che non possono assumere valori negativi e non maggiori di 255, risparmi preziona memoria della MCU, magari qui non ti servirà granché ma è proedeutico ad un uso corretto dei tipi e per cosa più "pesanti" può far la differenza

Standardoil

Torno a dire. Il programma secondo me non si impianta, altrimenti non accenderebbe il led.
Se accende il led ha eseguito il ramo else
Quindi non ha aperto il file
E questo lo può fare solo per:
SD non inizializzata
Filesystem compromesso
Filename inadeguato
SD guasta
Sono sicuro che ricadiamo in uno di questi 4 casi
Prima legge di Nelson (che sono io):
A parità di risultato maggiore è il pensiero, minore il lavoro.
Quindi prima di fare pensa!

Standardoil

E aggiungo:
A file non aperto non chiama la funzione dati()
quindi la dati(), per usando la classe string, non essendo stata chiamata certamente non ha provocato l'anomalia
Prima legge di Nelson (che sono io):
A parità di risultato maggiore è il pensiero, minore il lavoro.
Quindi prima di fare pensa!

docdoc

Torno a dire. Il programma secondo me non si impianta, altrimenti non accenderebbe il led.
Tu ragioni troppo da windowsiano... :)

Non dico che sia per forza come affermo, però non è che il programma o gira o si ferma del tutto, qui parliamo di piccoli processori e pezzettini di RAM, quale parte di "potrebbe funzionare una parte ma non funzionare la chiamata a dati()" non era chiara? :D

Per cui alla saturazione della RAM il sistema può iniziare a "fare cose strane" ossia non previste, fino anche a piantarsi del tutto, ma io non sono proprio certo che la causa del malfunzionamento sia proprio la scrittura sulla SD e non altro.

Per questo, ripeto, intanto leviamo quella robaccia-bacherozzo della classe String che neanche serve a nulla, evitiamo il valore di ritorno così "strano" e vediamo che succede.

Certamente però continuo anche io a non capire come mai non sia possibile lasciare Arduino collegato ad un PC per un paio di giorni e loggare quello che scrive sulla seriale... Questo sicuramente darebbe indizi molto più precisi ed interessanti sulle possibili cause.
Alex "docdoc" - ** se ti sono stato d'aiuto, un punto karma sarà gradito, clicca su "add" qui a sinistra, vicino al mio nome ;) **

Standardoil

Il mio pinguino sì è appena offeso, ti tocca di pagargli un ghiacciolo :)
comunque c'è del vero in quello che dici
Prima legge di Nelson (che sono io):
A parità di risultato maggiore è il pensiero, minore il lavoro.
Quindi prima di fare pensa!

Go Up