Java + Arduino ( 2 parte )

 

In questo articolo riprendiamo l’esempio visto nella prima parte aggiungendo questa volta la possibilità di leggere i valori misurati da alcuni sensori collegati ai pin A0,A1 e A2. Come sensori ho usato un termistore PTC e due trimmer, mentre per le uscite 5,6 e 7 i soliti tre led.

 

Arduino java communication

Per misurare la temperatura ho usato un KTY 81-120 ( un un termistore PTC la cui resistenza aumenta con l’aumentare della temperatura ) non paragonabile ad un LM35 ( più preciso e lineare ) ma comunque sufficiente per l’esempio proposto. I due trimmer, uno da 1k e l’altro da 10k collegati alle entrate A1 e A2, forniscono invece una tensione variabile da 0 a 5V.  Per ognuno la resistenza collegata in serie serve per limitare la corrente inviata sui pin della scheda quando, ruotandoli al minimo, il loro valore si riduce a pochi Ohm. Nella figura seguente invece è riportata la finestra del programma.

 

java arduino interface

 

La comunicazione tra PC e Arduino

Vediamo brevemente come avviene lo scambio di informazioni tra PC e Arduino in modo da comprendere meglio la lettura dei sorgenti riportati più avanti. Ogni 350 millisecondi Arduino invia una stringa composta da tutti i valori letti dai sensori e dallo stato delle uscite, rimanendo sempre in ascolto se dal Pc arrivano eventuali comandi. Il programma sul Pc si accorge dell’arrivo di dati sulla porta, verifica che questi appartengono alla scheda giusta ( attraverso un campo di riconoscimento ) e abilita i controlli sulla finestra. Successivamente si controlla che la stringa venga ricevuta per intero attraverso la ricerca di due caratteri che ne indicano l’inizio (*) e la fine (!) e che i valori dei sensori, anch’essi delimitati da tag inizio-fine,siano arrivati correttamente ( questa fase di controllo è necessaria perchè spesso, per disturbi o interferenze di varia natura, i dati arrivano incompleti o corrotti ). Da questa stringa vengono ricavati i valori corrispondenti alle misure effettuate sui pin A0,A1 e A2 e allo stato delle uscite 5,6 e 7. Nella figura sottostante si osserva com’è composta la stringa trasmessa da Arduino e che il programma sul PC elabora:

 

 

 

Osservando il codice del programma vediamo che, come nel sorgente visto nella prima parte, la gestione delle porte è regolata dalla libreria Jssc-2.8. Grazie a questa si ottiene prima l’elenco delle seriali disponibili sul PC attraverso la funzione SerialPortList.getPortNames(), successivamente l’apertura e la configurazione della porta attraverso l’oggetto SerialPort(). La classe PortReader() invece,attraverso il gestore d’eventi SerialPort.MASK_RXCHAR(), interpreta la stringa inviata da Arduino e visualizza i valori sui controlli corrispondenti. Per quanto riguarda le uscite, queste sono gestite da dei pulsanti che invocano la funzione inviaComando() che a sua volta richiama il metodo serialPort.writeString(…) della libreria. 

 

package interfaccia.arduino.pkg2;

import java.net.URL;
import java.util.Arrays;
import java.util.ResourceBundle;
import javafx.event.ActionEvent;
import javafx.fxml.Initializable;
import javafx.fxml.FXML;
import javafx.scene.control.Button;
import javafx.scene.control.ComboBox;
import javafx.scene.control.ProgressBar;
import javafx.scene.layout.Pane;
import javafx.scene.paint.Color;
import javafx.scene.shape.Circle;
import javafx.scene.text.Text;
import jssc.SerialPort;
import jssc.SerialPortEvent;
import jssc.SerialPortEventListener;
import jssc.SerialPortException;
import jssc.SerialPortList;
import jssc.SerialPortTimeoutException;

/**
 *
 * @author mario
 */
public class mainController implements Initializable {

    @FXML
    private Pane pannelloInput, pannelloOutput, panelStato;

    @FXML
    private ProgressBar prgA0, prgA1, prgA2;

    @FXML
    private Text textTemp, textTemp1, textTemp2;

    @FXML
    private Button btnUsc5, btnUsc6, btnUsc7, btnUscT, btnDisconnetti, btnConnetti;

    @FXML
    private Circle cUsc5, cUsc6, cUsc7;

    @FXML
    private ComboBox<String> cmbox;

    @FXML
    private Text labelStato, labelSeriale;

    SerialPort serialPort;

    private String nomePorta;

