Non riesco a leggere una stringa da seriale .....RISOLTO!!!!

Non riesco a leggere un stringa dalla seriale come vorrei io..
cioè sto cercando di mettere in un vettore di char la stringa che invio dalla seriale .. per il processamento successivo.

cioè se invio "ABCDEFGHIL" (senza gli apici) e fisso la lunghezza del vettore a 4, vorrei avere questo
stringa(0) ---> A
stringa(1) ---> B
stringa(2) ---> C
stringa(3) ---> D

ho cercato e letto decine di post che riguardano questo problema.
Ho capito che il problema è originato dalla velocità di lettura rispetto alla velocità di ricezione dei dati sulla seriale ..

ho fatto questo script e funziona, fintanto che ci sono le stampe su serial monitor per il debug:

#define dimVettore 5            // lunghezza max del buffer di lettura (stringa seriale)
char stringaSERIALE[dimVettore + 1]; // stringa buffer for contenente le letture estratte dal GPS
boolean fineLettura;                 // controllo sul checksum della stringa Seriale ricevuta

void setup() 
{Serial.begin(4800);}

void loop()
{if (Serial.available()>0){leggiSeriale();}}

void leggiSeriale()
 { 
   unsigned long count;      //interrompe la lettura dalla seriale se passano più di countMax ms senza che arrivino caratteri
   int const countMax = 5;  // limite max millisecondi senza che arrivino caratteri .. oltre chiude il ciclo di lettura (non scendere sotto i 5) 
   
   int index=0;
   do    
     { 
        if(Serial.available()> 0)                
         { 
           stringaSERIALE[index]=Serial.read();  
           Serial.print("\n\t"); Serial.print(stringaSERIALE[index]); Serial.print("  \t"); Serial.print(stringaSERIALE[index], DEC); Serial.print("   \t index:"); Serial.print(index);
           index++; count = millis();                            
            } 
            
      fineLettura = ( (index >= dimVettore) || (millis() - count > countMax));
      }
    while (!fineLettura);   
    Serial.print("\n   dopo do ---------------------\n");
 
  
     Serial.print("\n  VUOTA il BUFFER: eliminati:");
     byte tempByte;
     while (Serial.available()) {tempByte = Serial.read(); Serial.write(tempByte);}
     Serial.print("\n    ---------------------\n");
       
  
  
     Serial.print("\n INIZIO VETTORE MEMORIZZATO");
     Serial.print("\n index:"); Serial.print(index);
     for(int k=0; k<index; k++){
                            Serial.print("\n\t k=")   ; Serial.print(k); 
                            Serial.print("\t   ")     ; Serial.print(stringaSERIALE[k], DEC);
                            Serial.print("\t   ")     ; Serial.print(stringaSERIALE[k]); 
                          }
      Serial.print("\n FINE VETTORE MEMORIZZATO \n\n\n");
}

tutto questo funziona, ma basta che elimino una riga e già non funziona più...

#define dimVettore 5            // lunghezza max del buffer di lettura (stringa seriale)
char stringaSERIALE[dimVettore + 1]; // stringa buffer for contenente le letture estratte dal GPS
boolean fineLettura;                 // controllo sul checksum della stringa Seriale ricevuta

void setup() 
{Serial.begin(4800);}

void loop()
{if (Serial.available()>0){leggiSeriale();}}

void leggiSeriale()
 { 
   unsigned long count;      //interrompe la lettura dalla seriale se passano più di countMax ms senza che arrivino caratteri
   int const countMax = 5;  // limite max millisecondi senza che arrivino caratteri .. oltre chiude il ciclo di lettura (non scendere sotto i 5) 
   
   int index=0;
   do    
     { 
        if(Serial.available()> 0)                
         { 
           stringaSERIALE[index]=Serial.read();  
           //Serial.print("\n\t"); Serial.print(stringaSERIALE[index]); Serial.print("  \t"); Serial.print(stringaSERIALE[index], DEC); Serial.print("   \t index:"); Serial.print(index);
           index++; count = millis();                            
            } 
            
      fineLettura = ( (index >= dimVettore) || (millis() - count > countMax));
      }
    while (!fineLettura);   
    Serial.print("\n   dopo do ---------------------\n");
 
  
     Serial.print("\n  VUOTA il BUFFER: eliminati:");
     byte tempByte;
     while (Serial.available()) {tempByte = Serial.read(); Serial.write(tempByte);}
     Serial.print("\n    ---------------------\n");
       
  
  
     Serial.print("\n INIZIO VETTORE MEMORIZZATO");
     Serial.print("\n index:"); Serial.print(index);
     for(int k=0; k<index; k++){
                            Serial.print("\n\t k=")   ; Serial.print(k); 
                            Serial.print("\t   ")     ; Serial.print(stringaSERIALE[k], DEC);
                            Serial.print("\t   ")     ; Serial.print(stringaSERIALE[k]); 
                          }
      Serial.print("\n FINE VETTORE MEMORIZZATO \n\n\n");
}

