Création d'une librairie à partir d'un code - Projet de Bras Robotique

Bonjour à tous,
Pour le compte de notre club robotique, j'ai écrit un petit code qui permet de piloter un petit bras robotique composé de 5 servomoteurs.
Ce code fonctionne.
Le voici

[code]
#include <Servo.h>

Servo ouvepince; Servo base; Servo epaule; Servo poignet; Servo coude;

#define pinX    A0 // L'axe X du Joystick1 est branché sur la pin analogique A0
#define pinY    A1 // L'axe Y du Joystick1 est branché sur la pin analogique A1
#define pinX2   A2 // L'axe X du Joystick2 est branché sur la pin analogique A2
#define pinY2   A3 // L'axe Y du Joystick2 est branché sur la pin analogique A3

bool BP1, BP2;
int J1, J2, J3, J4;
int pulse1, pulse2, pulse3, pulse4, pulse5;

void setup() {

  coude.attach(3); //sortie numérique 3
  poignet.attach(5); //sortie numérique 9
  ouvepince.attach(10); //sortie numérique 10
  epaule.attach(6);//sortie numérique 6
  base.attach(9);//sortie numérique 9

  pinMode(7, INPUT_PULLUP); //entrée numérique 7
  pinMode(11, INPUT_PULLUP); //entrée numérique 11
  //On utilise le VCC et la résistance de pullup de l’Arduino >> cela évite de rajouter un VCC et une résistance dans le montage

  BP1 = BP2 = 1; // On initialise à 1 (HIGH) 1es deux entrées des boutons.

  pulse1 = pulse2 = pulse3 = pulse4 = pulse5 = 1200; //initialisation de la variable de positionnement des servomoteurs à 1200 (100°). La plage 0° – 180° se traduit par 1000µs – 2000µs.

  pinMode(pinX, INPUT); // les axes des joystick sont des entrées (INPUT)
  pinMode(pinY, INPUT);
  pinMode(pinX2, INPUT);
  pinMode(pinY2, INPUT);

}

void loop() {
  BP1 = digitalRead(7); BP2 = digitalRead(11); //Les variables BP1 et BP2 prennent La valeur booléenne de l'entrée 7 et 11, ce sera soit 1 ou soit 0 (si le bouton est appuyé)

  J1 = analogRead(pinX); J2 = analogRead(pinY); //Les variables J1,J2,J3 et J4 prennent la valeur des entrées analogiques pinX, pinY,pinX2 et pinY2 (de 0 à 1023)
  J3 = analogRead(pinX2); J4 = analogRead(pinY2);

  coude.writeMicroseconds(pulse1); // initialisation de la position de tous les servomoteurs
  poignet.writeMicroseconds(pulse2);
  epaule.writeMicroseconds(pulse3);
  base.writeMicroseconds(pulse4);
  ouvepince.writeMicroseconds(pulse5);

  int pas = 20; // création d'une variable "pas" qui établit l'écart de degré donné à chaque mouvement de joystick pour l'ensemble des servomoteurs

  //action sur le coude
  if (J1 < 15)   // si la valeur de pinX est inférieure à 15, autrement dit si l'utilisateur "met à gauche" le joystick1 au maximum ( <15)
  {
    pulse1 += pas; // la valeur de l'angle du servomoteur du "coude" augmente de la variable "pas" (ici 20°)
  }
  if (J1 > 1000) // si la valeur de pinX est supérieure à 1000, autrement dit si l'utilisateur "met à droite" le joystick1 au maximum ( >1000)
  {
    pulse1 -= pas; // la valeur de l'angle du servomoteur du "coude" baisse de la variable "pas" (ici 20°)
  }

  //action sur le poignet
  if (J2 < 15)      // si la valeur de pinY est inférieure à 15, autrement dit si l'utilisateur "baisse" le joystick1 au maximum ( <15)
  {
    pulse2 += pas;  // la valeur de l'angle du servomoteur du "poignet" augmente de la variable "pas" (ici 20°)
  }
  if (J2 > 1000) // si la valeur de pinY est supérieure à 1000, autrement dit si l'utilisateur "monte" le joystick1 au maximum ( >1000)
  {
    pulse2 -= pas; // la valeur de l'angle du servomoteur du "poignet" baisse de la variable "pas" (ici 20°)
  }

  //action sur l'épaule
  if (J3 < 15)
  {
    pulse3 += pas;
  }
  if (J3 > 1000) {
    pulse3 -= pas;
  }

  //action sur la base
  if (J4 < 15)
  {
    pulse4 += pas;
  }
  if (J4 > 1000)
  {
    pulse4 -= pas;
  }


  //lecture BP1 et BP2, et action sur l'angle du servomoteur pince
  if (BP1 == LOW) {
    pulse5 -= 10;
  }
  if (BP2 == LOW) {
    pulse5 += 10;
  }

  delay(15);
}
[/code]

