Deux moteurs pas à pas

moteur pas à pas arduino

Voici un programme permettant de piloter deux moteurs pas à pas ; il a été écrit pour animer un cargo à deux mats de déchargement conçu et réalisé par Willy DEWULF CAM 0590.

Le but du mouvement est de décharger une caisse contenue dans la cale et de la déposer sur le quai. Les deux moteurs pas à pas travaillent simultanément pour entraîner deux treuils visibles sur la photo. Le mouvement est complexe, Willy l’a découpé en 52 étapes et pour chaque étape, il a calculé le nombre de pas à réaliser pour chaque moteur.

Un shield DRI0023 V2 est monté sur Arduino Uno, il permet de raccorder les deux moteurs. Les moteurs sont alimentés par une alimentation externe 12 V branchée sur le shield.

Attention
– les moteurs sont gourmands : il faut compter 1,2 A pour l’ensemble des 2 moteurs
– bien repérer le + et le – de l’alimentation externe et ne pas se tromper dans le branchement sur le shield (Il est détruit en cas d’erreur de branchement).

Le DRI0023 permet de passer les moteurs en mode disable ce qui permet de couper l’alimentation des moteurs quand on n’en n’a plus besoin.

Si les moteurs sont alimentés et immobiles, ils maintiennent leur position avec un certain couple.

Le programme envoie ses ordres aux moteurs mais ne sait pas s’ils ont vraiment changé de position : c’est au mécanicien de s’assurer que le mouvement n’est pas bloqué ou trop difficile.

La librairie standard stepper n’est pas utilisée. J’ai utilisé l’excellente librairie Accelstepper qui peut gérer plusieurs moteurs. La librairie Multistepper permet de plus de les faire travailler simultanément et en synchronisation : à chaque étape, les moteurs démarrent en même temps et s’arrêtent en même temps. Ces mouvements s’effectuent en mode bloqué : l’arduino ne traite pas d’autre instruction.

Dernier point : la vitesse du port série, utilisé pour afficher des messages de contrôle a été portée à 115200 bauds car je me suis aperçu qu’à 9600, le mouvement des moteurs apparaissait saccadé.



// Willy-V2stepperF

// Commande de deux moteurs pas a pas
// fonctionnant de facon simultanée et coordonnée
// Ecrit pour un modele de Willy
// le cargo a deux mats de charge

// les librairies necessaires

#include <AccelStepper.h>
#include <MultiStepper.h>

// 1 shield controleur pas a pas
// DFRobot DRI0023 V2
// est monte sur un arduino Uno
// 2 moteurs bipolaires pas a pas
// Mercury SM-42BYG011-25

// declaration des deux moteurs et de leur pins associees

AccelStepper stepper1(1, 6, 7);//( controleur type 1, step pin, dir pin )
AccelStepper stepper2(1, 5, 4);//( controleur type 1, step pin, dir pin )

// le moteur 1 est du cote X sur la carte
// le moteur 2 est du coté Y sur la carte

// ils seront geres comme un ensemble de deux moteurs
// nomme steppers

MultiStepper steppers;

// La version 2 du DRI0023
// utilise 2 pins supplementaires par rapport a la V1
// pour mettre les moteurs en etat enable ou disable

const int M1enablePin=8;  
const int M2enablePin=12;

// positions absolues des deux moteurs 
long positions[2]; // tableau à deux valeurs positions[0] et positions[1]

const int max_etapes = 52 ; // le nombre total d'étapes;

int num_etape = 0; // l'etape courante de 1 a max_etapes

// les increments courants
int inc1 = 0;
int inc2 = 0;

// on definit une structure pour
// ranger les increments de chaque etape

typedef struct {
  int nb_pas_moteur1;
  int nb_pas_moteur2;
} Tableau_increments;

// Pour chaque etape, on indique
// le nb de pas que doit faire chaque moteur

const Tableau_increments tab_pas[] = { 
// etapes 1 a 5
{18,36}, {18,36} , {18,36} , {18,36}, {14,36},
// etapes 6 a 10
{14,36}, {14,36} , {11,36} , {11,36}, {11,36},
// etapes 11 a 15
{7,36}, {7,36} , {36,-4} , {32,-14}, {36,-18},
// etapes 16 a 20
{36,-25}, {36,-25} , {36,-29} , {32,-32}, {36,-29},
// etapes 21 a 25
{36,-32}, {32,-36} , {36,-32} , {36,-32}, {32,-36},
//etapes 26 a 30
{32,-36}, {36,-32} , {32,-36} , {29,-36}, {32,-32},
//etapes 31 a 35
{29,-36}, {25,-36} , {25,-36} , {18,-36}, {14,-32},
//etapes 36 a 40
{4,-36}, {-36,-7} , {-36,-7} , {-36,-11}, {-36,-11},
//etapes 41 a 45
{-36,-11}, {-36,-14} , {-36,-14} , {-36,-14}, {-36,-18},
//etapes 46 a 50
{-36,-18}, {-36,-18} , {-36,-22} , {-36,-18}, {-36,-22},
//etapes 51 a 52
{-36,-22}, {-36,-22} ,

};


//**************
// bouton GO
//**************

// L'appui sur le bouton GO
// lance la boucle principale.
// Une fois que les moteurs ont effectue
// la serie complete on revient
// en attente d'appui sur ce bouton

int bouton_GO = A0;

boolean APPUI_BOUTON = 1 ;

// Doit-on afficher sur le port serie 
// les traces d'execution ?

const boolean AFFICHE = 0;

//////////////////
void setup()
//////////////////
{
  
  Serial.begin(115200);
  
  pinMode(bouton_GO,INPUT);
  
// Faire gerer les deux steppers par Multistepper
  
  steppers.addStepper(stepper1);
  steppers.addStepper(stepper2);  
  
// Faut-il inverser l'etat des pin direction, step, enable ?
// oui il faut inverser les pin enable
// car sur le DRV8825 (DR0023 V2)  un etat bas correspond a l'etat enable
// c'etait le contraire avec le A4988 qui equipait la V1 du DRI0023

  stepper1.setPinsInverted(false,false,true);
  stepper2.setPinsInverted(false,false,true);


// Informer la librairie des pins
// utilisees pour mettre les moteurs enable

  stepper1.setEnablePin(M1enablePin);
  stepper2.setEnablePin(M2enablePin);

// Mettre les moteurs en mode enable
// en fait ils sont deja dans ce mode
// automatiquement

 stepper1.enableOutputs();
 stepper2.enableOutputs();  

  
// Configurer vitesses maxi
  
  stepper1.setMaxSpeed(80); 
  stepper2.setMaxSpeed(80);   
  
  
// initialisation des positions a zero
  
  positions[0]= 0; 
  positions[1]= 0;  
   
}