... mi sta facendo impazzire.... se già non lo sono!!!! ... che cavolo gli devo fare perchè funzioni sempre ..

Non ho capito assolutamente il problema

leo72:
Non ho capito assolutamente il problema

Se Tu non lo capisci come posso pensare di capirlo io?
Ciao Uwe

uwefed:

leo72:
Non ho capito assolutamente il problema

Se Tu non lo capisci come posso pensare di capirlo io?
Ciao Uwe

????????????????????? questa poi non l'ho capita nemmeno io .... :smiley: :smiley:

penso che il concetto sia che non riesce ad allocare un array di char da seriale, dando in ingresso un vettore più lungo dell'array di char dichiarato.

p.s non fai prima a salvare tutto un vettore e poi salvarti (magari in un altro array) solamente in primi n valori che ti interessano?

Se il problema è salvare e manipolare dati di cui non si conosce la dimensione, può venire in aiuto l'oggetto String che, a differenza degli array di char, è modificabile quindi può aggiungere elementi man mano che arrivano dal canale di input.

Ti sei spiegato malissimo, però se ho capito realmente il tuo problema devi estrarre i dati dalle sentenze NMEA del gps, per fare questo ti serve un parser, se è realmente per il gps esistono librerie per Arduino pronte all'uso.

Chiedo scusa ma ieri è successo un piccolo problema dopo che ho insrito mezzo post.... è mancata l'energia elettrica, poi non c'era collegamento, posi sono dovuto uscire e sono rientrato tardissimo.. quindi in realtà ho postato solo metà della descrizione...

e vi ringrazio anche per esserevi trattenuti negli improperi ....

adesso l'ho corretto ed all'incirca si dovrebbe leggere tutta la descrizione del problema che cerco di riassumere anche qui ..

il problema consiste nel mettere in un array di char (voglio fare così.. non con una stringa) ... i primi n caratteri ricevuti dalla seriale e svuotare i caratteri che eventualmente ancora ci fossero in attesa di essere letti ...

il primo schetc funziona, finche ci sono anche le righe di serial.print che uso per il debug ... nel secondo script, dove cancello una riga di debug .. non funziona più..... ci sono sopra due giorni e non trovo soluzione .. porka miseria!!!!!

... se per esempio, digito la seguente stringa "abcdefghilmno" mi da questo output

dopo do ---------------------

  VUOTA il BUFFER: eliminati:
    ---------------------

 INIZIO VETTORE MEMORIZZATO
 index:5
	 k=0	   97	   a
	 k=1	   98	   b
	 k=2	   99	   c
	 k=3	   100	   d
	 k=4	   101	   e
 FINE VETTORE MEMORIZZATO 



   dopo do ---------------------

  VUOTA il BUFFER: eliminati:mno
    ---------------------

 INIZIO VETTORE MEMORIZZATO
 index:5
	 k=0	   102	   f
	 k=1	   103	   g
	 k=2	   104	   h
	 k=3	   105	   i
	 k=4	   108	   l
 FINE VETTORE MEMORIZZATO

cioè il vettore alla fine contiene "fghil" anzichè i primi 5 caratteri "abcde"

invece di quello corretto che dovrebbe essere questo

	a  	97   	 index:0
	b  	98   	 index:1
	c  	99   	 index:2
	d  	100   	 index:3
	e  	101   	 index:4
   dopo do ---------------------

  VUOTA il BUFFER: eliminati:fghilmno
    ---------------------

 INIZIO VETTORE MEMORIZZATO
 index:5
	 k=0	   97	   a
	 k=1	   98	   b
	 k=2	   99	   c
	 k=3	   100	   d
	 k=4	   101	   e
 FINE VETTORE MEMORIZZATO

con il vettore che, correttamemte, alla fine contiene solo i primi 5 caratteri ....

credo di essere stato maggiormente chiaro ... saluti ciao

