Arduino/bash e porta seriale
Usiamo Processing?
No grazie, stiamo imparando.
Arduino è un dispositivo semplice, cerchiamo di usarlo con strumenti di base.
Dunque la bash. Bash è il linguaggio di terminale nativo su Linux. Prescelto da Torvalds fin dalla primissima versione del '91.
Premessa: configurare la porta seriale
Non sempre, quando vogliamo interagire con arduino attraverso la porta seriale, troviamo il sistema autoconfigurato correttamente. I parametri di connessione, infatti, sono spesso gestiti dai programmi di alto livello all'insaputa dell'utente finale e vengono alterati in un modo non adatto all'uso da terminale. Per non dilungarci oltre misura questo aspetto, suggeriamo di impostare la porta seriale con questo comando, testato su arduino uno:
* stty -F /dev/arduino 1:0:1cb2:80:3:1c:7f:15:4:0:1:0:11:13:1a:0:12:f:17:16:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0
Input ed output
D'accordo. Ora chiediamoci: cosa ci dobbiamo fare, con il terminale?
Semplice. Ci dobbiamo fare input/output.
Allora, se voglio scrivere qualcosa e comunicarlo ad arduino, è un ouput.
L'output, in bash è gestito dal comando echo.
Per esempio:
* echo "Ciao" # scrive Ciao sul terminale. echo "Ciao" >/dev/arduino # scrive Ciao e redige l'output verso arduino.
L'input, in bash, è gestito dal comando cat, oppure dal comando head.
* cat </dev/arduino # legge dalla porta seriale di arduino. * head -n3 </dev/arduino # legge le prime 3 righe provenienti dalla porta seriale di arduino.
L'ordine delle operazioni: prima leggere e poi scrivere.
Adesso dobbiamo istruire arduino a gestire la porta seriale.
Prima di cominciare, ci serve uno sketch per testare il sistema.
Questa è una soluzione minimale. Oppure possiamo usare quest'altro sketch, su cui stiamo lavorando.
Scriviamo dunque:
* echo 30 >/dev/arduino * cat /dev/arduino
Sorpresa. Non esce nulla. Anzi il terminale si blocca e siamo costretti a uscirne con la combinazione di tasti Ctrl-c.
Per forza!!! Stiamo lavorando con un dispositivo fisico esterno, mica con i soliti dispositivi virtuali a cui siamo abituati. Se avessimo provato a scrivere:
* echo 30 >/tmp/prova.txt * cat </tmp/prova.txt
Il risultato sarebbe molto più soddisfacente. E non si sarebbe incastrato nulla.
Qual è la differenza? Il contatto con la realtà. Se spediamo un echo verso arduino, la nostra stringa viene correttamente acquisita e processata, generando una risposta, che viene generata immediatamente.
Qualche istante dopo, eseguiamo il cat... Acciderba, troppo tardi! La risposta si è già perduta.
Davvero, comunicare con Arduino è infinitamente educativo. Soprattutto per noi insegnanti.
Dovremmo sempre comportarci così:
Prima disporci all'ascolto, e solo dopo interrogare.
Infatti, per comunicare con arduino bisogna rovesciare l'ordine delle operazioni. Proviamo così:
* head -n1 </dev/arduino & # il simbolo & permette di eseguire il comando in batch, lasciando libero il terminale per i comandi successivi. * echo >/dev/arduino # Come si diceva: prima leggere e poi scrivere!!!
Davvero. È tutta un'altra cosa.
Controllo e prontezza dell'input/output: il comando exec
Proviamo ora a includere una chiamata di input/output in uno script, oppure (che è lo stesso) a eseguire i due comandi precedenti in un'unica riga di bash:
* head -n1 </dev/arduino & echo 30 >/dev/arduino
Questa volta i risultati saranno instabili e deludenti. Solo qualche volte si otterrà una risposta corretta, ma il più delle volte si osserverà una completa mancanza di reazione o verranno ricevute risposte incomplete e scorrette.
In precedenza, infatti, digitando manualmente due istruzioni in successione, la distanza temporale tra i comandi copriva le latenze di comunicazione del sistema, inspirandoci una soddisfazione illusoria.
Anche in questo caso, si crea una occasione per riflettere sulle differenze tra la comunicazione virtuale e la comunicazione reale.
Le operazioni di input/ouput tra due sistemi reali, infatti, sono più complesse di quanto non si possa immaginare a priori. In particolare, non si esauriscono meramente allo scambio dei dati, ma richiedono una procedura di sincornizzazione mutua che introduce delle latenze piuttosto fastidiose (nel mio sistema sono spesso superiori ai due secondi).
In queste condizioni, ovviamente, non sarebbe possibile gestire un dialogo dinamico attraverso la porta seriale.
In questi casi, la procedura di connessione deve essere separata in due fasi distinte. La prima è proprio la fase di sincronizzazione mutua dei sistemi fisici. Questa operazione, però, deve essere eseguita una sola volta, prima di incominciare la comunicazione di scambio dati e non ripetuta sistematicamente ad ogni atto di lettura o di scrittura. La seconda fase, invece, è il semplice scambio dei dati, che è velocissimo.
La sincronizzazione, in molti linguiaggi di programmazione, è gestita dal comando open. In bash, invece, viene realizzata con il comando exec, in modo che l'input ouput lavori in questa maniera:
* exec 5<>/dev/arduino # apertura del dispositivo arduino sul descrittore di file 5, sia per operazioni di input che per operazioni di ouput. sleep 5 # forse cinque secondi è un ritardo eccessivo, ma l'operazione deve essere eseguita una volta sola. head -n1 <&5 & echo 30 >&5 exec 5<&- # chiusura corretta del descrittore di file. Da utilizzare solo alla fine delle operazioni di input/output.