    boolean onOff;

    public void Err() throws Exception {

        System.out.println("Valori letti non validi!");
    }

    // Procedura per inviare comandi ad Arduino
    //
    private void inviaComando(String c) throws SerialPortException {

        if (serialPort.isOpened()) {

            serialPort.writeString(c);

        }
    }

    //
    // Connessione ad Arduino
    //
    @FXML
    void Connetti(ActionEvent event) throws InterruptedException, SerialPortTimeoutException {

        nomePorta = cmbox.getValue();
        serialPort = new SerialPort(nomePorta);

        try {

            if (serialPort.isOpened() == false) {

                //Apre la porta seriale
                //
                serialPort.openPort();
                btnDisconnetti.disableProperty().set(false);
                btnConnetti.disableProperty().set(true);
                pannelloOutput.disableProperty().set(false);

                // Imposta alcune proprietà della porta seriale,come
                // ad esempio la velocità,in questo caso impostata a 57600 Baud
                //
                serialPort.setParams(SerialPort.BAUDRATE_57600,
                        SerialPort.DATABITS_8,
                        SerialPort.STOPBITS_1,
                        SerialPort.PARITY_NONE);

                serialPort.setFlowControlMode(SerialPort.FLOWCONTROL_RTSCTS_IN
                        | SerialPort.FLOWCONTROL_RTSCTS_OUT);

                //
                // Aggiunge un gestore d'eventi quando riceve qualcosa sulla
                // porta seriale
                //
                serialPort.addEventListener(new PortReader(), SerialPort.MASK_RXCHAR);

                Thread.sleep(500);

            }

        } catch (SerialPortException ex) {
            System.out.println("Errore nella lettura sulla porta: " + ex);
        }

    }
    
    //
    // Disconnessione e azzeramento dei controlli
    //

    @FXML
    void Disconnetti(ActionEvent event) throws InterruptedException, SerialPortTimeoutException {

        try {

            if (serialPort.isOpened()) {
                serialPort.closePort();
                btnConnetti.disableProperty().set(false);
                btnDisconnetti.disableProperty().set(true);
                panelStato.setStyle("-fx-background-color:gray");
                labelStato.setText("Disconnesso");
                prgA0.setProgress(0);
                prgA1.setProgress(0);
                prgA2.setProgress(0);
                textTemp.setText("?");
                textTemp1.setText("?");
                textTemp2.setText("?");
                cUsc5.setFill(Color.BLACK);
                cUsc6.setFill(Color.BLACK);
                cUsc7.setFill(Color.BLACK);
                pannelloOutput.disableProperty().set(true);

                System.out.println("Disconnesso");
            }

        } catch (SerialPortException ex) {
            System.out.println(ex);
        }

    }

    @Override
    public void initialize(URL url, ResourceBundle rb) {

        //
        // Attraverso l'oggetto SerialPortList si ottiene un elenco
        // delle porte seriali sul pc memorizzate nel'array port
        //
        String[] port = SerialPortList.getPortNames();
        String porta = Arrays.toString(port);
        for (String Porta : port) {

            if (porta.contains("[")) {
                porta = porta.replace("[", "");
                porta = porta.replace("]", "");
            }
            cmbox.getItems().add(Porta); // elenco delle porte disponibili
        }

        btnDisconnetti.disableProperty().set(true);
        btnConnetti.disableProperty().set(false);
        panelStato.setStyle("-fx-background-color:gray");
        pannelloOutput.disableProperty().set(true);

        labelStato.setText("Disconnesso");
        onOff = false;

    }

    // Uscita 5 Arduino
    //
    @FXML
    void cmdUsc5(ActionEvent event) throws SerialPortException {

        inviaComando("p5");
    }

    // Uscita 6 Arduino
    //
    @FXML
    void cmdUsc6(ActionEvent event) throws SerialPortException {

        inviaComando("p6");
    }

    // Uscita 7 Arduino
    //
    @FXML
    void cmdUsc7(ActionEvent event) throws SerialPortException {

        inviaComando("p7");
    }

    //
    // Inverte lo stato delle uscite 5-6-7 in una sola volta
    //
    @FXML
    void cmdTutti(ActionEvent event) throws SerialPortException {

        onOff = !onOff;

        if (onOff) {

            inviaComando("usciteON");
        } else {
            inviaComando("usciteOFF");
        }

    }

    //
    // Classe per leggere i dati da Arduino
    //
    class PortReader implements SerialPortEventListener {

        private String valore;

