Serial libraries avec Processing >> Arduino

J'essaie de faire une interface Processing pour configurer l'aéroquad.

Lorsque la carte reçoit un caractère, elle renvoie un string, Exemple :

Processing envoi "L", Arduino envoi 0.20,0.20,0.500

Si j'envoie un caractère en solo, pas de problème, par contre si j'envoie une série de caractère différents je devrais avoir ça :

Processing envoi "L", Arduino envoi 0.20,0.20,0.500 processing envoi "Q", Arduino envoi -1,-1,1,-525,-527,-757,0,0,0.00,0.00 Processing envoi "R", Arduino envoi -2,0,2,-426,-467,-679 et ainsi de suite....

Et ce n'est pas ce que j'obtiens... j'ai plutôt ça :

Q 0,0,0,-527,-529,-756,0,0,0.00,0
L .00

L 0.20,0.20,0.50,

Q -1,0,1,-523,-530,-756,0,0,0.00,0.00

L 0.20,0.20,0.50,

Q 0,-1,-1,-522,-531,-759,0,0,0.00,0.00

Q 0.20,0.20,0.50,

Q -1,0,-1,-526,-527,-758,0,0,0.00,0.00

Q 0.20,0.20,0.50,

L -1,-1,-1,-525,-530,-759,0,0,0.00,0.00

L 0.20,0.20,0.50,

L -1,-1,0,-522,-527,-755,0,0,0.00,0.00

L 0.20,0.20,0.50,

Q -1,-1,1,-525,-527,-757,0,0,0.00,0.00

Q 0.20,0.20

avec ce code test :

import processing.serial.*;
Serial myPort;
char send_char;
String portName;
void setup() 
{
  size(490, 200);
  background(51);
  frame.setResizable(true);
  println(Serial.list());
  String portName = Serial.list()[3];
  myPort = new Serial(this, portName, 115200);
}

void draw()
{ myPort.clear();
  new Serial(this, portName, 115200);
  send_char='Q';
  myPort.write(send_char); // envoi un caractere a l'arduino
  while (myPort.available() > 0) {
    String inBuffer = myPort.readString();   
    if (inBuffer != null) {
      print (send_char+" ");
      println(inBuffer);
    }
    myPort.clear();
  }
new Serial(this, portName, 115200);
  send_char='L';
  myPort.write(send_char); // envoi un caractere a l'arduino
  while (myPort.available() > 0) {
    String inBuffer = myPort.readString();   
    if (inBuffer != null) {
      print (send_char+" ");
      println(inBuffer);
    }
    myPort.clear();
  }
}

(Serial libraries)

Comment est ce que je peux faire pour pouvoir changer de caractère et que ça match à chaque fois avec le string entrant ?

Je connais pas très bien processing mais comme c'est à peu près la même histoire dans tous les languages : peut-être faut-il rajouter des delay le temps que l'arduino ait le temps de réagir ? Parce que la si je comprend bien ton code, tu envois le caractère et de suite après tu guettes le buffer d'entrée.

J'ai essayé avec des delay et des boucles for et le problème persiste. Le bout de code que j'ai mis est simplement un test, celui que j'ai pour le quadricoptère est un peu plus conséquent .

Lorsque je change de caractère avec des boutons-radios l'affichage de mes vumètres est figé pendant 10-15 secondes. lorsque je change le caractère destiné à l'Arduino, il ne match pas immédiatement avec le string reçu.

J'ai fait des test en utilisant myPort.stop() et apparemment on ne peut réouvrir un port depuis la boucle draw(), donc a la deuxième lecture du port ça plante.

d'après ce que je constate , ce serait les buffers qui ne se vident pas assez vite et qui serait relu une deuxième fois.

comment faire pour être sur que le buffer est bien vide, j'ai déjà tester avec myPort.clear() et il n'y a pas de changement.

Mais si je comprend tu ouvres et ferme ton port dans la boucle ?? Pourquoi ? Une fois que tu l'as ouvert dans ton setup tu n'as juste qu'à surveiller l'envoi et la réception dans le draw.

Normalement je ne fais pas d'ouverture fermeture dans la boucle... là il se trouve que dans le code publié, je vide le buffer (myport.clear()), mai je ne le ferme jamais. Par contre si je ne refais pas "new Serial(this, portName, 115200);" à chaque fois ça ne fonctionne pas du tout (dans cet exemple).

