Réception de plusieurs variables par bluetooth

Bonjour,

J’ai en ma disposition deux module HC-05, un maître et un esclave, qui sont connectés ensemble. Mon but est de pouvoir récupérer sur la carte “esclave” les deux valeurs d’un joystick (en X et en Y) pour me permettre à terme de piloter un servo ainsi qu’un moteur.

Pour le moment, j’arrive à envoyer et recevoir une des deux valeurs. Mais j’ai quelques soucis pour le faire avec les deux en même temps. J’ai déja parcouru ce forum et j’ai trouvé et essayé d’adapter le code à mon projet mais ça ne fonctionne toujours pas.

Pourriez-vous svp me faire dire ce que vous pensez du code ci-dessous. Merci

Maître :

#include <SoftwareSerial.h>                        

#define RxD         2
#define TxD         3

               
SoftwareSerial BTSerie(RxD,TxD);

const int x = A0;
const int y = A1;

void setup()
{
    Serial.begin(9600);
    pinMode(x, INPUT);
    pinMode(y, INPUT);
    BTSerie.begin(38400);
    delay(500);
    
}

void loop()
{
   int X = map(analogRead(x),0,1023,0,255);
   int Y = map(analogRead(y),0,1023,0,255);
      
        
        BTSerie.write("+");
        BTSerie.write(X);
        BTSerie.write(" ");
        BTSerie.write(Y);
        //BTSerie.write(Y);
            delay(500);
        Serial.println(X);
        Serial.println(Y);
        BTSerie.flush();
    
}

Esclave :

#include <Servo.h>
#include <SoftwareSerial.h>   //Software Serial Port

#define RxD         10
#define TxD         11
Servo servo;
int AR = 0;
int AV = 0;
int angle;
int i;
int l;
int k;
int c;

char X[3];
char Y[3];
char chaine_recue[9]="+;X;Y";
int hor;
int ver;

const int on_off = 5;
const int input1 = 2;
const int input2 = 3;
 SoftwareSerial BTSerie(RxD,TxD);

void setup() {
  servo.attach(9);
  pinMode(on_off, OUTPUT);
  pinMode(input1, OUTPUT);
  pinMode(input2, OUTPUT);
  Serial.begin(9600);
  BTSerie.begin(38400);

 
}

void loop() {

  
    if(BTSerie.available())

    {
      c = BTSerie.read();
      if (c == '+')
      {
        i = 0;
        while(chaine_recue[i] != '\0')
        
        {
          chaine_recue[i] = BTSerie.read();
          delay(10);
          i++;
        }
            Serial.print("Chaine recue: ");
            Serial.println(chaine_recue);
            StringToString();
      }
    }

    hor ;atoi(X);
    ver ;atoi(Y);
        
     Serial.println(hor);
     Serial.println(ver);
     BTSerie.flush();
     delay(10);

     \\\\\\\\\\\\\\\\\\\\\\\
        \\exploitation des variables hor et ver
     \\\\\\\\\\\\\\\\\\\\\\\
  
     }


  void StringToString () {
    k = 2;
    l = 2;
    while (chaine_recue[k]!=';') {
      X[l] = chaine_recue[k];
      k++;
      l++;
    }
      X[l] = '\0';
      k++;
      l = 0;
      while (chaine_recue[k]!='\0'){
        Y[l] = chaine_recue[k];
        k++;
        l++;
      }
      Y[l] = '\0';
      
    }

Si vous voulez comprendre comment bien écouter le port série vous pouvez jeter un oeil à mon petit tuto sur le sujet

Bonsoir Esteban8020

Voici comme je traite toutes mes communications asynchrones, en réception

//------------------------------------- Tool Input monitor Serial
String monCommandRx;                                                 // Chaine recue
boolean monCommandNew;                                               // Flag nouvelle chaine recue


void setup()
{
 Serial.begin(38400);
}

void loop()
{
 /*---------------------------------------------------------------------------------------------------------------------- 
   Ecoute du moniteur série pour la réception éventuelle d'une commande.
  ----------------------------------------------------------------------------------------------------------------------
 */
 serialEvent();                                                 // Voir void serialEvent()
 if (monCommandNew)                                               // Si reçu une nouvelle commande
 {
 monitCmdRecue(monCommandRx);
 
 monCommandRx = "";                                           // On vide la chaine recue
 monCommandNew  = false;                                      // On reset le flag de nouvelle réception
 }
}

void monitCmdRecue(String commandeRecue)
{
 Serial.println("Commande recue \t" + commandeRecue);
}
 
//===================================== Port série
/*-------------------------------------------------------------------------------------------------
 Lecture du port série en mode interruption sauf Leonardo, Micro et proc ATmega32U4
 https://www.arduino.cc/en/Tutorial/SerialEvent
 La void doit s'appeler serialEvent (serialEvent1() serialEvent2() serialEvent3() Mega)
'--------------------------------------------------------------------------------------------------
*/
void serialEvent()                                                   // Moniteur de l'IDE
{
 while (Serial.available())
 {
 char monChar = (char)Serial.read();                          // Char received from IDE monitor
 if (monChar == '\n')                                         // Si caractère Nouvelle Ligne (Lf 0xA ou 10)
 {
 monCommandNew  = true;                                   // Flag de nouvelle chaine recue
 }
 else
 {
 if (monChar >= ' ') {monCommandRx += monChar;}           // >= ' ' pour supprimer tout les caractères < que espace.
 }
 }
}