//////////////
void loop(){  
//////////////  

  Serial.println("\n * * * * * En attente appui 1 s sur bouton GO * * * * *");

  while  (digitalRead(bouton_GO) != APPUI_BOUTON )  {            
   
  }  

  alimente_moteurs();
  
  cale_vers_quai ();

  Serial.println("\n********** Attente 10 s puis mvt de retour a la position initiale ");
  
  delay(10000);

  quai_vers_cale ();

  Serial.println("\n************** Fin d'un cycle cale => quai suivi de quai => cale");

  // Couper les moteurs pour eviter
  // qu'ils ne chauffent

  coupe_moteurs();  
   
} // fin loop


///////////////////////////////////////////////////////////
// Fonctions
//////////////////////////////////////////////////////////

////////////////////////
void cale_vers_quai() {
////////////////////////
  
// mouvement aller de la cale vers le quai
  
  
for (num_etape = 1 ; num_etape <= max_etapes ; num_etape++) {
  
  // aller chercher les increments de l'etape
  
  inc1 = tab_pas[num_etape -1].nb_pas_moteur1;
  inc2 = tab_pas[num_etape -1].nb_pas_moteur2;
   
  
  if (AFFICHE) { Serial.print("\n=========== Debut etape etape : "); Serial.println(num_etape);}
  
  if (AFFICHE) { Serial.print("Position avant mvt Moteur 1 : ") ; Serial.print(positions[0]);}
  if (AFFICHE) { Serial.print(" === Increment a faire : "); Serial.println(inc1);}
  

  if (AFFICHE) { Serial.print("Position avant mvt Moteur 2 : ") ; Serial.print(positions[1]);}
  if (AFFICHE) {Serial.print(" === Increment a faire : "); Serial.println(inc2);}
  
  // nouvelles positions a atteindre
  
  positions[0]+= inc1;
  positions[1]+= inc2; 
  
  steppers.moveTo(positions);
  // On ne fait rien d'autre que de positionner 
  // correctement les deux steppers 
  // aucune autre instruction ne se déroule (mode bloque)
  // les deux steppers finissent en meme temps leur positionnement
  
  steppers.runSpeedToPosition();
  
  if (AFFICHE) { Serial.print("Position apres mvt Moteur 1 : ") ; Serial.println(positions[0]); }
  if (AFFICHE) { Serial.print("Position apres mvt Moteur 2 : ") ; Serial.println(positions[1]); }
  if (AFFICHE) { Serial.print("=========== Fin etape : "); Serial.println(num_etape); }   
  
 } // for

// fin mouvement cale vers quai

} // fin cale_vers_quai

/////////////////////////
void quai_vers_cale() {
/////////////////////////  

// mouvement retour du quai vers la cale
// on decrit les etapes en commencant par la derniere
// on inverse les sens de rotation de chaque etape
  
for (num_etape = max_etapes ; num_etape >= 1 ; num_etape--) {
  
  // aller chercher les increments de l'etape
  // et inverser le sens de rotation (signe -)
  
  inc1 = - tab_pas[num_etape -1].nb_pas_moteur1;
  inc2 = - tab_pas[num_etape -1].nb_pas_moteur2;
   
  
  if (AFFICHE) { Serial.print("\n=========== Debut etape etape : "); Serial.println(num_etape);}
  
  if (AFFICHE) { Serial.print("Position avant mvt Moteur 1 : ") ; Serial.print(positions[0]);}
  if (AFFICHE) { Serial.print(" === Increment a faire : "); Serial.println(inc1);}
  

 if (AFFICHE) { Serial.print("Position avant mvt Moteur 2 : ") ; Serial.print(positions[1]);}
 if (AFFICHE) { Serial.print(" === Increment a faire : "); Serial.println(inc2);}
 
 // nouvelles positions a atteindre
  
  positions[0]+= inc1; 
  positions[1]+= inc2;
  
  steppers.moveTo(positions);
  // On ne fait rien d'autre que de positionner 
  // correctement les deux steppers 
  // aucune autre instruction ne se déroule (mode bloque)
  // les deux steppers finissent en meme temps leur positionnement
  
  
  steppers.runSpeedToPosition();
  
  if (AFFICHE) { Serial.print("Position apres mvt Moteur 1 :") ; Serial.println(positions[0]); }
  if (AFFICHE) { Serial.print("Position apres mvt Moteur 2 :") ; Serial.println(positions[1]); }
  if (AFFICHE) { Serial.print("=========== Fin etape : "); Serial.println(num_etape); }
  
 
   
 } // for

// fin mouvement quai vers cale
// on est revenu a la position d'origine

} // fin quai_vers_cale

/////////////////////////
void coupe_moteurs() {
///////////////////////// 

  stepper1.disableOutputs();
  stepper2.disableOutputs();  
}



//////////////////////////
void alimente_moteurs() {
//////////////////////////  
 stepper1.enableOutputs();
 stepper2.enableOutputs();  
}

contrôle moteurs

Voici un article très court ! Pas de super montage mais un programme intéressant trouvé sur le site de DFRobot

Je n’ai pas ce nouveau shield et j’ai juste modifié dans le programme les pin de contrôle pour qu’il tourne sur le matériel que j’ai. Regardez bien comment le programme est écrit, pour ma part j’ai appris plein de choses en l’épluchant. La définition est assez sophistiquée mais à l’utilisation, les fonctions sont très simples.


// Programme venant du wiki DFrobot
// Adapté pour shield DRI0009
// cette version fonctionne aussi sur :
//   - DFRduino Romeo V1 et V2
//   - module MD 1.3 DRI0002

/**set control port**/
const int E1Pin = 5 ; // pin pwm vitesse
const int M1Pin = 4 ; // pin sens

const int E2Pin = 6 ; // pin pwm vitesse
const int M2Pin = 7 ; // pin sens
 
/**inner definition**/
typedef struct {
  byte enPin;
  byte directionPin;
} MotorContrl;
 
const int M1 = 0;
const int M2 = 1;
const int MotorNum = 2;
 
const MotorContrl MotorPin[] = { {E1Pin, M1Pin}, {E2Pin, M2Pin} } ;
 
const int Forward = LOW;
const int Backward = HIGH;
 
/**program**/
void setup() {
  initMotor();
}
 
