Arduino: tx/rx seriale

Buon giorno a tutti,
sto realizzando un cronometro digitale per una manifestazione in paese organizzata da associazioni volontarie.
Già nel 2019 abbiamo realizzato questa manifestazione con ottimi risultati ma che, a fine manifestazione, ha sollevato qualche perplessità. Mi spiego meglio.
Ho realizzato un programma in VisualStudioNet per la gestione e visualizzazione dei tempi, punteggi ec... Questo programma dialoga, tramite RS232/usb con Arduino impartendogli i comandi di start, stop, clear, reset.
Quando viene dato lo start, Arduino comincia a verificare gli ingressi digitali delle 4 corsie, e al sopraggiungere di una di esse, lo trasmette, con la seriale, al programma residente sul PC. Con questo sistema e con tutte le ottimizzazioni hardware, software e OS fatte, dalle prove fatte a casa, non si poteva garantire un controllo sotto i 15milliSecondi.
la mia domanda è questa: mentre Arduino sta trasmettendo i dati al PC, tutto il processore si dedica a questa operazione o continua col programma demandando la trasmissione alla unità seriale presente a bordo?
il poche parole, quando digito serialWrite(....), arduino si blocca su questa riga di programma fino a quando la trasmissione non termina o va avanti perchè il modulo Tx/Rx lo fa in autonomia prelevando i dati dalla cache seriale?
Grazie in anticipo a quanti mi aiutano a togliermi questo dubbio.
Buona giornata.

Ti segnalo che, nella sezione in lingua Inglese, si può scrivere SOLO in Inglese... quindi, per favore, la prossima volta presta più attenzione in quale sezione metti i tuoi post; questa volta esso e stato spostato, da un moderatore della sezione di lingua Inglese, nella sezione di lingua Italiana... la prossima volta potrebbe venire direttamente eliminato.

Serial.write() non fa altro che mettere il byte da trasmettere nel buffer, poi ci pensa l'hardware integrato nel microcontrollore a "serializzare" le informazioni.

Il tuo problema, non dipende da quello.
Se metti il firmware dell'Arduino, possiamo vedere se si riesce ad ottimizzarne il funzionamento.

Vero, a meno che non si saturi il buffer di trasmissione (64 byte), allora la write diventa bloccante.

In tal caso, se aumenta il baud rate fino al massimo consentito dal suo hardware dovrebbe ridurre drasticamente quel limite dei 15mS.
Solo che se satura il buffer della seriale per inviare dei comandi di start, stop e qualche tempo, secondo me c'è senza senza dubbio qualcosa da rivedere nel software.

Dipende, se il buffer è vuoto write scrive direttamente su UDR e restituisce 1 al chiamante. Se il buffer contiene dei dati e gli interrupt globali sono abilitati allora accoda il byte nel buffer.

Sono daccordo, serve il codice anche per capire quale write usa perché c'è anche quella ereditata da Print::

In ogni caso qui c'è il core arduino per avr, sempre che usi un arduino AVR no perché non lo ha specificato,

Ciao.

Grazie mille a tutti per le preziose informazioni.
Uso Arduino Uno, PC portatile e Windows 7. La trasmissione è settanta a 115k perché oltre avevo problemi di trasmissione col portatile.
I 64byte, non li raggiungo. Questo è confortante perché mi fa abbandonare l’idea di usare molte schede Arduino per aggirare l’ostacolo.
Credo, a questo punto, che il problema risieda nel software sul PC. Il PC non è in grado di gestire, molto probabilmente, due trasmissioni molto ravvicinate tra loro tenendo conto del fatto che quando arriva il numero di corsia, il PC preleva il valore del timer attuale, lo confronta con quello memorizzato allo start e si calcola il tempo trascorso, poi il tutto si traduce, in tempo reale, in una visualizzazione del tempo, dell’ordine di arrivo e del logo della squadra. Il tutto in forma grafica. Tutto questo, secondo me, ha bisogno di un certo tempo e quindi non si riesce a scendere sotto tale soglia dei 15milliSecondi.
Gli OS di oggi, fanno molto uso di programmi che lavorano in background.

Ciao,
in sostanza tu i tempi li cronometri con il PC?
In caso riesci a spiegare bene l'algoritmo?

