Ram+Seriale+Overflow

astrobeed:

leo72:
Non 64kB, 64 byte.
Cmq puoi aumentarlo.

Attenzione a non fare confusione tra il buffer di ricezione della seriale e la capacità di trattare i dati, il buffer serve solo come tampone momentaneo se il micro è impegnato in altre cose mentre arrivano i dati, ma se non riesci a processarli più velocemente di come arrivano non importa se il buffer è di 10-20-64-100k alla fine si riempie sempre e cominci a perdere dati.
Per farla breve, l’importante è che il software sia in grado di elaborare i dati in arrivo più velocemente di come vengono trasmessi, se questa condizione è soddisfatta puoi inviare dati 24/7 senza problemi, se non lo è devi prevedere un modo per bloccare l’invio dei dati temporaneamente fino a che il micro non è grado di elaborare quelli nuovi, che poi è il concetto che sta alla base dei vari segnali di handshake della RS232 quali RTS/CTS e DTR/DSR.

Esatto. Difatti questo è uno dei bug alla base della fallacità di ArduinoISP dell’IDE 1.0, che infatti soffre del buffer in ricezione di dimensioni ridotte perché mettendo quel famoso delay di 40ms nella funzione di pulsazione dei led di stato, se arriva una pagina da scrivere in flash (che se non ricordo male sono 512 byte) mentre è in quel delay, il buffer si riempie, l’indice va in overflow e lo sketch si blocca. Con la modifica di millis al posto dei delay si risolve la questione perché si permette al software di svuotare il buffer mentre i dati arrivano.

leo72:
perché mettendo quel famoso delay di 40ms nella funzione di pulsazione dei led di stato,

Infatti usare una delay con una discreta mole di dati in arrivo sulla seriale è veramente una “gran furbata” :grin:

Grazie anche per l'intervento di uwe

A questo punto approfondiamo.

1) l'handshake hardware RTS/CTS e DTR/DSR non si puo' usare su arduino ? 2) l'hanshake sw Xon Xoff non si puo' usare su arduino ? 3) il concetto di buffer e' presente anche lato Pc? cioe' un sw che spedisce su seriale dei dati lo fa in modo diretto, non ha anche il pc un buffer ?

mi avete snobbato, avete fatto bene :slight_smile:

pero’ ora sto’ sbattendo la testa in un problema reale, abbiate cuore :cold_sweat:

ho assodato che non dite stupidaggini quando asserite che l’IDE1.0 ha come buffer seriale 64Byte (non avevo dubbi, ma mi piace assodare, deformazione professionale :stuck_out_tongue_closed_eyes:)

quindi se spedisco ad arduino via seriale 100 caratteri, lui ne acchiappa solo 64, il resto si perde nel limbo

esiste un modo software per aggirare la situazione, oppure devo aumentare per forza il buffer come ha detto leo ?
a quanto potrei aumentarlo ? potrei ad esempio mettere il buffer a 1KB ?

Pro/Contro di Sw-Hw solution ?

thanks

Testato:
ho assodato che non dite stupidaggini quando asserite che l’IDE1.0 ha come buffer seriale 64Byte (non avevo dubbi, ma mi piace assodare, deformazione professionale :stuck_out_tongue_closed_eyes:)

quindi se spedisco ad arduino via seriale 100 caratteri, lui ne acchiappa solo 64, il resto si perde nel limbo

No, l’importante è svuotare quel buffer più velocemente di quanto si riempia.
Se controlli la presenza di nuovi caratteri nel buffer (Serial.available) e lo svuoti non appena arriva qualcosa, non hai problemi.

esiste un modo software per aggirare la situazione, oppure devo aumentare per forza il buffer come ha detto leo ?

a quanto potrei aumentarlo ? potrei ad esempio mettere il buffer a 1KB ?

Pro/Contro di Sw-Hw solution ?

