Skyduino:~#
Articles
arduino, programmation

[Arduino] Parser une chaine de caractéres

Bonjour tout le monde !

C’est ma semaine de partiels les articles se font donc rares.
Je vais tenter demain ou vendredi de finir mon article sur le DF3120 mais je ne garantie rien 😉

Histoire de vous donner votre dose d’arduino voici un petit morceau de code réalisé pour un membre du forum arduino.
Le but est simple, parser (= parcourir et interpréter) une chaine de caractères reçue sur le port série pour en extraire les données utiles quelle contient.

La chaine type utilisait dans le cadre de ce code a cette forme :
$ texte;valeur;index;diviseur;°c/f;

Le code est facilement modifiable pour coller à n’importe quelle format, il suffit de suivre le principe de base 😉

Sans plus attendre, le code :

// Buffer qui va contenir la trame série
#define TAILLE_MAX 32

// Buffer qui va contenir le texte (taille du buffer / 2, oui j'ai mis ça au pif)
char texte[TAILLE_MAX / 2], unite;
// Données utiles extraites
int valeur, index, diviseur;

// setup()
void setup() {
  // Initialisation du port série
  Serial.begin(9600);
}

// loop()
void loop() {
  // Récupération d'une trame + parsing
  if(recupInfo(texte, &valeur, &index, &diviseur, &unite)) {
    Serial.println("Erreur de trame !");
    return;
  }
  
  // Affichage
  Serial.print("Texte: ");
  Serial.println(texte);
  Serial.print("Valeur: ");
  Serial.println(valeur);
  Serial.print("Index: ");
  Serial.println(index);
  Serial.print("Diviseur: ");
  Serial.println(diviseur);
  Serial.print("Unité: ");
  Serial.println(unite);
}

/*
 * Parse une chaine au format $ texte;valeur;index;diviseur;°c/f;
 * Et retourne la valeur des différents champs.
 */
int recupInfo(char *texte, int *valeur, int *index, int *diviseur, char *unite) {
  char c, buf[TAILLE_MAX + 1];
  unsigned char i = 0;

  /* Attente du $ espace */
  do {
    /* Attente de 2 char sur le port série */
    while(Serial.available() < 2);

    /* Tant que chaine != $ espace -> boucle */
  } 
  while(Serial.read() != '$' && Serial.read() != ' ');

  /* Remplissage du buffer */
  do{
    /* Si la chaine a dépassé la taille max du buffer*/
    if(i == (TAILLE_MAX + 1)) 
      /* retourne 1 -> erreur */
      return 1;

    /* Attente d'un char sur le port série */
    while(Serial.available() < 1);

    /* Tant que char != 0x1A (fléche) -> boucle */
  } 
  while((buf[i++] = Serial.read()) != 0x1A);

  /* Cloture la chaine de char */
  buf[i] = '\0';

  /* Copie le texte au début de buf[] dans texte[] */
  i = 0;
  while((texte[i] = buf[i]) != ';') i++;
  texte[i] = '\0';

  /* Parse la chaine de caractères et extrait les champs */
  if(sscanf(buf + i, ";%d;%d;%d;%*c%c;", valeur, index, diviseur, unite) != 4)
    /* Si sscanf n'as pas pu extraire les 4 champs -> erreur*/
    return 1;

  /* retourne 0 -> pas d'erreur */
  return 0;
}

Voila voila Enjoy ! 🙂

Discussion

