Comunicazione seriale Processing Arduino Nano per muovere 5 servo motori

Salve, ho un problema con la comunicazione seriale tra il mio computer, con processing e arduino.

In processing ho creato 5 Scrollbars, da cui riesco a prendere la posizione, e ottenere un valore che va da 0 a 180.
Questi valori però sono praticamente identici per l’arduino, quindi per distinguerli ho provato ad aggiungere 200, 400 etc per ogni scrollbar, così per poi decodificare sull’arduino i valori, dopo la comunicazione seriale tramite usb.
Con un solo servo la comunicazione seriale funziona perfettamente, con due servo, solo il servo connesso al pin 5 funziona, oppure il servo connesso al pin 6 va a scatti.
La comunicazione seriale è disegnata in modo da attivarsi solo quando lo sketch di processing nota un cambio di posizione su una scrollbar, ed è pensata per muovere un servo alla volta.

Questo è il codice di processing

import processing.serial.*;

Serial myPort;

//creazione degli oggetti scrollbar
HScrollbar hs1;
HScrollbar hs2;
HScrollbar hs3;
HScrollbar hs4;
HScrollbar hs5;

PFont f;

int pollice, oldPol;
int indice, oldInd;
int medio, oldMed;
int anulare, oldAnu;
int mignolo, oldMig;

void setup() {
  size (270, 480);
  //fullScreen();
  noStroke();
  background(255);
  
  
  hs1 = new HScrollbar(0, height/5, width, 20, 1);
  hs2 = new HScrollbar(0, height/20*7, width, 20, 1);
  hs3 = new HScrollbar(0, height/2, width, 20, 1);
  hs4 = new HScrollbar(0, height/20*13, width, 20, 1);
  hs5 = new HScrollbar(0, height/5*4, width, 20, 1);
  
  String portName = Serial.list()[1];  //La porta può variare, ricordarsi di cambiarla in base alla porta dell'arduino, quando connesso di solito basta 1
  myPort = new Serial(this, portName, 9600);
  
  printArray(PFont.list());
  f = createFont("Trebuchet MS", 24);
  textFont(f);
  textAlign(LEFT);
}

void draw() {
  //myport.write();
  background(255);
  
  //per il confronto e la scrittura seriale
  oldPol = pollice;
  oldInd = indice;
  oldMed = medio;
  oldAnu = anulare;
  oldMig = mignolo;
  
  //per disegnare le scrollbar
  hs1.update();  hs1.display();
  hs2.update();  hs2.display();
  hs3.update();  hs3.display();
  hs4.update();  hs4.display();
  hs5.update();  hs5.display();
  
  //per ottenere la posizione della scrollbar
  pollice = (int)(((hs1.getPos())/width)*180);
  indice = (int)(((hs2.getPos())/width)*180);
  medio = (int)(((hs3.getPos())/width)*180);
  anulare = (int)(((hs4.getPos())/width)*180);
  mignolo = (int)(((hs5.getPos())/width)*180);
 
  //scrivere sopra le scrollbar il vcalorevalore della stessa
  text("Pollice: " + pollice, 0, height/5-10);
  text("Indice: " + indice, 0, height/20*7-10);
  text("Medio: " + medio, 0, height/2-10);
  text("Anulare: " + anulare, 0, height/20*13-10);
  text("Mignolo: " + mignolo, 0, height/5*4-10);
  
  /*La comunicazione seriale in questo sketch è attivata solo 
  quando viene riconosciuto un cambiamento nella posizione della scrollbar*/
  if (pollice != oldPol) {
    myPort.write(pollice);
  }
  if (indice != oldInd) {
    myPort.write(200 + indice);
  }
  if (medio != oldMed) {
    myPort.write(400 + medio);
  }
  if (anulare != oldAnu) {
    myPort.write(600 + anulare);
  }
  if (mignolo != oldMig) {
    myPort.write(800 + mignolo);
  }
}