Alan

Uso un algoritmo molto semplice.
Premetto che Arduino gestisce una console con 4 tasti ( start, stop, reset, clear) + 4 ingressi che provengono dalle postazioni di gioco + 1 ingresso che proviene da una postazione usata nei giochi individuali. Ovvio che tra Console e Pc avviene uno scambio dati per selezionare il tipo di gioco e per effettuare le operazioni di start, stop ecc..
Quando abilito il gioco, nel momento in cui Premo start sulla Console, Arduino invia al Pc il comando di Start. Il Pc, nella eccezione sollevata dalla SERIALE, memorizza il time di sistema in una variabile.
Quando il giocatore arriva al termine del percorso, preme il pulsante presente sulla sua corsia di gioco. Arduino ricevuto l’ingresso, invia al Pc semplicemente un Byte di dati che contiene il numero della corsia arrivata.
Il Pc, nella eccezione sollevata dalla seriale, memorizza nuovamente il timer di sistema e uscito dalla eccezione/interrupt effettua il calcolo dei due tempi memorizzati e lo associa alla corsia ricevuta da Arduino. A questo punto, il PC visualizza a video, il logo della squadra, il tempo impiegato e l’ordine di arrivo.
Ho rastremato al max le routine di interrupt per avere esecuzioni veloci ed evitare la sovrapposizione di eventuali nuovi interrupt.

Secondo me sarebbe sicuramente più efficiente se i tempi li memorizzassi su Arduino:
tu invii via seriale il comando di start, Arduino lo riceve e setta una variabile startTime = millis(), nelle funzioni di interrupt (non ho capito che scheda hai, e di conseguenza quanti interrupt hai) salvi un nuovo valore stopTime1 = millis() e calcoli il tempo con time1 = stopTime1 - startTime, aspetti che tutte le corsie finiscano e a quel punto invii tutti i tempi via seriale al PC. Se ti bastano i millisecondi va bene così, altrimenti sostituisci millis() con micros(); le variabili ti consiglio di dichiararle unsigned long e i risultati li hai o in millisecondi (millis) o in microsecondi (micros).
Spero di essere stato chiaro, altrimenti resto a disposizione.

Alan

Chiarissimo e grazie per il tempo che mi stai dedicando.
La scheda è un Arduino uno R3 originale quindi sono disponibili solo 2 interrupt se non sbaglio (insufficienti per le 4 corsie).
Inizialmente avevo preso la strada da te consigliata, che è tra l’altro più semplice ed efficiente e mi toglieva molte rogne.
Purtroppo questa soluzione non piace agli organizzatori e ai rappresentanti delle squadre perché loro vogliono l’apparizione dell’arrivo della squadra in tempo reale. È per questa richiesta che le cose si sono complicate notevolmente.

Con Arduino Uno, puoi usare il Pin Change Interrupt su tutti i pin della scheda. Ci sono un paio di librerie nel gestore libreire che ne semplicificano l'uso.

Arduino in effetti è sicuramente più realtime del PC, senza andare a scomodare i timer hardware interni puoi usare millis() per salvare il valore che restituisce quando premi START sulla console. Arrivato il giocatore preme il pulsante (Arrivato) e calcoli millis() - il tempo salvato quando hai premuto START. Così per le altre corsie. Al termine spedisce via seriale i 4 valori calcolati.
Questa è una possibile workaround, ma tieni conto che avrai i debounce su i pulsanti e che quando fai il tutto nel loop principale un concorrente a caso potrebbe essere quello avvantaggiato perché casualmente arduino sta eseguendo la digitalRead nel momento in cui il concorrente ha premuto su Arrivato e se sta eseguendo questo codice non può eseguire altro. Una soluzione potrebbe essere quella di impiegare gli interrupts su i 4 ingressi dotandoli di debounce hardware, cioè rete RC.

I tempi di reazione saranno sicuramente più rapidi di quanto fatto con il PC.

Esatto, la MEGA ne ha mi pare proprio 4, ma considera anche gli interrupts PIN_CHANGED che scattano su cambiamento di stato e si possono abilitare su ogni pin di arduino (inclusa la UNO).

Ciao.

Grazie mille. Lo vedrò subito.