le port est ouvert dans le setup et ne peut pas être réouvert depuis la boucle draw() (d'après mes essais).

Bon j'ai peut-être trouvé ce qui ne va pas.

Est ce possible de limiter la longueur d'un float en forçant à deux chiffres après la virgule, par exemple pour 0.234567, de le tronquer à 0.234 ?

L'idée saugrenue qui me vient comme ça : tu multiplis par 1 000 000 tu passes en integer, tu divises par 1 000, tu repasses en float et tu redivises par 1000.

Mais j'imagine qu'il doit bien y avoir une autre soluce mais la journée est longue aujourd'hui ... ;D

J'ai fait comme ça :

 String []send_value_filter=new String[7];
      send_value_filter[0]=str(send_char);
      send_value_filter[1]=nf(gyro_Filter,1,2);  //%2.f,nombre
      send_value_filter[2]=";";
      send_value_filter[3]=nf(accel_Filter,1,2);
      send_value_filter[4]=";";
      send_value_filter[5]=nf(time_Filter,1,2);
      send_value_filter[6]=";";
      String joinedsend_value_filter=join(send_value_filter,"");
      String[]list=split (joinedsend_value_filter,',');
      
      myPort.write(list[0]+"."+list[1]+"."+list[2]+"."+list[3]);

parce qu'avec la fonction nf() ça me met une "," dans mon nombre et ça ne passe qu'avec un ".".... ::) et ce ne sont pas les floats avec plus de deux chiffres après la virgule qui me font foirer mon appli.... :P mais ce n'est pas le but de ce post.

J'aimerais tout de même trouver une solution pour que le buffer entrant match avec le caractère sortant.

Je fais au plus simple avec une incrémentation de A+1 pour comptabiliser le nombre de cycle, avec ce code :

void setup() 
{
  size(490, 200);
  background(51);
  frame.setResizable(true);

  println(Serial.list());
  String portName = Serial.list()[3];
  myPort = new Serial(this, portName, 115200);
}

void draw()
{
  frame.setSize(800, 500);
  smooth(); 
  strokeWeight(2.0);
  stroke(0, 102, 153);
  background(150);
receive_value_filter();
println(A+"sortie "+gyro+" "+accel+" "+time);
send_value_filter();
A++;
}


void receive_value_filter()
{
 

    for(int a=0; a<=20;a=a+1){
      myPort.write(str('L')); // envoi un caractere a l'arduino
      while (myPort.available() > 0) {
        String inBuffer = myPort.readString();
        if (inBuffer != null) {
          String[] nums = split(inBuffer, ',');
          if((nums.length)==3){  // 6 pour'R"
            for (int i = 0; i <=2; i = i+1) {
              filtre_value[i] = nums[i];
            }
            myPort.clear();
            gyro_Filter=float(filtre_value[0]+"f");
              accel_Filter=float( filtre_value[1]+"f");
              time_Filter=float( filtre_value[2]+"f");
            gyro=float(filtre_value[0]);
            accel=float(filtre_value[1]);
            time=float(filtre_value[2]);
         }
        }
      }
  }
  
}


void send_value_filter()
{
      gyro_Send=0.2;
      accel_Send=0.3;
      time_Send=0.4;
  String send=('K'+str(gyro_Send)+';'+str(accel_Send)+';'+str(time_Send)+';'+';');
      for(int a=0; a<=40;a=a+1){
      myPort.write(send); // envoi un caractere a l'arduino
      }
      myPort.clear();
}

Le premier nombre affiché avant sortie est le nombre de cycle pour que l’arduino commence à afficher les valeurs que je lui envoi :

30.0sortie 0.2 0.2 0.5
31.0sortie 0.2 0.2 0.5
32.0sortie 0.2 0.2 0.5
33.0sortie 0.2 0.2 0.5
34.0sortie 0.2 0.3 0.4
35.0sortie 0.2 0.3 0.4
36.0sortie 0.2 0.3 0.4

Donc 34 boucles avec chaque fois 40 envois pour que mes valeurs passent :o

J’ai du faire une erreurs quelque part, alors si quelqu’un vois un truc qui cloche… ou bien aurait une piste à explorer :wink:

Je fais des essais avec serialEvent()