17 réflexions sur “[Arduino] Parser une chaine de caractéres

  1. Fonctionne parfaitement!!

    Publié par maccoa | 24 mars 2012, 14 h 09 min
  2. Salut,
    j’effectue un projet à peu près pareil, une communication via port série RS232 avec un module GPS, la carte arduino alors traite la trame GPS reçue & l’affiche sur écran, pourriez vous m’orienter vers une documentation qui peut m’aider à établir cette connection et dans un second lieu exploiter ce bout de code??

    Publié par Atef | 28 avril 2013, 21 h 14 min
  3. Bonjour,
    Lorsque je le lance le programme il me retourne systématiquement ‘Erreur de trame ».
    Est ce que l’erreur peut venir du sscanf? je ne connais pas cette fonction!!!
    Merci.

    Publié par Xavier | 6 juin 2013, 12 h 26 min
  4. Bonjour,
    Comment faire pour ne pas rendre l’application non bloquante, pour qu’elle puisse faire autre chose.
    si j’exécute se code, le programme est tjs en attente du serial et ne fait rien d’autre.
    pour afficher par exemple « je suis en attente »

    Publié par Thierry | 3 juillet 2014, 13 h 00 min
    • -> « Machine à états fini » + interruption sur le port série pour traiter les caractères un à un en parallèle du programme principal.

      Autant prévenir de suite c’est tout sauf simple et c’est ultra chiant à débugger.
      Cherche « FSM ragel » sur google pour avoir un outil bien pratique qui génère le code de l’automate en fonction d’une série d’expressions régulières.

      Publié par Skywodd | 4 juillet 2014, 21 h 04 min
  5. Bonjour

    je construit une télécommande ou je dois envoyer 3 paramètres.
    j’ai réussi à les concaténer avec sprintf(machaine, « %d-%d-%d »,angle,relaisemb,relais), mon séparateur étant -.
    Mais je n’arrive pas à le séparer à la réception (et à retrouver mes 3 valeurs: angle, relais emb, relais).

    le système envoie les caractères en ascii, mais je ne sais pas comment les séparer (sachant que la valeur angle peut prendre une valeur entre 1 et 180, y compris la valeur 49, valeur ascii du séparateur..

    Si quelqu’un pouvait m’aider, je lui en serait reconnaissant.

    Merci

    Publié par thomas lecomte | 12 juin 2015, 17 h 19 min
    • Un simple sscanf comme présenté dans l’article devrait suffire, quel est le problème ?

      sscanf(buf, « %d-%d-%d », &angle, &relaisemb, &relais)

      Publié par Skywodd | 14 juin 2015, 14 h 49 min
  6. Super ton bout de code je travaille sans succès sur du decodage de trame entre 2 arduinos en liaison série mais sans succès
    je test ton code dès ce soir cependant je ne comprends pas pourquoi « tant que char != 0x1A » pourquoi parle tu de flèche?

    Publié par alex | 16 juin 2015, 8 h 08 min
    • 0x1A est le code ascii pour SUB qui est souvent représenté par une flèche.
      C’est aussi le code généré par la séquence de touche CTRL+Z.

      Après je pourrai pas t’en dire plus. Je me rappelle plus du tout du pourquoi de ce code …

      Publié par Skywodd | 3 juillet 2015, 15 h 40 min
  7. Merci bcp pour ton code.
    Je cherche aussi à parser une chaine de caractère et je galère un peu.
    J’ai essayé avec ton code, sans même le modifié, et en envoyant une chaine type ‘$ Char;Int;Int;Int;Char’ sur le port série, mais j’ai toujours une réponse d’erreur de trame.
    As tu une explication?
    Merci d’avance.

    Publié par Got | 17 septembre 2015, 1 h 02 min
    • Tu as bien un point virgule à la fin de la trame ?

      Mon code d’exemple a été conçu à l’époque pour un cas où la fin de trame est ; quoi qu’il arrive.
      Exemple: « $ Bonjour;1;2;3;°c; »

      Sinon, en utilisant %s avec sscanf il est possible d’obtenir un résultat semblable avec moins de code (attention aux dépassement de mémoire avec %s seul sans taille max spécifié).
      J’ai modifié le code de l’article pour qu’il utilise un retour ligne au lieu de CTRL+Z pour la fin de donnée.

      // Buffer qui va contenir la trame série complète 
      #define TAILLE_MAX_TRAME 32
       
      // setup()
      void setup() {
        // Initialisation du port série
        Serial.begin(9600);
      }
       
      // loop()
      void loop() {
      
        // Données utiles extraites
        char texte[TAILLE_MAX_TRAME], unite;
        int valeur, index, diviseur;
      
        // Récupération d'une trame + parsing
        if(recupInfo(texte, &valeur, &index, &diviseur, &unite)) {
          Serial.println("Erreur de trame !");
          return;
        }
         
        // Affichage
        Serial.print("Texte: ");
        Serial.println(texte);
        Serial.print("Valeur: ");
        Serial.println(valeur);
        Serial.print("Index: ");
        Serial.println(index);
        Serial.print("Diviseur: ");
        Serial.println(diviseur);
        Serial.print("Unité: ");
        Serial.println(unite);
      }
       
      /*
       * Parse une chaine au format $ texte;valeur;index;diviseur;°c/f;
       * Et retourne la valeur des différents champs.
       */
      int recupInfo(char *texte, int *valeur, int *index, int *diviseur, char *unite) {
        char c, buf[TAILLE_MAX_TRAME + 1];
        unsigned char i = 0;
       
        /* Attente du $ espace */
        do {
          /* Attente de 2 char sur le port série */
          while(Serial.available() < 2);
       
          /* Tant que chaine != $ espace -> boucle */
        } 
        while(Serial.read() != '$' && Serial.read() != ' ');
       
        /* Remplissage du buffer */
        do{
          /* Si la chaine a dépassé la taille max du buffer*/
          if(i == (TAILLE_MAX_TRAME + 1)) 
            /* retourne 1 -> erreur */
            return 1;
       
          /* Attente d'un char sur le port série */
          while(Serial.available() < 1);
       
          /* Tant que char != retour ligne -> boucle */
        } 
        while((buf[i++] = Serial.read()) != '\n');
       
        /* Cloture la chaine de char */
        buf[i] = '\0';
       
        /* Parse la chaine de caractères et extrait les champs */
        if(sscanf(buf + i, "%s;%d;%d;%d;%*c%c;", texte, valeur, index, diviseur, unite) != 5)
          /* Si sscanf n'as pas pu extraire les 4 champs -> erreur*/
          return 1;
       
        /* retourne 0 -> pas d'erreur */
        return 0;
      }
      

      Publié par Skywodd | 12 octobre 2015, 10 h 12 min
  8. bonjour, moins que débutant je souhaitais utiliser cette fonction pour commander un train par ctrl C et ctrl V
    dans le moniteur serie je tape $ texte;1;2;3;°c/f;
    ou $ Bonjour;1;2;3;°c;
    la ligne s’efface mais je ne récupère aucun message
    si je tape une erreur j’ai bien le message erreur trame
    j’utilise un arduino mega 2560 et le logiciel 1.7.9 et le programmer AVRISP mkII
    ou est mon erreur
    merci

    ya

    Publié par camenen | 13 avril 2016, 11 h 05 min
  9. Bonsoir tout le monde, j’ai copié ce code et quand je l’ai compilé, Arduino a détecté une erreur « Stray \302 » au niveau du « return 0; » Pourriez vous m’indiquer la cause et l’éventuelle solution à suivre ^^ ?

    Publié par Ramirez | 31 mars 2017, 20 h 56 min
  10. je trouve votre blog tres interessant 🙂

    Publié par massage thai | 8 février 2018, 4 h 05 min

Skyduino devient Carnet du Maker

Le site Carnet du Maker remplace désormais le blog Skyduino pour tout ce qui touche à l'Arduino, l'informatique et au DIY.