[Risolto] - Comunicazione seriale

Ho creato questa semplice interfaccia grafica, con la libreria controlP5 per Processing, per comandare tramite slider dei servo che a loro volta azionano le dita e il polso di una mano robotica:

/* Questo sketch è un' prototipo di GUI per la gestione
 * di una mano robotica attuata da dei servo motori (0° to 180°).
 *
 * Program created by Hertz on 10/12/2013
 *
*/

import controlP5.*;

PFont f;
ControlP5 cp5;

// VARIABILI //
int gradiPolso = 0;
int gradiPollice = 0;
int gradiIndice = 0;
int gradiMedio = 0;
int gradiAnulare = 0;
int gradiMignolo = 0;

void setup() {
  size(900,500);
  f = createFont("TRN-27",27);
  textFont(f);
  cp5 = new ControlP5(this);
  
// START CONTROL //  
  
  // Slider comando pollice
  cp5.addSlider("POLLICE")
    .setPosition(250,175)
    .setSize(20,220)
    .setRange(0,180)
    .setValue(0)
    ;
  // Slider comando indice
  cp5.addSlider("INDICE")
    .setPosition(350,175)
    .setSize(20,220)
    .setRange(0,180)
    .setValue(0)
    ;
  // Slider comando medio
  cp5.addSlider("MEDIO")
    .setPosition(450,175)
    .setSize(20,220)
    .setRange(0,180)
    .setValue(0)
    ;
  // Slider comando anulare
  cp5.addSlider("ANULARE")
    .setPosition(550,175)
    .setSize(20,220)
    .setRange(0,180)
    .setValue(0)
    ;
  // Slider controllo mignolo
  cp5.addSlider("MIGNOLO")
    .setPosition(650,175)
    .setSize(20,220)
    .setRange(0,180)
    .setValue(0)
    ;
  
  // Knob rotazione polso
  cp5.addKnob("POLSO")
    .setRange(-90,+90)
    .setValue(0)
    .setRadius(30)
    .setPosition(750,240)
    ;
    
// END CONTROL //

  }

void draw() {
  background(0);
  fill(255);
  stroke(255);
  textSize(40);
  text("ROBOTIC HAND CONTROL",190,70);
  textSize(12);
  text("Created by HERTZ",750,475);
  }
  
  
// CONTROLLO EVENTI //

void POLSO(int knobPolso) {
  gradiPolso = knobPolso;
  println("setting POLSO to "+gradiPolso);
  }
void POLLICE(int sliderPollice) {
  gradiPollice = sliderPollice;
  println("setting POLLICE to "+gradiPollice);
  }
void INDICE(int sliderIndice) {
  gradiIndice = sliderIndice;
  println("setting INDICE to "+gradiIndice);
  }
void MEDIO(int sliderMedio) {
  gradiMedio = sliderMedio;
  println("setting MEDIO to "+gradiMedio);
  }
void ANULARE(int sliderAnulare) {
  gradiAnulare = sliderAnulare;
  println("setting ANULARE to "+gradiAnulare);
  }
void MIGNOLO(int sliderMignolo) {
  gradiMignolo = sliderMignolo;
  println("setting MIGNOLO to "+gradiMignolo);
  }

Ora però non so come far ricevere ad Arduino UNO i valori che riportano le slider.
Fino ad ora ho realizzato solo programmi con la lib. standardFirmata su Arduino, ma in questo caso non è possibile, credo. Qualcuno può spiegarmi come dovrei fare?
Grazie in anticipo per le risposte.

Serial.println(valoreSlider);

lato arduino leggi il valore. come dipende, se latoprocessing usi pritnlnallora come se lo avessi scritto da serialMonitor (mi pare ci siano degli esempi) se usi la write stai inviando il valore grezzo.

io consiglio: un byte (lettera) che indica COSA vuoi muovere, seguito da un byte (0-255) della posizione desirata

Puoi usare anche la libreria firmata per telecomandare Arduino dal PC.
Ciao Uwe

@lesto
Ho modificato lo sketch seguendo il tuo consiglio di mettere una lettera come identificativo+valoreSlider in questo modo:

/* Questo sketch è un' prototipo di un pannello di controllo per la gestione
 * di una mano robotica attuata da dei servo motori (0° to 180°).
 *
 * Program created by Hertz on 13/12/2013
 *
*/

// LIBRERIE //
import controlP5.*;
import processing.serial.*;

PFont f;
ControlP5 cp5;
Serial myPort;

// VARIABILI //

// Utlizzate per conservare i valori delle slider/knob
int gradiPolso = 0;
int gradiPollice = 0;
int gradiIndice = 0;
int gradiMedio = 0;
int gradiAnulare = 0;
int gradiMignolo = 0;

// Ultilizzate come identificativo per le slider/knob
char polso = 'A';
char pollice = 'B';
char indice = 'C';
char medio = 'D';
char anulare = 'E';
char mignolo = 'F';