On dirait que ça marche pas trop mal. Je vais creuser un peu ;)

Avec une liaison série en continu, le chip Ftdi a tendance à chauffer, je lui ai rajouté un radiateur.

Voici une petite vidéo de ce que donnent mes essais de liaisons série :

lors du lancement de l’application, les valeurs contenues dans la carte sont lues et les sliders sont alignés par rapports à ces dernières.
Ensuite, les valeurs sélectionnées par les sliders sont envoyées à l’arduino, tant que les valeurs de la carte ne sont pas égales aux sliders, le point reste rouge et les valeurs des sliders sont renvoyées à l’Arduino.

Lorsque les valeurs sont équivalentes entre la carte et les sliders, le point devient vert. Tant que l’équivalence est avérée seule une lecture de la carte 2-3 fois par seconde subsiste.

Voici mon code Processing :

import processing.serial.*;
import guicomponents.*;

GWSlider gyroSlider,accelSlider,timeSlider;
Serial myPort;
String portName;

PFont myFont;

float chrono=0;
float oldChrono=0;

float gyro=0;
float accel=0;
float time=0;

String []filtre_value = new String[4];
float gyro_Filter = 0;
float accel_Filter = 0;
float time_Filter = 0;

String inString;
float gyro_receive= 0;
float accel_receive = 0;
float time_receive = 0;

String send;
float gyro_Send= 0;
float accel_Send = 0;
float time_Send = 0;

float A=1;
float Z=1;

int B=1;
int M=0;
int lf = 10;

char send_char;

boolean OK=false;

void setup() 
{
  size(490, 200);
  background(51);
  frame.setResizable(true);

  println(Serial.list());
  String portName = Serial.list()[3];
  myPort = new Serial(this, portName, 115200);

  gyro_Filter=float("1.5f");
  gyroSlider = new GWSlider(this,"blue18px",500,80,260);  //Gyro filter slider 
 // gyroSlider.setInertia(2); 
  gyroSlider.setValueType(GWSlider.DECIMAL);
  gyroSlider.setLimits(1.5f, 0f, 3.0f);
  gyroSlider.setRenderValueLabel(true);

  accel_Filter=float("1.5f");
  accelSlider = new GWSlider(this,"blue18px",500,200,260);
 // accelSlider.setInertia(2); 
  accelSlider.setValueType(GWSlider.DECIMAL);
  accelSlider.setLimits(1.5f, 0f, 3.0f);
  accelSlider.setRenderValueLabel(true);

  time_Filter=float("1.5f");
  timeSlider = new GWSlider(this,"blue18px",500,320,260); 
 // timeSlider.setInertia(2); 
  timeSlider.setValueType(GWSlider.DECIMAL);
  timeSlider.setLimits(1.5f, 0f, 3.0f);
  timeSlider.setRenderValueLabel(true);
  
  myPort.bufferUntil(lf);
}

void draw()
{
  frame.setSize(800, 500);
  smooth(); 
  strokeWeight(2.0);
  background(150);
   
  if (A==0){
    receive_value_filter();
    A=20;
   }
  
     if(B<=10){
       while (B<=8) {
    receive_value_filter();
    if (inString != null) {
    String[] nums = split(inString, ',');
    if((nums.length)==3){  // 6 pour'R"
      gyro  = float(nums[0]);
      accel = float(nums[1]);
      time  = float(nums[2]);
    } B++;
  }set_slider_value();
  }
   B=20;}
  A--;
    if(OK==false){fill(240,30,0);
   ellipse(600, 40, 35, 35);}
   else{fill(0,240,100);
   ellipse(600, 40, 35, 35);}
   noFill();
   fill(80,80,80);
  text(Z+" valeurs reçues: " + inString, 200,50);
  text(str(OK), 200,80);
   text(Z+" valeurs envoyées: " + send, 200,110);

  if (inString != null) {
    String[] nums = split(inString, ',');
    if((nums.length)==3){  // 6 pour'R"
      gyro  = float(nums[0]);
      accel = float(nums[1]);
      time  = float(nums[2]);
    }
  }

  Z++;
   if((gyro!=gyro_Send)||(accel!=accel_Send)||(time!=time_Send)){
    OK=false;
    M++;
  }
  else{
     OK=true;
    M=0;
  }
  if(M>=10){
    OK=false;
    send_value_filter();
     M=0;
   }
  else{ return; }
}

