Accueil > La technologie > Arduino > Manœuvre des aiguilles avec des servo-moteurs > Un zeste de C++

Manœuvre des aiguilles en analogique avec des servo-moteurs

Un zeste de C++

samedi 23 novembre 2013, par Jean-Luc

Comme nous l’avons détaillé dans l’article précédent « 8 poussoirs et 8 servos, enfin ! », le passage d’un servomoteur à 8 servomoteurs, nous à contraint à rassembler les variables permettant de décrire l’état d’un servomoteur dans une struct afin de créer un tableau de 8 éléments de ce type.

struct DescripteurServo {
    Servo objetServo;
    int vitesse;
    int angle;
    int pin;
    byte etatServo;
};
 
struct DescripteurServo servoMoteur[8];

Malheureusement, le programme manipulant un servomoteur s’en est trouvé alourdi car dorénavant, il faut préciser de quel élément du tableau il s’agit quand on veut modifier l’un des membre du descripteur de servomoteur. On se retrouve partout avec des servoMoteur[numServo]., Ce qui n’est ni joli, ni agréable.

De plus, si on examine le programme tel qu’il est actuellement, on s’aperçoit que seules deux fonctions s’occupent du descripteur de servomoteur : gereServo() pour accomplir le mouvement lent et evenementServo() pour donner l’ordre de mouvement. Il y a aussi setup() qui initialise les descripteurs mais on pourrait très bien le faire dans une fonction appelée par setup(). Il est donc tentant de mettre tout cela ensemble, le descripteur de servomoteur et les fonctions qui le manipule, et c’est exactement ce que permet le C++.

Le C++ va donc venir à notre secours pour simplifier et aérer considérablement notre programme.

Le C++ pris dans sa globalité est un langage complexe et nous n’avons pas besoin de tout. Nous n’allons prendre que ce dont nous avons besoin et qui va nous faciliter la tâche.

Le C++ est très utilisé dans l’Arduino. Par exemple, la bibliothèque Servo que nous utilisons est écrite en C++. C++ est une extension du C qui ajoute principalement une chose : les objets. Un objet contient une ou plusieurs données et une ou plusieurs fonctions (on dit méthode dans le jargon) pour lire/écrire cette valeur mais pas seulement. Pour créer un objet, on a besoin d’un type, comme pour créer une variable entière, on a besoin du type int. Pour créer un type d’objet, on déclare une classe. Faisons ça directement avec notre DescripteurServo :

class DescripteurServo {
    Servo objetServo;
    int vitesse;
    int angle;
    int pin;
    byte etatServo;
};

Rien de renversant jusqu’à là. C’est exactement comme une struct. La particularité est de pouvoir y mettre aussi des fonctions qui deviennent donc membres de la classe. Mettons y gereServo() et evenementServo().

class DescripteurServo {
 
    Servo objetServo;
    int vitesse;
    int angle;
    int pin;
    byte etatServo;
 
  public:
    void gereServo()
    {
        objetServo.writeMicroseconds(angle);
     
        angle += vitesse;
     
        if (angle > angleMax) {
            angle = angleMax;
            vitesse = 0;
            objetServo.detach();
            etatServo = SERVO_A_ANGLE_MAX;
        }
        else if (angle < angleMin) {
          angle = angleMin;
          vitesse = 0;
          objetServo.detach();
          etatServo = SERVO_A_ANGLE_MIN;
        }
    }
    
    void evenementServo()
    {
        switch (etatServo) {
          case SERVO_A_ANGLE_MIN:
            objetServo.attach(pin);
          case SERVO_EN_MOUVEMENT_VERS_ANGLE_MIN:
            vitesse =  1;
            etatServo = SERVO_EN_MOUVEMENT_VERS_ANGLE_MAX;
            break;
          case SERVO_A_ANGLE_MAX:
            objetServo.attach(pin);
          case SERVO_EN_MOUVEMENT_VERS_ANGLE_MAX:
            vitesse = -1;
            etatServo = SERVO_EN_MOUVEMENT_VERS_ANGLE_MIN;
            break;
        } 
    }
};

Comme vous pouvez le constater, tous les servoMoteur[numServo]. ont disparu. En effet, les fonctions membre accèdent directement aux données membres de la classe. Par ailleurs, l’argument numServo des fonctions a disparu pour la même raison.
Enfin, le public: permet de rendre les deux fonctions membres appelables depuis l’extérieur de l’objet. Notez aussi que les données membres ne sont pas publiques, on ne peut donc y accéder depuis l’extérieur de l’objet.