        public void setValore(String valore) {
            this.valore = valore;
        }

        public String getValore() {
            return valore;
        }

        @Override
        public void serialEvent(SerialPortEvent event) {

            if (event.isRXCHAR() && event.getEventValue() > 0) {
                try {
                    String receivedData = serialPort.readString(event.getEventValue());
                    System.out.println(receivedData);
                    this.setValore(receivedData);

                    //
                    // Verifica che si sia collegata la scheda giusta
                    // e segnala l'avvenuto collegamento
                    //
                    if (this.valore.contains("ARDU328")) {

                        panelStato.setStyle("-fx-background-color:greenyellow");
                        labelStato.setText("Arduino connesso...");
                    } else {
                        panelStato.setStyle("-fx-background-color:gray");
                        labelStato.setText("Disconnesso");
                    }

                    //
                    // Verifica che la stringa inviata da
                    // Arduino sia arrivata per intero
                    //
                    if (this.valore.indexOf("*") > 0 && this.valore.indexOf("!") > 0) {

                        //
                        // Legge lo stato delle uscite 5-6-7
                        //
                        if (this.valore.contains("p5on")) {
                            cUsc5.setFill(Color.GREENYELLOW);
                        } else if (this.valore.contains("p5off")) {
                            cUsc5.setFill(Color.BLACK);
                        }
                        if (this.valore.contains("p6on")) {
                            cUsc6.setFill(Color.GREENYELLOW);
                        } else if (this.valore.contains("p6off")) {
                            cUsc6.setFill(Color.BLACK);
                        }
                        if (this.valore.contains("p7on")) {
                            cUsc7.setFill(Color.GREENYELLOW);
                        } else if (this.valore.contains("p7off")) {
                            cUsc7.setFill(Color.BLACK);
                        }

                        //
                        // Legge la temperatura rilevata dal sensore su A0
                        //
                        if (this.valore.indexOf("@") > 0 && this.valore.indexOf("#") > 0) {

                            int inizio = this.valore.indexOf("@") + 1;
                            int fine = this.valore.indexOf("#");
                            textTemp.setText(this.valore.substring(inizio, fine));
                            prgA0.setProgress(Double.parseDouble(textTemp.getText()) / 100);

                        }

                        //
                        // Legge la tensione fornita sull'ingresso A1
                        //
                        if (this.valore.indexOf("&") > 0 && this.valore.indexOf("%") > 0) {

                            int inizio = this.valore.indexOf("&") + 1;
                            int fine = this.valore.indexOf("%");
                            textTemp1.setText(this.valore.substring(inizio, fine));
                            prgA1.setProgress(Double.parseDouble(textTemp1.getText()) * 0.2);

                        }

                        //
                        // Legge la tensione sull'ingresso A2
                        //
                        if (this.valore.indexOf("$") > 0 && this.valore.indexOf("/") > 0) {

                            int inizio = this.valore.indexOf("$") + 1;
                            int fine = this.valore.indexOf("/");
                            textTemp2.setText(this.valore.substring(inizio, fine));
                            prgA2.setProgress(Double.parseDouble(textTemp2.getText()) * 0.2);

                        }

                    } else {

                        Err();
                    }
                    Thread.sleep(350); // pausa di 350 millisecondi

                } catch (SerialPortException ex) {
                    System.out.println("Errore nel ricevere dati da com-PORT: " + ex);
                } catch (InterruptedException ex) {
                    System.out.println(ex);

                } catch (Exception ex) {
                    System.out.println("?");
                }
            }

        }

    }

}

 

Di seguito è riportato lo sketch completo caricato sull’Arduino. In esso osserviamo come vengono definiti i pin di entrata e uscita, le variabili usate per formare la stringa finale ( pack ) da inviare al PC attraverso la porta seriale, come pure tutte le funzioni necessarie per misurare i valori forniti dai sensori. L’esecuzione del programma viene scandita in due tempi tramite l’utilizzo della funzione millis(), che come è noto conta il tempo trascorso dal momento in cui si fornisce alimentazione alla scheda, e due variabili long ( tver e tver1 ). Ogni 300 mS si invia la stringa formattata come visto in precedenza e ogni 350 mS si controlla se ci sono comandi provenienti dal PC. Per il resto del codice penso non ci sia bisogno di ulteriori spiegazioni.

 

 

