Séparer des données reçues par Bluetooth

Bonjour à tous !

Après de nombreuses recherches, je me décide à vous demander de l’aide pour un problème que j’ai. Voici le contexte : j’ai reçu une petite voiture Arduino pour noël (smart car). Cette dernière est fournie avec un module Bluetooth et une appli téléchargeable pour contrôler ladite voiture. Cette appli ne m’allant pas, je me suis attelé à faire une appli moi-même pour pouvoir contrôler via un joystick (plutôt que des boutons, avancer, reculer, tourner sur soi…). Résultat : mon appli marche et envoie par Bluetooth “XpYm”. X étant la coordonnée X du joystick, Y, la coordonnée Y, m et p étant les délimiteurs.

Pour avoir accès à ces coordonnées en tant qu’int, j’ai fais le code suivant en m’inspirant d’un tuto trouvé :

#include <SoftwareSerial.h>
SoftwareSerial mavoieserie(10, 11);
String trame1 ="";
String trame2 ="";
int CoordX=0;
int CoordY=0;

void setup() {
  Serial.begin(9600);
  mavoieserie.begin(9600);

}

void loop() {

  while (mavoieserie.available()) {
    delay(50);
    trame1 = mavoieserie.readStringUntil('p');
    CoordX=trame1.toInt();
    //delay(50);
    trame2=mavoieserie.readStringUntil('m');
    CoordY=trame2.toInt();
    //delay(50);
    Serial.println("Coord :");
    Serial.println("Coord X = " + trame1 + " " + CoordX);
    Serial.println("Coord Y = " + trame2 + " " + CoordY);
  }
}

Le problème est que je reçois les aberrations suivantes :

Coord :
Coord X = 61 61
Coord Y = 265p27 265
Coord :
Coord X = 20 20
Coord Y = -81 -81
Coord :
Coord X = -53-65 -53
Coord Y = -2-62p-1-67p-77 -2

Les valeurs de X et Y pouvant aller de +250 à -250.

J’ai pensé que mettre des delay le temps que le buffer se remplisse mais rien n’y fait. Bref, je suis un peu perdu, et je suis plutôt débutant en la matière.
Ainsi si vous avez quelques idées, je suis preneur !

Merci bien, bonne journée/soirée !

Ton code devrait marcher, bien qu'utiliser des Strings sur un ARDUINO de base soit déconseillé.
Tu es sûr de ton émetteur ?
Quand on voit "265p27" tout laisserait penser que le premier m est absent.

Imprimez ce que vous recevez pour de vrai sans faire de readString, juste caractère par caractère quand ils arrivent ( avec un read() )

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

Ce serait pas mal;d’avoir;un marquer de début genre envoyer sXpYm pour être sûr de lire en étant bien calé (ou <X,Y> ça ferait plus joli)

Bonjour,

Tout d’abord, je vous remercie pour vos réponses.

hbachetti:
Ton code devrait marcher, bien qu’utiliser des Strings sur un ARDUINO de base soit déconseillé.
Tu es sûr de ton émetteur ?
Quand on voit “265p27” tout laisserait penser que le premier m est absent.

Mon émetteur n’est autre que mon portable, et envoie les coordonnées byte par byte, quand je fais uniquement un read, je reçois les bonnes informations donc il est a priori sûr.
Mais effectivement, on a l’impression qu’un caractère saute pendant un moment.

J-M-L:
Imprimez ce que vous recevez pour de vrai sans faire de readString, juste caractère par caractère quand ils arrivent ( avec un read() )

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

Ce serait pas mal;d’avoir;un marquer de début genre envoyer sXpYm pour être sûr de lire en étant bien calé (ou <X,Y> ça ferait plus joli)

Et grâce à vous et votre tutoriel (tout à fait excellent au passage, agréable et bien rédiger pour que même un novice comme moi puisse comprendre), j’ai compris que le baud rate était problématique.

Ce qui m’a mis sur la piste est le fait qu’en déplaçant mon doigt très lentement sur l’écran, je n’avais aucun soucis, les problèmes venaient lorsque j’accélérais le mouvement. J’ai donc émis l’hypothèse que je ne recevais qu’une partie des infos, or j’envoie dans le pire des cas 10 octets, or je ne peux en afficher que 9 ou 10 (si j’ai bien compris cette histoire de baud rate) avec un petit delay(10).

Du coup, j’ai changé le baud rates de mon receveur (module BT HC05) et miracle, il n’y a presque plus de problème. Je dis presque parce qu’il arrive, mais très rarement qu’une info saute et que je retrouve le même problème qu’avant. Cependant, de manière un brin empirique, j’ai remarqué que le passage dans “toInt()” affiche quand même l’int voulu.

Je vais quand même continuer à creuser cette histoire car un code sans défaut, c’est quand même plus propre ! Et également parce que si je m’en réfère à votre tuto, utiliser des String n’est pas vraiment indiqué et que des char seraient plus appropriés.

Je vous remercie encore une fois, et je vous tiens au jus des changements. Si vous avez d’autres idées/pistes pour les derniers bug, je suis également preneur.

Bien à vous,

pouvez vous modifier l'émetteur pour inclure un marqueur de début?

Bonjour,

Je reviens après quelques soucis vraisemblablement dus au hardware, mon code s'est mis à me renvoyer des valeurs incompréhensibles du jour au lendemain et après avoir enlever tous les fils, essayer avec d'autres cartes/module BT, le code s'est remis à fonctionner "normalement". Bizarre.

J'ai effectivement inclus un délimiteur dans mon code de l'émetteur. Je suis ainsi censé recevoir rXmYp. Cependant des erreurs s'insèrent toujours. C'est comme si le readStringUntil faisait une "boucle" en plus. Je m'explique :

Je reçois parfois ceci :

Coord :
Coord X = 65 65
Coord Y = 87 87
Coord :
Coord X = 70⸮8⸮pr74 70
Coord Y = 90 90
Coord :
Coord X = 76 76
Coord Y = 90 90

La première valeur de la ligne coord X est, par exemple, trame1, donc un string et la deuxième valeur est trame1.toInt(), donc un int. Il semble donc que le code lise le "r", passe ensuite au "m" et fasse un tour jusqu'à un autre "m" sans passer à la ligne de code d'après. Ce qui m'intrigue c'est les points d'interrogations inversés. C'est sûrement des caractères non interprétables reçus, peut-être qu'un delay placé après le "r" pourrait résoudre le problème ? Je vais vérifier dès ce soir.

Également, j'ai testé de mettre un delay(10) juste après le while, il en résulte un plus grand nombre d’erreurs :

Coord X = 73 73
Coord Y = ⸮c2 0
Coord :
Coord X = 90 90
Coord Y = -19 -19
Coord :
Coord X = 104 104
Coord Y = -1 -1
Coord :
Coord X = 112 112
Coord Y = 18 18
Coord :
Coord X = 115 115
Coord Y = 37 37
Coord :
Coord X = 116 116
Coord Y = 56 56
Coord :
Coord X = 113 113
Coord Y = 7f⸮r109m88 7
Coord :
Coord X = ⸮04 0
Coord Y = 98 98

Ce qui m'intrigue ici c'est le fait d'avoir des lettres ("f") qui apparaissent sans avoir été envoyées. Est-ce lors de l'envoie de données ou la réception que les données peuvent être biaisées ?
Le point d'interrogation inversé est encore présent, mais cette fois-ci en premier caractère alors qu'il n'apparaît jamais (du moins sur tous mes tests) sans le delay(10).

Je continue de creuser, le challenge intellectuel de trouver la réponse devenant de plus en plus intéressant.

Encore une fois si jamais vous avez des idées, je suis preneur.

Merci,

AMHA si des erreurs se produisent de temps à autre, il est fort probable que la transmission soit en cause.
Je ne travaille pour ainsi dire jamais en BT, mais plutôt sur RS232.

La mise en place d’un protocole série n’est pas si simple, et sans CRC aucune chance que cela marche à 100%.

Mais le protocole BT implémente un CRC sur 24bits, et je ne comprends carrément pas que des erreurs puissent se produire.
Peux-tu simplement te contenter dans un premier temps d’afficher ce que l’ARDUINO reçoit, sans chercher à parser les données ?
Fais un test avec un read() tout simple, sans readStringUntil() et surtout sans String.

Bonjour,

Voici le code utilisé pour le read() :

#include <SoftwareSerial.h>
SoftwareSerial mavoieserie(0,1);