class HScrollbar {
  int swidth, sheight;    // width and height of bar
  float xpos, ypos;       // x and y position of bar
  float spos, newspos;    // x position of slider
  float sposMin, sposMax; // max and min values of slider
  int loose;              // how loose/heavy
  boolean over;           // is the mouse over the slider?
  boolean locked;
  float ratio;

  HScrollbar (float xp, float yp, int sw, int sh, int l) {
    swidth = sw;
    sheight = sh;
    int widthtoheight = sw - sh;
    ratio = (float)sw / (float)widthtoheight;
    xpos = xp;
    ypos = yp-sheight/2;
    spos = xpos + swidth/2 - sheight/2;
    newspos = spos;
    sposMin = xpos;
    sposMax = xpos + swidth - sheight;
    loose = l;
  }

  void update() {
    if (overEvent()) {
      over = true;
    } else {
      over = false;
    }
    if (mousePressed && over) {
      locked = true;
    }
    if (!mousePressed) {
      locked = false;
    }
    if (locked) {
      newspos = constrain(mouseX-sheight/2, sposMin, sposMax);
    }
    if (abs(newspos - spos) > 1) {
      spos = spos + (newspos-spos)/loose;
    }
  }

  float constrain(float val, float minv, float maxv) {
    return min(max(val, minv), maxv);
  }

  boolean overEvent() {
    if (mouseX > xpos && mouseX < xpos+swidth &&
       mouseY > ypos && mouseY < ypos+sheight) {
      return true;
    } else {
      return false;
    }
  }

  void display() {
    noStroke();
    fill(204);
    rect(xpos, ypos, swidth, sheight);
    if (over || locked) {
      fill(0, 0, 0);
    } else {
      fill(102, 102, 102);
    }
    rect(spos, ypos, sheight, sheight);
  }

  float getPos() {
    // Convert spos to be values between
    // 0 and the total width of the scrollbar
    return spos * ratio;
  }
  
}

questo invece è il codice dell’arduino

#include <Servo.h>
Servo pollice;
Servo indice;
Servo medio;
Servo anulare;
Servo mignolo;

int val;
int brightness = 0;
int j;

void setup() {

  pollice.attach(5);
  indice.attach(6);
  medio.attach(9);
  anulare.attach(10);
  mignolo.attach(11);
  
  Serial.begin(9600);
}

void loop() {
  if (Serial.available()) 
   { 
     val = Serial.read(); // read it and store it in val

     if (val < 200) {
        pollice.write(val);
      } else if (val < 400) {
          j = val - 200;
          indice.write(j);
        } else if (val < 600) {
            j = val - 400;
            medio.write(j);
          } else if (val < 800) {
              j = val - 600;
              anulare.write(j);
            } else if (val < 1000) {
                j = val - 800;
                mignolo.write(j);
              }
   }
   
   delay(10); 
}

Ho provato a leggere questo post (in inglese) ma non ho capito come fare nel mio caso.

Buonasera, :slight_smile:
essendo il tuo primo post, nel rispetto del regolamento della sezione Italiana del forum (… punto 13, primo capoverso), ti chiedo cortesemente di presentarti IN QUESTO THREAD (spiegando bene quali conoscenze hai di elettronica e di programmazione ... possibilmente evitando di scrivere solo una riga di saluto) e di leggere con molta attenzione tutto il su citato REGOLAMENTO ... Grazie. :slight_smile:

Guglielmo

P.S.: Ti ricordo che, purtroppo, fino a quando non sarà fatta la presentazione nell’apposito thread, nessuno ti potrà rispondere, quindi ti consiglio di farla al più presto. :wink:

up

Il problema sembra legato al fatto che un byte è grande 8 bit e che con 8 bit al massimo puoi rappresentare il valore decimale 255 (256 valori incluso lo zero).

Quindi dovrai da processig spedire almeno due byte e da arduino leggerli entrambe ovviamente sempre una alla volta poiché la seriale lavora un byte per volta.
Ad esempio il primo byte identifica il servo da 1 a 6.
Il secondo byte indica la posizione a cui si dovrà muovere il servo.