void loop() {
  int value;
  /**test M1 **/
  setMotorDirection( M1, Forward );
  setMotorSpeed( M1, 100 );
  delay(1000);
  setMotorSpeed( M1, 0 );
  delay(1000);
 
  setMotorDirection( M1, Backward );
  setMotorSpeed( M1, 50 );
  delay(1000);
  setMotorSpeed( M1, 0 );
  delay(1000);
 
  /**test M2**/
  setMotorDirection( M2, Backward );
  for (value = 0 ; value <= 100; value += 5) {
    setMotorSpeed( M2, value );
    delay(1000);
  }
  setMotorSpeed( M2, 0 );
  setMotorDirection( M2, Forward );
  for (value = 0 ; value <= 100; value += 5) {
    setMotorSpeed( M2, value );
    delay(1000);
  }
  setMotorSpeed( M2, 0 );
}
 
/**functions**/
void initMotor( ) {
  int i;
  for ( i = 0; i < MotorNum; i++ ) {
    digitalWrite(MotorPin[i].enPin, LOW);
 
    pinMode(MotorPin[i].enPin, OUTPUT);
    pinMode(MotorPin[i].directionPin, OUTPUT);
  }
}
 
/**  motorNumber: M1, M2
direction:          Forward, Backward **/
void setMotorDirection( int motorNumber, int direction ) {
  digitalWrite( MotorPin[motorNumber].directionPin, direction);
}
 
/** speed:  0-100   * */
inline void setMotorSpeed( int motorNumber, int speed ) {
  analogWrite(MotorPin[motorNumber].enPin, 255.0 * (speed / 100.0) ); //PWM
} 

Relais et fins de courses

Dans le montage précédent, il s’agissait d’automatiser un mouvement simple. Ici, on conserve le même genre de modèle, un chariot mobile sur se déplaçant sur des rails mais on change d’objectif : vous êtes aux commandes du modèle, avec un interrupteur à trois positions :

  • marche avant
  • arrêt
  • marche arrière

On va utiliser deux relais : un relais pour alimenter le moteur en marche avant, un autre pour alimenter le moteur en marche arrière.
On alimentera soit un relais, soit l’autre, soit aucun mais pas les deux en même temps ! (court-circuit).

  • Quand aucun relais n’est alimenté, ils sont tous les deux en position repos et rien ne bouge.
  • Quand le relais AV passe en position travail, deux séries de contacts T – C sont fermés et le moteur est alimenté dans un sens.
  • Quand le relais AR passe en position travail, deux séries de contacts T – C sont fermés et le moteur est alimenté dans l’autre sens.

Fichier hébergé par Archive-Host.com

Il est simple d’ajouter deux fins de courses NF qui permettent de s’assurer que le moteur s’arrête si le chariot mobile tente de dépasser la limite de position fixée à chaque extrémité.
Le fonctionnement est simple, on va prendre pour exemple le fonctionnement en marche avant : quand le modèle atteint la limite fixée, il appuie sur le fin de course AV, l’alimentation du relais marche AV est coupée, le relais passe en position repos ce qui coupe l’alimentation du moteur. Si on passe alors en marche arrière, c’est l’autre relais qui est mis sous tension et le modèle repart dans l’autre sens.

Relais inverseurs et relais autoalimentés

Fichier hébergé par Archive-Host.com

Est-ce qu’on peut automatiser des modèles meccano sans utiliser d’arduino et sans programmer ? Et oui, heureusement !

On peut faire des choses sympathiques avec des relais et des fins de course. Voilà un petit montage qui utilise deux fins de course ; une fois arrivé à une extrémité le chariot actionne un fin de course qui renverse le sens de rotation du moteur et le mouvement se répète. Il n’y a pas dans cette version de temps d’arrêt avant de repartir dans l’autre sens. Voir en fin d’article une solution mécanique pour mettre en place un délai lors de l’inversion de sens.

Pour rappel un relais est dispositif qui est à deux états :

  • – un état au repos quand il n’est pas alimenté
  • – un état travail quand on l’alimente

Pour alimenter le relais, on connecte (ici sur 12V continu) les deux bornes qui alimentent la bobine qui est à l’intérieur.

Repos, Travail, Commun

Cette bobine permet de mettre en mouvement des « interrupteurs » situés à l’intérieur du relais.

Plus précisément le relais utilisé fonctionne comme ceci :

  • quand il n’est pas alimenté (état repos), chaque borne « repos » est en contact avec la borne « commun » qui lui correspond
  • quand il est alimenté (état travail), chaque borne « travail » est en contact avec la borne « commun » qui lui correspond

Fichier hébergé par Archive-Host.com

L’ensemble de trois bornes « repos », « travail », « commun » en relation constitue un inverseur.

Pour l’instant, j’ai réalisé ce montage en utilisant un unique relais trouvé chez Conrad (502923 Relais-enfichable-Finder-553490120040-12-VDC-7-A-4-inverseurs-RT- + 503028 Support-relais-Finder-9404-). Le relais Conrad que j’ai choisi a 4 inverseurs (4 trios C,R,T) seuls 3 sont utilisés.

Le relais a donc 4 trios de 3 bornes (repos, travail, commun) + 2 bornes pour alimenter le relais lui-même. J’ai acheté un support, certes encombrant mais qui évite de réaliser des soudures. (Le relais s’enfiche dans le support et tous les contacts se font ensuite avec des borniers à vis).

Au lieu d’utiliser les termes « Repos », « Travail », « Commun », le relais que j’ai utilisé utilise les termes NC (Normally close), NO (Normally open) et COM (Commun).

On peut réaliser le montage en utilisant à la place d’un unique relais à 4 inverseurs, 3 relais comportant chacun un unique inverseur (Repos / Travail / Commun) comme ceux livrés dans les anciennes boîtes meccano « électronique ».

Quand le relais passe en position travail, le contact est coupé entre chaque borne « repos » et la borne « commun » qui lui correspond puis le contact s’établit entre chaque borne « travail » et la borne « commun » qui lui correspond.

Inversion de sens et auto-alimentation

  • 2 inverseurs sont utilisés pour changer le sens de rotation du moteur
  • 1 inverseur est utilisé en auto-alimentation du relais

Le principe de l’auto-alimentation d’un relais est rusé, il permet de maintenir le relais dans un état « travail » ou « repos » alors qu’on a actionné une fois le fin de course correspondant.

NF, NO …

NF signifie normalement fermé (contact établi quand le fin de course n’est pas actionné ; quand il est actionné, le contact se coupe)

NO normalement ouvert (pas de contact quand le fin de course n’est pas actionné ; quand il est actionné le contact s’établit).

Voilà des schémas possibles :

Avec 3 relais comportant un unique inverseur : relais1.pdf

Avec un seul relais comportant au moins trois inverseurs : relais2.pdf

