[Risolto] - Pilotare servo da seriale

Ho scritto un programma in processing che mi restituisce il valore di una slider sulla seriale (in byte), adesso vorrei che arduino ricevesse quel dato e piloti un servo. Ho fatto diverse prove ma con esito negativo =( .
Qualcuno sa dirmi come fare?? Il valore in byte che ricevo dalla seriale lo devo convertire in int? Se si come?
Grazie in anticipo per le risposte!

Byte copre il range da 0 a 180 percui dovrebbe bastare.
Manda il codice che non Ti funziona e vediamo.
Ciao Uwe

Potresti usare un sistema di comunicazione inviando all'Arduino una stringa da interpretare tipo S110G. Servo 110 gradi.
Nello sketch devi scrivere il codice per interpretare il dato ricevuto e comandare il servo.
Posta il codice che usi attualmente usando i tag CODE (vedi Regolamento punto 7).

Questo è il codice che utilizzo (lato Processing):

/* Programma per pilotare un servo tramite una slider 
 *
 * Program created by Hertz on 20-01-2014
 *
*/

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

ControlP5 cp5;
Serial myPort;

int val;

void setup() {
  size(400,400);       // Dimensione finestra
  cp5 = new ControlP5(this);
  println(Serial.list());
  myPort = new Serial(this, Serial.list()[0], 9600);
  println(myPort);
  
  // Disegno slider
   cp5.addSlider("Gradi")
    .setPosition(200,25)
    .setSize(20,220)
    .setRange(0,180)
    .setValue(0)
    ;
  }
  
void draw() {
  background(0);   // Colore di sfondo nero
 }
 
void Gradi(int gradi) {
  val = gradi;    // Salvo il valore della slider nella variabile(int) val
  byte gradiB = byte(val); // Converto la variabile val da int in byte
  myPort.write(gradiB);    // La invio sulla porta seriale 
  print(gradiB);           // Stampo sulla console il valore inviato in seriale per il debug
  }

Tramite la funzione print, che mi stampa i valori della slider convertiti in byte sulla console, noto che il range è da -128 a 128;
Quando piloto un servo tramite un potenziometro reale uso la funzione map(), allora avrei pensato che lato arduino dovrei prima memorizzare il valore dalla seriale su una variabile byte e poi convertirla in una int, successivamente mappare i valori con la map() è giusto?

Hertz:
....
Tramite la funzione print, che mi stampa i valori della slider convertiti in byte sulla console, noto che il range è da -128 a 128;
Quando piloto un servo tramite un potenziometro reale uso la funzione map(), allora avrei pensato che lato arduino dovrei prima memorizzare il valore dalla seriale su una variabile byte e poi convertirla in una int, successivamente mappare i valori con la map() è giusto?

Mmmm ... in arduino il tipo "byte" è un unsigned da 8 bit ... se ti interessa ricevere valori positivi e negativi ti conviene usare il tipo "char" che, dal reference, è descritto : "The char datatype is a signed type, meaning that it encodes numbers from -128 to 127".

Ricevuto il char, usi direttamente la map per convertirlo nel range che occorre a te ...

Guglielmo

Quindi dovrei fare una cosa del tipo:

char valore;
// codice 

valore = Serial.read();
int val = map(valore,-128,127,0,180);

// codice

Ho capito bene?

Solo una domanda ...
... perché dichiari int val quando gli assegni un valore che va da 0 a 180 ??? Tanto per sprecare SRAM ? Perché una variabile byte è più che sufficiente. :wink:

Guglielmo

P.S. : Mai fare un Serial.read() se prima non si è verificato che ci sia qualche cosa da leggere con Serial.available(). Se non c'è nulla da leggere, Serial.read() ritorna -1 che ... nel tuo caso E' un valore valido ... e come fai poi a distinguere il vero valore -1 dal indicazione di errore "buffer vuoto" ? Quindi ... verifica sempre, prima di leggere, che ci sia qualche cosa da leggere :wink:

Si hai ragione :grin:
Nel codice lo metto sempre il serial.available(); prima del serial.read(); solo che ero dal cellulare e ho evitato di scriverlo....
Quindi a parte per int val che devo correggere (byte val) poi mi basta mappare il valore contenuto in essa e di conseguenza far muovere il servo giusto?
La funzione map()

map(valore, -128, 127, 0, 180);

va bene cosi?

Si, la sintassi della map() è corretta ...
... magari, quello che potresti fare, prima di pilotare direttamente il servo è fare un po' di debug ... ad esempio potresti stamparti sul monitor seriale il valore che ricevi (valore) e il valore mappato (val) ... giusto per essere sicuro di ricevere i corretti valori e di avere la giusta mappatura :wink:

Guglielmo

Avendo il programma della slider che occupa la seriale quando è in funzione e quindi quando restituisce i valori ad Arduino posso aprire lo stesso il Serial monitor?

Ho notato che quando cambio valore sulla slider se resto troppo con il tasto del mouse premuto, anche per qualche secondo, mi riporta diverse volte lo stesso valore, dovrei aggiungere qualche delay?

Hertz:
Avendo il programma della slider che occupa la seriale quando è in funzione e quindi quando restituisce i valori ad Arduino posso aprire lo stesso il Serial monitor?

Mmm ... in teoria, almeno per guardare, non dovrebbero esserci problemi (salvo che, nel momento in cui apri il monitor, il tutto si resetta) ...
Altrimenti i dati li potresti mandare su una porta seriale virtuale, creata con SoftwareSerial ed usare la vera porta seriale HW solo per il debug.

Hertz:
Ho notato che quando cambio valore sulla slider se resto troppo con il tasto del mouse premuto, anche per qualche secondo, mi riporta diverse volte lo stesso valore, dovrei aggiungere qualche delay?

NON uso processing, non lo conosco e quindi ... non ti seguo ...
... comunque ... prova a mettere il codice completo e ... vediamo se ne veniamo fuori :wink:

Guglielmo

Allora ecco il codice (Processing):

/* Programma per pilotare un servo tramite una slider 
 *
 * Program created by Hertz on 20-01-2014
 *
*/

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

ControlP5 cp5;
Serial myPort;

int val;

void setup() {
  size(400,400);       // Dimensione finestra
  cp5 = new ControlP5(this);
  println(Serial.list());
  myPort = new Serial(this, Serial.list()[0], 9600);
  println(myPort);
  
  // Disegno slider
   cp5.addSlider("Gradi")
    .setPosition(200,25)
    .setSize(20,220)
    .setRange(0,180)
    .setValue(0)
    ;
  }
  
void draw() {
  background(0);   // Colore di sfondo nero
 }
 
void Gradi(int gradi) {
  val = gradi;    // Salvo il valore della slider nella variabile(int) val
  byte gradiB = byte(val); // Converto la variabile val da int in byte
  myPort.write(gradiB);    // La invio sulla porta seriale 
  print(gradiB);           // Stampo sulla console il valore inviato in seriale per il debug
  }

Questo codice a seconda di come muovo la slider mi restituisce dei valori che memorizzo nella variabile di tipo int val, successivamente li converto in byte e li salvo nella variabile gradiB (range di valori -128, 127), questa variabile poi la invio sulla seriale dove la riceve questo sketch che carico su Arduino:

#include <Servo.h>

Servo myServo;

char valore;

void setup() {
  Serial.begin(9600);
  Serial.print("Pronto");
  myServo.attach(9);

  }
  
void loop() {
  if (Serial.available()) {
    valore = Serial.read();             // Leggo il valore dalla seriale
    byte val = byte(valore);            // converto il valore da char a byte
    val = map(val,-128, 127, 0, 180);   // imposto il range
    myServo.write(val);            // posiziono il servo 
    delay(15);                     // Aspetto che il servo si posizioni
    }
  }

Dove memorizzo i dati ricevuti nella variabile di tipo char (valore) che riconverto in byte, trasformo il range da -128, 127 a 0, 180 e imposto i gradi del servo a seconda di quel valore, aspetto 15ms affinché il servo si posizioni.

Sulla parte Arduino posso correggere un po' la forma ...

#include <Servo.h>

Servo myServo;

char valore;
byte val;

void setup() {
   Serial.begin(9600);
   Serial.print("Pronto");
   myServo.attach(9);
}

void loop() {
   if (Serial.available()) {
      valore = Serial.read();               // Leggo il valore dalla seriale
      val = map(valore,-128, 127, 0, 180);  // imposto il range
      myServo.write(val);                   // posiziono il servo 
      delay(15);                            // Aspetto che il servo si posizioni
   }
}

... sulla parte processing non metto bocca ... come ho detto non lo conosco ... :frowning: Ad esempio ... come si accorge che stai premendo il mouse e deve trasmettere i dati ?

Guglielmo

Nel software che ho realizzato io di questo se ne occupa interamente la libreria controlP5.
Se non si vuole utilizzare la libreria si deve utilizzare la funzione mousePressed();
Ecco un esempio realizzato da me che disegna un rettangolo bianco sullo schermo se il mouse viene premuto entro quel rettangolo allora esce dal programma:

/* GUI di esempio per pulsante 
 *
 * Program created by Herz on 22-01-2014
 *
*/

PFont f;
void setup() {
size(600,500);    // Dimensione della finestra
f = createFont("Arial",27);
textFont(f);
background(0);  // Colore di sfondo nero
}

void draw() {
  stroke(255);          // Riempimento bianco per il rettangolo
  fill(255);            // Controno bianco per il rettangolo
  rect(30,420,90,40);   // Disegno il rettangolo
  stroke(0);            // Colore testo nero
  fill(0);              // Colore bordo testo nero
  text("esci",50,448);  // Scrivo esci all' interno del rettangolo
  //verifico se la pressione del mouse è avvenuta all interno del pulsante
if (mousePressed && mouseY < 460 && mouseY > 420 && mouseX < 120 && mouseX > 30) {
 exit();  //esco dal programma
   }
  }

P.S.: Grazie dell aiuto!! Domani provo e vedo se funziona.

Ok, quindi se ben capisco, quella libreria controlP5, quanto tu fai click con il mouse richiama la tua funzione "Gradi" ... che trasmette il valore.
Ovviamente se la continua a chiamare in continuazione, finché tieni premuto il mouse, "Gradi" trasmetterà in continuazione un valore ... e su Arduino continuerai a ricevere quel valore ... :roll_eyes:

Bé ... per aggirare la cosa, potresti aggiungere un piccolo controllo in cui, se il valore che ricevi è uguale al valore precedente ... non è cambiato niente e quindi non devi fare nulla ... con un IF è una variabile in più (char vecchioValore) ... te la cavi :wink:

Guglielmo

Ho provato il codice (senza l' aggiunta dell' if) ma non va: ho collegato un display lcd che mi visualizza i valori ricevuti da Processing come debug ma non ricevo valori da 0 a 180 come volevo. Come mai?

Pensi che ho la palla di vetro ??? :astonished: :astonished: :astonished:

Se non descrivi ESATTAMENTE cosa succede (che valori ricevi, come fai a visualizzarli, copia del codice che stai usando, ecc. ecc.) non se ne può venire fuori ...

... e, per inciso, sei sicuro che l'istruzione :

myPort.write(gradiB);    // La invio sulla porta seriale

invii veramente il byte sulla porta e non una sfilza di caratteri ASCII che lo rappresentano ??? Esempio, per un valore pari a 120 ... sei sicuro che invia sulla porta 0x78 e non i tre caratteri 0x31 0x32 0x30 ???

Per questo t'ho detto che quando si lavora con la seriale ... tocca averne un'altra dove fare il debug ... per poter vedere VERAMENTE cosa transita ... :roll_eyes:

Guglielmo

No no, nessuna palla di vetro :smiley: il codice che utilizzo lo già postato precedentemente con l' unica aggiunta che stampo i valori che riceve arduino su un display LCD 16*2 poiché non so come si crea una Seriale virtuale. In ogni caso riecco il codice:

#include <LiquidCrystal.h>
#include <Servo.h>


Servo myServo;
LiquidCrystal lcd(12, 11, 5, 4, 3, 2);

char valore;
byte val;

void setup() {
   Serial.begin(9600);
   lcd.begin(16,2);
   myServo.attach(9);
}

void loop() {
   if (Serial.available()) {
      valore = Serial.read();               // Leggo il valore dalla seriale
      val = byte(valore);
      val = map(valore,-128, 127, 0, 180);  // imposto il range
      lcd.print(val);
      delay(500);
      lcd.clear();
      myServo.write(val);                   // posiziono il servo 
      delay(15);                            // Aspetto che il servo si posizioni
   }
}

Nel codice processing faccio stampare in console il valore che invia sulla seriale: Muovendo la slider ho che:
Slider 0 = Console 0
Slider 127 = Console 127
Slider 128 = Console -128
Slider 180 = Console -1
Quindi invece del LCD dovrei utilizzare una Seriale virtuale. Come si crea? Potresti farmi un esempio?
Grazie della pazienza!!

Aspetta,
prima vediamo se con quello che hai già messo in piedi riesci a vedere qualche cosa ...

Intanto ... perché c'è ancora la riga :

val = byte(valore);

... che non serve assolutamente a nulla ... visto che è seguita immediatamente da una nuova assegnazione a val ...

val = map(valore,-128, 127, 0, 180);  // imposto il range

... e poi, riesci a farti visualizzare sul LCD cosa ritorna l'istruzione :

valore = Serial.read();

... ovvero il valore di "valore" ? Perché quello ci chiarirebbe cosa sta realmente arrivando ... :wink:

Guglielmo

L' avevo tolta, forse non ho salvato :.
Sul LCD ecco cosa leggo:

Slider 1 = LCD 1
Slider 87 = LCD 57
.....
Slider 127 = LCD 7F

Il problema arriva quando si supera 127 nella slider, e cioè quando i valori inviati da processing diventano negativi:

Slider 128 = LCD FFFFFF80

Mentre dovrebbe essere:

Slider 128 = LCD 80

Giusto? Cosa ci può essere di sbagliato?