Per adesso non stai usando un protocollo dati, ma spedisci byte grezzi, se al posto di questi spedisci codici ascii puoi implementare un protocollo, con byte di inizio trasmissione. Occhio che se scegli questa strada il numero di byte che transitano per ogni comando aumentano, in sostanza 200 e composto da 3 byte.

Lato arduino dovrai trovare il modo di collezionare i byte e convertirli in numeri. Oppure puoi usare parser incluso nella libreria arduino parseInt()

Ciao.

Grazie per la tua risposta.
Seguendo il tuo consiglio sono andato a cercare dei metodi migliori di comunicazione seriale, e sono capitato sul post Serial Input Basics di Robin2, da cui ho preso questi snippet di codice:

void recvWithStartEndMarkers() {
    static boolean recvInProgress = false;
    static byte ndx = 0;
    char startMarker = '<';
    char endMarker = '>';
    char rc;

    while (Serial.available() > 0 && newData == false) {
        rc = Serial.read();

        if (recvInProgress == true) {
            if (rc != endMarker) {
                receivedChars[ndx] = rc;
                ndx++;
                if (ndx >= numChars) {
                    ndx = numChars - 1;
                }
            }
            else {
                receivedChars[ndx] = '\0'; // terminate the string
                recvInProgress = false;
                ndx = 0;
                newData = true;
            }
        }

        else if (rc == startMarker) {
            recvInProgress = true;
        }
    }
}

void parseData() {      // split the data into its parts

    char * strtokIndx; // this is used by strtok() as an index

    strtokIndx = strtok(tempChars,",");      // get the first part - the string
    strcpy(messageFromPC, strtokIndx); // copy it to messageFromPC
 
    strtokIndx = strtok(NULL, ","); // this continues where the previous call left off
    integerFromPC = atoi(strtokIndx);     // convert this part to an integer
}

Non è il codice completo, ho tolto la parte che converte l’ultima parte del messaggio ricevuto in un valore float.

Da quello che ho capito, questo codice riceve tramite la connessione seriale una stringa di testo, che io posso mandare (in questo esempio) dal monitor seriale dell’ide arduino, o (nel mio caso) tramite una connessione aperta da un’applicazione processing.

L’arduino tramite seriale riceve la stringa di testo carattere per carattere, e la conserva in un array definito all’inizio, poi col metodo ParseData, analizza l’array, cominciando da appena dopo il carattere ‘<’, e copia i caratteri mandati in un array secondario, tutti definiti così

const byte numChars = 32;
char receivedChars[numChars];
char tempChars[numChars];

char messageFromPC[numChars] = {0};
int integerFromPC = 0;

Fa ciò solo se riceve una stringa con i caratteri ‘<’ e ‘>’ a inizio e fine, perciò ho modificato una parte del mio codice processing in questo modo:

  if (pollice != oldPol) {
    myPort.write("<a," + pollice + ">");
  } else if (indice != oldInd) {
    myPort.write("<b," + indice + ">");
  } else if (medio != oldMed) {
    myPort.write("<c," + medio + ">");
  } else if (anulare != oldAnu) {
    myPort.write("<d," + anulare + ">");
  } else if (mignolo != oldMig) {
    myPort.write("<e," + mignolo + ">");
  }
}

E ho scritto per l’arduino un metodo per far girare i motori:

void motorTurn() {
      switch(messageFromPC[0]) { 
        case 'a':
          pollice.write(integerFromPC);
          break;
        case 'b':
          indice.write(integerFromPC);
          break;
        case 'c':
          medio.write(integerFromPC);
          break;
        case 'd':
          anulare.write(integerFromPC);
          break;
        case 'e':
          mignolo.write(integerFromPC);
          break;
      }

}

I motori funzionano e sono connessi correttamente, poichè quando resetto l’arduino si posizionano a 90°, ma accendendo il programma di processing e provando a spostare gli slider, non si muove nulla.

Come dovrei fare per riuscire a far muovere i motori?
Grazie mille per la risposta,
Thrap.

Hai controllato che messagefrompc e integerfrompc contengano quello che ti aspetti?

Aggiungi allo switch un default e fai stampare le scritte