Retarder l’inversion de marche

Une solution mécanique très simple existe. On peut mettre plusieurs dispositifs en série pour accroître le délai de retard.

Mesure de distance avec un capteur à ultrasons

Ici on a utilisé un URM37 V4.0, un Arduino Uno et un écran lcd 1602.

Il n’y a rien à faire de spécial sur le capteur, la version 4.0 est très simple à utiliser. Plusieurs modes sont possibles, j’ai utilisé le mode dit « PWM ».
Deux Pins sont utilisées, l’une en OUTPUT pour lancer l’ordre de mesurer, l’autre en INPUT pour recueillir la mesure.

Ce n’est pas une tension que l’on mesure en INPUT mais on récupère directement une valeur numérique. Cette valeur est une durée en microsecondes. Plus précisément on mesure le temps durant lequel le signal passe à l’état bas. Ensuite il suffit de savoir qu’un cm correspond à 50 microsecondes.

Pour que cela fonctionne bien, on a intérêt à mettre en hauteur les deux capteurs.

Si votre capteur est en version 3.2, il y a des adaptations à faire (voir les programmes du wiki de DFRobot).


// UltrasonP2
// fonctionne pour la version 4.0 de l'URM37
// teste sur Arduino Uno

// Ecrit sur la base d'un programme
// Présent sur le wiki de DfRobot 
// (# Editor     : ZRH from DFRobot
// # Date       : 29.08.2014 
// # Product name: URM V4.0 ultrasonic sensor
// # Product SKU : SEN0001
// # Version     : 1.0 ) 

// Description:
// On utilise un capteur a ultrasons
// pour mesurer des distances
// La distance mesurée est affichee sur un écran LCD

// La plage de detection est de  3 à 200 cm 
// Le mode de detection choisi est le "PWM trigger pin" 


// # Connexions capteur
// # Cote  Arduino  //  Cote URM 37 Vv4.0

// #       Vcc     -> Pin 1 VCC 
// #       GND     -> Pin 2 GND 
// #       Pin 3   -> Pin 4 ECHO 
// #       Pin 5   -> Pin 6 COMP/TRIG 


// # Connexions écran LCD 1602 (DFR0063)
// # Cote  Arduino  //  Cote LCD

// #       Pin A4  -> SDA
// #       Pin A5  -> SCL
// #       Vcc     -> VCC
// #       GND     -> GND



#include <Wire.h> // librairie standard
#include <LiquidCrystal_I2C.h> // librairie à télécharger
 
// Parametrer l'adresse du LCD à 0x27 
// pour deux lignes de 16 caractères
 
LiquidCrystal_I2C lcd(0x27,16,2);  
 

// La pin URECHO de l'Arduino recoit le signal 
// qui code la mesure de distance
int URECHO = 3;         

// Quand la pin URTRIG de l'Arduino passe a l'état bas
// la mesure est lancee
int URTRIG = 5;         

// Distance mesuree en cm 
unsigned int DistanceMeasured= 0;
 
void setup() 

{
   lcd.init();     // initialiser le lcd 
   lcd.backlight(); // allumer écran
   lcd.print("Distance en cm :    "); // affichage fixe  
  
  // Pin de lancement de la mesure 
  pinMode(URTRIG,OUTPUT);      
  // Pin mise à l'état haut
  // Un futur passage à l'etat bas 
  // lancera la mesure  
  digitalWrite(URTRIG,HIGH);                 
  
  // Pin en attente du signal
  pinMode(URECHO, INPUT);                    
  delay(500);
 
 }
 
 
void loop()
{
  PWM_Mode();
  delay(200);
} 
 
void PWM_Mode()                              
{ 
 
  // On lance une mesure
  digitalWrite(URTRIG, LOW);  
  digitalWrite(URTRIG, HIGH);  

  // On recueille sur la Pin URECHO
  // le resultat de la mesure
  // sous la forme d'une duree : 
  // on compte le temps passe
  // a l'etat bas 
 
  unsigned long LowLevelTime = pulseIn(URECHO, LOW) ;
  
    // On prefère ne pas aller jusqu' a 500 cm
    if(LowLevelTime>= 10000)                 
    {
    // Mesure invalide  
    lcd.setCursor(0,1); // curseur début deuxième ligne
    lcd.print("    "); // effacer la mesure précédente
    lcd.setCursor(0,1); // curseur début deuxième ligne
    lcd.print("###");
        }
    else{
    // 44 microsecondes correspondent a 1 cm
    // 50 selon la doc mais 44 donne de meilleurs résultats
    DistanceMeasured = LowLevelTime /44;   
    
    // Afficher sur ecran lcd    
    lcd.setCursor(0,1); // curseur début deuxième ligne
    lcd.print("    "); // effacer la mesure précédente
    lcd.setCursor(0,1); // curseur début deuxième ligne
    lcd.print(DistanceMeasured);
    
  }
  
}

Une version simplifiée qui utilise le moniteur série du PC au lieu de l’écran LCD :


// UltrasonP1
// fonctionne pour la version 4.0 de l'URM37

// Ecrit sur la base d'un programme
// Présent sur le wiki de DfRobot 
// (# Editor     : ZRH from DFRobot
// # Date       : 29.08.2014 
// # Product name: URM V4.0 ultrasonic sensor
// # Product SKU : SEN0001
// # Version     : 1.0 ) 

// Description:
// On utilise un capteur a ultrasons
// pour mesurer des distances

// La plage de detection est de  3 à 500 cm 
// Le mode de detection choisi est le "PWM trigger pin" mode
// La distance mesurée est affichee sur le moniteur serie
// Il faut donc utiliser le cable USB pour relier PC et Arduino

// # Connexions:
// # Cote  Arduino  //  Cote URM 37 Vv4.0

// #       Vcc     -> Pin 1 VCC 
// #       GND     -> Pin 2 GND 
// #       Pin 3   -> Pin 4 ECHO 
// #       Pin 5   -> Pin 6 COMP/TRIG 

// La pin URECHO de l'Arduino recoit le signal 
// qui code la mesure de distance
int URECHO = 3;         

// Quand la pin URTRIG de l'Arduino passe a l'état bas
// la mesure est lancee
int URTRIG = 5;         

// Distance mesuree en cm 
unsigned int DistanceMeasured= 0;
 
void setup() 
{
  //Initialization port serie
  Serial.begin(9600);                        
  
  // Pin de lancement de la mesure 
  pinMode(URTRIG,OUTPUT);      
  // Pin mise à l'état haut
  // Un futur passage à l'etat bas 
  // lancera la mesure  
  digitalWrite(URTRIG,HIGH);                 
  
  // Pin en attente du signal
  pinMode(URECHO, INPUT);                    
  delay(500);
  
  Serial.println("Init the sensor");
 
 }
