Lettura da Seriale limitata a 128 byte?

Ciao a tutti, sto usando un Arduino Mega per comandare un lettore rfid e leggere l'id dei tag presenti nelle vicinanze dell'antenna. La comunicazione tra l'Arduino e il lettore avviene tramite la funzione Seriale3.read()

Il problema sorge quando dalla seriale devo leggere una risposta superiore a 128 byte. Infatti i primi 128 byte della risposta riesco a leggerli correttamente, tutti gli altri è come se andassero persi...

Leggendo la documentazione mi sembra di capire che il buffer in ingresso può contenere 128 byte. E' possibile leggere una risposta di dimensioni maggiore?

Per la lettura utilizzo questo:

while (Serial3.available() > 0) {

Serial.print(Serial3.read());

}

il biffer credo proprio sia hardware. Ci sono 2 soluzioni: 1. leggere i dati più velocemente che il buffer si riempia, ciò vuol dire un loop molto veloce (quello che hai fatto te va bene), ma anche iniziare a leggere appena arriva il primo byte. In teoria se il tuo loop() è abbastanza veloce non dovresti avere problemi... se fai un loop solo con il codice che hai postato dovrebbe funzionare 2. Usare la NewSoftSerial, ma da modificare pesantemente: A. facendola funzionare anche sul mega, perchè funziona solo su 168 e 328 B. modificando la grandezza del buffer alla cifra che ti serve.

entrambe le soluzioni sono più o meno complesse.

Jok3r: Per la lettura utilizzo questo:

  while (Serial3.available() > 0) {

                Serial.print(Serial3.read());                 }

Mi stranizza ... prova questo:

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

void loop() {
  int count = 0;
  while (Serial.available() > 0) {
    Serial.print("Count = ");
    Serial.println(count);
    Serial.print("Valore = ");
    Serial.println(Serial.read());
    count++;
  }
}

Da me va oltre i 128byte senza problemi ... come giustamente dovrebbe.

se il buffer è di 128 caratteri, e arrivano + di 128 caratteri mentre il buffer non è svuotato da read() & co, che succede?

robitabu prima di dire che funziona il tuo codice, hai provato ad inviare più di 128 caratteri via seriale al tuo arduino in un colpo solo(o meglio usando tutto il baud-rate disponibile)?

lesto: se il buffer è di 128 caratteri, e arrivano + di 128 caratteri mentre il buffer non è svuotato da read() & co, che succede?

robitabu prima di dire che funziona il tuo codice, hai provato ad inviare più di 128 caratteri via seriale al tuo arduino in un colpo solo(o meglio usando tutto il baud-rate disponibile)?

Si ho provato, ma visto che dici così ho appena riprovato ... è vero che va oltre i 128byte ma solo di poco ... arriva si e no a 139bytes (sia a 9600 che a 115200). Se poi inserisco un delay(100) dopo il count++ allora arrivo solo ai fatidici 128byte. Quindi mi sa che il buffer qui c'entra eccome :-(

senza delay arrivi a 139 perchè leggi il buffer e quindi lo svuoti, e reggi per 11 caratteri prima di arrivare in overflow.

lesto: senza delay arrivi a 139 perchè leggi il buffer e quindi lo svuoti, e reggi per 11 caratteri prima di arrivare in overflow.

Mi pare sensato.