l core di Arduino usa questo accorgimento: ha inserito una ISR (Interrupt Service Routine) per gestire l’interrupt che si genera quando si attiva la seriale e da dentro essa preleva i caratteri in arrivo e li inserisci nel buffer. Quindi c’è già una gestione HW/SW. Potresti riaumentare il buffer riportandolo a 128 byte (oltre non andrei, togli SRAM alle variabili del tuo programma). Risolvi ma non del tutto, vista la memoria limitata di Arduino.
Mica puoi creare un buffer di 10 kB :wink:

Un’altra soluzione potrebbe essere quella di usare una memoria tampone esterna (tipo una RAM seriale veloce) e parcheggiare lì i byte in arrivo, creando un grosso buffer esterno. Ma a questo punto si entra nel campo delle cose assurde: cosa mai ti arriva dal PC per dover gestire migliaia di byte di dati? :fearful:

Testato: 3) il concetto di buffer e' presente anche lato Pc? cioe' un sw che spedisce su seriale dei dati lo fa in modo diretto, non ha anche il pc un buffer ?

sì, nel caso di arduino (quindi simulazione seriale) il buffer è "simulato" anche lui (buffer software) ma non dubito che le seriali HW abbiano un proprio buffer HW. In oltre, grazie al multithreading, è facile fare un'applicazione che svuota il buffer del driver/SO e li parcheggi in un "mortale" array come fa la serial di arduino, con la differenza che la ram di un PC hai voglia a riempirla :)

quindi se spedisco ad arduino via seriale 100 caratteri, lui ne acchiappa solo 64, il resto si perde nel limbo

esiste un modo software per aggirare la situazione, oppure devo aumentare per forza il buffer come ha detto leo ?

allora, ogni quanto tempo tu entri nel ciclo di lettura? sai che un solo carattere impiega 1/baudrate secondi per essere inviato. quindi 100 caratteri a 9600 impiegano 0.010 secondi (100 millis). con un baud di 4800, i caratteri impiegano 200 millis. quindi dimezzando il baudrate, abbiamo raddoppiato il tempo di overflow della seriale. Certo, abbiamo rallentato TUTTO il processo di lettura, ma se prima perdevi tanto tempo a fare altro, credo che la cosa sia ininfluente.

per completezza: a 9600 baud vai in overflow ogni 0.006 secondi, ovvero 60millis. a 115200 baud (il massimo da ide) ogni 5ms a 300 baud (il minimo da ide 1.0) ogni 0.21 secondi , 210 millis

X leo: semplicemente stampo su seriale 100 caratteri, sto facendo prove con caratteri senza senso, abcdef,ecc. Lato Arduino ne afferro solo sempre esattamente 64.

X lesto: avevo pensato al trucco della velocita'. Ho fatto prove a 4800 ma resto fermo a 64 ricevut

mi sa che sbaglio candeggio.

Io faccio: if serial available concatena tutto Ma mi sa che in questo modo visto che io spedisco 100 caratteri, la seriale e' sempre abailable e quindi non esce dal IF continuando a concatenare, arrivato a 64 il buffer va in overflow. Che ne pensate ? Mi chiedevo, ma inviare 100 caratteri su seriale e' talmente una coda strana ?

scusami, ma quando fai una read elimini il dato letto dal buffer... puoi postare ilcodice?

Testato:
(non avevo dubbi, ma mi piace assodare, deformazione professionale :stuck_out_tongue_closed_eyes:)

Adesso ci fai diventare tutti curiosi :slight_smile: , non mi dire che come lavoro sei un qualche tipo di ispettore ?

Testato: X leo: semplicemente stampo su seriale 100 caratteri, sto facendo prove con caratteri senza senso, abcdef,ecc. Lato Arduino ne afferro solo sempre esattamente 64.