void loop()
{
  PWM_Mode();
  delay(100);
} 
 
void PWM_Mode()                              
{ 
  Serial.print("Distance Mesuree = ");
  // On lance une mesure
  digitalWrite(URTRIG, LOW);
  
  digitalWrite(URTRIG, HIGH);  

  // On recueille sur la Pin URECHO
  // le resultat de la mesure
  // sous la forme d'une duree : 
  // on compte le temps passe
  // a l'etat bas 
 
  unsigned long LowLevelTime = pulseIn(URECHO, LOW) ;
  
    if(LowLevelTime>=45000)                 
    {
      Serial.print("Invalide");
    }
    else{
    // 50 microsecondes correspondent a 1 cm
    DistanceMeasured = LowLevelTime /50;   
    Serial.print(DistanceMeasured);
    Serial.println(" cm");
  }
  
}

Moteur avec encodeur – asservissement

On a vu que les programmes précédents ne permettaient pas une super exactitude de positionnement car, bien que le positionnement soit suivi avec précision grâce aux encodeurs, le moteur continue de tourner une fois la position atteinte et l’alimentation coupée.

Première façon assez « bourrine » mais efficace, si on a besoin de 720 pas de rotation, par exemple, retirer dans le programme une centaine de pas qui seront faits moteur coupé. Cette correction est constante pour une valeur pwm donnée et une tension donnée à condition de ne pas s’occuper de rotations de moins d’un tour.

Deuxième façon, recourir à une fonction d’asservissement. Je me suis inspiré de cet article qui m’a bien aidé car je ne connaissais rien de ce domaine.

On utilise un timer pour lancer une action d’échantillonnage à intervalles réguliers et suivant l’écart entre la position actuelle et la cible, on ajuste la vitesse du moteur et, éventuellement, on fait marche arrière si on a été trop loin.

Les paramètres ont été trouvés par ajustements successifs, si vous avez du matériel différent il faudra sans doute les modifier. La précision obtenue est satisfaisante avec des erreurs maxi autour de 3%. Les déplacements inférieurs à un tour sont à éviter.


/*
   Moteur encodeur V6

avec asservissement de type P  
 

*/
 
 #include <Encoder.h>
 
 // le moteur a deux encodeurs
 // en quadrature et
 // chaque pin est controlee
 // par interruption
 
 int A = 2; // pin encodeur A
 int B = 3; // pin encodeur B
 
Encoder position_moteur1(A,B);

// position cible exprimée en 
// nb de ticks
// 1 tour arbre moteur = 360 ticks

long position_cible_M1=0;

// un utilise une librairie
// pour commander l'echantillonage

#include <SimpleTimer.h>

SimpleTimer timer;

// Intervalle de temps entre deux mesures
// ici 80 ms

int intervalle_echantillonage=80;

// constante de correction
// coeff proportionnel
// asservissement de type P

float kp=0.95; 
 
int E1 = 5; //M1 Controle de vitesse PWM
 
int M1 = 4; //M1 Controle du sens de rotation

// valeur mini et maxi pwm
// si trop bas degradation des balais
// et certains moteurs ne tournent pas


int pwm_max=255;

int pwm_min=120;

// fonction de controle des moteurs
 
void marche_avant(int a) //Marche avant
{
     if ( a >= pwm_min) {
      analogWrite (E1,a); // PWM (reglage vitesse)
      digitalWrite(M1,HIGH); // sens de rotation
     } 
  else analogWrite(E1,0);; 
 
} 
 
void marche_arriere (int a) //Marche arrière
{
     
   if ( a >= pwm_min) {
       analogWrite (E1,a); // PWM (reglage vitesse)
       digitalWrite(M1,LOW); // sens de rotation
       } 
   else analogWrite(E1,0);
}

 
void setup() {
   
   // initialiser la communication serie
    
   Serial.begin(9600);
   
   // pin moteur
    
   pinMode(E1, OUTPUT); // E1 est utilisee en sortie    
   pinMode(M1, OUTPUT); // M1 est utilisee en sortie
   
   // pin encodeurs
   
   pinMode(A,INPUT); 
   pinMode(B,INPUT); 
   
   // pour que les capteurs à effet Hall fonctionnent
   // il faut activer la resistance pull-up
   // de l'Arduino
   
   digitalWrite(A,HIGH);
   digitalWrite(B,HIGH);
   
   // initailiser la position de l'axe moteur
   
   position_moteur1.write(0); 
   
   // la position que l'on veut atteindre
   // exprimée en degrés
   // remarquer que ce n'est pas un appel de fonction
   
   position_cible_M1=5000; 
   
   //demander au timer de lancer l'echantillonage
   
   timer.setInterval(intervalle_echantillonage,asservissement);    
    
}
 
// executee en boucle
 
void loop() {
  
   timer.run();              
    
   Serial.print("*** Dans loop, position actuelle :");
   
   Serial.println(position_moteur1.read());        
   
   // la position finit par se stabiliser
   // mais la fonction d'asservissement continue
   // cependant sans relache
   // à envoyer des ordres au moteur
   // Ceux-ci ne font pas tourner le moteur
   // car la vitesse demandee est trop faible  
}

// la fonction appelée par le timer

void asservissement()

{
   
  long erreur;
  
  int valeur_pwm;
    
  erreur = position_cible_M1 -  position_moteur1.read()  ;      
  
  valeur_pwm = kp * erreur;
  
  if (valeur_pwm >= 255) {
     valeur_pwm = pwm_max;
  }

  else   
  if (valeur_pwm <= -255) {
     valeur_pwm = -pwm_max ;
  }
  
  // petits ajustements avec map sur
  // les valeurs pwm
  // pour obtenir davantage de valeurs
  // actives
  
    if (valeur_pwm > 0) {
       valeur_pwm=map(valeur_pwm,0,255,80,255);   
       marche_avant(valeur_pwm);
     }
     
     else { valeur_pwm=map(valeur_pwm,-255,0,-255,-80);         
            marche_arriere(-valeur_pwm) ; 
          }
    
}

Moteur avec encodeur

Il existe des moteurs avec encodeur incorporé. J’ai trouvé celui-ci chez Lextronic, le modèle est un DCM2. Il y a 6 fils connectés au moteur. Le rouge et le noir sont les alimentations habituelles d’un moteur. Les 4 autres fils s’occupent de capteurs intégrés au moteur. Ce ne sont pas des capteurs optiques comme dans les montages précédents mais des capteurs magnétiques à effet Hall.