void setup() {
  size(900,500);
  f = createFont("TRN-27",27);
  textFont(f);
  cp5 = new ControlP5(this);
  println(Serial.list());
  myPort = new Serial(this, Serial.list()[0], 9600);
  println(myPort);
  
// START CONTROL //  
  
  // Slider comando pollice
  cp5.addSlider("POLLICE")
    .setPosition(250,175)
    .setSize(20,220)
    .setRange(0,180)
    .setValue(0)
    ;
  // Slider comando indice
  cp5.addSlider("INDICE")
    .setPosition(350,175)
    .setSize(20,220)
    .setRange(0,180)
    .setValue(0)
    ;
  // Slider comando medio
  cp5.addSlider("MEDIO")
    .setPosition(450,175)
    .setSize(20,220)
    .setRange(0,180)
    .setValue(0)
    ;
  // Slider comando anulare
  cp5.addSlider("ANULARE")
    .setPosition(550,175)
    .setSize(20,220)
    .setRange(0,180)
    .setValue(0)
    ;
  // Slider controllo mignolo
  cp5.addSlider("MIGNOLO")
    .setPosition(650,175)
    .setSize(20,220)
    .setRange(0,180)
    .setValue(0)
    ;
  
  // Knob rotazione polso
  cp5.addKnob("POLSO")
    .setRange(-90,+90)
    .setValue(0)
    .setRadius(30)
    .setPosition(750,240)
    ;
    
 // Bang chiusura mano
 cp5.addBang("CHIUDI")
   .setPosition(100,200)
   .setSize(80,30)
   ;
 // Bang apertura mano
 cp5.addBang("APRI")
   .setPosition(100,270)
   .setSize(80,30)
   ;
 // Bang dimostrazione
 cp5.addBang("DIMOSTRAZIONE")
   .setPosition(100,340)
   .setSize(80,30)
   ;
    
// END CONTROL //

  }

void draw() {
  background(0);
  fill(255);
  stroke(255);
  textSize(40);
  text("ROBOTIC HAND CONTROL",190,70);
  textSize(12);
  text("Created by HERTZ",750,475);
  }
  
  
// CONTROLLO EVENTI E INVIO SERIALE //

void POLSO(int knobPolso) {
  gradiPolso = knobPolso;
  byte IDPolso = byte(polso);
  byte gradA = byte(gradiPolso);
  myPort.write(IDPolso + gradA);
  }
void POLLICE(int sliderPollice) {
  gradiPollice = sliderPollice;
  byte IDPollice = byte(pollice);
  byte gradB = byte(gradiPollice);
  myPort.write(IDPollice + gradB);
  }
void INDICE(int sliderIndice) {
  gradiIndice = sliderIndice;
  byte IDIndice = byte(indice);
  byte gradC = byte(gradiIndice);
  myPort.write(IDIndice + gradC);
  }
void MEDIO(int sliderMedio) {
  gradiMedio = sliderMedio;
  byte IDMedio = byte(medio);
  byte gradD = byte(gradiMedio);
  myPort.write(IDMedio + gradD);
  }
void ANULARE(int sliderAnulare) {
  gradiAnulare = sliderAnulare;
  byte IDAnulare = byte(anulare);
  byte gradE = byte(gradiAnulare);
  myPort.write(IDAnulare + gradE);
  }
void MIGNOLO(int sliderMignolo) {
  gradiMignolo = sliderMignolo;
  byte IDMignolo = byte(mignolo);
  byte gradF = byte(gradiMignolo);
  myPort.write(IDMignolo + gradF);
  }

Ho fatto bene? O c' è qualcosa di sbagliato?

@uwe
Come faccio ad utilizzare firmata con la libreria Servo?

sarebbe bello se mi dicessi “ho provato, e non funzionava, con errore X, allora ho provato cosà ma non funzionava con errore Y”, però mi piace che ti sei impegnato a scrivere il codice, quindi ecco un errore che ho individuato:

 myPort.write(IDMedio + gradD);

sommi i due byte (e tronchi se il risultato supera 512 per via del valore massimo del byte)

devi inviare i due byte semparati, quindi

 myPort.write(IDMedio);
 myPort.write(gradD);

oppure in modo piùelegante puoi usare un byte di array, quindi qualcosa tipo

 myPort.write(new byte[]{IDMedio,gradD) );

sembra una cosa stupida così, main realtà puoi usare una classe (processin è derivato dal java) per creare una struttura “bella”

class Dito{
byte[] messaggio = new byte[2]; //crea un array, di default contiene tutti 0

  public Dito(byte id){
    messaggio[0] = id;
  }

  public setPosizione(byte pos){
    messaggio[1] = pos;
  }

  public esegui(Serial myPort){
    myPort.write( messaggio );
  }
}

e poi la usi

ArrayList<Dito> dita = new ArrayList<>();
void setup(){
    for (byte i=0; i< 4; i++){
       dita.add( new Dito ( i ) );
    }
}
void POLSO(int knobPolso) {
    Dito polso = dita.get(2); //a priori abbiamo deciso che 2 è il polso
    polso.setPosizione((byte)knobPolso);
    polso.scrivi(myPort)
}

