Invio Ricezione esempio Test01
Ci sto lavorando
Oggetto: Problematiche inerenti l'invio/ricezione dati tra Arduino e Pc
Inizieremo con un esempio. Progetto Test01
Un semplice slider che ha un valore minimo di 500 ed un valore masssimo di 2500. Valore iniziale 700. Muovendo lo slider il valore impostato viene inviato ad Arduino il quale poi lo reinvierà al Pc con un messaggio più valore.
Qui sotto lo stralcio di codice che invia i valori
54
55 sd=new Slider("/image/cnt-cer-pom.png",
56 "/image/cnt-cer-dx.png",
57 "/image/cnt-red-sx.png");
58 sd.setMaxDim(500);
59 sd.setOrienta(0);
60 sd.setVal(500, 2500, 0);
61 sd.setEtic(new Font("Arial",1,18),"0000");
62 sd.setValIniz(700);
63 sd.setLocation(30, 80);
64 sd.jd.addDragListener(new MyDragListener() {
65 @Override
66 public void valChanged(DragEvent ev) {
67 vrea=ev.getRea();
68 sm="hm"+vrea+"\r\n";
69 System.out.println("sm: " + sm);
70 try{
71 serialPort.writeString(sm);
72 }catch (SerialPortException ex) {
73 System.out.println(ex);
74 }
75 }
76 }); |
Qui lo stesso codice ma con un ascoltatore diverso
56
57 sd=new Slider("/image/cnt-cer-pom.png",
58 "/image/cnt-cer-dx.png",
59 "/image/cnt-red-sx.png");
60 sd.setMaxDim(500);
61 sd.setOrienta(0);
62 sd.setVal(500, 2500, 0);
63 sd.setEtic(new Font("Arial",1,18),"0000");
64 sd.setValIniz(700);
65 sd.setLocation(30, 80);
66 sd.jd.addTakListener(new MyTakListener() {
67 @Override
68 public void takChanged(TakEvent ev) {
69 vrea=ev.getRea();
70 sm="hm"+vrea+"\r\n";
71 System.out.println("sm: " + sm);
72 try{
73 serialPort.writeString(sm);
74 }catch (SerialPortException ex) {
75 System.out.println(ex);
76 }
77 }
78 }); |
La differenza tra questi due ascoltatori è che mentre "addDragListener(new MyDragListener()" spara eventi continuamente man mano che si draga il mouse, nel nostro caso man mano che si trascina il cursore dello slider, l'atro "addTakListener(new MyTakListener()" invece lancia un evento solo quando viene rilasciato il mouse, quando si arresta e rilasia il cursore. Nel primo caso l'invio dei dati risultava piuttosto problematico. Infatti bisogna pensare che dall'altra parte, lato Arduino, c'è un loop che cicla ad un ritmo dettato dai millisecondi dl delay(), se si spara una raffica di eventi ad una velocità molto alta si potrebbero perdere dei dati. Per risolvere il problema si dovrebbero in qualche modo sincronizzare i tempi di invio con quelli di ricezione, cosa che potrebbe essere difficile da fare. Nel nostro caso a noi interessa in realtà solo il dato coincidente con il rilascio e non tutti i dati intermedi da dove si parte a dove si arriiva. Per cui ho preferito usare il secondo ascoltatore che processerà un solo evento (mouseRelased) e qundi lancerà un solo pacchetto di dati. Questo fa funzionare correttamente la cosa. Nel caso in cui invece servissero tutti i dati intermedi allora il problema dovra essere studiato con attenzione.
Vediamo cosa fa il codice: all'istanza sd dello slider viene associato l'ascoltatore. Ogni qualvolta modifichiamo il valore dello slider spostando il cursore, al rilascio del mouse, il sorgente (lo slider sd) lancerà l'evento TakEvent che porterà con se il valore selezionato. Lo recupereremo mediante il metodo vrea=ev.getRea(); poi andremo a costruire la stringa da inviare ad Arduino. un 'h' che sta per orizzontale, seguita da una 'm' che sta per valore minimo, poi il valore ed infine un CR '\r' ed un NL 'n', se ad es. il valore fosse 234 avremo: "hm234\r\n". Importante aggiungere i caratteri di fine riga pena malfunzionamenti. Arduino quando inviamo un valore con Serial.println("val") aggiunge automaticamente in coda un "\r\n". La stringa così formattata viene inviata con serialPort.writeString(sm); questa istruzione va messa nel blocco try catch per eventuali eccezioni.
Per completezza riporto anche il codice lato Arduino
byte cmd,sec,tch;
String str,st1,st2;
long pot;
void setup(){
Serial.begin(115200);
cmd=0;
pot=0;
}
void loop(){
str="";
if (Serial.available()>0) cmd = Serial.read();//leggi il primo
switch (cmd) {
case 'h':
if (Serial.available()>0) sec = Serial.read();//leggi il secondo
while(true){//Cicla--------------
if (Serial.available()>0) tch = Serial.read();
if(tch!=10 && tch!=13){//se diverso da ritorno carrello
if(tch>47 && tch<58){ //se è un numero
pot = (pot * 10) + (tch - 48);//forma il numero
}
}else{//se invece è un ritorno carrello
str=String(pot);//trasforma un long in stringa
if(sec=='m'){//se il secondo carattere era una 'm'
st1="m"+str;//concatena
Serial.println(st1);
}else if(sec=='x'){//se il secondo carattere era una 'x'
st2="x"+str;
Serial.println(st2);
}
break;
}
}//End while -------------------
break;
}
cmd=0;
pot=0;
delay(10);//non togliere
}
|
- Si attiva la porta seriale con Serial.begin(115200); poi si entra nel loop
- legge il primo byte in arrivo in cmd
- con il cilo switch case si verifica se questo è un 'h' se si abbiamo bisogno di leggere il secondo carattere in sec. Questo poteva essere potenzialmente una 'm' o una 'x' nel nostro casoè una 'm' lo stokiamo in sec ci servirà dopo
- si prosegue leggendo il valore numerico. Con il ciclo while tireremo fuori uno alla volta i byte successivi fino ad incontrare un CR o NL verificando se sono dei numeri. Se sono numeri verrà ricostruito il valore per intero
- giunti al CR, la stringa è terminata, si formatta la stringa da rispedire aggiungendo al valore un 'm', se avessimo ricevuto un hx234\r\n allora sarebbe stata una x e si reinvia tutto al mittente. Notare che nel buffer di arrivo c'è rimasto un carattere NL (ASCII 10) che verrà scaricato al prossimo loop senza nessuna conseguenza in quanto non intercettato dal ciclo switch case.
Torniamo al Pc ed andiamo ad osservare cosa avviene nella classe interna
105 public class SerialPortReader implements SerialPortEventListener {
106 @Override
107 public void serialEvent(SerialPortEvent event) {
108 System.out.println("Sto leggendo");
109 str="";
110 byte[] by= new byte[1];
111 byte bb=0;
112 try {
113 by = serialPort.readBytes(1);
114 bb=by[0];
115 System.out.println("bb: " + bb);
116 while(true){
117 by = serialPort.readBytes(1);
118 System.out.println("by[0]: " + by[0]);
119 if(by[0]!=10 && by[0]!=13){
120 if(by[0]>47 && by[0]<58){
121 str +=(char)by[0];
122 }
123 }else if(by[0]==13){
124 by = serialPort.readBytes(1);
125 if(bb=='m'){
126 txt="Minimo: "+str;
127 System.out.println("Minimo: " + str);
128 }else if(bb=='x'){
129 txt="Massimo: "+str;
130 System.out.println("Massimo: " + str);
131 }
132 jv1.setText(txt);
133 bb=0;
134 break;
135 }
136 }
137 }catch (SerialPortException ex) {
138 System.out.println(ex);
139 }
140 }
141 } |
Vi ricordate che Arduino ha inviato come risposta una stringa "m234". Come questa arriva il nostro ascoltatore SerialPortReader la processa nel metodo serialEvent. Vediamo come:
- Si crea un array di byte byte[] by= new byte[1]; con un solo elemento. Perchè creare un array non bastava una variabile di tipo byte? No perchè l'istruzione by = serialPort.readBytes(1); mi ritorna comunque un array di byte per cui la vriabile di tipo byte sarebbe incompatibile.
- Si legge il primo byte, che dovrebbe essere una 'm', lo metiamo da parte nella variabile bb (di tipo byte)
- Entriamo nel ciclo while ed iniziamo ad estrarre uno alla volta i byte che seguono verificando che siano dei numeri ed accumuliamoli nella striga str
- Verrà il momento che incontreremo il carattere CR (ASCII 13), la striga è terminata ed abbiamo il suo valore nella variabile str
- Andiamo a ripescare il primo carattere estratto (la 'm') e assembliamo la stringa del messaggio da visualizzare nella label
Attenzione bisogna sottolineare un particolare importante che in Arduino non aveva creato problemi invece qui neprovoca eccome. Osservate la linea 124 qui ci siamo preoccupati di eliminare dal buffer il carattere NL (ASCII 10) scaricandolo con un'altra istruzione by = serialPort.readBytes(1); perchè se non lo facessimo avremmo dei problemi. Dopo aver scaricato il CR si uscirebbe dal ciclo while e dal metodo serialEvent. A questo punto nel buffer rimarrebbe ancora un carattere NL che l'ascoltatore interpreterebbe come un altro arrivo (evento). Si ricomincia il processo:
- la prima istruzione by = serialPort.readBytes(1); mi estrarrebbe il carattere NL e lo metterebbe nella variabile bb
- Entrerebbe nel ciclo while e si avrebbe una seconda istruzione by = serialPort.readBytes(1); la quale questa volta mi creerebbe un'accezione, non trovando più nulla da leggere e il tutto si blocca.
Ora vi chiederete perchè in questa libreria non cè un metoto simile a quello presente in Arduino;
if (Serial.available()>0) che mi verifica se ci sono dati nel buffer e con in if possiamo estrarli solo se ce ne sono. Non so il perchè. Importande quindi è assicurarsi di uscire con il buffer completamente scaricato.
|