A l’émission, il suffit d’envoyer

BTSerie.println(“X=” + String(X));
ou
BTSerie.println(“Y=” + String(Y));
ou
BTSerie.println(“Bouton=” + String(B));

et il s’affiche, à l’autre bout

X=nn
ou
Y=nn
ou
Bouton=nn

Après il ne reste plus qu’à traiter les valeurs reçues en fonction de leur début comme X= ou Y= ou Bouton=nn ainsi:

if(monCommandRx.startsWith(“X=”))

Cette méthode permet d’ajouter des commandes sans se prendre la tête.

Il te faut juste adapter mon code à SoftwareSerial, si tu as le moindre problème, je suis à ta disposition.

(Code exemple ci-dessus, non testé mais très souvent utilisé)

Cordialement
jpbbricole

Salut JP

Cette méthode permet d’ajouter des commandes sans se prendre la tête.

Mais elle a l’inconvenient d’utiliser la classe String au lieu des cString. Sur un petit programme qui ne tourne pas longtemps c’est généralement OK mais si vous voulez un truc stable dans la durée avec un programme un peu long, alors ce n’est pas une bonne idée car vous risquez de morceler la mémoire sans vous en rendre compte et comme votre code ne traite pas les erreurs d’opérations sur Strings, ça peut planter.

Éventuellement lire The Evils of Arduino Strings pour se faire une opinion

Sinon dans votre code

  if (monChar == '\n')                                         // Si caractère Nouvelle Ligne (Lf 0xA ou 10)
  {
    monCommandNew  = true;                                   // Flag de nouvelle chaine recue
  }

il faudrait peut être un break; une fois le ‘\n’ reçu, sinon vous allez concaténer potentiellement deux lignes si les données arrivent assez vite

Bonjour Esteban8020 et bon dimanche

Je te mets le programme complet de réception de chaînes de caractères (commandes) ainsi qu’un semblant de traitement, cette “mécanique”, je l’utilise dans tout mes programmes où c’est nécessaire, parfois pour 3 ports de communications sur un Arduino. Il te sera facile de l’adapter à SoftwareSerial (Si tu as des soucis…)

String monCommandRx;                                                 // Chaine recue
boolean monCommandNew;                                               // Flag nouvelle chaine recue


void setup()
{
 Serial.begin(38400);
}

void loop()
{
 /*---------------------------------------------------------------------------------------------------------------------- 
 Ecoute du moniteur série pour la réception éventuelle d'une commande.
 ----------------------------------------------------------------------------------------------------------------------
 */
 serieEvent();                                                    // Voir void serialEvent()
 if (monCommandNew)                                               // Si reçu une nouvelle commande
 {
 monitCmdRecue(monCommandRx);                                    // Pour le traitement de la commande recue
 
 monCommandRx = "";                                               // On vide la chaine recue
 monCommandNew  = false;                                          // On reset le flag de nouvelle réception
 }
}
/*---------------------------------------------------------------------------------------------------------------------- 
 Traitement des commandes recue, séparation du nom de la commande de son paramètre.
 Commandes reconnues X=   y=   Bouton=
 x=243
 Y=998
 Bouton=milieu
'----------------------------------------------------------------------------------------------------------------------
*/
void monitCmdRecue(String commandeRecue)
{
 Serial.println("Commande recue \t" + commandeRecue);
 
 String commandeNom = commandeRecue.substring(0, commandeRecue.indexOf("="));         // On prend la gauche de la commande recue, j'usqu'avant le =
                                                                                     // comme nom de commande
 commandeNom.toUpperCase();                                                           // Mise en majuscule pour des raisons pratiques 
 String commandeParamStr = commandeRecue.substring(commandeRecue.indexOf("=")+1);       // On prend la droite de la commande recue, depuis après le =
                                                                                 // comme paramètre chaine de caractères
 int commandeParamNum = commandeParamStr.toInt();                                     // Transformer le paramètre string en integer

 if (commandeNom == "X")
 {
 Serial.println("X vaut " + String(commandeParamNum));
 } 
 else if (commandeNom == "Y")
 {
 Serial.println("Y vaut " + String(commandeParamNum));
 }
 else if (commandeNom == "BOUTON")
 {
 Serial.println("Le bouton > " + commandeParamStr + " < a ete presse");
 }
 else
 {
 Serial.println("Commande " + commandeRecue + " inconnue!!");
 }
}
 
//------------------------------------- Lecture port série