Ora, immagiuna che vuoi aggiungere la velocità;
basterà aggiungere un byte all’array messaggio, aggiungere la funzione setVelocità(), e nel costruttore della classe public Dito(byte id)
aggiungere il valore di default.
così voilà, il codice precedente funziona ancora, ma con un semplice settaggio puoi cambiare tutto. Non solo; puoi anche spostare il write nel draw(), e usarlo così per fare movimenti complessi!

(okok troppa carne sul fuoco per ora)

edit:

@uwe
Come faccio ad utilizzare firmata con la libreria Servo?

se c’è il comando lato processing bene, se no dovresti modificare firmdata o fare come stai facendo ora. Io preferisco questo metodo, perchè firmdata castra l’arduino alla velocità della seriale, che è molto lenta in confronto alla potenza reale di arduino

sommi i due byte (e tronchi se il risultato supera 512 per via del valore massimo del byte)

Lesto.
Byte come i Tuoi vorrei avere anch io.
I miei terminano a 255. :wink: :wink: :wink: :wink:
Ciao Uwe

uwefed:
Byte come i Tuoi vorrei avere anch io.
I miei terminano a 255. :wink: :wink: :wink: :wink:

Lui usa gli extended byte che hanno un bit in più :grin:

Ok. Grazie per le risposte e scusa per non aver messo più informazioni...
Ora lato Processing credo di aver quasi tutto pronto, ma su Arduino come faccio a fargli capire quando incontra una lettera e quindi il servo che deve muovere? Devo utilizzare Serial.read() ?

esatto, ci sono vari esempi.
Un consiglio: prima fai il codice lato arduino, inviando le lettere con il serial monitor incluse nell'IDE, così è facile debuggare il tutto, e poi qunado funziona ci attacchi processing.

Lato Arduino ho scritto questo programma che legge il valore di 2 potenziometri ai quali assegna un ID:
Potenziometro_1 = A;
Potenziometro_2 = B;
Li converte in byte e li invia sulla seriale:

// Pin dei potenziometri
int potPin_1 = 5;
int potPin_2 = 4;

// Variabili che contengono i valori letti
int potVal_1 = 0;
int potVal_2 = 0;

// ID Potenziometri
char pt_1 = 'A';
char pt_2 = 'B';


void setup() {
  Serial.begin(9600);  // Avvio la comunicazione seriale a 9600 baud
  }
  
void loop() {
  // Queste variabili contengono l' ultimo valore letto
  int lastValue1 = potVal_1;
  int lastValue2 = potVal_2;
  
  // Leggo i valori dai potenziometri
  potVal_1 = analogRead(potPin_1);
  potVal_2 = analogRead(potPin_2);
  
  // Se i potenziometri vengono mossi scrivine il valore sulla seriale
  // N.B. i valori prima di essere inviati sulla seriale vengono convertiti in byte
  if (potVal_1 != lastValue1) {
    Serial.print(byte(pt_1));
    Serial.println(byte(potVal_1));
    }
  if (potVal_2 != lastValue2) {
    Serial.print(byte(pt_2));
    Serial.println(byte(potVal_2));
    }
    delay(500);  
  }

Invece su Processing ho scritto questo:

import processing.serial.*;

Serial myPort;

void setup() {
  size(400,400);
  background(0);
  println(Serial.list());
  myPort = new Serial(this, Serial.list()[0], 9600);
  }
  
void draw() {
  byte[] inBuffer = new byte[5];
  if (myPort.available() > 0) {
    inBuffer = myPort.readBytes();
    myPort.readBytes(inBuffer);
     if (inBuffer != null) {
       String myString = new String(inBuffer);
       println(myString);
    }
   }
  }

che mi legge i valori della seriale e me li stampa. Fin qui tutto bene.
Ovviamente questa è una prova per vedere se funziona, solo che non ho capito come fare, una volta ricevuti i dati dalla seriale, a capire quando si incontra un ID.
Qualcuno potrebbe spiegarmi come fare?

bhe i valori dei potenzionmetri sono numerici (trasformati in lettere dalla print, secondo la tabella ascii), mentre gli id sono lettere... quindi tu alla prima lettera (in tal caso puoi semplicemente cercare solo 'A' o 'B') ti fermi e leggi tutte le cifre seguenti (che formano il valore letto, in stringa, da ritrasformare in numero)

lesto:
bhe i valori dei potenzionmetri sono numerici (trasformati in lettere dalla print, secondo la tabella ascii), mentre gli id sono lettere… quindi tu alla prima lettera (in tal caso puoi semplicemente cercare solo ‘A’ o ‘B’) ti fermi e leggi tutte le cifre seguenti (che formano il valore letto, in stringa, da ritrasformare in numero)

Ma non so come “fermarmi” per leggere le cifre seguenti: come dovrei fare?

lato processing non usare

inBuffer = myPort.readBytes();
myPort.readBytes(inBuffer);
if (inBuffer != null) {
String myString = new String(inBuffer);
println(myString);
}

ma usa una normale

myPort.read()

che legge il prossimo carattere..

OK, grazie delle risposte ho risolto finalmente!!!! :slight_smile: :slight_smile: :slight_smile: