Découper une trame

Bonjour,

Je suis actuellement en train d'écrire un programme qui me permet de découper une trame pour en extraire les différentes informations.
La trame est reçue par le port série de l'Arduino, sous la forme : <AAAA,BB.BB,CCCC,DDDD,EEEE,FFF.F,GGG.G,HHH.H,III.I,JJJ.J,KKK.K,LLL.L,QQ,RR,SSSSSSSSS>
Elle ne comporte que des chiffes (entiers et flottant), chaque nombre est séparé d'une virgule. Enfin, la trame commence toujours par "<" et termine par ">".

J'ai tant bien que mal essayé de faire un bout de code mais ce n'est pas du tout concluant :

String trame = "<AAAA,BB.BB,CCCC,DDDD,EEEE,FFF.F,GGG.G,HHH.H,III.I,JJJ.J,KKK.K,LLL.L,QQ,RR,SSSSSSSSS>";
String tableau[100];

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

void loop()
{
  if (Serial.available() > 0)
  {
    for (int i = 0; i < 19; i++) {
      tableau [i] = Serial.readStringUntil(",");
    }
    Serial.println(tableau[i]);
  }
}

Mon but est de mettre chaque valeurs dans un tableau pour les exploiter par la suite.
Exemple : tableau[1] = AAAA; tableau[2] = BB.BB ...

Merci à ceux qui voudront bien m'aider.

i = 19;Oops

Il y a peut-être des erreurs, ça fait un an que je n'ai pas touché à la programmation...

Il faut penser comme si on devait le faire à la main et traduire en C

  • j'attends de recevoir un < (pas fait)
  • je lis une chaine jusqu'à la virgule (ce qui est fait)
  • si je lis >, j'ai fini (on s'est arrêté trop tôt ou trop tard?)

Le problème est que la dernière valeur ne finit pas par virgule mais par > Du coup, on va ajouter le début de la trame suivante. On peut faire:

  • j'attends de recevoir un < (pas fait)
  • je lis une chaine jusqu'à la virgule ou supérieur (moins commode!) et je recommence tant que cela finit par virgule

Une solutions:

  • j’attends un <
  • je positionne pointeur à 0 (je vais écrire la chaine dans tableau[pointeur])
  • je répète:
  • je vide tableau[pointeur] en faisant ( tableau[pointeur] = "" )
  • je lis un caractère
  • si c'est un chiffre ou un point, je l'ajoute à tableau[pointeur]
  • si c'est virgule, j'incrémente pointeur
    jusqu'à lire un >