A parte che il codice è scritto un po' male.... si mette in genere una istruzione per riga, la "riga" che tu elimini in realtà sono più istruzioni.... poi gli spazi tra la fine di una istruzione ed il ";" di fine riga proprio non si possono vedere :sweat_smile:
Poi c'è un controllo che non capisco.
fineLettura = ( (index >= dimVettore) || (millis() - count > countMax));

Fatto così, che senso ha? Carichi millis subito prima del check. Quella parte lì sarà sempre false finché ricevi caratteri. Sarà sempre vera se non li ricevi perché count non lo inizializzi con millis() prima di fare l'if.

PS:
ricordati che la seriale ha un buffer in ricezione dove vengono memorizzati i caratteri che arrivano e da cui la Serial.read() va a pescare. Se non ci trova nulla può darsi che esca prima del dovuto.

Altra cosa curiosa è che della stringa
abcde-fghil-mno
guarda caso a te perde proprio i primi 5 byte nonostante sembra li abbia ricevuti.

Altra cosa, sempre riferita al controllo per uscire da do..while

do {
  ...
  fineLettura = ( (index >= dimVettore) || (millis() - count > countMax));
} while (!fineLettura);

Ragioniamo.
fineLettura è il risultato di un OR logico. Per uscire dal while fineLettura deve essere true perché tu fai un test **!**fineLettura, che cicla finché fineLettura è false, dato che !false = true. Ma fineLettura sarà vera se anche 1 sola delle 2 condizioni è vera. Per uscire basta che una delle condizioni sia false, così che fineLettura sia anch'essa true e quindi !true = false.
E' corretta questa logica?

Grazie Leo..... allora:

leo72:
A parte che il codice è scritto un po' male.... si mette in genere una istruzione per riga, la "riga" che tu elimini in realtà sono più istruzioni.... poi gli spazi tra la fine di una istruzione ed il ";" di fine riga proprio non si possono vedere :sweat_smile:
Poi c'è un controllo che non capisco.
fineLettura = ( (index >= dimVettore) || (millis() - count > countMax));

Fatto così, che senso ha? Carichi millis subito prima del check. Quella parte lì sarà sempre false finché ricevi caratteri. Sarà sempre vera se non li ricevi perché count non lo inizializzi con millis() prima di fare l'if.

Ok... per lo stile che sicuramente è da rivedere....

il controllo dovrebbe disporre l'uscita dal do-while al verificarsi di una delle due condizioni:

  1. se l'indice supera la dimesione del vettore (questo si verifica quando la stringa ricevuta è più lunga della dimesione del vettore dove conservo i byte (char) ricevuti;
  2. se invece la stringa è piu corta, al raggiungimento dell'ultimo carattere dovrebbe iniziare a contare millis - count (con count che contiene l'ultimo valore di millis di quando ha letto l'ultimo carattere, quindi appena supera il tempo max di attesa significa che non ci sono più caratteri ed esce lo stesso.

ma questa parte mi pare che funzioni bene...

ciò che non funziona è la parte che dovrebbe cancellare il buffer di ricezione della seriale, cioè questa:

     Serial.print("\n  VUOTA il BUFFER: eliminati:");
     byte tempByte;
     while (Serial.available()) {tempByte = Serial.read(); Serial.write(tempByte);}
     Serial.print("\n    ---------------------\n");

e questo, io credo, sia dovuto al fatto che mentre prima la riga che contiene le quattro istruzioni di serial print, introducono un certo ritardo, fanno si che quando l'struzine passa sul serial.available lo trova positivo, perchè nel frattempo è arrivato un altro carattere, quindi entra e cancella il buffer ...

se invece tolgo quei serial.print, passa troppo rapidamente a fare quella valutazione e serial.available è false... quindi non entra e non calcella il buffer... io credo sia quasto ma non trovo la soluzione .... ogni volt che mi sembra di aver trovato qualcosa basta che tolgo qualche serial print e non funziona più....

Se è questo, metti un delay(5) al posto di quei Serial.print e guarda che succede. Oppure rivedi l'uscita con timeout perché secondo me la sua logica non è corretta (hai letto il mio ultimo post?).

si Leo... se metto un delay al posto dei serial print .. funziona di nuovo tutto. Ma non mi piace la soluzione. Voglio trovare una soluziione che vada bene sempre e che non rallenti il codice più di quanto non sa srettaene necessario.

l'uscita con timeout la vedo corretta perchè:

  1. se invece la stringa è piu corta, al raggiungimento dell'ultimo carattere dovrebbe iniziare a contare millis - count (con count che contiene l'ultimo valore di millis di quando ha letto l'ultimo carattere, quindi appena supera il tempo max di attesa significa che non ci sono più caratteri ed esce lo stesso.