Les capteurs sont à alimenter en 5 v :

– le fil marron est l’alimentation des capteurs
– le fil vert est la masse pour les capteurs

– le fil bleu est la sortie du capteur A
– le fil violet est la sortie du capteur B

On peut utiliser un seul capteur et travailler dans le même esprit que précédemment. On peut aussi utiliser les deux capteurs ce qui est intéressant car cela nous permet de connaître le sens de rotation du moteur. (Les capteurs envoient des signaux carrés, en quadrature et cela permet de retrouver le sens de rotation du moteur).

On peut écrire un programme analogue à ceux utilisés jusqu’ici :


/*
   Moteur encodeur V0
   
   on utilise ici uniquement 
   
   le capteur A
    
       
 */
 
int ENC_A = 2; // encodeur A à la pin 2 
 
int E1 = 5; //M1 Controle de vitesse PWM
 
int M1 = 4; //M1 Controle du sens de rotation

// on va stocker les chgts de l'encodeur 

volatile long chgt_encodeur = 0;


// la fonction de traitement de l'interruption

void compte_chgt()
{

chgt_encodeur ++ ;

}

 
void marche_avant(char a) //Marche avant
{
  analogWrite (E1,a); // PWM (reglage vitesse)
  digitalWrite(M1,HIGH); // sens de rotation
} 
 
void marche_arriere (char a) //Marche arrière
{
  analogWrite (E1,a); // PWM (reglage vitesse)
  digitalWrite(M1,LOW); // sens de rotation
}

void stop_moteur () 
{
  analogWrite (E1,0); // PWM (reglage vitesse)
  delay(100);
}
 
 
/////////////////////////////////////////////////////////////////////
void marche_nb_pas( int vitesse, int nb_pas, char sens)
/////////////////////////////////////////////////////////////////////

{


chgt_encodeur = 0;


// lancer le moteur

 if (sens == 'A') {
      marche_avant (vitesse);
     }
     
 if (sens == 'R') {
      marche_arriere (vitesse);
     }
     

while ( chgt_encodeur < nb_pas  ) {        
  
  // on ne fait rien
  // chgt_encodeur est incrémentée
  // par la fonction de
  // traitement de l'interruption
     
  }      

stop_moteur();
}
 
 
 
// Executee une seule fois
 
void setup() {
   
   // initialiser la communication serie
    
   Serial.begin(9600);
    
   pinMode(E1, OUTPUT); // E1 est utilisee en sortie
    
   pinMode(M1, OUTPUT); // M1 est utilisee en sortie
   
   pinMode(ENC_A,INPUT); 
   
   // pour que le capteur à effet Hall fonctionne
   // il faut activer la resistance pull-up
   // de l'Arduino
   
   digitalWrite(ENC_A,HIGH);
   
   // l'interruption 0 (pin 2) déclenche la fonction compte_chgt
   
   attachInterrupt(0, compte_chgt, CHANGE);        
  
}
 
// executee en boucle
 
void loop() {
  
    
   Serial.println("On avance de 300 pas");
     
   marche_nb_pas(200,300,'A');
   
   delay(3000);
    
   
  Serial.println("On recule de 300  pas");
   
   marche_nb_pas(200,300,'R');
   
   delay(3000);
   
  
}


On peut aussi recourir à des bibliothèques optimisées et qui simplifient l’écriture , ce que l’on a fait dans le programme ci-dessous :


/*
   Moteur encodeur V1   
    
       
 */
 
 #include <Encoder.h>
 
 int A = 2; // pin encodeur A
 int B = 3; // pin encodeur B
 
Encoder position_moteur1(A,B);
 
 
int E1 = 5; //M1 Controle de vitesse PWM
 
int M1 = 4; //M1 Controle du sens de rotation



 
void marche_avant(char a) //Marche avant
{
  analogWrite (E1,a); // PWM (reglage vitesse)
  digitalWrite(M1,HIGH); // sens de rotation
} 
 
void marche_arriere (char a) //Marche arrière
{
  analogWrite (E1,a); // PWM (reglage vitesse)
  digitalWrite(M1,LOW); // sens de rotation
}

void stop_moteur () 
{
  analogWrite (E1,0); // PWM (reglage vitesse)
  delay(100);
}
 
 
/////////////////////////////////////////////////////////////////////
void marche_nb_pas( int vitesse, int nb_pas, char sens)
/////////////////////////////////////////////////////////////////////

{

position_moteur1.write(0);

long pos_M1  = 0;

// lancer le moteur

 if (sens == 'A') {
      marche_avant (vitesse);      
      
     }
     
 if (sens == 'R') {
      marche_arriere (vitesse);      
         
     }
     

 while (  abs(position_moteur1.read()) < nb_pas  ) {        
  
  }      

stop_moteur();

} 
 
 
// Executee une seule fois
 
void setup() {
   
   // initialiser la communication serie
    
   Serial.begin(9600);
    
   pinMode(E1, OUTPUT); // E1 est utilisee en sortie
    
   pinMode(M1, OUTPUT); // M1 est utilisee en sortie
   
   pinMode(A,INPUT); 
   pinMode(B,INPUT); 
   
   // pour que le capteur à effet Hall fonctionne
   // il faut activer la resistance pull-up
   // de l'Arduino
   
   digitalWrite(A,HIGH);
   digitalWrite(B,HIGH);
          
  
}
 
// executee en boucle
 
void loop() {
  
    
   Serial.println("On avance de 720 pas");
     
   marche_nb_pas(200,720,'A');
   
   delay(3000);
    
   
   Serial.println("On recule de 720  pas");
   
   marche_nb_pas(200,720,'R');
   
   delay(3000);
   
  
}

On peut enfin décider de raisonner non pas en nombre de pas à faire dans un sens ou dans l’autre mais en position à atteindre.


/*
   Moteur encodeur V2   
    
       
 */
 
 #include <Encoder.h>
 
 int A = 2; // pin encodeur A
 int B = 3; // pin encodeur B
 
Encoder position_moteur1(A,B);

 
int E1 = 5; //M1 Controle de vitesse PWM
 
int M1 = 4; //M1 Controle du sens de rotation

 
void marche_avant(char a) //Marche avant
{
  analogWrite (E1,a); // PWM (reglage vitesse)
  digitalWrite(M1,HIGH); // sens de rotation
} 
 
void marche_arriere (char a) //Marche arrière
{
  analogWrite (E1,a); // PWM (reglage vitesse)
  digitalWrite(M1,LOW); // sens de rotation
}