Pour des raisons pédagogiques, j'aimerai créer une librairie contenant un fichier joybrasservo.h et un joybrasservo.cpp contenant ma fonction controlebras(), et n'avoir dans mon programme principale que quelque chose comme ça

#include <Servo.h>
#include "joybrasservo.h"


void setup() {
  
  
}

  void loop() {
    
controleBras(9, 6, 3, 5, 10, 7, 11, 20); // void controleBras(int pinBase, int //pinEpaule, int pinCoude, int pinPoignet, int pinPince, int pinJoyBouton1, int 
// pinJoyBouton2, int pas)

  delay(15);

}

J'ai essayé beaucoup de choses, mais je me retrouve tout le temps en difficulté avec tout un tas de variables qui ne sont pas dans les bons "scope", je m'arrache les cheveux.

Pourriez vous m'aiguiller pour parvenir à organiser cette librairie?
Je vous remercie!!!

Romain pour Novamake73

Pour moi, une bibliothèque sert à créer (une instance d') un objet sur lequel tu peux agir pendant l'exécution de ton programme.
Par exemple, tu crées un objet de type 'bras' et ensuite tu le pilotes grâce à des 'méthodes' (des fonctions de la bibliothèque) comme tourner l'épaule, tourner le coude, ouvrir ou fermer la pince.
Si tu veux faire ça, en effet ce sera plus joli de créer une bibliothèque.

Disons que tu nommes la classe (le type) 'bras', tu déclareras un objet comme
bras monBras;
Et ensuite, dans la loop, tu lui donneras des ordres, par exemple :

monBras.tourneEpaule(45);
monBras.ouvrePince();
mon bras.tourneCoude(-10);
monBrad.fermePince();

etc.
Ces méthodes / fonctions, tu les auras définies dans ta bibliothèque.
L'avantage c'est que tu pourras aussi déclarer plusieurs 'bras' et les faire évoluer de la même manière.

Par contre, si tu veux simplement faire jouer un scénario figé à un seul bras, autant faire une simple fonction que tu placeras dans ton fichier ino et que tu appelleras dans le setup (pour une seule exécution) ou dans la loop (pour une exécution périodique).

Un lien pour en savoir plus sur le notion de classe (plusieurs pages à lire)

1 Like

Merci pour ta réponse lesept

Tu as raison, je vais me pencher sur les notions d'objets, de classe. Il faut que je m'y mette, c'est vraiment excellent comme approche.

Cependant, au club, nous avons des enfants de 8 ans qui démarre tout juste la programmation, l'arduino.
Nous sommes peu d'accompagnants bénévoles et des gros groupes! difficile de les accompagner en détail sur le code, d'autant que nous ne sommes que de simples amateurs nous même!

L'idée que j'ai est donc de leur proposer des projets avec des codes ultra simplifiés.
Pour ce projet de bras robotique, j'aimerai qu'ils n'aient, dans un premier temps, qu'à renseigner dans le fichier .ino les numéros des pins où ils auront branché leurs servomoteurs, et de tester de faire varier le "pas" de changement d'angle.

Je pensais faire cela en créant effectivement une fonction très "figée", que je localiserai dans un fichier .cpp, inclue dans le .ino, et ils n'auraient plus qu'à passer en paramètre de la fonction les pins et le pas souhaité.
Et éventuellement, si ils le souhaitent, dans un second temps, mettre le nez dans le cambouis de la fonction et éventuellement la modifier.

d'accord avec @lesept

ce qui serait envisageable serait de grouper un servo + un axe de joystick dans une classe SerStick éventuellement avec votre logique de tester < 15 et > 1000 pour faire un pas dans un sens ou dans l'autre.

par exemple jetez un oeil ici avec 2 instances

le sketch

#include "SerStick.h"

SerStick coude(3, A0);
SerStick poignet(5, A1);

void setup() {
  coude.begin();
  poignet.begin();
}

void loop() {
  coude.tick();
  poignet.tick();
  delay(50);
}

SerStick.h

#ifndef SERSTICK_H
#define SERSTICK_H
#include <Arduino.h>
#include <Servo.h>
class SerStick {
  public:
    SerStick(const uint8_t sPin, const uint8_t jPin, const int us = 1200, const int pas = 20): servoPin(sPin), joystickPin(jPin), microSec(us), deltaMicroSec(pas) {};
    void begin();
    void tick();
  private:
    Servo moteur;
    uint8_t servoPin;
    uint8_t joystickPin;
    int microSec;
    int deltaMicroSec;
};
#endif

SerStick.cpp

#include "SerStick.h"

void SerStick::begin() {
  moteur.attach(servoPin);
}

void SerStick::tick() {
  int jPos = analogRead(joystickPin);                 // pas besoin de pinMode() pour un analogRead
  if (jPos < 15)   microSec += deltaMicroSec;         // 15 pourrait être passé au constructeur
  else if (jPos > 1000) microSec -= deltaMicroSec;    // 1000 pourrait être passé au constructeur 
  if (microSec < 0) microSec = 0;                     // 0 pourrait être passé au constructeur
  else if (microSec > 2000) microSec = 2000;          // 2000 pourriat être passé au constructeur
  moteur.writeMicroseconds(microSec);
}
1 Like

C'est exactement ce que tu fais avec une classe.
L'utilisateur crée une instance bras en lui passant en argument les paramètres nécessaires à son fonctionnement. Et pour lui bras est une boite noire.
Si tu veux ensuite qu'il puisse manipuler le bras tu lui mets à disposition des méthodes lui permettant de faire bouger telle ou telle articulation d'un certain angle. Et là pareil il ne voit pas ce qui est sous le capot.

1 Like

Super les gars, merci pour toutes ces pistes et éclaircissements.
Je vais essayer de créer cette classe, et quand ça marchera (j'espère!!!), je posterai le code.
Bravo pour la réactivité!!!
A++

essayez le wokwi c'est sans doute proche de ce que vous aviez en tête

Bonsoir,

Je reviens donc, après un travail acharné, pour vous présenter le travail enfin terminé, grâce à vos indications et conseils!!!

Voilà le résultat final:

Le Sketch

#include "SerStick.h"
#include "SerBouton.h"

SerStick coude(3, A0,100);
SerStick poignet(5, A1, 100);
SerStick epaule(6, A2, 100);
SerStick doigt(9, A3, 100);

SerBouton pince(10,2,4,100);


void setup() {
  coude.begin();
  poignet.begin();
  epaule.begin();
  doigt.begin();

  pince.begin();
}

void loop() {
  coude.stick();
  poignet.stick();
  epaule.stick();
  doigt.stick();
  
  pince.click();
  delay(50);
}

SerStick.h

#ifndef SERSTICK_H
#define SERSTICK_H
#include <Arduino.h>
#include <Servo.h>
class SerStick {
  public:
    SerStick(const uint8_t sPin, const uint8_t jPin, const int pas, const int us = 1500, const int JSH = 1000, const int JSB = 15, const int SMax = 2000, const int SMin=1000): servoPin(sPin), joystickPin(jPin), deltaMicroSec(pas), microSec(us), JposSeuilHaut(JSH), JposSeuilBas(JSB), ServoPosMax(SMax), ServoPosMin(SMin) {};
    void begin();
    void stick();

  private:
    Servo moteur;
    uint8_t servoPin;
    uint8_t joystickPin;
    int microSec;
    int deltaMicroSec;
    int JposSeuilHaut;
    int JposSeuilBas;
    int ServoPosMax;
    int ServoPosMin; 
};
#endif

SerStick.cpp

#include "SerStick.h"
#include <Servo.h>

void SerStick::begin() {
  moteur.attach(servoPin);
  
}

void SerStick::stick() {
  int jPos = analogRead(joystickPin);                 // pas besoin de pinMode() pour un analogRead
  if (jPos < JposSeuilBas)   microSec += deltaMicroSec;         // 15 pourrait être passé au constructeur
  else if (jPos > JposSeuilHaut) microSec -= deltaMicroSec;    // 1000 pourrait être passé au constructeur 
  if (microSec < ServoPosMin) microSec = ServoPosMin;                     // 0 pourrait être passé au constructeur
  else if (microSec > ServoPosMax) microSec = ServoPosMax;          // 2000 pourriat être passé au constructeur
  moteur.writeMicroseconds(microSec);
}

SerBouton.h

#ifndef SERBOUTON_H
#define SERBOUTON_H
#include <Arduino.h>
#include <Servo.h>

class SerBouton {
  public:
    SerBouton(const uint8_t sPin, const uint8_t bPin1, const byte bPin2, const int pas, const int us = 1500, const int SMax = 2000, const int SMin=1000): servoPin(sPin), bouton1Pin(bPin1), bouton2Pin(bPin2), deltaMicroSec(pas), microSec(us), ServoPosMax(SMax), ServoPosMin(SMin) {};
    void begin();
    void click();
    

  private:
    Servo moteur;

    uint8_t servoPin;
    uint8_t bouton1Pin;
    byte bouton2Pin;
    int microSec;
    int deltaMicroSec;
    int ServoPosMax;
    int ServoPosMin; 
    bool bouton1;
    bool bouton2;
};
#endif

SerBouton.cpp

#include "SerStick.h"
#include "SerBouton.h"
#include <Servo.h>

void SerBouton::begin() {
  moteur.attach(servoPin);
  pinMode(bouton1Pin, INPUT_PULLUP);
  pinMode(bouton2Pin, INPUT_PULLUP);
}

void SerBouton::click()
{
  bool bouton1 = digitalRead(bouton1Pin);
  bool bouton2 = digitalRead(bouton2Pin);
  
  if (bouton1 == LOW) 
{
  microSec += deltaMicroSec;
  if (microSec > ServoPosMax) {microSec = ServoPosMax; } 
  moteur.writeMicroseconds(microSec);
  delay(50);
}
if (bouton2 == LOW)
{microSec -= deltaMicroSec;
  if (microSec < ServoPosMin) {microSec = ServoPosMin;  }
  moteur.writeMicroseconds(microSec);
  delay(50);
}
}

Comme vous le constatez, j'ai créé deux classes : (sur la base de l'exemple proposé par J-M-L)

  • Une pour les servomoteurs couplés aux axes des joystick
  • Une pour le servomoteur (la pince) couplé aux boutons des joystick (un bouton "ouvre" la pince, l'autre la "ferme" grâce à ce magnifique gripper SG90 gripper by aarruti - Thingiverse)

Il y a sans doute moyen de faire mieux, genre de fusionner les deux classes ensemble ou alors à minima de ne pas avoir à redéclarer deux fois les mêmes variables... mais je n'y suis pas arrivé.
Je n'ai pas non plus réussi à utiliser comme à mon habitude la librairie "OneButton" car j'avais de multiples erreurs du genre "invalid use of non-static fonction"...

Bref, je pense qu'il me faudra encore un peu de théorie pour comprendre l'utilisation de classes dans des classes de classes :slight_smile:

Mais j'obtiens le résultat souhaité et cela va grandement nous faciliter la vie au club : les enfants n'ont qu'à renseigner les pins de branchement de leurs servos et des joystick, ainsi que les pas souhaités pour les déplacements de leurs servos, et zou! on peut envoyer!! c'est génial!

Un grand merci également pour la découverte de WOKWI, c'est juste énorme! Je pense l'utiliser quasi systématiquement tellement je trouve ça pratique, facile d'accès, parfait pour apprendre, essayer, rater, recommencer... On va surement utiliser cet outil au club avec les enfants!

A bientôt pour de nouvelles aventures et encore MERCI pour votre réactivité et votre effort de mise à niveau!

on crée une arborescence de classes (héritage) que si ça fait du sens. Il faut se poser la question "est-ce que Y est une sorte de X" . S'il y a ce type de lien alors l'héritage / la spécialisation est pertinent.

par exemple vous pouvez avoir un classe véhicule, des sous classes automobile, moto, camion,.... parce qu'on peut voir une auto comme un type de véhicule.


bravo pour le boulot :slight_smile:

This topic was automatically closed 180 days after the last reply. New replies are no longer allowed.