void serieEvent()                                                    // Moniteur de l'IDE
{
 while (Serial.available())                                       // Tant qu'il y a des caractères dans le buffer                        
 {
 char monChar = (char)Serial.read();                          // Lecture caractères
 if (monChar == '\n')                                         // Si caractère Nouvelle Ligne (Lf 0xA ou 10) donc fin de ligne
 {
 monCommandNew  = true;                                   // Flag de nouvelle chaine recue
 }
 else
 {
 if (monChar >= ' ') {monCommandRx += monChar;}           // Prise du careactère seulement si plus grand qu'espace (>= ' ') 
                                                     // pour supprimer tout les caractères "Indisérables".
 }
 }
}

Pour envoyer depuis “l’autre bout”:
La terminaison de la chaîne à envoyer/recevoir est le caractère ASCII Nouvelle Ligne (NL ou Ascii 10 ou Ascii 0xD ou \n) \n étant la syntaxe “Arduino”
Donc pour envoyer une commande:

Serial.println(“X=” + String(valeurX));

.println est important puisqu’il ajoute le \n en fin de chaîne comme terminaison. Il ajoute aussi \r (retour chariot CR Ascii 13 ou 0xD). Ces caractère, \n et \r, sont supprimés à la réception.

On peut aussi écrire:
Serial.print(“X=” + String(valeurX) + “\n”);
Serial.print(“X=” + String(valeurX) + “\n\r”);
Serial.print(“Bouton=milieu\n”);

Mais pas:
Serial.print(“X=” + String(valeurX));
Serial.print(“X=” + String(valeurX) + “\r”);
Pas ou mauvaise terminaison.

Cordialement
jpbbricole

jpbbricole:
On peut aussi écrire:
Serial.print("X=" + String(valeurX) + "\n");
Serial.print("X=" + String(valeurX) + "\n\r");
Serial.print("Bouton=milieu\n");

On peut aussi écrire