void stop_moteur () 
{
  analogWrite (E1,0); // PWM (reglage vitesse)
  delay(100);
}
 
 
/////////////////////////////////////////////////////////////////////
void marche_vers_position( int vitesse, int position_cible_M1)
/////////////////////////////////////////////////////////////////////

{
   
       while (position_moteur1.read() < position_cible_M1) {
              marche_avant(vitesse);           
       }
      
       while (position_moteur1.read() > position_cible_M1) {
              marche_arriere(vitesse);
        }           
        

stop_moteur();

}

 
// Executee une seule fois
 
void setup() {
   
   // initialiser la communication serie
    
   Serial.begin(9600);
    
   pinMode(E1, OUTPUT); // E1 est utilisee en sortie
    
   pinMode(M1, OUTPUT); // M1 est utilisee en sortie
   
   pinMode(A,INPUT); 
   pinMode(B,INPUT); 
   
   // pour que le capteur à effet Hall fonctionne
   // il faut activer la rsistance pull-up
   // de l'Arduino
   
   digitalWrite(A,HIGH);
   digitalWrite(B,HIGH);
   
          
  
}
 
// executee en boucle
 
void loop() {
  
   position_moteur1.write(0);     
      
   Serial.println("On avance position 300");
     
   marche_vers_position(200,300);
   
   Serial.println(position_moteur1.read());
   
   delay(3000);    
   
  Serial.println("On retourne position 0");
   
   marche_vers_position(200,0);
   
   Serial.println(position_moteur1.read());
   
   delay(3000);
   
   Serial.println("******************");
   
  
}

Ce dernier programme met en lumière un souci : on demande la position 300 mais on l’atteint pas forcément… En effet si l’alimentation moteur est bien coupée à la position 300, le moteur continue à tourner par inertie et va jusqu’à la position 305 (alim moteur 4,5v) ou 370 (alim moteur 12v).

Nous verrons comment améliorer la situation dans un prochain article.

Compter les tours avec une fourche optique – utilisation des interruptions

Pour être sûr de ne pas louper un événement (par exemple plus de lumière reçue par le détecteur de la fourche optique), il faut trouver un moyen de le traiter immédiatement, même quand le programme est occupé à tout autre chose, par exemple faire un calcul ou attendre par un appel à delay() … En effet, lorsqu’il n’est pas précisément en train d’exécuter les quelques lignes du programme qui sont à l’affût du changement d’état, l’Arduino est sourd !

Les interruptions permettent, toutes affaires cessantes, de traiter un événement dès qu’il se produit, en interrompant provisoirement l’exécution du programme. Une fois l’interruption traitée, le programme reprend son cours là où il en était resté.

Si vous ne savez pas du tout ce que c’est qu’une interruption, jetez un coup d’oeil ici.

Le programme précédent est simplifié, il va supporter des vitesses de rotation plus élevées… que des avantages !


/*
   Compte-tours avec fourche optique
   et roue encodeuse 10 dents
   DFrobot SEN0038
   utilisation des interruptions
    
       
 */
 
int FO = 2; // fourche optique relié à la pin 2 
 
int E1 = 5; //M1 Controle de vitesse PWM
 
int M1 = 4; //M1 Controle du sens de rotation

// on va stocker les chgts d'état de la fourche

volatile long chgt_fo = 0;


// la fonction de traitement de l'interruption

void compte_chgt()
{

chgt_fo ++ ;

}

 
void marche_avant(char a) //Marche avant
{
  analogWrite (E1,a); // PWM (reglage vitesse)
  digitalWrite(M1,HIGH); // sens de rotation
} 
 
void marche_arriere (char a) //Marche arrière
{
  analogWrite (E1,a); // PWM (reglage vitesse)
  digitalWrite(M1,LOW); // sens de rotation
}

void stop_moteur () 
{
  analogWrite (E1,0); // PWM (reglage vitesse)
  delay(100);
}
 
 
/////////////////////////////////////////////////////////////////////
void marche_nb_pas( int vitesse, int nb_pas, char sens)
/////////////////////////////////////////////////////////////////////

{

int nb_top = 0;

chgt_fo = 0;


// lancer le moteur

 if (sens == 'A') {
      marche_avant (vitesse);
     }
     
 if (sens == 'R') {
      marche_arriere (vitesse);
     }
     

while ( nb_top < nb_pas  ) {     
   
   // pour une dent passée,
   // deux changements d'état :
   // lumière=> pas de lumière
   // pas de lumière => lumière
   
   // c'est la fonction de traitement
   // de l'interruption qui s'occupe
   // d'incrémenter la variable chgt_fo
     
    nb_top = chgt_fo / 2;
    
  //  Serial.print ("*************** nb_top : ");
    
   // Serial.println(nb_top);    
  
     
  }     
 

stop_moteur();
}
 
 
// Executee une seule fois
 
void setup() {
   
   // initialiser la communication serie
    
   Serial.begin(9600);
    
   pinMode(E1, OUTPUT); // E1 est utilisee en sortie
    
   pinMode(M1, OUTPUT); // M1 est utilisee en sortie
   
   pinMode(FO,INPUT); // fourche optique reliée pin 2
   
   // l'interruption 0 (pin 2) déclenche la fonction compte_chgt
   
   attachInterrupt(0, compte_chgt, CHANGE);     
   
  
}
 
// executee en boucle
 
void loop() {
  
    
   Serial.println("On avance de 20 pas");
   // 10 pas = 1 tour
   
   marche_nb_pas(200,20,'A');
   
   delay(3000);
    
   
  Serial.println("On recule de 5  pas");
   
   marche_nb_pas(200,5,'R');
   
   delay(3000);
   
  
  Serial.println("On recule de 15 pas");
   
   marche_nb_pas(200,15,'R');    
    
  delay(3000);
}

Compter les tours avec une fourche optique

Voici une variante qui utilise une roue encodeuse de 10 dents et une fourche optique (Voir Dfrobot SEN0038).

Ici pas de contrainte mécanique comme un fin de course peut poser, il n’y a pas de contact.

Pour des vitesses de rotations élevées, ce type de programme ne suffit pas, on perd des événements ; pour s’en sortir, il faut faire appel aux interruptions comme nous le verrons dans le prochain article.

Le programme est pratiquement le même :



/*
   Compte-tours avec fourche optique
   et roue encodeuse 10 dents
   DFrobot SEN0038
    
       
 */
int FO = 2; // fourche optique relié à la pin 2 
 
int E1 = 5; //M1 Controle de vitesse PWM
 
int M1 = 4; //M1 Controle du sens de rotation
 