Come ti ha spiegato Leo Arduino gestisce la seriale tramite interrupt, fino alla revisione 0023 solo in lettura, la trasmissione avviene tramite polling, dalla 1.0 in poi anche in scrittura. In pratica ogni volta che l'UART ha disponibile un nuovo carattere viene attivato il relativo interrupt e la sua ISR altro non fa che prelevarlo dal buffer (1 solo carattere) dell'UART e porlo nella prima posizione disponibile dell'array dati ricevuti è lungo 64 byte. Ogni volta che vai a leggere il buffer ricezione seriale, la Serial.read, l'indice viene scalato di uno per ogni byte che leggi realmente pertanto rendi disponibile una nuova locazione nel buffer stesso, la Serial.available() si limita a dirti se ci sono caratteri nel buffer, ma non lo libera. Se leggi i dati dal buffer con una velocità maggiore di quella con cui arrivano il buffer non si riempie mai, se non lo fai non appena arriva a 64 byte si ferma e i dati vengono persi. Per come la vedo io la gestione che fa Arduino di questo buffer è sbagliata, il modo corretto è usare un ring buffer, ovvero un buffer dove in caso di superamento della capacità viene riscritto il primo byte e così via, e un flag di stato che avvisa dell'eventuale overflow, così posso sapere se c'è stata perdita di dati e comunque ho sempre disponibili gli ultimi 64 ricevuti. Volendo fare le cose in modo perfetto l'ideale sarebbe rendere possibile tutti e due i modi di funzionamento, cioè sia il sistema con fermo a 64 byte e perdita di tutti gli altri, come è attualmente, e il ring buffer potendo scegliere tra i due durante l'init della seriale, p.e. scrivendo serial.begin(9600, RING) o serial.begin(9600, NORING). Comunque per farla breve se mentre arrivano i dati sulla seriale tu li leggi con maggiore velocità non perdi un solo byte. Come tempo (ipotizzando un flusso dati continuo) per riempire 64 byte a 9600 bps ci vogliono circa 67 ms, quindi non dovresti avere problemi per svuotare il buffer se lo gestisci ad ogni ciclo della loop senza usare in mezzo delle delay o altre istruzioni bloccanti.

Se leggi i dati dal buffer con una velocità maggiore di quella con cui arrivano il buffer non si riempie mai, se non lo fai non appena arriva a 64 byte si ferma e i dati vengono persi.

non ricordo che versione di IDE stavo guardando, ma son certo si trattasse di un ring buffer.

astrobeed: Adesso ci fai diventare tutti curiosi :) , non mi dire che come lavoro sei un qualche tipo di ispettore ?

nooo :) fortunatamente sono un tecnico. Non di rado le procedure che dovrei seguire sono sbagliate, quindi per abitudine pretto tutto con le molle

ho eliminato tutti i delay dal sw, sostituendoli con millis e sono passato da 64byte a 410 circa.

Va bene cosi' :)

Giusto per test ho provato a rimettermi a 4800baud per vedere se salivo oltre i 410, ma niente.

ho un problema con l'oveflow di millis pero'. che c'e' di sbagliato in questo codice ? Ho volutamente messo a INT la variabile ScrollTime, seguendo il concetto che appena va in overflow, cosa che capita dopopochi secondi, dovrebbe ripartire da zero. invece si blocca tutto. Se la metto unsigned long, come la variabile Tempo, funziona, ma in realta' non ho aspettato l'arrivo dell'0overflow, vado a fiducia ?

unsigned long Tempo;
int  ScrollTime;

void setup ()
{}

void loop ()
{ 

Tempo=millis();

 if (Tempo > ScrollTime + 300)
    {
    faistacosaqui();
    ScrollTime = millis();
    } 
}

quando la variabile va in overflow la prima volta, millis ha passato il valore di 16483, che è il valore massimo che può contenere una variabile di tipo signed int. Ripartito lo sketch avrai un'altra assegnazione di TempoScroolTime=millis(), ma Tempo non conterrà più un valore positivo ma negativo perché un tipo signed int che contiene un valore superiore al limite positivo, ha il 16 bit marcato, ed il sedicesimo bit marcato è il bit del segno. Quindi il confronto adesso sarà: -16384>ScrollTime+300? e non sarà mai vero perché tutte le volte che riparte lo sketc Tempo sarà negativo. Finché non va in overflow millis() e non riparte da zero.

Prova ad usare un unsigned int.

