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         //Minimo Orizzontale
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);//invia la stringa
72                  }catch (SerialPortException ex) {
73                      System.out.println(ex);
74                  }
75             }                                                                          
76         });        

 

Qui lo stesso codice ma con un ascoltatore diverso

56        //Minimo Orizzontale
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);//invia la stringa
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){//se diverso da CR e NL
120                         if(by[0]>47 && by[0]<58){  //se è un numero
121                            str +=(char)by[0];//accumola nella stringa 
122                         }
123                     }else if(by[0]==13){//se invece è un CR 
124                         by = serialPort.readBytes(1);//scarica il NL 
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.