Il reste une dernier point à régler. Dans setup(), une boucle initialise chaque descripteur de servo. Certaines initialisations sont dépendantes du numéro de servo (en fait du numéro de broche à laquelle le servo est connecté), d’autre ne le sont pas. En C++, un objet peut avoir un constructeur. Il s’agit d’une fonction membre qui porte le même nom que la classe et qui est exécutée lors de la création de l’objet. Nous allons ajouter ce constructeur pour y mettre les initialisations indépendantes du numéro de broche. Enfin nous allons ajouter une fonction membre pour les initialisations qui sont dépendantes du numéro de broche.

    DescripteurServo()
    {
        angle = angleMin;
        vitesse = 0;
        etatServo = SERVO_A_ANGLE_MIN;
    }
    
    void connecte(int pinDeConnection)
    {
        pin = pinDeConnection;
        objetServo.attach(pinDeConnection);
    }

Il reste à modifier le reste du programme pour l’adapter. Tout d’abord, dans setup() les initialisations consistent maintenant à appeler la fonction membre connecte(...). Les intialisation indépendantes du numéro de broche sont déjà faites.

void setup()
{
    /* Initialisation des servos */
    int numServo;
  
    for (numServo = 0; numServo < 8; numServo++)
        servoMoteur[numServo].connecte(numServo + 2);
}

Notez la syntaxe pour appeler une fonction membre. servoMoteur[numServo] est un objet du tableau, le . permet d’accéder à un de ses membres et connecte(numServo +2) permet d’appeler la fonction membre connecte en lui passant numServo + 2 comme argument.

De la même manière, la boucle qui appelait gereServo() pour chacun des servomoteurs est modifiée.

    for (numServo = 0; numServo < 8; numServo++)
        servoMoteur[numServo].gereServo();

Enfin, la notification d’un événement sur le poussoir est aussi modifié.

    byte evenement = lireEvenement(&numServo);
  
    if (evenement == EVENEMENT_PRESSE) {
        servoMoteur[numServo].evenementServo();
    }

Rien de bien sorcier finalement. Le programme est beaucoup plus clair écrit de cette manière et cerise sur le gâteau, il prend 274 octets de moins une fois compilé !

Voici le sketch Arduino correspondant :

Logiciel de pilotage de 8 servos avec 8 boutons réécrit en C++

Messages

  • Bonjour,

    Merci pour ces explications très claires et ce code que je cherchais depuis longtemps. Mais malheureusement étant novice, malgré avoir lu et relu votre cours, il y a plusieurs points qui m’échappe !

    J’aurais aimé ne faire fonctionner que deux servomoteurs, chacun pilotés par un bouton, seriez-vous prêt à me donner quelques explications ?

    En vous remerciant, cordialement.

  • Permettez moi de préciser mon projet :

    J’ai deux servos se mouvant séparément. Chacun a son bouton. J’aimerai que la pression d’un des boutons n’empêche pas la pression de l’autre bouton. Jes souhaite garder le même comportement que vous décrivez : aller/retour minimum/maximum avec arrêt à chaque mini et maxi et possibilité de changement d’état en cours d’exécution.

    Et parce que j’en veux toujours plus, un mouvement dégressif, de rapide à lent, c’est à dire aller/une vitesse et retour/une autre vitesse serait un peu comme la cerise sur le gâteau (Et oui je suis gourmand !). Je recherche un mouvement souple et doux.

    Je suis marionnettiste, je joue avec des marionnettes à fils. Pour construire des automates les servos m’attirent énormément, mais sculter du bois et écrire des scripts sont des choses très différentes !!!

    Espérant être clair et avoir une réponse de votre part, merci d’avance

  • Ma demande se précise : Chaque servo devrait avoir une ouverture d’angle différente.

    J’espère que vous verrez ce message...

Un message, un commentaire ?

Qui êtes-vous ?
Votre message

Pour créer des paragraphes, laissez simplement des lignes vides.

Lien hypertexte

(Si votre message se réfère à un article publié sur le Web, ou à une page fournissant plus d’informations, vous pouvez indiquer ci-après le titre de la page et son adresse.)