scusa ma Tempo e' Unsigned Long, se guardi in cima al codice c'e' scritto. Quindi e' ScrollTime il problema. Puoi riformulare la teoria in base a questa notizia ? :)

Testato: Puoi riformulare la teoria in base a questa notizia ? :)

Leo ha ragione, solo che doveva citare Scrolltime e non Tempo, in pratica non appena Scrolltime supera il valore di 32767 diventa un numero negativo e il confronto sarà sempre vero. Time è destinato a diventare sempre maggiore di Scrolltime perché millis() è un valore che cresce costantemente nel tempo e dopo solo 65 secondi è maggiore anche di un unsigned int.

Testato: scusa ma Tempo e' Unsigned Long, se guardi in cima al codice c'e' scritto. Quindi e' ScrollTime il problema. Puoi riformulare la teoria in base a questa notizia ? :)

Ho sbagliato a scrivere il nome della variabile (succede alle 10 di sera con codice non tuo ;) ), il succo non cambia.

non mi ritrovo, ma visto che voi avete sempre ragione forse non ho capito :)

(vai che stavolta ho ragione io :))

Riguardiamo il mio esempio, appena SrollTime va in overflow diventa negativo, quindi

Time > ScrollTime (- 16483) +300 ?

SIIII, e' superiore, cioe' nell'istante dell'overflow time e' superiore

ebbene cosa chiedo di fare nell'IF ? Oltre a fare xxx mi metti Srolltime = millis, quindi non e' piu' -16483.

lesto:

Se leggi i dati dal buffer con una velocità maggiore di quella con cui arrivano il buffer non si riempie mai, se non lo fai non appena arriva a 64 byte si ferma e i dati vengono persi.

non ricordo che versione di IDE stavo guardando, ma son certo si trattasse di un ring buffer.

ho controllato, nell'IDE 1.0 il buffer è circolar... peccato che chi ha messo mano a quella libreria NON NE FOSSE SICURO (come si legge dai commenti).

questo mi crea molti dubbi riguardo alla qualità delle librerie stesse :zipper_mouth_face:

Time > ScrollTime (- 16483) +300 ?

detta così hai ragione, Time è praticamente sempre superiore a ScrollTime salvo rari casi. ma io credo che la devi vedere in questo modo (cioè questo è ciò che fa il compilatore):

Time > (unsigned long)(ScrollTime (- 16483) +300) ?

(- 16483) +300 = -16183 in binario: infiniti uno (vedi complemento a due) e 00000011001001 notiamo a priori che quegli infiniti uno davanti vogliono dire un valore molto vicino all'overflow, mettiamo quei bit in un unsigned long in 32 bit (senza segno) corrisponde a 4.294.951.113

come testare se ho ragggggione (sono al lavoro, quindi sono supposizioni)? int test = (- 16483) unsignded long test2 = 4294951113; if (test+300 == test2){ Serial.println("lesto rulez"); }else{ Serial.println("lesto go back home!"); }

Premetto una cosa. Ho sbagliato. Ragionare la sera alle 10 sdraiato sul letto con una giornata alle spalle di quelle pesantucce porta a ragionamenti errati. Ho sbagliato a indicare la variabile e tutto il mio ragionamento che ne è seguito è stato inquinato da quell'errore iniziale.

Lesto ha ragione, c'entra il complemento a 2 ed il fatto che il compilatore esegua un casting automatico di (ScrollTimer+300) da signed int a unsigned long. Abbiamo un numero negativo di tipo signed int, per confrontarlo con millis il compilatore esegue il casting a unsigned long, così che il numero viene "rigirato" (1 al posto di 0 e 0 al posto di 1), col risultato che il numero ottenuto diventa maggiore di millis. E lo sarà finché millis non arriverà ad avere un valore di 300 unità inferiore al valore massimo di un unsigned long.

Usando l'unsigned long da subito si evita questo problema. Per avere la certezza su quest'affermazione andrebbe esaminata la versione assembly dello sketch compilato e vedere cosa realmente viene passato al micro.