Quindi mi par di capire che per evitare un buffer overflow (ma si chiama così anche quel che succede al buffer dell'Arduino in questa circostanza?) bisogna coordinare meglio la comunicazione con chi gli manda i dati sulla seriale, no?

Insomma, bisogna fare in modo che il software che comunica via seriale con Arduino non gli spari più di 128 byte alla volta, o che almeno attenda che Arduino lo "avverta" di aver già letto una prima parte da 128byte e solo dopo gli mandi dell'altro.

Beh, dato che proprio per adesso sto mettendo mano alla Seriale non mi dispiacerebbe approfondire anche quest'aspetto.

Il buffer di Arduino è limitato a 128 byte:
http://www.arduino.cc/en/Serial/Available

Un baudrate lento ( 1200 o 2400) dovrebbe aiutare che Arduino possa svuotare il buffer prima che si riempia del tutto. Un altra soluzione (che non viene sopprtato da Arduino é usare un protocollo di flusso ( xon/xoff o uno hardware con i collegamenti supplementari della RS232). Ciao Uwe

io un paio di soluzioni le ho postate, se poi volete usare chip esterni fate vobis.

Quindi mi par di capire che per evitare un buffer overflow (ma si chiama così anche quel che succede al buffer dell’Arduino in questa circostanza?)

il buffer overflow è quando un buffer supera le sue dimensioni massime. Un buffer è una locazione di memoria temporanea per alcuni elementi. Il termine vale per arduino, mille altri linguaggi, e pure quando la minestra esce dal piatto :smiley:

bisogna coordinare meglio la comunicazione con chi gli manda i dati sulla seriale, no?

no, con la seriale che usiamo noi (TTL) puoi scegliere solo la baud-rate, il massimo che puoi fare è impostare il baud-arte minimo e sperare che il loop sia più veloce del tempo di riempimento del buffer.

Insomma, bisogna fare in modo che il software che comunica via seriale con Arduino non gli spari più di 128 byte alla volta, o che almeno attenda che Arduino lo "avverta" di aver già letto una prima parte da 128byte e solo dopo gli mandi dell'altro.

Non sempre il chip con cui comunichi può essere riprogrammato, e quindi devi risolvere lato arduino.

TROVATA LA SOLUZIONE:
senza sbattere troppo la testa… ATTENZIONE: Bisona modificare una libreria di sistema di arduino IDE, quindi fai usa un’installazione a parte per evitare casini su altri progetti!

  1. vai nella crtella arduino-00XX/hardware/arduino/cores/arduino/
  2. apri il file HardwareSerial.cpp

e cerca:

// Define constants and variables for buffering incoming serial data.  We're
// using a ring buffer (I think), in which rx_buffer_head is the index of the
// location to which to write the next incoming character and rx_buffer_tail
// is the index of the location from which to read.
#if (RAMEND < 1000)
  #define RX_BUFFER_SIZE 32
#else
  #define RX_BUFFER_SIZE 128
#endif

modifica RX_BUFFER_SIZE 128 con un numero più elevato. Attenzione a non superare i limiti della RAM! e soprattutto rendi più veloce il loop(), altrimenti puoi avere anche un buffer di gigabite, ma se il loop dure 3 settimane…

lesto: no, con la seriale che usiamo noi (TTL) puoi scegliere solo la baud-rate, il massimo che puoi fare è impostare il baud-arte minimo e sperare che il loop sia più veloce del tempo di riempimento del buffer.

L'assenza di un protocollo di flusso non dipende che i livelli logici sono TTL o no, ma solo dal fatto che non é stato implementato.

lesto: TROVATA LA SOLUZIONE: senza sbattere troppo la testa...

Gratulazioni!! Ciao Uwe

lesto:

bisogna coordinare meglio la comunicazione con chi gli manda i dati sulla seriale, no? ... Insomma, bisogna fare in modo che il software che comunica via seriale con Arduino non gli spari più di 128 byte alla volta, o che almeno attenda che Arduino lo "avverta" di aver già letto una prima parte da 128byte e solo dopo gli mandi dell'altro.

Non sempre il chip con cui comunichi può essere riprogrammato, e quindi devi risolvere lato arduino.

Concordo, se non puoi condizionare l'invio dei dati ad Arduino, allora l'unica cosa che rimane da fare è adattare Arduino a lavorare su quel che gli arriva. Certo, in questo scenario la soluzione che proponi (l'aumento del buffer di Arduino modificando HardwareSerial.cpp) può essere una strada valida.

Io però continuo a riflettere su quale approccio tenere nel caso in cui si possa controllare la comunicazione seriale su entrambi i fronti (io al momento lavoro ad uno scenario di quest'ultimo tipo) e non si voglia modificare HardwareSerial.cpp. Continuo a pensare che un buon design del software debba assolutamente prendere in considerazione un meccanismo che coordini la spedizione/ricezione dei pacchetti di dati per evitare l'overflow del buffer seriale di Arduino. O in alternativa bisogna scrivere il codice in modo che sia tollerante all'eventuale perdita di dati dalla seriale.

Devi confrontarti con 2 problemi: 1) chi ti manda i dati 2) dove appoggiare temporaneamente questi dati.

Il punto 1), come ti hanno spiegato, è risolvibile solo se puoi modificare il protocollo di trasmissione. In questo caso inserisci dei controlli o prepari un protocollo affinché la trasmissione dei dati avvenga solo: a) quando il ricevente invia un segnale di attesa dati, così sei sicuro che appena arrivano questi vengono prelevati dal buffer; b) inviando pacchetti di dimensioni tali che possano essere trattati dal ricevente senza perdite.