void set_slider_value(){
  
  gyroSlider.setValue(gyro);
 
  accelSlider.setValue(accel);

  timeSlider.setValue(time);

  gyro_Filter =  gyro;
  accel_Filter = accel;
  time_Filter =  time;
}


void handleSliderEvents(GSlider slider) {

  if ((mouseY >65) && (mouseY< 95)){
    gyro_Filter = slider.getValuef();
  }  
  else if ((mouseY >185) && (mouseY< 215)){
    accel_Filter=slider.getValuef();
  }
  else if ((mouseY >305) && (mouseY< 335)){
    time_Filter=slider.getValuef();
  }  
  else{
    return;
  }
   slider.getValuef());
   gyro_Send=float(int(gyro_Filter*100))/100;
  accel_Send=float(int(accel_Filter*100))/100;
  time_Send=float(int(time_Filter*100))/100;
}


void receive_value_filter()
{
 char send_char='L';
  myPort.write('L');
  myPort.clear();
}


void send_value_filter()
{
  gyro_Send=float(int(gyro_Filter*100))/100;
  accel_Send=float(int(accel_Filter*100))/100;
  time_Send=float(int(time_Filter*100))/100;
  send=('K'+str(gyro_Send)+';'+str(accel_Send)+';'+str(time_Send)+';'+';');
  for(int a=0; a<=40;a=a+1){
    myPort.write(send); 
  }
  myPort.clear();
}


void serialEvent(Serial p)
{ 
  inString = p.readString();
  myPort.clear();
}

En l’état, ça ne fonctionne pas de façon très “stable”, les valeurs ne sont pas enregistrées immédiatement dans la carte donc le point reste au rouge pendant de long moment et parfois la liaison est rapide et le point devient vert en 1-2 seconde.

Si quelqu’un à des corrections à me suggérer, c’est vraiment bien venu :wink:

bien que n'y connaissant rien , j'ai remarqué en regardant la vidéo que le temps de réaction été plus long lorsque tu diminues une valeur peut être que je me trompe :-/

C'est aléatoire.

Yessssss :slight_smile: j’ai supprimé une ligne dans cette fonction :

void send_value_filter()
{
  gyro_Send=float(int(gyro_Filter*100))/100;
  accel_Send=float(int(accel_Filter*100))/100;
  time_Send=float(int(time_Filter*100))/100;
  send=('K'+str(gyro_Send)+';'+str(accel_Send)+';'+str(time_Send)+';'+';');
  println("send "+send);
 // for(int a=0; a<=20;a=a+1){    // ligne que j'ai supprimé
    myPort.write(send); 
     myPort.clear();
  //}    // accolade supprimée également

}

Et maintenant ça marche, les valeurs sont équivalentes en moins de 1 seconde à chaque changement sur les sliders.
La capture d’écran me bouffe pas mal de CPU, d’où une certaine lenteur sur la vidéo.

;D ;D ;D

Pourquoi avais-tu mis cette boucle ?

parce que dans les premier essais que j'avais fait si j'envoyais la ligne de caractères 1 seule fois ça ne marchait pas, par contre si je faisais une "rafale", ça avait l'air de fonctionner. Ensuite j'ai trainé cette boucle dans tous mes essais en changeant de temps en temps les valeurs de cette boucle.

j'avais un petit soucis depuis quelques jours.

Je devais envoyer trois caractères différents pour que l'arduino m'envoie des valeurs en rapport avec ces caractères.

La liaison s'initie environ au 5ème caractère envoyé, donc j'ai fait plusieurs boucles imbriquées qui m'envoie une salve de A, une de B et une de C.... et ensuite j'avais toutes les valeurs qui arrivaient en vrac.... toutes les strings l'une derrière l'autre, alors que j'aurais voulu envoyer un caractère, recevoir une string.

J'avais mis un delay(7) après chaque caractère envoyé. En lui mettant une valeur plus grande, par exemple 20, je me suis aperçu que je recevais une string au milieu de chaque salve... En augmentant progressivement je suis arrivé à un delay(85) qui me permet d'avoir des coupures dans mes salves pour permettre au string entrant de prendre sa place dans le buffer et de laisser le temps à ce dernier de se vider.

Pour un peu plus de marge je suis passé à delay(100). ;)