il count viene inizializzato con millis al momento della letttura dell'ultimo carattere .... quindi esce se
(millis() - count > countMax) ... che diventa vero dopo che sono passati countMax mS ...
mi pare che questa logica sia correttà...

sto pennsando di controllare la cancellazione del buffer con un altro controllo anzichè con seruial.available... perchè e lì che cè il punto critico...

ciao

Se col delay funziona, allora significa che c'è qualcosa nell'implementazione dell'uscita con timeout. La logica dovrebbe essere come dici tu, ma lo sviluppo della funzione non la ricalca. Almeno penso.

credo di aver trovato la soluzione:

il carattere lo leggo così:

do    
     { 
        if(Serial.available()> 0)                
          { 
           stringaSERIALE[index]=Serial.read();  
           index++; count = millis();                            
            } 
      fineLettura = ( (index >= dimVettore) || (millis() - count > countMax));
     }
 while (!fineLettura);

quindi c'è un ciclo do - while controllato dalla variabile booleana "fineLettura". all'interno, però, c'è un IF nidificato che fa si che si possa leggere il carattere solo se serial.vailable è true ... se è falso ì non viene letto nulla, ma non esce comunque dal do-while .. attende, infatti, che serial.vailable sia di nuovo vero , se nel frattempo è arrivato un altro carattere, oppure aspetta il timeout (perchè non sono arrivati altri caratteri... Tutto questo funziona bene....

devo semplicemente riproporre lo stesso meccanismo per cancellare il buffer della seriale, quando è uscito dal primo do-while.. e tutto funzionerà (.. almeno credo)...
insomma questo è sbagliato:

  while (Serial.available()) {tempByte = Serial.read();}

devo riproporre lo stesso schema, con do-while controllato con una condizione logica, e timeout per accertarsi che non ci sia più nulla in coda al buffer (come faccio sopra per leggere il carattere) .. altrimenti, se non faccio così, se l'esecuzione passa velocemente a queste istruzioni (come quando cancello i serial.print di debug), trova serial.available ancora false, perchè non è ancora giunto il byte (specie se la connesisone seriale è impostata a bassa velocità) e mi salta la cancellazione del buffer, quindi al ciclo successivo mi mette nel mio vettore gli ultimi valori ricevuti anzichè i primi ...
Credo sia questa la soluzione .... stasera riprovo con questa idea ...
grazie ciao

ok... adesso funziona bene .... così:

#define dimVettore 4            // lunghezza max del buffer di lettura (stringa seriale)
char stringaSERIALE[dimVettore + 1]; // stringa buffer for contenente le letture estratte dal GPS

void setup() 
{
Serial.begin(4800);
}

void loop()
{
if (Serial.available()>0){leggiSeriale();}
}


void leggiSeriale()
 { 
   unsigned long count = millis();      //interrompe la lettura dalla seriale se passano più di countMax ms senza che arrivino caratteri
   unsigned long const countMax = 5;    // limite max millisecondi senza che arrivino caratteri .. oltre chiude il ciclo di lettura (non scendere sotto i 5) 
   boolean fineLettura;                 
   
   int index=0;
   // LEGGE i primi dimVettore caratteri ........
   do    
     { 
        if(Serial.available()> 0)                
         { 
           stringaSERIALE[index]=Serial.read(); count = millis();  
           index++;                           
         } 
         fineLettura = ( (index >= dimVettore) || (millis() - count > countMax));
      }
    while (!fineLettura);   

     fineLettura = false;

    byte tempByte;
   // CANCELLA il buffer, se c'è ancora qualcosa nel buffer .....
   do
     {
       if(Serial.available()> 0)                
       { 
         tempByte = Serial.read(); count = millis();                         
       } 
       fineLettura = (millis() - count > countMax);
     }
   while (!fineLettura);
  
     Serial.print("\n INIZIO VETTORE MEMORIZZATO");
     Serial.print("\n index:"); Serial.print(index);
     for(int k=0; k<index; k++){
                            Serial.print("\n\t k=")   ; Serial.print(k); 
                            Serial.print("\t   ")     ; Serial.print(stringaSERIALE[k], DEC);
                            Serial.print("\t   ")     ; Serial.print(stringaSERIALE[k]); 
                          }
      Serial.print("\n FINE VETTORE MEMORIZZATO \n\n\n");
}

adesso posso passare al post-proccesso della stringa....
grazie ciao