void marche_avant(char a) //Marche avant
{
  analogWrite (E1,a); // PWM (reglage vitesse)
  digitalWrite(M1,HIGH); // sens de rotation
} 
 
void marche_arriere (char a) //Marche arrière
{
  analogWrite (E1,a); // PWM (reglage vitesse)
  digitalWrite(M1,LOW); // sens de rotation
}

void stop_moteur () 
{
  analogWrite (E1,0); // PWM (reglage vitesse)
  delay(100);
}
 
 
/////////////////////////////////////////////////////////////////////
void marche_nb_pas( int vitesse, int nb_pas, char sens)
/////////////////////////////////////////////////////////////////////

{
// un axe comporte une roue
// encodeuse 
// 10 "dents" par roue
// on compte les changements d'etat
// de la pin reliée :
// pour une dent passée,
// deux changements d'état
// lumière=> pas de lumière
// pas de lumière => lumière

boolean etat_fo = false;

boolean etat_fo_precedent = false ;

int chgt_fo = 0 ;

int nb_top = 0;


// lancer le moteur

 if (sens == 'A') {
      marche_avant (vitesse);
     }
     
 if (sens == 'R') {
      marche_arriere (vitesse);
     }
     

while ( nb_top < nb_pas  ) {
  
      
 etat_fo = 0;
  
 if (digitalRead(FO)) {       
        
      delay(5);
      
      etat_fo = 1;
            
 }             
 
  
   // est-ce qu'on a changé d'état ?
  
   if ( etat_fo != etat_fo_precedent ) {
     
       chgt_fo++;
   }
   
  
   
   // 1 passage de dent entraine
   // 2 changements d'état
     
    nb_top = chgt_fo / 2;
    
  //  Serial.print ("*************** nb_top : ");
    
   // Serial.println(nb_top);
    
    etat_fo_precedent= etat_fo;     
     
  }     
 

stop_moteur();
}
 
 
 
// Executee une seule fois
 
void setup() {
   
   // initialiser la communication serie
    
   Serial.begin(9600);
    
   pinMode(E1, OUTPUT); // E1 est utilisee en sortie
    
   pinMode(M1, OUTPUT); // M1 est utilisee en sortie
   
   pinMode(FO,INPUT); // fourche optique reliée pin 2
   
   
  
}
 
// executee en boucle
 
void loop() {
  
    
   Serial.println("On avance de 20 pas");
   // 10 pas = 1 tour
   
   marche_nb_pas(200,20,'A');
   
   delay(3000);
    
   
  Serial.println("On recule de 5  pas");
   
   marche_nb_pas(200,5,'R');
   
   delay(3000);
   
  
  Serial.println("On recule de 15 pas");
   
   marche_nb_pas(200,15,'R');    
    
  delay(3000);
}

Compter les tours d’un axe avec un fin de course

Un petit montage avec lequel on va pouvoir contrôler le nombre de rotations d’un axe ; on va même pouvoir contrôler le nombre de quart de tours !

Le montage est adapté à des axes tournant à des vitesses faibles et reliés à des moteurs à faible inertie. Dans ces conditions, il offre une précision tout à fait satisfaisante.

Presque un moteur pas à pas 🙂 !

Pour ceux qui ont besoin d’un compte-tours, en voici un ici à base de fourche optique.

Voilà un modèle (construction très inspirée par un modèle de Willy) qui utilise les fonctions ci-dessous pour contrôler la montée et la descente de la benne.



/*
   Compte-tours avec fdc
    
       
 */
int FDC = 2; // fdc relié à la pin 2 
 
int E1 = 5; //M1 Controle de vitesse PWM
 
int M1 = 4; //M1 Controle du sens de rotation
 
int APPUI = 0; // sur ce fdc appui = etat bas
 
void marche_avant(char a) //Marche avant
{
  analogWrite (E1,a); // PWM (reglage vitesse)
  digitalWrite(M1,HIGH); // sens de rotation
} 
 
void marche_arriere (char a) //Marche arrière
{
  analogWrite (E1,a); // PWM (reglage vitesse)
  digitalWrite(M1,LOW); // sens de rotation
}

void stop_moteur () 
{
  analogWrite (E1,0); // PWM (reglage vitesse)
  delay(100);
}
 
 
/////////////////////////////////////////////////////////////////////
void marche_nb_pas( int vitesse, int nb_pas, char sens)
/////////////////////////////////////////////////////////////////////

{
// une poulie porte des plots
// qd le plot touche le fdc
// on compte les changements d'etat
// de la pin reliée
// pour un plot appuyé,
// deux changements d'état


boolean etat_fdc = false;

boolean etat_fdc_precedent = false ;

int chgt_fdc = 0 ;

int nb_top = 0;


// lancer le moteur

 if (sens == 'A') {
      marche_avant (vitesse);
     }
     
 if (sens == 'R') {
      marche_arriere (vitesse);
     }
     


while ( nb_top < nb_pas  ) {
  
      
 etat_fdc = 0;
  
 if (digitalRead(FDC) == APPUI) {     
    
      delay(5);     
        
      etat_fdc = 1;
            
 }             
 
  
   // est-ce qu'on a changé d'état ?
  
   if ( etat_fdc != etat_fdc_precedent ) {
     
       chgt_fdc++;
   }
   
  
   
   // 1 passage de plot entraine
   // 2 changements d'état
     
    nb_top = chgt_fdc / 2;
    
    //Serial.print ("*************** nb_top : ");
    
   // Serial.println(nb_top);
    
    etat_fdc_precedent= etat_fdc;     
     
  }     
 

stop_moteur();
}
 
 
 
 
// Executee une seule fois
 
void setup() {
   
   // initialiser la communication serie
    
   Serial.begin(9600);
    
   pinMode(E1, OUTPUT); // E1 est utilisee en sortie
    
   pinMode(M1, OUTPUT); // M1 est utilisee en sortie
   
   pinMode(FDC,INPUT); // fin de course relié pin 2
   
   // ici on n'active pas la resistance pull-up interne 
   //  on utilise un crash sensor DFRobot
   // qui est  FDC avec resistance de rappel intégrée
   // et resistance de protection intégrée
   // modèles utilisés :
   // SEN0138-L et SEN0138-R
  
}
 
// executee en boucle
 
void loop() {
  
    
   Serial.println("On avance de 4 pas");
   
   marche_nb_pas(200,4,'A');
   
   delay(3000);
    
   
  Serial.println("On recule d'un pas");
   
   marche_nb_pas(200,1,'R');
   
   delay(3000);
   
  
  Serial.println("On recule de 3 pas");
   
   marche_nb_pas(200,3,'R');    
    
   delay(3000);
}