Il punto 2) dipende dalla scarsa dimensione della memoria dell'Arduino. Datosi che i dati temporaneai (leggi "variabili") l'Arduino li memorizza nella SRAM, questa ha una dimensione limitata: nell'Atmega328 è di 2 KB, e deve contenere non solo le variabili del tuo programma, ma anche il buffer ed altri dati come lo stack e l'heap, per cui devi vedere l'occupazione globale delle tue variabili. Una soluzione potrebbe essere quella di dichiarare alcune variabili come costanti e salvarle nella Flash, così da liberare spazio in SRAM per allargare il buffer.

leo72: Devi confrontarti con 2 problemi: 1) chi ti manda i dati 2) dove appoggiare temporaneamente questi dati.

Il punto 1), come ti hanno spiegato, è risolvibile solo se puoi modificare il protocollo di trasmissione. In questo caso inserisci dei controlli o prepari un protocollo affinché la trasmissione dei dati avvenga solo: a) quando il ricevente invia un segnale di attesa dati, così sei sicuro che appena arrivano questi vengono prelevati dal buffer; b) inviando pacchetti di dimensioni tali che possano essere trattati dal ricevente senza perdite.

Per fortuna posso modificare il protocollo di chi invia dati a piacere, è un software OpenSource che prevede già per design questa customizzazione :-) Nello specifico, senza modifiche rilevanti, spedisce pacchetti di dati (nel mio caso circa 50byte alla volta) con una frequenza costante (qui non parlo però di bitrate). Questo mi rende più facile il lavoro perchè so già quanti dati arrivano e con quale cadenza.

Però ahimè non prevede un meccanismo di invio su richiesta, quindi al momento non ho modo di dire al programma di spedire dati solo quando Arduino si dichiara disponibile a riceverli :-( Questo aspetto lo devo ancora risolvere, forse opterò per un controllo su Arduino che potrebbe utilizzare solo i pacchetti ritenuti validi, magari specificando nel protocollo una stringa di inizio ed una di fine invio dati così Arduino saprà distinguere i pacchetti "interi" da quelli "corrotti". Vedremo ...

Di certo, eviterò come la peste di fargli arrivare pacchetti più grandi di 128byte.

Il punto 2) dipende dalla scarsa dimensione della memoria dell'Arduino. Datosi che i dati temporaneai (leggi "variabili") l'Arduino li memorizza nella SRAM, questa ha una dimensione limitata: nell'Atmega328 è di 2 KB ...

Beh, io al momento uso un Mega2560 ma vorrei realizzare tutto in modo che possa girare anche su un qualunque UNO. Quindi, si, cerchero cmq di rimanere in dei limiti modesti in quanto ad uso della memoria.

Come molti di voi suggerivano dovevo gestire meglio la comunicazione. :) Nel mio caso succedeva che l'antenna rfid, quella che scriveva nel buffer, era inizializzata a 115200, e la seriale (sulla quale scrivevo subito dopo aver letto dal buffer) a 9600... Inizializzando quest'ultima a 115200 riesco a leggere tutti i byte che mi servono! Con le velocità uguali, ho messo i dati letti dal buffer in un vettore di interi, in modo da poter elaborare la risposta ricevuta dell'antenna. Anche in questo modo si ricevono tutti i byte. La soluzione che ho usato è semplice (ero io che sbagliavo a inizializzare le velocità), posto cmq il codice se a qualcuno potesse servire. Ho provato anche ad aumentare la dimensione del buffer cambiando il valore RX_BUFFER_SIZE. Funziona, ma visto che erano gestite male le velocità mi sono trovato con lo stesso problema che avevo prima, solo per valori più grandi.

void setup() {

  Serial.begin(115200);
  Serial1.begin(115200);

}

void loop() {

  int dim=0;
  int risp[1500];
  while(Serial3.available()){
  risp[dim]=Serial3.read();
  dim++;

  }

}