A réfléchir, si c'est des nombres pourquoi stocker dans un tableau de chaine et pas dans un tableau de nombre?
Pour rentrer un entier en lisant d'abord les poids forts:
répéter: nombre = nombre *10 + valeur_lue
Si c'est un float, on peut si on voit le point compter combien de chiffres suivent et diviser par 10ce_nombre:

  • je répète:
  • je vide tableau[pointeur] en faisant tableau[pointeur] = 0.0
  • je met decimales à -1 (-1 c'est "je n'ai pas vu de point", N>0: il y a N chiffres après la virgule
  • je lis un caractère
  • si c'est un chiffre, je l'ajoute tableau[pointeur] = tableau[pointeur]*10 + chiffreLu
    et si decimales >= 0 j'incrémente décimales
  • si c'est un point je mets decilmales à 0
  • si c'est virgule ou un >
  • si decimales >0 tableau[pointeur] = tableau[pointeur]/10decimales
  • j'incrémente pointeur
    jusqu'à lire un >

strtok est ton ami... :slight_smile:

Un truc du genre :

String trame = "<1111,22.22,3333,4444,5555,666.66,777.7,888.8,999.9,101.1,121.2,314.1,59,26,12345678>";
char trame2[];
float val[];

void setup()
{
  Serial.begin(9600);
  trame2 = trame.c_str();  // <-- pas sûr, à vérifier
// Si ça compile pas,essayer :
// strcpy (trame2, trame.c_str());
}

void loop()
{
  char * pch;
  int i = 0;
  pch = strtok (trame2,"<,>");
  while (pch != NULL)
  {
    pch = strtok (NULL, "<,>");
    if (i>0 && pch!='>') val[i] = atof(pch);
    Serial.print(i); Serial.print(" : "); Serial.println(val[i]);
    ++i;
  }
}

A tester...

NON :

  for (int i = 0; i < 19; i++) {
    tableau [i] = Serial.readStringUntil(",");
  }
  Serial.println(tableau[i]);

OUI:

  for (int i = 0; i < 19; i++) {
    tableau [i] = Serial.readStringUntil(",");
    Serial.println(tableau[i]); // A L'INTERIEUR DE LA BOUCLE
  }
String trame = "<1111,22.22,3333,4444,5555,666.66,777.7,888.8,999.9,101.1,121.2,314.1,59,26,12345678>";

Il manque un truc important, si la trame arrive par la voie série, il faut déjà la lire caractère par caractère pour arriver jusqu'au > final, c'est à dire faire une analyse avant de l'avoir. Le problème est différent si la trame est sans une string.

Moi j'utilise Json et il fait le boulot à ta place :wink:

Autant lire la trame en une seule fois, la stocker dans un tableau de char et la traiter ensuite (après détection du '>'), plutôt que tout faire d'un coup.

lesept:
Autant lire la trame en une seule fois, la stocker dans un tableau de char et la traiter ensuite (après détection du '>'), plutôt que tout faire d'un coup.

tout dépend de la longueur de la trame et s'il faut faire des traitements pendant la réception. Dans l'absolu, oui, si la trame n'est pas trop longue, c'est plus simple de traiter une trame entière d'un coup car on peut vérifier sa validité totale.

éventuellement vous pouvez jeter un oeil à mon petit tuto sur le sujet

Merci à tous pour vos réponses !!
Effectivement lesept j'ai utilisé la fonction strtok. Je ne savais pas qu'elle existait mais elle simplifie beaucoup de choses.
Je suis actuellement en train d'essayer de récupérer la trame, puis de la stocker dans un tableau pour ensuite la découper.
Je regrouperais les 2 fonctions ensemble par la suite.

char string[200];
  int availableBytes = Serial.available();
  for (int i = 0; i < availableBytes; i++)
  {
    string[i] = Serial.read();
  }
Serial.print (string);

J'ai un petit soucis avec mon bout de code j'obtiens quelque chose comme : <<<<<<<<<<<<<<<AAAA,BB.BB,etc...
Comme si la boucle prenais le premier caractère plusieurs fois. Je ne comprends pas très bien ...

Your string isn't terminated by the code snippet you posted, so you can't print it and expect consistent results.

Cherche le tuto de J-M-L qui explique comment lire le port série.

Pense à mettre un 0 à la fin de la chaine, donc après le } de la boucle for :

string[availableBytes] = '\0';

Damned, grilled by Prince !!!

mon petit tuto sur le sujet du port série :wink:

J-M-L tu es mon sauveur ! :grinning:
J'ai suivi ton tuto pour comprendre comment fonctionne le port série (on dirait pas mais c'est pas si simple)
Finalement, j'ai adapté ton code pour qu'il réagisse à ma trame et tout fonctionne !

En bonus j'ai réussi rassembler les 2 codes en 1.
Le port série en lecture c'est beaucoup plus complexe que ça y parait, je ne pensais pas que sans "délimitation" tout partirait en sucette comme avec mon code précédent...

bravo ! :slight_smile:

Une dernière question J-M-L. Comme expliqué, j'arrive bien à récupérer et séparer ma trame pour obtenir :
tableau[0] = AAAA
tableau[1] = BB.BB
...

Le problème que je rencontre c'est quand j'appel un élément du tableau, exemple tableau[1], il me retourne que la première valeur à savoir B et non pas BB.BB...
J'ai loupé quelque chose ou il y a un problème dans mon tableau ?

Ce serait plus facile si tu postais ton code

Le tableau est défini sous forme de char ou de char* ?

Oui postez le code sinon on joue aux devinettes

Effectivement ...

char tableau[19];
char * pch;

char decoupe_message(char trame[100])
{
  pch = strtok (trame, "<>,");
  while (pch != NULL)
  {
    for (int i = 0; i < 19; i++)
    { 
      tableau[i] = pch;
      Serial.print ("tableau[");
      Serial.print  (i);
      Serial.print  ("] : ");
      Serial.println  (pch);
      fonction (i, pch);
      pch = strtok (NULL, "<>,");
      
    }
  }
}

Pour l'explication, ma trame est réceptionnée précédemment dans un tableau.
Ma fonction "decoupe_message" retires tous les caractères "inutiles" et sépare les valeurs dans un tableau (tableau[ ]). A la fin j'obtiens :
tableau[0] = AAAA
tableau[1] = BB.BB ....

Dans une autre fonction, j'appelle i et pch. En fonction de i, il me retourne la valeur qui lui ai associée.
C'est la que ça coince ...