void setup() {
  Serial.begin(115200);
  mavoieserie.begin(115200);

}

void loop() {

  while (mavoieserie.available()) {
      Serial.print(mavoieserie.read());
     
  }
}

J’obtiens ceci :
114454949109565611211445495310956531121144550491095453112114454956109525011211445521094956112114494810948112114515010945495611211453541094551481121144949511094551511121144950491094557112114206491091121141121145757109454950112114575010953481121151145152109525211211452571094554112114545710945524911211457551094557571121144948571094549514811211449495310945495053112114494956109454949491121151144554109545211211445495510952561121144550481095511211445505210945524911211445495610945494855112114514810945495352112114565210945495451112114494948109454952521121144949531094555571121144948501094549112114555410952491121145453109525111211454561094553112114575410945554811211457561094549495111211454531094549515511211448109454951571121144552541094549525411211445545010945495156112114455554109454948551121144555531094553521121144555501094948112114455556109524911211445555710952551121144552491094811211448109455553112114505710945495155112114535510945495548112114494855109454955511121144952541094549525411211449555410945555411211449525110949531121145552109494948112114511094950531121144554511095552112114454948551094555112114454948501094549485711211445535110945495652112114515410945504956112114495054109455049521121144955531094549545611211449575010945574911211449535010949112114555610949485711211445494810949515111211445555710955571121144549485610945505611211445565410945494948112114495110945494952112114555710945565511211543687383675883856767698383131079751310…

C’est intriguant de ne pas avoir de “-”.

Je vais regarder ceux que signifie tous ces termes obscures pour moi !

Merci de la réponse en tout cas.

SoftwareSerial mavoieserie(0,1);

pourquoi t'as mis 0,1?

Ah. Évidemment le print affiche les caractères en décimal.
Tu devrais utiliser un char :

  while (mavoieserie.available()) {
    char c = mavoieserie.read();
    Serial.print(c);
  }

pourquoi t'as mis 0,1?

C'est vrai. Avant tu utilisais 10 et 11.
0 et 1 c'est le port Serial hardware.

Bonjour,

Effectivement, j'ai modifié le RX et TX de mavoieserie car sur la petite voiture à commander, tous les autres pins sont occupés.
Il y a un shield à mettre sur l'arduino, et ce dernier relie le RX/TX du module BT au port Serial Hardware (donc 0,1 si j'ai bien compris), j'ai ainsi voulu vérifier que ce que je faisais pouvait marcher si j'utilisais ces deux derniers pins.
A priori ça me donne les mêmes résultats que lorsque j'utilise (10,11). Cela pose-t-il problème d'utiliser le port Serial Harware étant donner que SoftWare Serial ne fait qu'émuler une liaison série ?

Ah. Évidemment le print affiche les caractères en décimal.
Tu devrais utiliser un char :

Effectivement, je n'obtiens que du décimal, selon la doc, Serial.read() renvoie le premier byte qui arrive sous forme d'int. Ce dernier est-il donc le décimal de la table ascii correspondant à la valeur reçue ?

Et en utilisant un char j'obtiens :
xr-17m-46pr3m-44pr26m-45pr54m-51pr78m-57pr103m-66pr124m-79pr135m-89psr5m-7pr29m-23pr77m-43pr118m-53pr158m-53pr184m-40pr198m-32psr37m16pr91m-12pr151m-42pr187m-59pr221m-75pr236m-83pr236m-83psr7m48pr16m35pr38m20pr60m5pr93m-16pr125m-37pr165m-62pr182m-76psr-14m66pr-3m52pr30m26pr72m-6pr131m-46pr188m-80pr216m-92pr221m-93psr-63m70pr-60m46pr-40m16pr-8m-18pr28m-43pr70m-76pr107m-105pr127m-122psr31m105pr27m106pr30m107psr59m136pr54m138pr45m142pr47m141pr46m140pr43m135pr43m134psr89m141pr98m143psr78m180pr75m179pr78m167pr92m152pr103m140pr111m132pr116m128pr118m127ps