Avete ragione, Arduino è più realtime di un Pc è che spedire i 4 risultati contemporaneamente all’arrivo dell’ultimo concorrente sarebbe la migliore soluzione. Avrei, in questo caso, un conteggio più accurato.
Come scritto prima, gli organizzatori vogliono la visualizzazione dell’arrivo con relativo tempo e posizione in tempo reale. Quindi se per esempio arriva il primo e il secondo arriva dopo 10 sec., l’organizzazione vuole vedere, a schermo, l’arrivo del primo concorrente e dopo 10 secondi vedere comparire, sempre a schermo i dati del secondo concorrente e così via. Per questo motivo che le cose si sono complicate.
Per ottimizzare, nella versione attuale del programma Arduino, sto utilizzando gli accessi diretti alle porte I/O tramite le istruzioni PORTx ecc… che mi permettono di leggere le 4 corsie contemporaneamente.
Potrei risolvere il tutto è usare il conteggio di Arduino se questo fosse equipaggiato con due seriali. In questo modo una la utilizzerei per scambio dati comando (reset, stop ecc…) e l’altra per la trasmissione della corsia arrivata, posizione di arrivo e tempo impiegato. In questo modo potrei intervallare la trasmissione con un tempo sufficiente per far sì che il PC possa elaborare ogni singolo arrivo.

Altra soluzione che mi viene in mente, visto che Arduino ha solo una porta seriale, è usare due schede Arduino una master è una slave che lavorano in questo modo:
Le due schede le connetto entrambe al PC su due porte USB ovviamente per cui posso aprire due porte seriali distinte una per ogni scheda.
La prima (es. com1) la uso per la gestione dei tasti della console e quindi dei comandi start, stop ecc…
La seconda la dedico interamente al controllo delle corsie e calcolo sia della posizione di arrivo che del tempo impiegato.
Quindi, quando premo start, la prima scheda attiva il cronometro sulla seconda scheda tramite interrupt (visto che posso applicarlo a tutti i pin).
La seconda scheda memorizza il timer interno in una variabile. All’arrivo del 1* concorrente, calcola posizione e tempo impiegato. Questi dati vengono messi in buffer sulla seriale che sarà la com2 ad esempio sul PC. Per evitare di riempire il buffer in caso arrivino tutti e quattro in quasi contemporanea, utilizzo un conteggio non bloccante ( senza fare uso di delay) che impedisce di scrivere altre informazioni sulla seriale per un certo intervallo di tempo. Credo che in questo modo di risolvere il problema.
Dovrò attivare una seconda seriale sul PC ma è poca cosa.

Se deve ancora essere così il problema rimane. L'esempio specifico parla di 10 secondi e il problema non sussiste poiché arduino in 10 secondi è in grado di spedire via seriale, tuttavia quando spedisce via seriale byte dopo byte tra l'uno è l'altro deve verificare i pulsanti delle altre 3 corsie per questo motivo tutti hanno pensato agli interrupts.

Intanto pensiamo a simulare quello che arduino deve spedire quando viene premuto il pulsante dal concorrente. Serve spedire numero corsia (1 byte) tempo in secondi, millesimi (un float 4 byte) totale 5 byte se spedisci raw data se spedisci caratteri allora 10.125 sono 6 byte più uno della corsia 7. Sono davvero pochi byte trasferibili in un lampo.

Ciao.

Che significa raw data e perché 10.125 se caratteri?
Scusa la mia ignoranza.

Non capisco dove sia il problema, invece di spedire solo il numero della corsia al PC gli spedisci anche il tempo magari espresso in millesimi per semplificare (cosi usi solo l'istruzione millis() per "congelare il tempo") e poi il pc con calma si fa i suoi bei conti per la conversione HH:MM:SS.mmm

Il sistema è "real time" per quanto riguarda i tempi e l'aggiornamento a video è immediato.

... scusa ma che problema c'è ? I dati da trasferire sono in un buffer circolare, quando il registro di trasmissione è vuoto il tempo che si perde è quello di muovere UN carattere dal buffer al regsitro di trasmissione; la trasmissione viene fatta tramite HW dedicato e il programma, durante la trasmissione fisica, può continuare a fare tutto quello che gli pare ...

Mi pare veramente un falso problema.

Guglielmo