//
//
// Programma lettura e invio dati 
//
#define pin5 5
#define pin6 6
#define pin7 7
#define kty A0
#define tensione1 A1
#define tensione2 A2
unsigned long tempo = 0;
unsigned long tver = 0;
unsigned long tver1 = 0;
String u5, u6, u7;
String id = "[ARDU328]"; // Stringa di riconoscimento
String pack;
float temperatura, va1, va2;
String rx;


void setup() {

  pinMode(pin5, OUTPUT);
  pinMode(pin6, OUTPUT);
  pinMode(pin7, OUTPUT);
  pinMode(kty, INPUT);
  pinMode(tensione1, INPUT);
  pinMode(tensione2, INPUT);

  digitalWrite(pin5, HIGH);
  digitalWrite(pin6, HIGH);
  digitalWrite(pin7, LOW);

  Serial.begin(57600);

}

// Misura la temperatura su A0 e imposta il valore da inviare al PC
//
void misuraTempertatura() {

  float temp = analogRead(kty);
  float ukty = 4.9 * temp / 1023.0 ;
  float a = 0.00001874 * 1000;
  float b = 0.007884 * 1000;
  float c = 1000 - 3300 * ukty / (5 - ukty);
  float delta = b * b - 4 * a * c;
  float delta1 = sqrt (delta);
  float x2 = (-b + delta1) / (2 * a);
  float temp1 = x2 + 25 ;
  temperatura = temp1;


}

// Misura la tensione su A1 e imposta il valore da inviare al PC
//
void misuraTensioneA1() {

  const float riferimento = 5;
  const float R1 = 1000;
  const float R2 = 10000;
  const float re = 1023.0 * (R2 / (R1 + R2));
  int valore = analogRead(tensione1);
  float vx = (valore / re) * riferimento;
  va1 = vx;

}

// Misura la tensione su A2 e imposta il valore da inviare al PC
//
void misuraTensioneA2() {

  const float riferimento = 5;
  const float R1 = 1000;
  const float R2 = 1000;
  const float re = 1023.0 * (R2 / (R1 + R2));
  int valore = analogRead(tensione2);
  float vx = (valore / re) * riferimento;
  va2 = vx;

}


void loop() {

  tempo = millis();
 
// Invio dati al PC

  if (tempo > tver + 300) { // Ogni 300 millisecondi rileva i valori dei sensori

    misuraTensioneA1();
    delay(5);
    misuraTensioneA2();
    delay(5);
    misuraTempertatura();
    delay(5);

    // Legge lo stato delle uscite 5-6-7 e imposta il valore da inviare al PC
    //
    if (digitalRead(pin5) == HIGH) {
      u5 = "p5on";
    } else {
      u5 = "p5off";
    }
    if (digitalRead(pin6) == HIGH) {
      u6 = "p6on";
    } else {
      u6 = "p6off";
    }
    if (digitalRead(pin7) == HIGH) {
      u7 = "p7on";
    } else {
      u7 = "p7off";
    }

    // Stringa inviata al pc con tutti i valori dei sensori e delle uscite 5-6-7
    //
    pack += "*" + id  + "@" + (String) temperatura + "#" + "&" + (String) va1 +  "%"  + "$" +  (String) va2 +  "/"  + u5 +  u6 + u7 + "!" ;
    Serial.println(pack);
    pack = "";

    tver = millis();
  }

  // Ricezione dati dal PC
  // Ogni 350 millisecondi legge (se ci sono) i comandi pin arrivo da PC e li esegue
  //
  if (tempo > tver1 + 350) {


    if (Serial.available() > 0)  {

      rx = Serial.readString();

      if (rx.equals("p5")) {

        digitalWrite(pin5, not digitalRead(pin5));
      }
      if (rx.equals("p6")) {

        digitalWrite(pin6, not digitalRead(pin6));
      }
      if (rx.equals("p7")) {

        digitalWrite(pin7, not digitalRead(pin7));
      }
      
      if (rx.equals("usciteON")) {

        digitalWrite(pin5, HIGH);
        digitalWrite(pin6, HIGH);
        digitalWrite(pin7, HIGH);
      }
      if (rx.equals("usciteOFF")) {

        digitalWrite(pin5, LOW);
        digitalWrite(pin6, LOW);
        digitalWrite(pin7, LOW);
      }

    }
    tver1 = millis();
  }

}

 

Qui si può osservare il funzionamento dell’applicazione

 

Qui in basso è possibile scaricare il progetto dell’applicazione ( in formato Netbeans ), lo sketch e la libreria Jssc-2.8.

Interfaccia Arduino 2

Sketch Arduino2

Jssc2.8

Ricordo che sul proprio PC è richiesta la versione 1.8 di Java