le "s" reçu provient du fait que mon émetteur envoie "s" lorsque mon doigt n'est plus sur le joystick de mon portable.
Il n'y a ici aucune erreur, ainsi le problème viendrait-il de l'utilisation de la classe string comme mentionné dans le tutoriel de J-M-L ? Mais je ne comprends pas pourquoi l'utilisation de readStringUntil invoquerait des erreurs dans les données reçue alors que la fonction ne fait "que" regarder les données qui passent, s'arrêter au délimiteur, mettre les valeurs dans un String et continuer.

Je vous remercie encore une fois, et je vous remercie surtout de votre patience à l'égard d'un novice un peu perdu :slight_smile:

Je te conseillerais d'utiliser un buffer (une chaîne caractères, char ) que tu remplis avec les caractères que tu lis avec read().
Avec un petit index (int) qui part de zéro (buffer vide) que tu incrémentes au fur et à mesure de la réception.
Comme tu lis des coordonnées X Y, il serait préférable de lire des couples X et Y.
Donc arrête la lecture aussitôt que tu reçois le caractère de début de la trame suivante.
Ensuite, une fonction comme strtok pourra aider à parser les données (extraire les tokens X et Y).
Ensuite tu affiches le résultat, tu vides le buffer (mettre simplement l'index à ZERO), et tu écoutes la suite.

Dlabarts:
mon appli marche et envoie par Bluetooth “XpYm”. X étant la coordonnée X du joystick, Y, la coordonnée Y, m et p étant les délimiteurs.

Bonjour,

Ca ne semble pas être le cas.

Dlabarts:
xr-17m-46pr3m-44pr26m-45pr54m-51pr78m-57pr103m-66pr124m-79pr135m-89psr5m-7pr29m-23pr77m-

Il faudrait peut être se pencher sur le programme d’émission.

Bonjour,

Effectivement, j'ai rajouté le "r" en début d'émission et inversé le "m" et le "p". Mon émetteur envoie donc rXmYp.

Quand au x, il se fait à l'initialisation et ne me dérange pas plus que ça pour le moment, je le retirerais plus tard, et idem pour le s qui correspond à l'action d'enlever son doigt de l'écran.

Il suffit d’attendre de recevoir le ‘r’ puis de lire les deux nombres

#include <SoftwareSerial.h>
SoftwareSerial mavoieserie(10, 11);
String trame1 ="";
String trame2 ="";
int CoordX=0;
int CoordY=0;

void setup() {
  Serial.begin(115200);
  mavoieserie.begin(9600);

}

void loop() {
  if (mavoieserie.available()) {
    mavoieserie.find('r');
    trame1 = mavoieserie.readStringUntil('m');
    CoordX=trame1.toInt();
    trame2=mavoieserie.readStringUntil('p');
    CoordY=trame2.toInt();
    Serial.println("Coord :");
    Serial.println("Coord X = " + trame1 + " " + CoordX);
    Serial.println("Coord Y = " + trame2 + " " + CoordY);
  }
}

Comme le dit hbachetti, il serait préférable d’utiliser des char plutot que des String

Bonjour,

Me revoici une (grosse) semaine après mon dernier message, j’ai pris le temps de faire une pause pour aérer mon cerveau et continuer de chercher et découvrir des fonctions d’arduino dans mon coin.
J’ai notamment réutilisé le tuto de J-M-L afin d’utiliser des char et non plus des String pour lire les données qui arrivent, et j’ai également chercher à parser mes données reçues.
Il en résulte le code suivant qui ne comporte presque plus d’aberrations.
Je pense ces dernières résulte d’une erreur lors du “voyage” des données.

Voici le code :

// On va mettre le RX/TX du module BT sur le RX/TX de l'arduino

#include <SoftwareSerial.h>
#include <string.h>
#include <stdio.h>
SoftwareSerial mavoieserie(0, 1);

const byte tailleMessageMax = 50;
char message[tailleMessageMax + 1];
char coord[tailleMessageMax + 1];
const char MarqueurDeFin = 'p';
int posX;
int posY;

char* pch;

boolean ecouter() {
  static byte indexMessage = 0;
  boolean messageEnCours = true ;

  while (mavoieserie.available() && messageEnCours) {
    char c = mavoieserie.read();
    if (c != -1) {
      switch (c) {
        case MarqueurDeFin:
          Serial.println("Fin");
          message[indexMessage] = '\0';
          indexMessage = 0;
          messageEnCours = false;
          break;
        default:
          if (indexMessage <= tailleMessageMax - 1) {
            message[indexMessage++] = c;

          }
          else {
            Serial.println("j'ignore");
          }
      }
    }
  }
  return messageEnCours;
}


void setup() {
  Serial.begin(115200);
  mavoieserie.begin(115200);
}

void loop() {
  if (!ecouter()) {
    Serial.print("coords : "); Serial.println(message);
    pch = strtok(message, "rm");
    int i = 0;
    while (pch != NULL) {
      if (i == 0) {
        posX = atoi(pch);
        Serial.print("pch = "); Serial.println(posX);
        i++;
      }
      pch = strtok(NULL, "rm");
      if (i == 1) {
        posY = atoi(pch);
        Serial.print("pch = "); Serial.println(posY);
        i++;
      }
    }
    Serial.print("CoordX="); Serial.print(posX); Serial.print(" CoordY="); Serial.print(posY);
  }

}

Donc a priori, j’ai fait le tour ce problème, cependant, une question subsiste, j’ai compris qu’utiliser des char revient à être moins gourmand en mémoire dynamique. Cependant ici, si j’en crois Arduino, j’utilise 20% de mémoire dynamique disponible.

Avec le code suivant : j’en utilise que 17%

#include <SoftwareSerial.h>
SoftwareSerial mavoieserie(10,11 );

String trame ="";
String trame1 ="";
String trame2 ="";

int CoordX=0;
int CoordY=0;

void setup() {
  Serial.begin(115200);
  mavoieserie.begin(115200);

}

void loop() {

  while (mavoieserie.available()) {
    //delay(10);
    
    trame=mavoieserie.readStringUntil('r');
    
    trame1 = mavoieserie.readStringUntil('m');
    CoordX=trame1.toInt();
    
    //delay(50);
    
    trame2=mavoieserie.readStringUntil('p');
    CoordY=trame2.toInt();
    
    //delay(50);
    Serial.println("Coord :");
    Serial.println("Coord X = " + trame1 + ' ' + CoordX);
    Serial.println("Coord Y = " + trame2 + ' ' + CoordY);
  }
}

Ce dernier provoque un peu plus d’erreur mais le passage toInt() les supprime.

Ainsi devrais-je mieux utiliser le deuxième programme qui utilise les strings ?

J’aurais pu m’arrêter aux strings, mais j’ai quand même souhaité découvrir la piste des char et cela m’a permis d’apprendre de nouvelles choses, et je vous en remercie grandement :slight_smile:

Avec le code suivant : j'en utilise que 17%

Normal, une instance de String prend peu de place en mémoire, jusqu'à ce que tu mettes quelque chose dedans, et à ce moment là la chaîne est allouée sur le tas (mémoire dynamique), donc elle n'est pas comptabilisée dans les résultats d'occupation mémoire au niveau de l'exécutable.

De plus cela prend du temps :
Ajouter un caractère à un String revient à allouer un espace plus important, copier le contenu de la chaîne précédente dans le nouvel espace, libérer l'espace précédent.

Avec une chaîne C, tu es obligé de prévoir la place maximale d'entrée de jeu, donc cela prend de la place dès le départ, et l'espace est comptabilisé d'office.

Par contre l'utilisation de String provoque de la fragmentation.
Sur une carte ARDUINO MEGA ce sera moins sensible, sur un ESP8266 encore moins.

Bonjour,

Merci pour la réponse, je comprends mieux à présent.

Merci encore à tous ceux qui ont pris le temps de me répondre, cela m'a permis d'approfondir mes connaissances en programmation.

J'ai également réussi à faire avancer ma petite voiture en avant/arrière et à droite (seulement!) et la satisfaction de la voir bouger est juste géniale ! :slight_smile:

Merci !

Ajouter un caractère à un String revient à allouer un espace plus important, copier le contenu de la chaîne précédente dans le nouvel espace, libérer l’espace précédent.

Merci hbachetti pour avoir donné, dans ma tête, un coup de grâce à String!
C’est exactement ce que je voulais savoir!

Il ne faut pas pour autant s'en priver sur des processeurs ayant plus de RAM.
Sur un ESP8266 ou ESP32, ce serait dommage.
Sur une MEGA pourquoi pas ? mais sans être trop gourmand.