Motoriser un pont tournant (Tuto Arduino #02)

Dans ce tutoriel, nous verrons comment utiliser l’Arduino pour piloter un moteur pas à pas et motoriser un pont tournant.

Pour le pont tournant de notre réseau fixe HO, nous avons souhaité mettre en œuvre une carte Arduino pour nous familiariser avec, mais surtout disposer d’une motorisation à faible coût avec une certaine valeur ajoutée. Utiliser l’Arduino pour ce type de projet permet d’aller plus loin qu’une simple motorisation (ex : moteur à courant continu ou la vitesse de rotation varie en fonction de la tension appliquée) pour apporter un réalisme plus important.

Valeur ajoutée avec Arduino :

  • L’accélération / décélération du moteur pas-à-pas de sa position de départ à celle de destination pour obtenir un bel effet visuel d’accélération et ralenti et ce sans à-coups.
  • Permettre de caler le pont automatiquement face aux voies (sans avoir à tâtonner en appuyant un coup à gauche… un coup à droite sur les boutons de commande)
  • D’aller directement à une voie souhaitée tout en calant la voie en face
  • Paramétrer et ajuster des vitesses de rotation par programmation sans jouer sur la tension appliquée

Le tutoriel sera décomposé en 2 parties :

  1. Motorisation du pont tournant selon 2 modes de fonctionnement
  2. Ajout du module décodeur DCC pour contrôler le pont depuis la centrale (optionnel)

La première partie permettra de mettre en œuvre le pont tournant selon 2 modes de fonctionnement. Un mode dans lequel le pont pourra tourner dans le sens des aiguilles d’une montre (ou sens inverse) à une vitesse définit en maintenant les boutons de commandes enfoncés. Puis un mode ou le pont se positionnera automatiquement (après avoir effectué un « zéro » par rapport à une voie de référence) sur la voie suivante ou précédente.

Dans la seconde partie nous viendrons ajouter le module DCC qui permettra de commander le pont tournant depuis la centrale (et/ou l’ordinateur). Cette deuxième partie n’est donc en aucun cas indispensable.

Pré-requis :

Copier le programme tel quel ne posera pas de problème particulier si vous souhaitez utiliser le mode de rotation manuel uniquement. L’utilisation du mode de positionnement « automatique » nécessitera d’enregistrer dans le programme toutes les positions du moteur correspondant à chaque alignement de voie.

 
 
 
 

Matériel nécessaire à la réalisation

Partie électronique

ATTENTION : L’article ci-dessous s’appuie sur un moteur 28BYJ. Ce « petit moteur » a été mis en œuvre pour des raisons de coût. Mais il pourra parfois avoir du mal à entraîner le pont en fonction du poids en charge. Une évolution de ce montage est prévue à terme et basée sur carte contrôleur TBB6600 et un moteur de type NEMA17 qui lui possède beaucoup plus de couple pour un fonctionnement sans a-coups. N’hésitez pas à nous contacter pour plus d’informations.

Liste des composants utilisés pour la motorisation :

  • 1x Carte Arduino UNO (U1)
  • 1x Moteur 28BYJ-48-08 5 Vcc avec son driver à base de ULN2003
  • 1x Interrupteur 2 positions
  • 2x Boutons poussoir
  • 1x Alimentation 5V/1A pour la commande du moteur à base de ULN2003
  • 1x Alimentation 9V/1A pour l’alimentation de la carte Arduino

Liste des composants utilisés si vous voulez ajouter l’interface DCC :

  • 1x Résistance 1KΩ / 1 Watt (R1)
  • 2x Résistance 10KΩ / 1/4 Watt (R2 et R3)
  • 1x Diode 1N4148 (D1)
  • 1x Optocoupleur 6N137 (U2)

La mise en place de l’interface DCC est présentée en détail dans le précédent tutoriel.

Composants optionnel :

  • 1x Shield à borniers DFR0060 (marque DF Robot)
  • 1x Sachet de fils de connexions M/M
  • 1x Sachet de fils de connexions M/F

Le « Shield » à borniers s’enfiche directement sur la carte Arduino et permet d’accéder à toutes les entrées/sorties à l’aide de bornier à vis. Il peut s’avérer intéressant lorsque l’on a pas mal de câblage à faire pour sécuriser les connexions.

Partie programmation

Nous allons utiliser ici la classe « AccelStepper.h » permettant de gérer le moteur pas-à-pas et de mettre en place des phases d’accélération et de décélération du moteur entre son point de départ et d’arrivée.

Installation de la librairie :

Pour installer une librairie dans l’IDE Arduino (outil de développement) :

  1. Dans le menu « Outils », sélectionner « Gérer les librairies »
  2. Dans le champs de rechercher, renseigner « accel stepper »
  3. Puis installer la dernière version de la librairie

Le moteur pas-à-pas 28BYJ-48

Caractéristiques :

  • Alimentation : 5 V, Intensité: 25 mA, Résistance: 21 ohms
  • Nombre de pas par tour: 64 (réduction de 4096 en sortie d’axe)
  • Réduction: 1/64

Modes des moteurs pas-pas :

PLEIN-PAS : Dans ce mode la tension est appliquée aux deux enroulements en inversant le courant alternativement. Le moteur fait un tour (360°) en 64 pas. Pour un pas, l’angle est donc de 360/64 = 5,625°. Mais avec la réduction on obtient une foulée de 5,625°/64 soit 0,087°. Cela se traduira par une valeur de « 2048 » pour faire un tour avec l’utilisation de la librairie « AccelStepper ».

DEMI-PAS : Dans ce mode, un enroulement est mis sous tension et ensuite deux enroulements sont alimentés alternativement, ce qui fait que le rotor ne tourne que sur la moitié de la distance. Le moteur tourne alors en 2x son nombre de pas. Ici 128 pas par tour donc. Amenant l’angle du pas à 360/128 = 2,81125°. Et réduction appliquée, cela ramène la foulée à 5,625°/128 soit 0,043°. Cela se traduira par une valeur de « 4096 » pour faire un tour avec l’utilisation de la librairie « AccelStepper ».

Le mode demi-pas va produire une rotation avec moins d’à-coups mais perdra en couple. Environ 30 % de perte de couple.

Motorisation du pont tournant

Schéma structurel

Ici le câblage du moteur sur le driver ne pose pas de problème avec la fiche et son détrompeur. La connexion à l’Arduino est faite par les entrées IN1 à IN4. Le VCC est fournit par le transformateur +5V. Le pole moins du transformateur étant relié à la masse commune à l’ensemble du montage.

Les 2 boutons poussoirs et interrupteur sont reliés à la masse commune du montage. Le point central pour l’interrupteur et une des 2 pattes du bouton poussoir. Puis les autres contacts sont reliés à l’Arduino sur les pattes D3, D4, D5.

Les boutons et l’interrupteur sont reliés directement à l’Arduino sans la mise en place de résistance de Pull-up. Les broches de l’Arduino pouvant remplir cette fonction si elles sont correctement définit dans le programme, ce que nous verrons plus loin.

La patte D2 est laissée libre pour l’insertion (en option) du module DCC et pouvoir commander le pont depuis la centrale.

Pull-up ? Qu’est ce que c’est ? :

Une résistance de Pull-Up (ou résistance de tirage) permet dans le cadre de montages « numériques » (0 ou 1) de ne pas laisser l’entrée (ici de l’Arduino) en « l’air » lorsque le bouton poussoir n’est pas enfoncé.
Lorsqu’une entrée numérique est laissée en « l’air » (ou flottante) sans fixer son potentiel (ou masse), il y a un risque que la valeur lue en entrée fluctue entre l’état bas (0) et l’état haut (1). Un parasitage qui viendrait donc perturber le bon fonctionnement du montage (ici du programme informatique).

L’Arduino permet de s’affranchir de cette résistance physique puisque déjà « intégrée » dans l’Arduino. Pour utiliser cette résistance, il suffit de déclarer l’entrée en tant que « PULLUP » et réduire ainsi le nombre de composants du montage.

Par opposition on parlera de résistance de pull-down lorsque celle-ci sera raccordée à la masse et non au VCC comme dans l’exemple ci-dessus.

Principe de fonctionnement

Le montage comporte 2 boutons poussoir et un interrupteur. Pour notre pont tournant nous avons choisi de mettre en place le fonctionnement suivant : Les 2 boutons poussoir permettront de faire tourner le pont dans le sens des aiguilles d’une montre (BP_DROIT) ou inverse (BP_GAUCHE) alors que l’interrupteur permettra de sélectionner un mode de fonctionnement.

Les modes étant :

  1. Rotation continue du pont (le temps que la pression sur l’un des 2 poussoirs est maintenue)
  2. Rotation d’un cran à gauche (ou droite). Soit vers la voie suivante et selon les positions pré-enregistrées dans le programme.

Selon l’utilisation que vous souhaitez mettre en place, on peu également imaginer que l’interrupteur de mode ne soit pas utilisé pour rotation libre/rotation cran mais permette d’avoir 2 vitesses de rotations libre (lente/rapide). Ou même d’avoir un mode « Démo » dans le quel le pont tourne en continu (type présentation vitrine d’une machine sur le pont).

En mode rotation continue, lorsque celle-ci s’arrête, le système sauvegarde cette position comme étant le point 0. Soit le point de départ lorsque l’on voudra basculer en mode cran à cran.

La plaque de tests

Avant de se lancer directement sur le pont tournant, il est indispensable de faire des tests. Monter les différents composants sur une planche ne prend pas un temps fou et permettra de tester le programme en amont.

Le programme

// Inclure la librairie
#include "AccelStepper.h"
#define nbPosition 11                 // Le nombre de positions d’arrêt face aux voies (crans)
#define vitesseRotationManuelle 30   // Vitesse de Rotation en mode manuel/calibrage (~50 = très lent)
#define vitesseMaxCrans 75           // Vitesse de Rotation en mode crans (~500)
#define vitesseAccelerationCrans 40  // Vitesse d'accélération / décélération en mode crans (~100) 
int tabPosition[nbPosition] = {-2048, -1877, -284, -216, -147, 0, 163, 1740, 1840, 1994, 2048};
int ptrCranZero;                // Position du point 0 dans le tableau
int ptrCranCourant;             // Pointeur de position courante dans le tableau
bool btnClic = false; 
// Assignation des broches moteurs aux broches arduino
#define motorPin1  8                  // IN1 (Module ULN2003)
#define motorPin2  9                  // IN2 (Module ULN2003)
#define motorPin3  10                 // IN3 (Module ULN2003)
#define motorPin4  11                 // IN4 (Module ULN2003)
// Le type d'interface est défini sur 8 (HALF4WIRE), car nous voulons utiliser des demi-pas avec un moteur à 4 fils
// Si l'on souhaite mettre le moteur en mode plein-pas, la valeur devra être défini à 4 (FULL4WIRE)
#define MotorInterfaceType 8
// Initialisation pour utiliser AccelStepper avec les paramètres du dessus (Séquence IN1-IN3-IN2-IN4 avec le moteur 28BYJ-48)
AccelStepper stepper = AccelStepper(MotorInterfaceType, motorPin1, motorPin3, motorPin2, motorPin4);
// Assigner les boutons aux Pins
const int BPG = 3;              // Bouton tourne sens inverse aiguilles d'une montre
const int BPD = 4;              // Bouton tourne sens aiguilles d'une montre
const int Inter = 5;            // Interrupteur de mode
const int ledTemoin = 13;       // Led intégrée à la carte de l'arduino
bool rotationEnCours = false;   // true = Rotation en Cours
/* 
 *  Coupe l'alimentation sur le moteur
 *  Pour éviter qu'il ne chauffe à l'arrêt
 */
void powerOffMoteur () {
  digitalWrite( 8, LOW); // Si le moteur chauffe toujours à l'arret
  digitalWrite( 9, LOW); // Remplacer les LOW par des HIGH (et inversement)
  digitalWrite(10, LOW);
  digitalWrite(11, LOW); 
}
/* 
 *  Initialisation de la carte Arduino
 *  Et des composants qui lui sont raccordés
 */
 
void setup() {
    // Le mode PULL-UP permet de s'affranchir d'une résistance
    pinMode(Inter, INPUT_PULLUP);
    pinMode(BPD, INPUT_PULLUP);
    pinMode(BPG, INPUT_PULLUP);
    // On utilise la LED intégrée à la carte comme témoin
    pinMode(ledTemoin, OUTPUT);  //LED
    // Cherche l'emplacement du point 0 dans le tableau des positions
    for (int ii=0; ii < nbPosition; ii++) {
      if (tabPosition[ii] == 0) ptrCranZero = ii;
    }
    // Force l'emplacement du point courant dans le tableau au 0
    ptrCranCourant = ptrCranZero;   // Au point 0 au début
    // Pour le mode rotation par crans 
    // On définit la vitesse MAX 
    // Et la vitesse dé départ et de fin de rotation
    // La classe "AccelStepper" se charge de calculer la courbe de vitesse :)
    stepper.setMaxSpeed(vitesseMaxCrans);
    stepper.setAcceleration(vitesseAccelerationCrans);
    // Par défaut on dit que le moteur démarre au point 0
    // Mais on peu l'ajuster en manuel
    stepper.setCurrentPosition(0);
}
/* 
 * Fait tourner le moteur à gauche ou à droite en continu
 * Lorsque l'un des boutons est pressé
 */
void rotationManuelle (int sensRotation = 1) {
  rotationEnCours = true;
  if (digitalRead(ledTemoin)== LOW) digitalWrite(ledTemoin, HIGH);
  
  stepper.setSpeed(vitesseRotationManuelle * sensRotation);
  stepper.runSpeed();
}
/* 
 * Fait tourner le moteur à gauche ou à droite par crans prédéfinis
 * Lorsque l'un des boutons est pressé
 * On définit la position de destination avec moveTo()
 * Puis runToPosition() pour lancer la rotation à la vitesse définit et avec accélération
 * Le moteur ralentira en atteignant sa position de destination
 * (1 tour = 4096)
 */
void rotationByCrans () {
  rotationEnCours = btnClic = true;
  if (digitalRead(ledTemoin)== LOW) digitalWrite(ledTemoin, HIGH);  
  
  int goTO = tabPosition[ptrCranCourant];
  stepper.moveTo(goTO);
  stepper.runToPosition();
  delay(250);
  powerOffMoteur();
  
  if (ptrCranCourant == 0 || ptrCranCourant == (nbPosition-1)) {
    ptrCranCourant = ptrCranZero;
    stepper.setCurrentPosition(0);
  }
}
void loop() {
  // Mode rotation Manuelle / Calibrage
  // ---------------------------------------------
  if (digitalRead(Inter) == HIGH) {
    
    if (digitalRead(BPG) == LOW) {
      rotationManuelle (-1) ;  
    } else if (digitalRead(BPD) == LOW ){
        rotationManuelle (1) ;
    } else {
      // Arreter la rotation lorsqu'un bouton poussoir est relaché
      if (rotationEnCours == true) {
        rotationEnCours = false;
        if (digitalRead(ledTemoin) == HIGH) { digitalWrite(ledTemoin, LOW); }
        // Le point d'arrêt devient alors le point 0
        stepper.setCurrentPosition(0);
        ptrCranCourant = ptrCranZero;
        powerOffMoteur();
      }
    }
  } else {
  
    // Rotation par crans / taquets
    // ---------------------------------------------
    if (digitalRead(BPG) == LOW) {
      ptrCranCourant --;
      rotationByCrans (); 
    } else if (digitalRead(BPD) == LOW ) {
      ptrCranCourant ++;
      rotationByCrans ();
    } else {
      if (rotationEnCours == true) {
        rotationEnCours = false;
        if (digitalRead(ledTemoin) == HIGH) { digitalWrite(ledTemoin, LOW); }
      }
    }
    // Lorsque les 2 poussoirs sont relachés
    if (digitalRead(BPG) == HIGH && digitalRead(BPD) == HIGH) { btnClic = false; }    
  
  }  
}

Dans le principe, les valeurs qui sont déclarées en tête de programme permettront d’ajuster selon les besoins, les vitesses de rotation, le nombre de crans ou le pont devra s’arrêter et surtout le tableau de l’ensemble les positions possibles.

Puis la carte est initialisée pour la gestion des 2 poussoirs, de l’interrupteur ainsi que le driver du moteur pas-à-pas. Initialisation de la librairie « AccelStepper » qui permettra d’avoir des ralentis et accélération du moteur.

Une fois initialisé, le programme scrute en boucle les boutons poussoirs. En fonction de leur état le programme appellera les fonctions de rotations du moteur soit de manière continue, soit en allant à sa position suivante pré-enregistrée.

En mode rotation continue, lorsque la rotation s’arrête, le point 0 est mémorisé systématiquement. C’est à partir de ce point que seront « jouées » les différentes positions pré-enregistrées si l’on bascule dans le mode « crans ». Évidemment le point 0 d’alignement devra toujours être fait par rapport à une seule et même voie.

Une fois la rotation terminée et ou la destination atteinte, la fonction annulant tout courant dans le moteur est appelée pour éviter que le moteur ne chauffe inutilement.

Définition des positions enregistrées

Pour le mode « crans » nous devons donc renseigner :

  • La constante du nombre de positions du pont que nous auront
  • Le tableau de l’ensemble des positions (dans les 2 sens)
#define nbPosition 11

int tabPosition[nbPosition] = {-2048, -1877, -284, -216, -147, 0, 163, 1740, 1840, 1994, 2048};

Pour rappels :

  • En mode demi-pas, le moteur parcours pour une foulée 0,08789° (4096 foulées pour 360°)
  • En mode plein-pas, le moteur parcours pour une foulée 0,17575° (2048 foulées pour 360°)
  • Nous avons choisi d’utiliser le mode demi-pas pour une meilleure précision
  • Il est possible d’utiliser le mode plein-pas pour bénéficier du couple maximum du moteur au cas ou le pont rencontrerait des difficultés à tourner (+30% de couple avec le mode plein-pas).

Notre tableau de positions devra donc avoir des valeurs comprises (pour le demi-pas) entre 0 et 4096 pour représenter un tour. Sauf qu’ici nous motorisons un pont tournant. Et le pont tournant n’a besoin que de faire 1/2 tour dans les faits pour revenir en position « identique/alignée » (sans tenir compte du sens de la machine qui serait posée dessus bien évidemment).

Soit :

  • En mode demi-pas, 2048 = 180° = 1/2 Tour

Dans le programme, lorsque le parcours des valeurs du tableau des positions atteins soit la limite basse, soit la limite haute, le programme repositionne alors le pointeur de tableau sur le 0.

if (ptrCranCourant == 0 || ptrCranCourant == (nbPosition-1)) {
    ptrCranCourant = ptrCranZero;
    stepper.setCurrentPosition(0);
  }
Sur une feuille, noter approximativement les différents angles qui vont intervenir sur le pont.

Prenons l’exemple ou le pont part du point 0 et tourne dans le sens inverse des aiguilles d’une montre. Pour calculer nos valeurs nous allons devoir encoder toutes les positions ou le pont (soit par l’avant, soit par l’arrière) se retrouve face à une voie.

  • Soient les angles relevés : -13°, -19°, -25°, -165°, -180°
  • Se traduisant en programme par les valeurs : -147, -216, -284, -1877, -2048
  • Et dans le tableau : {-2048, -1877, -284, -216, -147, 0, 163, 1740, 1840, 1994, 2048};

Faire de même pour les valeurs ou le pont tourne dans le sens des aiguilles d’une montre et les reporter dans le tableau des positions. Les valeurs seront bien évidemment ajustées une fois testées en situation.

Commander le pont depuis la centrale

Schéma structurel

On ajoute ici les composants qui vont permettre de lire les commandes DCC :

Décoder les trames DCC avec Arduino

Explication du programme pour décoder les trames DCC

Le programme plus haut permet la commande du pont de façon manuelle. Voici quelques lignes de code supplémentaires qui permettrons de le faire fonctionner depuis la centrale DCC à l'aide du décodeur de trames. Quelques précisions :

Ajout de la librairie qui permettra de décoder les trames DCC. On donne aussi une adresse de base à laquelle le programme réagira :

#include "DCC_Decoder.h"

#define baseAdresse 100  // Adresse de base du Arduino-Decodeur

Ces 2 fonctions permettront de décoder l'adresse qui a été envoyée et réagir en fonction. Le programme interagit sur 3 adresses. Tourner d'un cran en sens inverse des aiguilles, tourner d'un cran en sens des aiguilles et replacer le pont à son point 0.

/* 
 *  Fonction appelée par la librairie DCC et qui permet
 *  d'activer ou de désactiver les sorties accessoires
 *  Dans notre cas pour mettre en rotation le pont
 *  Attention, une ligne doit être décommentée si vous utilisez une Z21 qui a un décalage d'adressage)
 */
void BasicAccDecoderPacket_Handler(int address, boolean activate, byte data) {
  address -= 1;
  address *= 4;
  address += 1;
  address += (data & 0x06) >> 1;
  //address = address - 4;        // Retirer le commentaire de cette ligne en cas d'utilisation d'une Z21

  // Appel de la fonction qui va réagir en fonction de l'adresse lue
  actionsDecodeurArduino (address);
}

/* 
 *  Permet de traduire l'adresse qui a été activée en action
 */

void actionsDecodeurArduino (int adresseLue) {
  
  // Ex: si adresse de base est 100 alors l'arduino réagit aux adresse 100, 101, 102,...
  switch (adresseLue) {

    // Faire tourner le pont d'un cran à gauche (= comme bouton gauche)
    case (baseAdresse): 
      ptrCranCourant --;
      rotationByCrans (); 
      break;

    // Faire tourner le pont d'un cran à droite (= comme bouton droite)
    case (baseAdresse + 1):
      ptrCranCourant ++;
      rotationByCrans ();
      break;

    // Mettre le pont à son point 0 (s'il n'est pas déjà en position 0)
    case (baseAdresse + 2):

      if (ptrCranCourant != ptrCranZero) {

        ptrCranCourant = ptrCranZero;
        int goTO = tabPosition[ptrCranCourant];
        stepper.moveTo(goTO);
        stepper.runToPosition();
        stepper.setCurrentPosition(0);
        delay(250);
        powerOffMoteur();
      }
      break;
  }
}

Dans la partie setup(), initialisation de la librairie DCC.

    // Déclaration pour la partie DCC
    // La fonction est appelée sur Interruption de la broche 2
    // A chaque action faite sur la centrale

    DCC.SetBasicAccessoryDecoderPacketHandler(BasicAccDecoderPacket_Handler, true);
    DCC.SetupDecoder( 0x00, 0x00, 0 );


Et pour terminer, on détectera les trames DCC que lorsque le mode de fonctionnement du pont est en mode "crans".

// Appel de la librairie DCC, Lecture des adresses DCC uniquement dans le mode Crans.
    DCC.loop();



À vous de jouer ! Allez... Sur le pont moussaillons.

Retours d'expérience

Yannick m'a contacté fin octobre 2020 à propos du Tuto sur le pont tournant PECO à base d'Arduino. Il souhaitait obtenir quelques conseils et être rassuré sur la faisabilité d'un tel montage. Un mois plus tard son pont tournant était en place et complètement opérationnel. En complément de mon article plus "informatique" j'ai souhaité qu'il puisse donner son retour d'expérience et décrive la mise en œuvre de son pont tournant. Expérience qu'il a accepté volontiers de raconter et de partager dans cet article et je l'en remercie.

Pont tournant de Yannick V.

Partager sur les réseaux sociaux en un clic.