Serial.print(F("X=");
Serial.print(valeurX);
Serial.write('\n');

ou encore

Serial.print(F("X=");
Serial.println(valeurX);

ce qui a l'avantage de ne pas surcharger ni morceler la mémoire avec la classe String.

(à noter dans le programme d'origine de Esteban8020 qu'il n'utilise pas de manière approprié la fonction [url=https://www.arduino.cc/en/Serial/Write]write()[/url] avec confusion entre binaire, ASCII, octets, caractère seul versus chaîne de caratères)

Bonsoir et merci beaucoup pour votre aide !

jpbbricole, j'ai copié et adapté ton programme à mon module bluetooth et je récupère bien 2 valeurs. Par contre, je dois récupérer les deux valeurs en simultané donc pour le programme "Maitre", au lieu d'écrire cela :

BTSerie.println("X=" + String(X));
BTSerie.println("Y=" + String(Y));
delay(1000);

J'ai dû écrire cela :

BTSerie.println("X=" + String(X));
delay(500);
BTSerie.println("Y=" + String(Y));
delay(500);

J'ai rajouté un delay entre les deux envoyer car sinon ton programme "receveur" ne voit la valeur Y. Au passage, peut-tu m'expliquer dans la ligne ci-dessous comment "commandeParamStr" arrive à prendre chaque chiffre de la valeur à recevoir.

String commandeParamStr = commandeRecue.substring(commandeRecue.indexOf("=")+1);

Et au niveau du delay que j'ai dû rajouter, n'y a t'il pas une autre méthode pour envoyer les 2 variables séparement car on m'a déja dit que la fonction delay() n'était pas toujours forcément une bonne chose pour l'éxécution d'un programme. Et si il n'y a pas d'autre moyen, quel est le delay minimum que je peux appliquer sans risquer de faire bugger l'arduino (le but étant d'avoir le plus grand nombre de récéption de valeurs par seconde histoire d'avoir un mouvement de servomoteur fluide).

Merci encore pour votre aide, vous m'avez sortis d'une belle galère :slight_smile: !

Super, belle avancée, maintenant que c'est en place, on peut affiner.
Le problème de temporisation nécessaire, provient sûrement de la lenteur des servo. Je ne sais pas comment tu les gère, interface spécifique pour servo ou gestion PWM par l'Arduino ?
Une chose est sûre tu ne peux pas laisser l'émetteur envoyer les données sans s'occuper de si le récepteur est disponible. Le mieux est de le faire que ce soit le récepteur qui demande les positions.
On peut étudier ça.

Cordialement
jpbbricole

J’ai rajouté un delay entre les deux envoyer car sinon ton programme “receveur” ne voit la valeur Y. Au passage, peut-tu m’expliquer dans la ligne ci-dessous comment “commandeParamStr” arrive à prendre chaque chiffre de la valeur à recevoir.

Cf mon point en #3 sur le soucis du break qui manque et de la concaténation de deux lignes si les données arrivent trop vite… (ou alors il faut écrire un parser qui tient compte qu’une ligne peut contenir plusieurs définitions de X et Y où alors émettre toujours par paire <xxx,yyy> et analyser en fonction du caractère de début ‘<‘, de la virgule en séparateur et du ‘>’ comme marqueur de fin au lieu de ‘\n’)

Il ne faut pas essayer de faire du code synchrone pour gérer un protocole asynchrone - c’est chercher des ennuis. (Cf le petit tuto que je pointais en #1)

Votre code émetteur n’a pas besoin du tout de delay(), vous pouvez balancer des X=xxx et des Y=yyy aussi vite que vous pouvez depuis le joystick effectivement pour avoir la meilleure réactivité

C’est au processus récepteur qui devra s’en débrouiller pour bâtir des paires (X,Y) si vous avez vraiment besoin des 2 en même temps et que vous ne pouvez pas traiter à la réception au moment t d’un Xt une commande avec (Xt,Yt-1) par exemple

Est-ce que l’idee C’est que le joystick fait bouger 2 moteurs et donc que X et Y finalent sont indépendants… (s’il vous receviez X=12 puis X=14 puis Y=15 et bien vous bougez 2 fois le moteur lié à X et qu’une seule fois celui lié à Y) ?

Exemple: si X c’est la gouverne d’orientation d’un bateau télécommandé et Y la vitesse des hélices - les deux sont bien indépendants et vous auriez intérêt pour minimiser le flot de communication que d’envoyer un X=xxx ou Y=yyy que quand cette valeur doit changer (si vous êtes vitesse Max du moteur, joystick poussé à fond en haut par exemple, vous ne pouvez envoyer que les commandes d’orientation d’un gouvernail)

Bonjour Esteban8020

Esteban8020:
J'ai dû écrire cela :

BTSerie.println("X=" + String(X));
delay(500);
BTSerie.println("Y=" + String(Y));
delay(500);

J'ai rajouté un delay entre les deux envoyer car sinon ton programme "receveur" ne voit la valeur Y.

Cela vient du fait que tu commandes des servo, en génétal assez lents et que, à la réception de la donnée de X, tu envoies l'ordre à X de bouger. Suivant le type de gestion adoptée de tes servo (quelle méthode? Bibliothèque, PWM ou interface spécifique?), pendant la rotation, on ne peut pas faire grand chose que d'attendre (tout ça n'est que suposition).

Le mieux serait d'envoyer les coordonnées de X et Y en même temps avec un séparateur.
Au lieu de
BTSerie.println("X=" + String(X));
ça
BTSerie.println(String(X) + "|" + String(Y));
L'indication "X=" ou "Y=" n'est pas vraiment utile du fait que c'est nous qu'on a fait le programme donc on sait très bien ce qui arrive à l'autre bout.

Esteban8020:
Au passage, peut-tu m'expliquer dans la ligne ci-dessous comment "commandeParamStr" arrive à prendre chaque chiffre de la valeur à recevoir.

String commandeParamStr = commandeRecue.substring(commandeRecue.indexOf("=")+1);

Si la commande est X=1234
la fonction commandeRecue.indexOf("=") nous donne la position du = dans commandeRecue donc cette position +1 est le début de la valeur reçue et cette valeur se termine à la fin de commandeRecue.
La fonction commandeRecue.substring extrait cette partie de commandeRecue ce qui donne une string (ici commandeParamStr) 1234 que l'on transforme en integer par commandeParamStr.toInt()

Regardes ici pour voire les fonctions applicables aux string, ça vaut la peine de maîtriser ça.

Je te mets un résumé du programme pour l'autre façon de transmettre et recevoir.

String monCommandRx;                                                 // Chaine recue
boolean monCommandNew;                                               // Flag nouvelle chaine recue

int commandeX, commandeY;

void setup()
{
	Serial.begin(115200);
	
	monitCmdRecue("123|543");
	
	Serial.println(commandeX);
	Serial.println(commandeY);
	
	while(1);
}

void monitCmdRecue(String commandeRecue)
{
	Serial.println("Commande recue \t" + commandeRecue);
	
	commandeRecue.toUpperCase();                                                         // Mise en majuscule pour des raisons pratiques
	String commandeXstr = commandeRecue.substring(0, commandeRecue.indexOf("|"));        // On prend la gauche de la commande recue, j'usqu'avant le |
	String commandeYstr = commandeRecue.substring(commandeRecue.indexOf("|") +1);        // On prend la droite de la commande recue, j'usqu'aaprès le |
	commandeX = commandeXstr.toInt();                                                    // Transforme la commande string en integer
	commandeY = commandeYstr.toInt();                                                    // Transforme la commande string en integer
}

Cordialement
jpbbricole

jpbbricole:
Bonjour Esteban8020 Cela vient du fait que tu commandes des servo, en génétal assez lents et que, à la réception de la donnée de X, tu envoies l’ordre à X de bouger. Suivant le type de gestion adoptée de tes servo (quelle méthode? Bibliothèque, PWM ou interface spécifique?), pendant la rotation, on ne peut pas faire grand chose que d’attendre (tout ça n’est que suposition).

aie - je ne vais pas être d’accord :slight_smile: mais c’est constructif hein…

Je persiste à dire que cela vient du code proposé (et pas directement de la vitesse des moteurs) et du fait que vous ne vous arrêtez pas à la réception du marqueur de fin ‘\n’ dans ce cas

  while (Serial.available())                                       // Tant qu'il y a des caractères dans le buffer
  {
    char monChar = (char)Serial.read();                          // Lecture caractères
    if (monChar == '\n')                                         // Si caractère Nouvelle Ligne (Lf 0xA ou 10) donc fin de ligne
    {
      monCommandNew  = true;                                   // Flag de nouvelle chaine recue
    }
    else
    {
      if (monChar >= ' ') {
        monCommandRx += monChar; // Prise du careactère seulement si plus grand qu'espace (>= ' ')
      }
      // pour supprimer tout les caractères "Indisérables".
    }
  }

en effet si le buffer d’entrée série contient [color=purple]X=123\nY=22\n[/color] comme Serial.available() va être non nul vous continuez à tourner dans le while (Serial.available()) et donc à bâtir la String monCommandRx qui au final va contenir (puisque vous virez les [color=purple]\n[/color] la chaîne “[color=purple]X=123Y=22[/color]

Lorsque vous appliquez la fonction commandeRecue.indexOf("=") ça vous donne bien la position du PREMIER symbole = dans commandeRecue, donc cette position +1 est bien le début de la valeur reçue mais vous perdez le fait que la commande avait aussi une valeur pour Y.

→ en rajoutant un break après avoir fait le monCommandNew  = true; vous sortez du while et de la fonction serieEvent() et vous donnez à la loop() une chance de lire la nouvelle commande puisque le flag a bien été mis à true. Une fois la loop() passée, on retourne tester serieEvent(), il y aura encore des data dans le buffer d’entrée et on continue et cette fois on capte le Y=22\n

En faisant une pause d’une demi seconde entre l’envoie du X et du Y, l’OP influençait le rythme d’arrivée des données dans le buffer série et la situation où vous auriez [color=purple]X=123\nY=22\n[/color] dans le buffer ne se produit concrètement plus (même s’il y a toujours le risque en fonction de la lourdeur du code côté moteur)

comme exposé par JP ou dans mon post #8, émettre toujours par paire <xxx,yyy> règle ce problème si vous avez une dépendance ente les 2 valeurs.

Bonjour et merci pour vos réponses,

J’ai modifié le programme “Maitre” de sorte qu’il n’envoye des données seulement si les valeurs à envoyer ont changés:

Maître :

void loop()
{
   X = map(analogRead(x),0,1023,0,255);
   Y = map(analogRead(y),0,1023,0,255);

   if (X != previousX) {
     BTSerie.println("X=" + String(X));
     previousX = X;
     Serial.print("X=");
     Serial.println(X);
   }

   delay(30);

   if (Y != previousY) {
     BTSerie.println("Y=" + String(Y));
     previousY = Y;
     Serial.print("Y=");
     Serial.println(Y);
   }  

     delay(30);
    
}

Et dans le code “Esclave”, j’ai intégré la partie qui traite les valeurs de X et Y mais j’ai l’impression que l’arduino “bloque” dans l’éxécution du programme. Je n’ai même plus les valeurs de X et Y qui s’affiche sur le moniteur série. Pensez-vous que ma partie “Exploitation de X et Y” soit mal placé dans le programme ci-dessous ?

Esclave :

#include <Servo.h>
#include <SoftwareSerial.h>   //Software Serial Port

#define RxD         10
#define TxD         11
Servo servo;
SoftwareSerial BTSerie(RxD,TxD);

String monCommandRx;                                                 // Chaine recue
boolean monCommandNew;                                               // Flag nouvelle chaine recue

int X;
int Y;
int AR = 0;
int AV = 0;
int angle;

const int on_off = 5;
const int input1 = 2;
const int input2 = 3;

void setup() {
  servo.attach(9);
  pinMode(on_off, OUTPUT);
  pinMode(input1, OUTPUT);
  pinMode(input2, OUTPUT);
  Serial.begin(38400);
  BTSerie.begin(38400);

  X=0;
  Y=0;
}

void loop()
{
 /*---------------------------------------------------------------------------------------------------------------------- 
 Ecoute du moniteur série pour la réception éventuelle d'une commande.
 ----------------------------------------------------------------------------------------------------------------------
 */
 serieEvent();                                                    // Voir void serialEvent()
 if (monCommandNew)                                               // Si reçu une nouvelle commande
 {
 monitCmdRecue(monCommandRx);                                    // Pour le traitement de la commande recue
 
 monCommandRx = "";                                               // On vide la chaine recue
 monCommandNew  = false;                                          // On reset le flag de nouvelle réception
 }

                                                          // Exploitation de X et Y

    while (125<Y<130) 
  {
    digitalWrite(on_off, LOW);
    AR = 0;
    AV = 0;
  }

  if (Y < 125) 
  { 
    AR = Y;
    AV = 0;
    analogWrite(on_off, AR);
    digitalWrite(input1, LOW);
    digitalWrite(input2, HIGH);
  }

  else if (Y > 130)
  { 
    AV = Y;
    AR = 0;
    analogWrite(on_off, AV);
    digitalWrite(input1, HIGH);
    digitalWrite(input2, LOW);
  }

  while (125 < X < 130)
  {
    angle = 90;
    servo.write(angle);
  }

  if (X < 125) 
  {
    angle = map(X,125,0,90,180);
    servo.write(angle);
  }

  else if (X > 130)
  {
    angle = map(X,130,255,90,0);
    servo.write(angle);
  }
  delay(10);

 

}
/*---------------------------------------------------------------------------------------------------------------------- 
 Traitement des commandes recue, séparation du nom de la commande de son paramètre.
 Commandes reconnues X=   y=   Bouton=
 x=243
 Y=998
 Bouton=milieu
'----------------------------------------------------------------------------------------------------------------------
*/
void monitCmdRecue(String commandeRecue)
{
 Serial.println("Commande recue \t" + commandeRecue);
 
 String commandeNom = commandeRecue.substring(0, commandeRecue.indexOf("="));         // On prend la gauche de la commande recue, j'usqu'avant le =
                                                                                     // comme nom de commande
 commandeNom.toUpperCase();                                                           // Mise en majuscule pour des raisons pratiques 
 String commandeParamStr = commandeRecue.substring(commandeRecue.indexOf("=")+1);       // On prend la droite de la commande recue, depuis après le =
                                                                                 // comme paramètre chaine de caractères
 int commandeParamNum = commandeParamStr.toInt();                                     // Transformer le paramètre string en integer

 if (commandeNom == "X")
 {
 Serial.println("X vaut " + String(commandeParamNum));

 X = commandeParamNum;

 } 
 else if (commandeNom == "Y")
 {
 Serial.println("Y vaut " + String(commandeParamNum));

 Y = commandeParamNum;
 
 }
 else if (commandeNom == "BOUTON")
 {
 Serial.println("Le bouton > " + commandeParamStr + " < a ete presse");
 }
 else
 {
 Serial.println("Commande " + commandeRecue + " inconnue!!");
 }


}
 
//------------------------------------- Lecture port série

void serieEvent()                                                    // Moniteur de l'IDE
{
 while (BTSerie.available())                                       // Tant qu'il y a des caractères dans le buffer                        
 {
 char monChar = (char)BTSerie.read();                          // Lecture caractères
 if (monChar == '\n')                                         // Si caractère Nouvelle Ligne (Lf 0xA ou 10) donc fin de ligne
 {
 monCommandNew  = true;                                   // Flag de nouvelle chaine recue
 }
 else
 {
 if (monChar >= ' ') {monCommandRx += monChar;}           // Prise du careactère seulement si plus grand qu'espace (>= ' ') 
                                                     // pour supprimer tout les caractères "Indisérables".
 }
 }
}

essayez pour voir avec ce code (tapé ici en piquant la fonction de lecture du port série dans mon tuto pointé en #1, non testé)

Mettez la console série Arduino à 115200 bauds, j’ai conservé la vitesse du BT à ce que vous aviez mis soit 38400

sur le joystick:

#include <SoftwareSerial.h>

const byte RxD = 2;
const byte TxD = 3;
SoftwareSerial BTSerie(RxD, TxD);

const byte potXPin = A0; // ces pins analogiques sont en input par défaut
const byte potYPin = A1;

const char marqueurDeFin = '\n';

int previousX = -1, X;
int previousY = -1, Y;

void setup()
{
  Serial.begin(115200);
  BTSerie.begin(38400);
  Serial.println(F("----- TEST EMISSION JOYSTICK -------"));
  delay(500);
}


void loop()
{
  X = map(analogRead(potXPin), 0, 1023, 0, 255);
  if (X != previousX) {
    BTSerie.write('X');
    BTSerie.print(X);
    BTSerie.write(marqueurDeFin);
    previousX = X;
    Serial.print(F("X=")); Serial.println(X);
  }

  Y = map(analogRead(potYPin), 0, 1023, 0, 255); // c'est mieux de laisser un peu de temps entre 2 analogRead de pins différentes, donc on ne groupe pas les appels
  if (Y != previousY) {
    BTSerie.write('Y');
    BTSerie.print(Y);
    BTSerie.write(marqueurDeFin);
    previousY = Y;
    Serial.print(F("Y=")); Serial.println(Y);
  }
}

sur le récepteur avec les moteurs:

#include <SoftwareSerial.h>   //Software Serial Port
const byte RxD = 10;
const byte TxD = 11;
SoftwareSerial BTSerie(RxD, TxD);

const byte tailleMessageMax = 10; // 10 est largement assez grand pour des messages qui sont au max " Xnnn\n" soit 5 caractères... (on aurait pu mettre 5 si on est juste en mémoire) 
char message[tailleMessageMax + 1]; // +1 car on doit avoir un caractère de fin de chaîne en C, le '\0'
const char marqueurDeFin = '\n';

int X = 0, Y = 0;

boolean ecouterBT()
{
  static byte indexMessage = 0; // static pour se souvenir de cette variable entre 2 appels consécutifs. initialisée qu'une seule fois.
  boolean messageEnCours = true;

  while (BTSerie.available() && messageEnCours) {
    int c = BTSerie.read();
    if (c != -1) {
      switch (c) {
        case marqueurDeFin:
          message[indexMessage] = '\0'; // on termine la c-string
          indexMessage = 0; // on se remet au début pour la prochaine fois
          messageEnCours = false;
          break;
        default:
          if (indexMessage <= tailleMessageMax - 1) message[indexMessage++] = (char) c; // on stocke le caractère et on passe à la case suivante
          break;
      }
    }
  }
  return messageEnCours;
}

// LA FONCTION QUI FAIT QUELQUE CHOSE QUAND ON A UN NOUVEL X
void moteurX()
{
  Serial.print(F("X=")); Serial.println(X);
}

// LA FONCTION QUI FAIT QUELQUE CHOSE QUAND ON A UN NOUVEL Y
void moteurY()
{
  Serial.print(F("Y=")); Serial.println(Y);
}

// L'ANALYSEUR DE LIGNE RECUE.
void traiterMessage()
{
  switch (message[0]) {       // on regarde quel est le premier caractère
    
    case 'X':                 // si c'est un X
      X = atoi(message + 1);  // la valeur numérique suit ce caractère, on la lit cf http://www.cplusplus.com/reference/cstdlib/atoi/
      moteurX();              // et on fait quelque chose avec
      break;

    case 'Y':
      Y = atoi(message + 1);
      moteurY();
      break;

    default:
      Serial.println(F("Erreur de communication"));
      break;
  }
}

void setup()
{
  Serial.begin(115200);
  BTSerie.begin(38400);
  Serial.println(F("----- TEST RECEPTION JOYSTICK -------"));
  delay(500);
}

void loop()
{
  if (! ecouterBT()) traiterMessage();
  
  // ici on peut faire autre chose

}

ça donne quoi? (vous devriez voir aussi que le mémoire flash utilisée baisse car j’ai viré la classe String qui prends plus d’1,5Ko)

au passage - cette partie là nest pas bonne
while (125<Y<130)et idem pour

  while (125 < X < 130)
  {
    angle = 90;
    servo.write(angle);
  }

On ne peut pas faire les tests comme cela, il faut séparer en 2 tests ((X > 125) && (X < 130))

et bien sûr il faut que ce qui se trouve dans le while modifie X (ou Y)… sinon vous êtes coincé s’il n’y pas de break dans le corps

enfin n’oubliez pas de tester quand c’est égal à 125 ou 130 dans un des cas, par l’usage de <= ou >= sinon quand le joystick sera sur cette valeur rien ne changera car aucun de vos if ne sera vérifié

Esteban8020:
Pensez-vous que ma partie “Exploitation de X et Y” soit mal placé dans le programme ci-dessous ?

Oui, la partie en-dessous de
if (monCommandNew) // Si reçu une nouvelle commande
{

}
s’exécute en permanence alors qu’il faut le faire que lors de la réception d’une commande.

Je te mets le programme corrigé (pas testé)

#include <Servo.h>
#include <SoftwareSerial.h>   //Software Serial Port

#define RxD         10
#define TxD         11
Servo servo;
SoftwareSerial BTSerie(RxD,TxD);

String monCommandRx;                                                 // Chaine recue
boolean monCommandNew;                                               // Flag nouvelle chaine recue

int X;
int Y;
int AR = 0;
int AV = 0;
int angle;

const int on_off = 5;
const int input1 = 2;
const int input2 = 3;

void setup() {
  servo.attach(9);
  pinMode(on_off, OUTPUT);
  pinMode(input1, OUTPUT);
  pinMode(input2, OUTPUT);
  Serial.begin(38400);
  BTSerie.begin(38400);

  X=0;
  Y=0;
}

void loop()
{
 /*---------------------------------------------------------------------------------------------------------------------- 
 Ecoute du moniteur série pour la réception éventuelle d'une commande.
 ----------------------------------------------------------------------------------------------------------------------
 */
 serieEvent();                                                    // Voir void serialEvent()
 if (monCommandNew)                                               // Si reçu une nouvelle commande
 {
 monitCmdRecue(monCommandRx);                                    // Pour le traitement de la commande recue
 nouvellesValeurs();
 monCommandRx = "";                                               // On vide la chaine recue
 monCommandNew  = false;                                          // On reset le flag de nouvelle réception
 }

                                                          // Exploitation de X et Y
  delay(10);
}

void nouvellesValeurs()
{

	while (125<Y<130)
	{
		digitalWrite(on_off, LOW);
		AR = 0;
		AV = 0;
	}

	if (Y < 125)
	{
		AR = Y;
		AV = 0;
		analogWrite(on_off, AR);
		digitalWrite(input1, LOW);
		digitalWrite(input2, HIGH);
	}

	else if (Y > 130)
	{
		AV = Y;
		AR = 0;
		analogWrite(on_off, AV);
		digitalWrite(input1, HIGH);
		digitalWrite(input2, LOW);
	}

	while (125 < X < 130)
	{
		angle = 90;
		servo.write(angle);
	}

	if (X < 125)
	{
		angle = map(X,125,0,90,180);
		servo.write(angle);
	}

	else if (X > 130)
	{
		angle = map(X,130,255,90,0);
		servo.write(angle);
	}
}
/*---------------------------------------------------------------------------------------------------------------------- 
 Traitement des commandes recue, séparation du nom de la commande de son paramètre.
 Commandes reconnues X=   y=   Bouton=
 x=243
 Y=998
 Bouton=milieu
'----------------------------------------------------------------------------------------------------------------------
*/
void monitCmdRecue(String commandeRecue)
{
	Serial.println("Commande recue \t" + commandeRecue);
 
	String commandeNom = commandeRecue.substring(0, commandeRecue.indexOf("="));         // On prend la gauche de la commande recue, j'usqu'avant le =
                                                                                     // comme nom de commande
	commandeNom.toUpperCase();                                                           // Mise en majuscule pour des raisons pratiques 
	String commandeParamStr = commandeRecue.substring(commandeRecue.indexOf("=")+1);       // On prend la droite de la commande recue, depuis après le =
                                                                                 // comme paramètre chaine de caractères
	int commandeParamNum = commandeParamStr.toInt();                                     // Transformer le paramètre string en integer

	if (commandeNom == "X")
	{
		Serial.println("X vaut " + String(commandeParamNum));

		X = commandeParamNum;

	} 
	else if (commandeNom == "Y")
	{
		Serial.println("Y vaut " + String(commandeParamNum));

		Y = commandeParamNum;
 
	}
	else if (commandeNom == "BOUTON")
	{
		Serial.println("Le bouton > " + commandeParamStr + " < a ete presse");
	}
	else
	{
		Serial.println("Commande " + commandeRecue + " inconnue!!");
	}


}
 
//------------------------------------- Lecture port série

void serieEvent()                                                    // Moniteur de l'IDE
{
 while (BTSerie.available())                                       // Tant qu'il y a des caractères dans le buffer                        
 {
 char monChar = (char)BTSerie.read();                          // Lecture caractères
 if (monChar == '\n')                                         // Si caractère Nouvelle Ligne (Lf 0xA ou 10) donc fin de ligne
 {
 monCommandNew  = true;                                   // Flag de nouvelle chaine recue
 }
 else
 {
 if (monChar >= ' ') {monCommandRx += monChar;}           // Prise du careactère seulement si plus grand qu'espace (>= ' ') 
                                                     // pour supprimer tout les caractères "Indisérables".
 }
 }
}

Cordialement
jpbbricole

Très bien, ça marche mais je ne sais pas si c'est grâce à la modif que tu m'a proposé.
J'ai testé avec ta modif mais ça ne fonctionné pas... j'ai donc retiré des petits bouts de programme jusqu'au moment où il y aurait un changenement et j'ai remarqué une chose intéressante : avec le "while" ça ne fonctionne pas, il faut que je le remplace par un if pour que mon programme tourne correctement. Et idem avec les else if, il faut que je les change en if. Saurai-tu m'expliquer pourquoi ?

Allons allons On ne sait pas de quel while() vous parlez... :slight_smile:

vous avez lu ma réponse #12 ? si vous ne voulez pas tester le code c'est pas important, mais regardez les autres commentaires sur vos tests

Bonsoir Esteban8020

Esteban8020:
Saurai-tu m'expliquer pourquoi ?

Non, parce que j'ai pas tout compris (peut être l'âge) :o
Peux-tu expliquer ta logique?

Cordialement
jpbbricole

Oui c’est vrai je me suis peut-être un peu mal exprimé, donc avec le code que tu m’a donné jpbbricole ça fonctionne mais j’ai dû modifier 2-3 trucs. Dans cette partie :

void nouvellesValeurs()
{

	while (125<Y<130)
	{
		digitalWrite(on_off, LOW);
		AR = 0;
		AV = 0;
	}

	if (Y < 125)
	{
		AR = Y;
		AV = 0;
		analogWrite(on_off, AR);
		digitalWrite(input1, LOW);
		digitalWrite(input2, HIGH);
	}

	else if (Y > 130)
	{
		AV = Y;
		AR = 0;
		analogWrite(on_off, AV);
		digitalWrite(input1, HIGH);
		digitalWrite(input2, LOW);
	}

	while (125 < X < 130)
	{
		angle = 90;
		servo.write(angle);
	}

	if (X < 125)
	{
		angle = map(X,125,0,90,180);
		servo.write(angle);
	}

	else if (X > 130)
	{
		angle = map(X,130,255,90,0);
		servo.write(angle);
	}
}

j’ai changé le while() en if() et j’ai aussi dû changer le else if() en if(), sinon ça ne fonctionnait pas mais je ne sais pourquoi.

Super, alors :), bonne soirée.

Cordialement
jpbbricole

Comme dit plus haut en C++ 125<Y<130ne fait pas ce que vous pensez… cf un post 12