ModelleisenbahN

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++

publié par Jean-Luc, le samedi 23 novembre 2013

Tags arduino

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.

  1. struct DescripteurServo {
  2. Servo objetServo;
  3. int vitesse;
  4. int angle;
  5. int pin;
  6. byte etatServo;
  7. };
  8.  
  9. struct DescripteurServo servoMoteur[8];

Télécharger

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 :

  1. class DescripteurServo {
  2. Servo objetServo;
  3. int vitesse;
  4. int angle;
  5. int pin;
  6. byte etatServo;
  7. };

Télécharger

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().

  1. class DescripteurServo {
  2.  
  3. Servo objetServo;
  4. int vitesse;
  5. int angle;
  6. int pin;
  7. byte etatServo;
  8.  
  9. public:
  10. void gereServo()
  11. {
  12. objetServo.writeMicroseconds(angle);
  13.  
  14. angle += vitesse;
  15.  
  16. if (angle > angleMax) {
  17. angle = angleMax;
  18. vitesse = 0;
  19. objetServo.detach();
  20. etatServo = SERVO_A_ANGLE_MAX;
  21. }
  22. else if (angle < angleMin) {
  23. angle = angleMin;
  24. vitesse = 0;
  25. objetServo.detach();
  26. etatServo = SERVO_A_ANGLE_MIN;
  27. }
  28. }
  29.  
  30. void evenementServo()
  31. {
  32. switch (etatServo) {
  33. case SERVO_A_ANGLE_MIN:
  34. objetServo.attach(pin);
  35. case SERVO_EN_MOUVEMENT_VERS_ANGLE_MIN:
  36. vitesse = 1;
  37. etatServo = SERVO_EN_MOUVEMENT_VERS_ANGLE_MAX;
  38. break;
  39. case SERVO_A_ANGLE_MAX:
  40. objetServo.attach(pin);
  41. case SERVO_EN_MOUVEMENT_VERS_ANGLE_MAX:
  42. vitesse = -1;
  43. etatServo = SERVO_EN_MOUVEMENT_VERS_ANGLE_MIN;
  44. break;
  45. }
  46. }
  47. };

Télécharger

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.

  1. DescripteurServo()
  2. {
  3. angle = angleMin;
  4. vitesse = 0;
  5. etatServo = SERVO_A_ANGLE_MIN;
  6. }
  7.  
  8. void connecte(int pinDeConnection)
  9. {
  10. pin = pinDeConnection;
  11. objetServo.attach(pinDeConnection);
  12. }

Télécharger

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.

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

Télécharger

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.

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

Télécharger

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

  1. byte evenement = lireEvenement(&numServo);
  2.  
  3. if (evenement == EVENEMENT_PRESSE) {
  4. servoMoteur[numServo].evenementServo();
  5. }

Télécharger

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 :

Zip - 1.8 ko
Logiciel de pilotage de 8 servos avec 8 boutons réécrit en C++
(clic sur l'image pour agrandir ou télécharger)

6 Messages

  • Un zeste de C++ 17 mars 2016 15:24, par Mue

    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.

    repondre message

  • Un zeste de C++ 17 mars 2016 17:36, par Mue

    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

    repondre message

  • Un zeste de C++ 17 mars 2016 21:36, par Mue

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

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

    repondre message

Répondre à cet article

Les thèmes

Archives

Informations

ModelleisenbahN | publié sous licence Creative Commons by-nc-nd 2.0 fr | généré dynamiquement par SPIP & Blog'n Glop