IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)
Navigation

Inscrivez-vous gratuitement
pour pouvoir participer, suivre les réponses en temps réel, voter pour les messages, poser vos propres questions et recevoir la newsletter

Arduino Discussion :

Traiter des commandes envoyées depuis le Moniteur série [Sources]


Sujet :

Arduino

  1. #1
    Responsable Arduino et Systèmes Embarqués


    Avatar de f-leb
    Homme Profil pro
    Enseignant
    Inscrit en
    Janvier 2009
    Messages
    12 759
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 53
    Localisation : France, Sarthe (Pays de la Loire)

    Informations professionnelles :
    Activité : Enseignant

    Informations forums :
    Inscription : Janvier 2009
    Messages : 12 759
    Points : 57 835
    Points
    57 835
    Billets dans le blog
    42
    Par défaut Traiter des commandes envoyées depuis le Moniteur série
    Bonjour,

    Je vous propose un nouvel élément à utiliser : Traiter des commandes envoyées depuis le Moniteur série

    Ce petit projet consiste, à titre de démonstration, à montrer comment traiter des commandes avec des paramètres envoyées depuis un Moniteur série (ou tout autre terminal série).

    Ici, on prend comme prétexte une Led RVB (à cathodes communes) qui va clignoter selon la couleur et la luminosité saisies dans le moniteur Série.

    Exemples de commandes (délimiteur = [espace]):

    rgb 1 0 0 --> allume la Led en rouge
    rgb 0 1 0 --> allume la Led en vert
    rgb 0 0 1 --> allume la Led en bleu
    rgb 1 0 1 --> allume la Led en rouge + bleu = magenta
    rgb 1 1 0 --> allume la Led en rouge + vert = jaune
    rgb 1 1 1 --> allume la Led en rouge + vert + bleu = blanc
    rgb 1 1 0 50 --> allume la Led en rouge + vert = jaune, mise à jour luminosité = 50%

    une commande help supplémentaire donne le mode d'emploi de la commande.

    Dans le sketch, on trouve :
    - une classe LedRGB de gestion de la Led ;
    - une fonction serialEvent() qui récupère le message saisi dans le Terminal série (Entrée pour fin du message) ;
    - une fonction parseMessage() qui traite le message, sépare la commande et les paramètres, et modifie l'état de la Led en conséquence.


    Qu'en pensez-vous ?

  2. #2
    Expert confirmé

    Homme Profil pro
    mad scientist :)
    Inscrit en
    Septembre 2019
    Messages
    2 809
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : Etats-Unis

    Informations professionnelles :
    Activité : mad scientist :)

    Informations forums :
    Inscription : Septembre 2019
    Messages : 2 809
    Points : 5 665
    Points
    5 665
    Par défaut
    sympa !

    (vous avez complexifié un peu l'objet premier du tuto en rajoutant une classe et des petits hacks sur les couleurs ce qui peut dérouter )

  3. #3
    Membre émérite
    Avatar de jpbbricole
    Homme Profil pro
    Retraité des réseaux informatiques
    Inscrit en
    Février 2013
    Messages
    1 015
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : Suisse

    Informations professionnelles :
    Activité : Retraité des réseaux informatiques
    Secteur : High Tech - Électronique et micro-électronique

    Informations forums :
    Inscription : Février 2013
    Messages : 1 015
    Points : 2 350
    Points
    2 350
    Par défaut
    Bonjour f-leb
    Citation Envoyé par f-leb Voir le message
    - une fonction serialEvent() qui récupère le message saisi dans le Terminal série (Entrée pour fin du message) ;
    Attention, serialEvent() utilisé ainsi, en "automatique", donc sans appel à la fonction, ne fonctionne pas sur tout les Arduino.
    Citation Envoyé par f-leb Voir le message
    Qu'en pensez-vous ?
    Je trouve ça terrrrrriblement compliqué!

    Cordialement
    jpbbricole

  4. #4
    Responsable Arduino et Systèmes Embarqués


    Avatar de f-leb
    Homme Profil pro
    Enseignant
    Inscrit en
    Janvier 2009
    Messages
    12 759
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 53
    Localisation : France, Sarthe (Pays de la Loire)

    Informations professionnelles :
    Activité : Enseignant

    Informations forums :
    Inscription : Janvier 2009
    Messages : 12 759
    Points : 57 835
    Points
    57 835
    Billets dans le blog
    42
    Par défaut
    Hello,

    Citation Envoyé par jpbbricole Voir le message
    Attention, serialEvent() utilisé ainsi, en "automatique", donc sans appel à la fonction, ne fonctionne pas sur tout les Arduino.
    Ah oui, cela m'avait échappé, merci


    Citation Envoyé par jpbbricole Voir le message
    Je trouve ça terrrrrriblement compliqué!
    La complexité vient déjà du fait que je ne suis pas passé par la fameuse classe String pour les traitements sur les chaînes de caractères. Et c'est tout à fait volontaire, tu sais déjà que cette classe abuse des allocations dynamiques qui fragmentent le tas. Il reste à se documenter sur strtok() et strtol(), c'est moins convivial mais ça ne peut pas être si terrrrriblement compliqué si j'ai moi-même réussi à les exploiter

    Après, si tu as une version "plus simple", tu peux la poster ici pour comparaison. Si je trouve un moment, j'essaierai aussi d'écrire une version exploitant la classe String.

  5. #5
    Expert confirmé

    Homme Profil pro
    mad scientist :)
    Inscrit en
    Septembre 2019
    Messages
    2 809
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : Etats-Unis

    Informations professionnelles :
    Activité : mad scientist :)

    Informations forums :
    Inscription : Septembre 2019
    Messages : 2 809
    Points : 5 665
    Points
    5 665
    Par défaut
    le choix effectivement de ne pas utiliser la classe String se défend, surtout sur les petits µc.

    Vous avez un double objectif:

    - le premier c'est recevoir une ligne de commande
    - le second c'est de faire de l'analyse

    je ne suis pas sûr qu'il faille complexifier avec des LEDs RGB, juste montrer de l'analyse dans le terminal série pourrait suffire

    ça pourrait faire l'objet d'un tuto avec progression dans la complexité:

    1/ La réception de ligne de commandes avec marqueur de fin

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    const byte longeurMaxMessage = 30;
    char message[longeurMaxMessage + 1];              // + 1 pour le caractère nul en fin de chaîne
     
    bool messageDisponible(const char marqueurDeFin = '\n') {
      static byte indiceEnCours = 0;
      int r = Serial.read();
     
      if (r == -1)  return false;                   // Il n'y a rien à lire
     
      if (r == marqueurDeFin) {                     // si c'est le marqueur de fin
        message[indiceEnCours] = '\0';              // on termine la c-string
        indiceEnCours = 0;                          // on se prépare pour la prochaine fois
        return true;                                // on dit que le message est disponible
      }
     
      if (indiceEnCours < longeurMaxMessage) {      // s'il y a encore de la place
        message[indiceEnCours++] = r;               // on stocke le caractère et incrémente l'indice
      }
     
      return false;                                 // Le message n'est pas terminé
    }
     
    void traiterMessage() {
      Serial.print("J'analyse \"");
      Serial.print(message);
      Serial.println("\"");
    }
     
    void setup() {
      Serial.begin(115200); Serial.println();
      Serial.println("Entrez des commandes.");
    }
     
    void loop() {
      if (messageDisponible()) {
        traiterMessage();
      }
    }


    2/ La segmentation de la ligne de commandes en commandes unitaires

    On pourrait commencer par analyser une commande avec un séparateur de type caractère (ici la virgule)

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    const byte longeurMaxMessage = 30;
    char message[longeurMaxMessage + 1];              // + 1 pour le caractère nul en fin de chaîne
     
    bool messageDisponible(const char marqueurDeFin = '\n') {
      static byte indiceEnCours = 0;
      int r = Serial.read();
     
      if (r == -1)  return false;                   // Il n'y a rien à lire
     
      if (r == marqueurDeFin) {                     // si c'est le marqueur de fin
        message[indiceEnCours] = '\0';              // on termine la c-string
        indiceEnCours = 0;                          // on se prépare pour la prochaine fois
        return true;                                // on dit que le message est disponible
      }
     
      if (indiceEnCours < longeurMaxMessage) {      // s'il y a encore de la place
        message[indiceEnCours++] = r;               // on stocke le caractère et incrémente l'indice
      }
     
      return false;                                 // Le message n'est pas terminé
    }
     
    void traiterCommande(const char * element) {
      Serial.print("Element \"");
      Serial.print(element);
      Serial.println("\"");
    }
     
    void traiterMessage(const char separateur = ',') {
      Serial.print("J'analyse \"");
      Serial.print(message);
      Serial.println("\"");
     
      char * debutCommande = message;
      char * virgulePtr;
     
      do {
        virgulePtr = strchr(debutCommande, separateur);
        if (virgulePtr == nullptr) {                  // il n'y a plus de séparateur
          traiterCommande(debutCommande);
          break;
        } else {
          *virgulePtr = '\0';                         // on coupe la chaîne à cet endroit
          traiterCommande(debutCommande);             // on effectue l'analyse
          *virgulePtr = separateur;                   // on remet comme c'était
          debutCommande = virgulePtr + 1;             // on continue l'exploration après la virgule
        }
      } while (*debutCommande != '\0');               // on continue tant qu'on n'est pas au bout (pas de caractère nul pointé par debut)
    }
     
    void setup() {
      Serial.begin(115200); Serial.println();
      Serial.println("Entrez des commandes séparées par des virgules.");
    }
     
    void loop() {
      if (messageDisponible()) {
        traiterMessage();
      }
    }

    3/ Explorer l'analyse/parsing.

    c'est ici qu'on introduit alors les fonctions comme comme strtok(), strtol(), strtoul(), strtod() et autres sscanf() en enrichissant la fonction analyseElement()c'est là qu'éventuellement vous pouvez introduire la LED RGB

    le langage de commande pourrait être composé de composante=valeur
    ou composante c'est R, V ou B ou r, v ou b
    et valeur est un nombre entier entre 0 et 255

    le code

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    120
    121
    122
    123
    // les pins de la led RVG
    const byte rPin = 6;
    const byte vPin = 5;
    const byte bPin = 3;
     
    byte rouge = 0, vert = 0, bleu = 0; // les composantes entre 0 et 255
     
    const byte longeurMaxMessage = 30;
    char message[longeurMaxMessage + 1];              // + 1 pour le caractère nul en fin de chaîne
     
    bool messageDisponible(const char marqueurDeFin = '\n') {
      static byte indiceEnCours = 0;
      int r = Serial.read();
     
      if (r == -1)  return false;                   // Il n'y a rien à lire
     
      if (r == marqueurDeFin) {                     // si c'est le marqueur de fin
        message[indiceEnCours] = '\0';              // on termine la c-string
        indiceEnCours = 0;                          // on se prépare pour la prochaine fois
        return true;                                // on dit que le message est disponible
      }
     
      if (indiceEnCours < longeurMaxMessage) {      // s'il y a encore de la place
        message[indiceEnCours++] = r;               // on stocke le caractère et incrémente l'indice
      }
     
      return false;                                 // Le message n'est pas terminé
    }
     
    void gestionLed(byte r, byte v, byte b) {
      analogWrite(rPin, r);
      analogWrite(vPin, v);
      analogWrite(bPin, b);
    }
     
    void traiterCommande(const char * element) {
      Serial.print("Element \"");
      Serial.print(element);
      Serial.println("\"");
     
      long valeur;
      char * endPtr;
     
      const char * debut = element;
      while (*debut == ' ') debut++;                // on saute les espace en début de chaîne
      if (*(debut + 1) != '=') return;              // On doit avoir un seul caractère suivi de =
     
      switch (*debut) {                            // on regarde le premier caractère
        case 'r':
        case 'R':
          Serial.println("\tRouge");
          valeur = strtol(debut + 2, &endPtr, 10 );
          if (endPtr == debut + 2) return; // on n'a pas réussi à lire un entier
          if (valeur <= 0) rouge = 0;
          else if (valeur > 255) rouge = 255;
          else rouge = valeur;
          Serial.write('\t'); Serial.println(rouge);
          break;
     
        case 'v':
        case 'V':
          Serial.println("\tVert");
          valeur = strtol(debut + 2, &endPtr, 10 );
          if (endPtr == debut + 2) return; // on n'a pas réussi à lire un entier
          if (valeur <= 0) vert = 0;
          else if (valeur > 255) vert = 255;
          else vert = valeur;
          Serial.write('\t'); Serial.println(vert);
          break;
     
        case 'b':
        case 'B':
          Serial.println("\tBleu");
          valeur = strtol(debut + 2, &endPtr, 10 );
          if (endPtr == debut + 2) return; // on n'a pas réussi à lire un entier
          if (valeur <= 0) bleu = 0;
          else if (valeur > 255) bleu = 255;
          else bleu = valeur;
          Serial.write('\t'); Serial.println(bleu);
          break;
     
        default: break;
      }
    }
     
    void traiterMessage(const char separateur = ',') {
      Serial.print("J'analyse \"");
      Serial.print(message);
      Serial.println("\"");
     
      char * debutCommande = message;
      char * virgulePtr;
     
      do {
        virgulePtr = strchr(debutCommande, separateur);
        if (virgulePtr == nullptr) {                  // il n'y a plus de séparateur
          traiterCommande(debutCommande);
          break;
        } else {
          *virgulePtr = '\0';                         // on coupe la chaîne à cet endroit
          traiterCommande(debutCommande);             // on effectue l'analyse
          *virgulePtr = separateur;                   // on remet comme c'était
          debutCommande = virgulePtr + 1;             // on continue l'exploration après la virgule
        }
      } while (*debutCommande != '\0');               // on continue tant qu'on n'est pas au bout (pas de caractère nul pointé par debut)
     
      gestionLed(rouge, vert, bleu);                  // on applique les changements
    }
     
    void setup() {
      pinMode(rPin, OUTPUT);
      pinMode(vPin, OUTPUT);
      pinMode(bPin, OUTPUT);
     
      Serial.begin(115200); Serial.println();
      Serial.println("Entrez des commandes des couleur, par exemple pour du jaune -> r=255,v=255,b=0");
    }
     
    void loop() {
      if (messageDisponible()) {
        traiterMessage();
      }
    }


    ensuite on peut pousser plus loin en détectant les erreurs pour refuser l'ensemble de la ligne de commande si une des sous commande était erronée et autres améliorations comme la détection d'entrée non correcte (ici je l'ai fait qu'en partie).
    On peut aussi expliquer qu'avec cette approche on n'a jamais dupliqué en mémoire le message ou une sous chaîne du message, on travaille sur ce que l'on a reçu avec des fonctions adaptées.

    Enfin on peut aussi mettre tout cela sous le simulateur wokwi pour tester:

    tuto 1 = https://wokwi.com/projects/357923027041579009
    tuto 2 = https://wokwi.com/projects/357926980527759361
    tuto 3 = https://wokwi.com/projects/357983392910900225


    voilà comment j'approcherais la chose.

Discussions similaires

  1. Envoyer des commandes système depuis Swift
    Par gstephane dans le forum Swift
    Réponses: 2
    Dernier message: 07/10/2016, 16h53
  2. Réponses: 0
    Dernier message: 21/04/2014, 17h22
  3. Réponses: 2
    Dernier message: 21/10/2008, 10h44
  4. Méthode pour traiter des commandes
    Par Kr00pS dans le forum C
    Réponses: 17
    Dernier message: 15/06/2006, 17h58

Partager

Partager
  • Envoyer la discussion sur Viadeo
  • Envoyer la discussion sur Twitter
  • Envoyer la discussion sur Google
  • Envoyer la discussion sur Facebook
  • Envoyer la discussion sur Digg
  • Envoyer la discussion sur Delicious
  • Envoyer la discussion sur MySpace
  • Envoyer la discussion sur Yahoo