Un protocole de communication simple pour Arduino

Logo ArduinoLa carte Arduino contient un micro-contrôleur et des broches d’entrées/sorties, ce qui permet de faires des prototypes électroniques, inventer votre propre périphérique ou tout simplement ouvrir votre PC vers l’extérieur afin de lire la valeur de capteurs ou vous interfacer avec n’importe quel équipement électronique.

J’ai reçu une carte Arduino de chez RS. Afin de me faire la main, j’ai eu l’idée de créer un petit protocole orienté texte afin de faciliter l’échange d’instructions et de demandes de valeurs entre l’Arduino et un PC.

Présentation fonctionnelle

Voici une série d’instructions afin de vous faire comprendre le fonctionnement par l’exemple :

<PrintConfirmation|1>

Demander un accusé de réception pour chaque commande

<Echo|This is a test>

Retourner le texte passé en paramètre

<WriteDigital|13|1>

Ecrire une valeur sur la 14ème broche numérique. En l’occurrence, un signal de valeur 1.

<WriteDigital|13|0>

Ecrire une valeur sur la 14ème broche numérique. En l’occurrence, un signal de valeur 0.

<ReadAnalog|4>

Lire la valeur de la 5ème broche analogique.

Le principe du protocole est :

  • d’indiquer les débuts et fins de trame de commande
  • d’avoir une syntaxe de type liste d’arguments variable, l’argument 0 étant la commande et les suivants les arguments de la commande. Les arguments sont séparés par un caractère.

Décortiquons le programme

Nous allons nous baser sur les fonctions offertes par le logiciel pré-installé sur la carte et composé de deux parties :

Fichiers inclus

Les fichiers inclus sont :

  • <stdlib.h> pour la fonction de conversion chaîne de caractère vers numérique (atoi).
  • <string.h> pour les fonctions de découpage d’une chaîne de caractères en tokens (strtok) et l’initialisation d’une zone de mémoire ou d’une chaîne de caractères (memset).

Fonction principale

La fonction principale du schéma boucle en attente des entrées et fin de trame. Lorsqu’elle reçoit le caractère de début de trame (‘<‘), elle commence à composer une chaîne de caractère qui contiendra la commande (command_line). Lorsqu’elle reçoit le caractère de fin de trame (‘>’), elle appelle une fonction d’évaluation de la commande et de ses paramètres (parse_command), puis une fonction d’exécution de la commande (exec_command).

void loop() {
  if (Serial.available()) {
    incomingByte = Serial.read();
    switch (incomingByte)
    {
      case '':
        command_line[index+1] = 0;
        parse_command();
        exec_command();
        break;
      default :
        command_line[index++] = incomingByte;
    }
  }
}

Comment parser une ligne de commande ?

La fonction d’évaluation de la commande reçue est appelée par la boucle du schéma, une fois qu’une trame complète et valide a été reçue (par exemple « <WriteDigital|13|1> »). La fonction va donc évaluer une chaîne de caractères débarrassée des délimiteurs de trames (par exemple « WriteDigital|13|1 »). Le but de la fonction est de découper cette chaîne (command_line) dans un tableau (arg_list). L’entrée 0 contiendra la commande et les entrées suivantes les arguments de la commande (par exemple, l’entrée 1 contiendra « 13 » et l’entrée 2 contiendra « 1 »). Ensuite, on associe la commande à son numéro (par exemple, « WriteDigital » est la commande numéro 2).

void parse_command()
{
  char * pch;
  int ii = 0;
  //Identify the command attributes
  pch = strtok (command_line,"|/¤");
  while (pch != NULL)
  {
    sprintf(arg_list[ii],"%s", pch);
    pch = strtok (NULL, "|/¤");
    ii++;
  }
  //Identify the command ID
  for (ii=0; ii {
    if (strcmp(Command_List[ii], arg_list[0]) == 0)
    {
      Command_ID = ii;
      break;
    }
  }
}

Implémentation de la commande

La fonction d’exécution exécute la commande que la fonction d’évaluation a précédemment associée à un numéro (le switch/case du C ne peut faire que des comparaisons entre des nombres). On peut utiliser les arguments qui ont été séparés les uns des autres. Par exemple, la fonction WriteDigital accepte deux arguments :

  • Le numéro de la broche (par exemple 13).
  • La valeur qui doit être écrite sur la broche (par exemple 1).

Tandis que la commande Echo se contente d’écrire sur le port série le paramètre texte qu’elle a reçu.

void exec_command()
{
  char user_feedback[512];
  switch (Command_ID)
  {
    case CReadAnalog:
      Serial.println(analogRead(atoi(arg_list[1])));
      ack_command("done");
      break;
    case CWriteDigital:
      digitalWrite(atoi(arg_list[1]), atoi(arg_list[2]));
      sprintf(user_feedback, "done : Inputs %i,%i", atoi(arg_list[1]), atoi(arg_list[2]));
      ack_command(user_feedback);
      break;
    case CEcho:
      Serial.println(arg_list[1]);
      ack_command("done");
      break;
    default :
      Serial.print("Unknown Command");
      Serial.println(command_line);
  }
}

Ajouter vos propres commandes

Je vous propose le code sous licence BSD, ainsi vous pourrez en faire ce que vous voulez. Par exemple, en ajoutant de nouvelles commandes. Pour cela, il vous suffit de :

  1. Incrémenter la valeur de la constante NB_COMMANDS.
  2. Ajouter le nom de la commande dans le tableau Command_List.
  3. Créer son numéro dans l’énumération Command_IDs.
  4. Et bien sûr, la coder dans la fonction exec_command.

A suivre, un article qui illustre comment j’ai utilisé ce protocole avec PHP.

4 réflexions au sujet de « Un protocole de communication simple pour Arduino »

  1. Liberty CARECHE

    Bravo, c’est un article intéressant.
    Ceci étant dit, il faut penser qu’il n’y a pas que les programmeur C confirmés (ce que je ne suis pas) qui arrive sur cette page, et qu’il est bon je pense de décrire les déclaration des variables et tableaux le cas échéant (voir le programme entier).

    Répondre
  2. Spols

    Bonjour,

    J’aurais voulu tester votre code, mais le lien vers celui-ci est mort et renvoi une 404. Pourriez vous le réparer ou m’envoyez le code.

    Merci

    Répondre

Laisser un commentaire

Votre adresse de messagerie ne sera pas publiée. Les champs obligatoires sont indiqués avec *