Le pont tournant d'Olivier

Avatar du membre
France Amigorl
Posteur irrécupérable
Posteur irrécupérable
Messages : 830
Enregistré le : 02 févr. 2025, 20:15
Prénom : Rémy
LOCALISATION : Pau
Centres d'intérêts : Digitalisation matériel ancien, construction d'un réseau HO. Arduino, Dccex-Conmmand-Station, Rocrail, Digitalisation pont tournant Jouef, Decodeur accessoires DIY. 3D PLA et débutant SLA.
Echelle pratiquée : H0
Âge : 64
Pays :
France
France
juin 2026 12 20:23

Re: Le pont tournant d'Olivier

Message par France Amigorl

Pour mon pont tournant, j'utilise également le dit "homing" (point zéro ) en deux temps.
Je passe dans un sens à vitesse moyenne devant le capteur IR puis je change de sens pour repasser devant à faible vitesse.
Ainsi la précision est excellente (je crois que c'est le principe utilisé pour les imprimantes 3D).
Donc en parfait accord avec toi (Patrick)
Avatar du membre
France ramasoft
Chef es bogies
Chef es bogies
Messages : 5471
Enregistré le : 29 juil. 2024, 19:06
Prénom : Patrick
LOCALISATION : Indre - Levroux
Centres d'intérêts : Modélisme ferroviaire
Programmation C++, VB6 et assembleur
Électronique
Echelle pratiquée : H0
Âge : 68
Pays :
France
France
juil. 2026 05 17:40

Re: Le pont tournant d'Olivier

Message par France ramasoft

Suite et fin du pont tournant Arduino.

Faute de pont à motoriser, j'ai fait un "démonstrateur" pour mettre mon code au point car forcément entre la théorie qui marche à tous les coups et la pratique, il y a quelques différences. 😜
A commencer par l'encodeur KY-40 que j'avais prévu de lire avec des interruptions mais qui s'est révélé capricieux avec des rebonds intempestifs et je suis passé à une scrutation dans le loop, moins réactive mais plus stable.
Le mieux est souvent l’ennemi du bien.

Ce que je présente ici peut servir à motoriser n’importe quel pont : il suffit de pouvoir solidariser la partie tournante avec la couronne de 110 dents et de fixer le moteur et son engrenage à côté.
La couronne de 110 dents peut aussi être agrandie ou diminuée si besoin mais une bonne démultiplication augmente la précision et aussi le couple fourni par le moteur. Sachant que ces moteurs Nema 17 sont très puissants et même avec une loco Lematec de 10 kg il tournera sans problème.

Le principe de fonctionnement est simple :
- on définit un point de référence : le zéro
- tous les déplacements sont comptés en nombre de pas depuis cette position zéro. Donc chaque voie à une position comptée en nombre de pas. Le moteur se repositionne ensuite sur cette position avec une grande précision.
- les positions des voies sont enregistrées dans l’Eprom de l’Arduino
- la dernière bonne position atteinte est aussi enregistrée ainsi à l’allumage, le pont lit cette position et sait exactement où il se trouve ce qui évite d’avoir à faire un calibrage à chaque fois.
- le nombre de voies est codé « en dur » dans le code car je n’ai pas voulu m’embêter avec des fonctions d’ajout/insertion/suppression de voies et généralement, on ne modifie pas le nombre de voies tous les matins.

Si on voulait commander le pont avec un logiciel de train et on aurait juste à assigner une adresse à chaque position de voie avec un décodage par la bibliothèque DCC_Decoder. Mais comme ce n’est pas du tout mon truc, j’ai laissé ça de côté. Mais c’est facile à greffer sur le code de base.

Le dessus
IMG_20260705_172127.jpg
Le dessous
IMG_20260705_172139.jpg
La face avant du boitier de commande
IMG_20260629_155313.jpg
IMG_20260629_161457.jpg
L'intérieur du boitier de commande
IMG_20260705_090443.jpg
Pour le boitier de commande, j’ai utilisé un écran LCD rétroéclairé de 4 lignes de 20 caractères mais on aurait aussi pu utiliser un écran OLED. Il est piloté en I2C ce qui permet de n’avoir que deux fils de liaison vers l’Arduino.
Les voies sont choisies avec un encodeur KY-40 et il y a 4 boutons pour les réglages manuels avec deux vitesses de déplacement : rotation dans le sens horaire ou anti-horaire à vitesse lente ou rapide.
Et un bouton d’arrêt d’urgence qui se verrouille en position ouverte lorsqu’on a appuyé dessus.
Un calibrage est fait automatiquement après un arrêt d’urgence puisque la dernière position n’a pas pu être enregistrée dans l’Eprom.
L’interrupteur marche arrêt coupe le 5V et donc aussi le 12V grâce au relais de la platine du driver.
L’alimentation 5V et 12V se fait à cet endroit.


Le moteur Nema 17
IMG_20260705_090355.jpg
L’Arduino Mega qui permet de disposer de suffisamment de broches et de s’adapter à une évolution future.
IMG_20260705_090405.jpg
La platine du driver avec l’arrivée du 12V sur une prise cinch
IMG_20260705_090448.jpg
Le 5V sur des bandes de cuivre autocollantes où on peut venir se brancher par simple soudure.
IMG_20260705_090519.jpg
Les relais qui mettent le courant sur les voies
IMG_20260705_090325.jpg
Pour la détection du point zéro, j’avais prévu un capteur IR mais j’ai mis un capteur à effet Hall plus facile à installer et qui s’avère suffisamment précis.
Attention à le mettre à l’opposé du moteur car je me suis rendu compte qu’il dégageait un champ magnétique assez fort, suffisamment pour faire tourner l’aimant sur lui-même quand il n’est pas collé.
De même il faut que ce soit le côté Sud qui soit en face du capteur.
Le capteur envoie un état bas sur sa broche signal quand il détecte l’aimant.
KY-003 PinOut.jpg
Le capteur posé à la hauteur de la roue dentée
IMG_20260705_090316.jpg
Le pignon du moteur (16 dents et 110 dents)
IMG_20260705_090542.jpg
Pour garantir une bonne précision il faut que la recherche du zéro se fasse toujours dans le même sens de rotation
Le pont tourne à vitesse réduite jusqu’à trouver l’aimant puis revient doucement en arrière pour affiner la position.
On pourrait aussi utiliser deux vitesses mais j’ai fait au plus simple car le calibrage n’est à faire qu’à la première utilisation ou si on déplace le pont à la main ou encore si on a fait un arrêt d’urgence
Au niveau de la mécanique, je me suis mis dans la même configuration que le pont d’Olivier qui a déjà une couronne de 110 dents solidaire du pont et un engrenage de 16 dents sur l’axe du moteur.
Là, c’est imprimé en PLA mais si on voulait le faire « pour de vrai » il faudrait utiliser un autre matériau plus solide.
Donc ça fait une réduction de 110/16 = 6,875
Le moteur est piloté en micro-pas avec une résolution de 1/32e de pas (ça se règle sur le driver TMC2209) :
• 1 tour de moteur = 200 pas x 32 micropas = 6400 pas
• 1 tour de pont (6,875 tours moteur) = 6400 x 6,875 = 44 000 pas pour faire un tour complet (360°).
Cela signifie que chaque pas du moteur représente un déplacement angulaire du pont de :
360° / 44000 = 0,008° donc on aura une bonne precision d’alignement au niveau des rails.



Attention à ce sujet, car les infos qu’on trouve sur le net sont parfois fausses et au départ j’avais mis une table de configuration fausse (plus haut dans le sujet) et je croyais être en 1/16e de pas alors que j’avais 44000 pas pour un tour complet …
Donc avec la broche MS1 à +Vcc et MS2 à Gnd on est bien en 1/32e de pas.
Config_TMC2209.jpg

Pour le driver, c’est le TMC2206 qui est monté sur une petite platine en veroboard : il y a juste à mettre des connecteurs pour pouvoir brancher le moteur, les arrivées de courant et l’Arduino. Le TMC2209 est monté sur des connecteurs à bandes et pas directement soudé pour pouvoir le changer éventuellement. Il ne doit pas chauffer du tout ainsi que le moteur. Si ça chauffe: coupez tout, il y a un problème ...
Le moteur tourne dans un sens ou dans l’autre selon l’orientation du câble sur le driver. Donc si on constate que le pont tourne à l’envers par rapports aux boutons de réglage manuel, il suffit de l’inverser sur la prise de la carte du driver.
Platine Driver.jpg
Il y a un relais pour mettre le 12V au moteur quand le 5V est présent ce qui permet de tout commander avec un seul interrupteur. Les led bleue et jaune servent à indiquer la présence de courant aux entrées.
Les broches DIR STEP et ENABLE sont connectés à l’Arduino
MS1 et MS2 permettent de choisir la résolution voulue parmi 4.
Ici MS1 est relié au +5V et MS2 à la masse ce qui donne une résolution en 1/16e de pas
Ne pas oublier le condensateur de 100µF 35V en parallèle sur l’alimentation 12V et qui protège le driver des pics de tension qui apparaissent lors des commutations pendant la rotation du moteur.
Ni la diode de roue libre sur la bobine du relais : elle est montée en inverse par rapport à l’alimentation.
Pour la tension du moteur, on peut aussi mettre 9V seulement : ce sera suffisant et ça évite les risques de chauffe du moteur et du driver.
En ce qui concerne le réglage de la VRef du driver, j’avais expliqué tout ça en détail dans la 2eme vidéo de l’ascenseur mais en restant en 9V ou en 12V on n’aura pas besoin d’y toucher. C’est seulement si on voulait augmenter le courant du moteur pour lui donner plus de puissance mais ici c’est inutile. Par défaut les TMC2209 sont réglés à environ 0,9A sachant qu’on peut monter jusqu’à 2A mais avec risques pour le driver et le moteur.
Diapositive7.JPG
Diapositive8.JPG

Le courant est mis uniquement sur la voie d’arrivée quand le pont est bien positionné en face.
Pour Olivier, la partie tournante du pont est alimentée par des lamelles de contact sur la voie d’arrivée mais ce serait pareil avec un pont alimenté par le centre : il faut couper les voies inutilisées que ce soit en analogique ou en DCC pour éviter de gaspiller le courant de la centrale.

Et une vidéo du fonctionnement:
Pont Tournant1.mp4
Vous n’avez pas les permissions nécessaires pour voir les fichiers joints à ce message.
Avatar du membre
France ramasoft
Chef es bogies
Chef es bogies
Messages : 5471
Enregistré le : 29 juil. 2024, 19:06
Prénom : Patrick
LOCALISATION : Indre - Levroux
Centres d'intérêts : Modélisme ferroviaire
Programmation C++, VB6 et assembleur
Électronique
Echelle pratiquée : H0
Âge : 68
Pays :
France
France
juil. 2026 05 17:55

Re: Le pont tournant d'Olivier

Message par France ramasoft

Au niveau du code, c'est assez simple et il n'y a pas de fioritures....
Il y a 3 modules: il faut tout mettre dans un répertoire appelé "Pont_Tournant_V1_2" et l'IDE Arduino les charge dans 3 onglets automatiquement
Pont_Tournant_V1_2.ino
Fonctions_Menus.ino
Fonctions_Deplacement.ino

Code : Tout sélectionner

/* 
Commande Pont tournant Nema 17 avec TMC2209 et Arduino Mega
**************************************************************
              Version 1.20  © Ramasoft Juillet 2026
**************************************************************
Broches de contrôle du Moteur
2 -> PIN_ENABLE Bleu
3 -> PIN_STEP  	Orange	
4 -> PIN_DIR   	Jaune	

Autres Broches utilisées 
5 -> PIN_IR   	          Capteur Infrarouge ou effet Hall  Vert
6 -> PIN_ARRET_URGENCE    Bouton Arrêt d'urgence             Blanc 

Boutons de commande           
7 PIN_CLK   Encodeur CLK Jaune 
8 PIN_DT    Encodeur DT  Bleu
9 PIN_SW   Clic Encodeur                                     Marron

10 PIN_BTN_CW_LENT    rotation sens horaire                  Vert     
11 PIN_BTN_CCW_LENT   rotation sens anti horaire             bleu 
12 PIN_BTN_CW_RAPIDE  rotation sens horaire rapide           Jaune  
13 PIN_BTN_CCW_RAPIDE rotation sens anti-horaire rapide      rouge
Bien s'assurer que les boutons Vert et Jaune font tourner le pont dans le sens des aiguilles d'une montre
Si ça tourne à l'envers, il faut inverser le connecteur du moteur sur la carte du driver

Écran LCD LiquidCrystal I2C : 20 -> SDA Oranger 21 -> SCL Vert

Broches pour les relais qui mettent le courant 
sur les 8 voies de stockage: 22 à 29
      22 -> Blanc/marron  E1
      23 -> Marron        E2
      24 -> Blanc/Vert    S1
      25 -> Vert          S2
      26 -> Blanc/Bleu    S3
      27 -> Bleu          S4
      28 -> Blanc/orange  S5  
      29 -> Orange        S6
Bug de l'IDE Arduino à l'ajout de lignes de remarques en début de code -> sauvegarder pour éviter l'erreur
*/
#include <Wire.h>
#include <LiquidCrystal_I2C.h>
#include <EEPROM.h>
#include <AccelStepper.h>

// --- AFFECTATION DES BROCHES (PINOUT) ---
const int PIN_ENABLE = 2;
const int PIN_STEP = 3;
const int PIN_DIR = 4;
const int PIN_IR = 5;
const int PIN_ARRET_URGENCE = 6;

const int PIN_CLK = 7;  // Encodeur CLK 
const int PIN_DT = 8;   // Encodeur DT  
const int PIN_SW = 9;    // Clic Encodeur

const int PIN_BTN_CW_LENT = 10;
const int PIN_BTN_CCW_LENT = 11;
const int PIN_BTN_CW_RAPIDE = 12;
const int PIN_BTN_CCW_RAPIDE = 13;

// Tableau des pins pour les 8 relais
const int PINS_RELAIS[8] = {22, 23, 24, 25, 26, 27, 28, 29};

// --- CONFIGURATION ADRESSES EEPROM ---
const int ADDR_POS_ACTUELLE = 0;
const int ADDR_VOIES_START = 4; 

// --- CONFIGURATION DU MOTEUR ---
const float VMax = 4000;
const float Acceleration = 500;
const int VitesseLente = 100;    
const int VitesseRapide = 800;  

// --- INSTANCIATION DES OBJETS ---
LiquidCrystal_I2C lcd(0x27, 20, 4); 
AccelStepper Moteur(AccelStepper::DRIVER, PIN_STEP, PIN_DIR);

// --- VARIABLES GLOBALES ---
int etatPrecedentCLK;        // Stocke l'état précédent de CLK pour détecter le changement
int voieSelectionnee = 0; 
int derniereVoieAffichee = -1;
long positionsVoies[8]; 
long positionActuelle = 0;

long pasAEffectuer = 0; // Variables pour la détermination du chemin le plus court
long ecartBrut = 0;
unsigned long dernierTempsClic = 0;
unsigned long dernierTempsEncodeur = 0;
const unsigned long DELAI_DEBOND_CLIC = 250; 
const unsigned long DELAI_DEBOND_ROTATION = 150; // Filtre anti-rebond en millisecondes pour la rotation

// États pour le menu principal
enum Etats { INITIALISATION, VEILLE, DEPLACEMENT, REGLAGE_MANUEL, URGENCE };
Etats etatCourant = INITIALISATION;


void setup() {
  
    // Configuration des entrées/sorties
  pinMode(PIN_ENABLE, OUTPUT);
  pinMode(PIN_IR, INPUT_PULLUP);
  pinMode(PIN_ARRET_URGENCE, INPUT_PULLUP);
  
  pinMode(PIN_CLK, INPUT_PULLUP);
  pinMode(PIN_DT, INPUT_PULLUP);
  pinMode(PIN_SW, INPUT_PULLUP);
  
  pinMode(PIN_BTN_CW_LENT, INPUT_PULLUP);
  pinMode(PIN_BTN_CCW_LENT, INPUT_PULLUP);
  pinMode(PIN_BTN_CW_RAPIDE, INPUT_PULLUP);
  pinMode(PIN_BTN_CCW_RAPIDE, INPUT_PULLUP);

  for (int i = 0; i < 8; i++) {
    pinMode(PINS_RELAIS[i], OUTPUT);
  }
  couperToutesLesVoies();

  // Configuration AccelMoteur
  Moteur.setMaxSpeed(VMax);
  Moteur.setAcceleration(Acceleration);
  digitalWrite(PIN_ENABLE, HIGH); 

  // Initialisation LCD
  lcd.init();
  lcd.backlight();
  lcd.setCursor(0, 0);
  lcd.print("PONT TOURNANT V1.20 ");
  lcd.setCursor(0, 2);
  lcd.print("Initialisation...");

  // Lecture EEPROM
  EEPROM.get(ADDR_POS_ACTUELLE, positionActuelle);
  Moteur.setCurrentPosition(positionActuelle); 
  
  for (int i = 0; i < 8; i++) {
    EEPROM.get(ADDR_VOIES_START + (i * sizeof(long)), positionsVoies[i]);
  }

  delay(1500);

  // Lecture de l'état initial pour la scrutation de l'encodeur
  etatPrecedentCLK = digitalRead(PIN_CLK);

  // Premier affichage du menu
  AffichageInitial();
  
  etatCourant = VEILLE;
}

void loop() {
  // L'arrêt d'urgence est traité en priorité avant tout le reste
  if (digitalRead(PIN_ARRET_URGENCE) == LOW || etatCourant == URGENCE) {
    
    // Actions immédiates de sécurité
    digitalWrite(PIN_STEP, LOW);     
    digitalWrite(PIN_ENABLE, HIGH);  // Relâche le moteur
    couperToutesLesVoies();          // Coupe le jus des voies
    //etatCourant = URGENCE;           // Verrouille l'état
    
    // Affichage écran
    lcd.clear();
    lcd.setCursor(0, 0);
    lcd.print("!!! ARRET URGENCE !!!");
    lcd.setCursor(0, 2);
    lcd.print("Deverrouillez Bouton");
   // Boucle bloquante tant que le bouton physique est enfoncé
    while (digitalRead(PIN_ARRET_URGENCE) == LOW) {
      delay(100); 
    }

    // Une fois le bouton déverrouillé, on force le calibrage
    delay(1000);
    calibrerZero();
    
    // 5. Reconstruction du menu principal
    AffichageInitial();
    etatCourant = VEILLE;
    derniereVoieAffichee = -1;
    return; // On repart proprement sur un loop tout neuf en mode VEILLE
  }

  // Si pas d'urgence, le switch classique prend le relais
  switch (etatCourant) {
          
    case VEILLE:
      // Lecture de l'encodeur 
      readEncoder();
      
      // Mise à jour de l'affichage de la voie uniquement sur changement
      if (voieSelectionnee != derniereVoieAffichee) {
        lcd.setCursor(12, 1);
        lcd.print("    "); 
        lcd.setCursor(12, 1);
        
        if (voieSelectionnee < 2) {
          lcd.print("Entree " + String(voieSelectionnee + 1)); 
        } else {
          lcd.print("Sortie " + String(voieSelectionnee - 1)); 
        }
        derniereVoieAffichee = voieSelectionnee;
      }
      
      // Affichage de la position : sert au débogage mais pas que ...
      lcd.setCursor(13, 2);
      lcd.print(Moteur.currentPosition());
      
      // Clic de validation -> on passe en mode Déplacement
      if (digitalRead(PIN_SW) == LOW && (millis() - dernierTempsClic > DELAI_DEBOND_CLIC)) {
        dernierTempsClic = millis();
        etatCourant = DEPLACEMENT;
      }

      // Passage en mode réglage si appui sur les boutons manuels
      if (digitalRead(PIN_BTN_CW_LENT) == LOW || digitalRead(PIN_BTN_CCW_LENT) == LOW ||
          digitalRead(PIN_BTN_CW_RAPIDE) == LOW || digitalRead(PIN_BTN_CCW_RAPIDE) == LOW) {
        lcd.clear();
        etatCourant = REGLAGE_MANUEL;
      }
      break;

    case DEPLACEMENT:
      digitalWrite(PIN_ENABLE, LOW); 
      couperToutesLesVoies();         
      
      lcd.setCursor(0, 3);
      lcd.print("Statut: En route... ");
      
      

      // 1. Calcul de l'écart géométrique brut
      ecartBrut = positionsVoies[voieSelectionnee] - Moteur.currentPosition();

      // 2. Détermination du sens de rotation le plus court et du nombre de pas réels
      if (ecartBrut >= 0) {
        if (ecartBrut <= 22000) {
          // Sens Horaire direct
          pasAEffectuer = ecartBrut;
        } else {
          // Sens Anti-Horaire (on contourne par l'autre côté)
          pasAEffectuer = ecartBrut - 44000; 
        }
      } 
      else { // ecartBrut < 0
        if (ecartBrut >= -22000) {
          // Sens Anti-Horaire direct
          pasAEffectuer = ecartBrut;
        } else {
          // Sens Horaire (on contourne par l'autre côté)
          pasAEffectuer = ecartBrut + 44000;
        }
      }

      // 3. On applique l'ordre de mouvement relatif à AccelStepper
      // AccelStepper gère lui-même le changement de sens (Dir Pin) selon le signe (+ ou -)
      Moteur.move(pasAEffectuer);

      // 4. Boucle d'exécution du mouvement
      while (Moteur.distanceToGo() != 0) {
        if (digitalRead(PIN_ARRET_URGENCE) == LOW) {
          Moteur.stop();
          return; 
        }
        Moteur.run();
      }

      // 5. Une fois arrivé, on "re-confectionne" la position actuelle entre 0 et 43999
      // pour que l'Arduino ne garde pas en mémoire des chiffres cumulés géants
      positionActuelle = (Moteur.currentPosition() % 44000 + 44000) % 44000;
      //positionActuelle=(positionsVoies[voieSelectionnee]);
      Moteur.setCurrentPosition(positionActuelle);
      
      EEPROM.put(ADDR_POS_ACTUELLE, positionActuelle); 
      
      alimenterVoie(voieSelectionnee); 
      digitalWrite(PIN_ENABLE, HIGH);  

      lcd.setCursor(0, 3);
      lcd.print("Statut: Aligne & OK ");
      delay(1000);
      
      lcd.setCursor(0, 3);
      lcd.print("                    ");
      etatCourant = VEILLE;
      derniereVoieAffichee = -1; 
      break;

   case REGLAGE_MANUEL:
      lcd.setCursor(0, 0);
      lcd.print("=== MODE REGLAGE ===");
      lcd.setCursor(0, 1);
      if (voieSelectionnee < 2) {
        lcd.print("Reglage Voie: E" + String(voieSelectionnee + 1));
      } else {
        lcd.print("Reglage Voie: S" + String(voieSelectionnee - 1));
      }
      
      lcd.setCursor(0, 2);
      lcd.print("Position: ");
      lcd.print(Moteur.currentPosition());
            
      lcd.setCursor(0, 3);
      lcd.print("Tourner Enc = Annule"); // Message mis à jour pour l'utilisateur

      digitalWrite(PIN_ENABLE, LOW); // Active le moteur

      // Sortir sans sauvegarder si on tourne l'encodeur
      int etatActuelCLK = digitalRead(PIN_CLK);
      if (etatActuelCLK != etatPrecedentCLK && etatActuelCLK == LOW) {
        // L'encodeur a bougé ! On annule tout.
        lcd.setCursor(0, 3);
        lcd.print("Mouvement ANNULE !  ");
        delay(1000);
        
        // On recharge l'ancienne position de la voie depuis la mémoire RAM 
        // pour effacer les déplacements manuels qu'on venait de faire
        Moteur.moveTo(positionsVoies[voieSelectionnee]);
        while(Moteur.distanceToGo() != 0) Moteur.run(); 
        
        // Retour au menu principal
        AffichageInitial();
        digitalWrite(PIN_ENABLE, HIGH); // Relâche le moteur
        
        etatCourant = VEILLE;
        derniereVoieAffichee = -1;
        etatPrecedentCLK = etatActuelCLK; // On synchronise l'état avant de quitter
        break; // On sort immédiatement du case
      }
      etatPrecedentCLK = etatActuelCLK; // Sauvegarde de l'état pour le prochain tour de loop

      // 1. Rotation Horaire Lente
      if (digitalRead(PIN_BTN_CW_LENT) == LOW) {
        Moteur.setSpeed(VitesseLente);
        while (digitalRead(PIN_BTN_CW_LENT) == LOW) {
          Moteur.runSpeed();
        }
        lcd.setCursor(10, 2); lcd.print(Moteur.currentPosition()); lcd.print("    ");
      }
      // 2. Rotation Anti-Horaire Lente
      else if (digitalRead(PIN_BTN_CCW_LENT) == LOW) {
        Moteur.setSpeed(-VitesseLente);
        while (digitalRead(PIN_BTN_CCW_LENT) == LOW) {
          Moteur.runSpeed();
        }
        lcd.setCursor(10, 2); lcd.print(Moteur.currentPosition()); lcd.print("    ");
      }
      // 3. Rotation Horaire Rapide
      else if (digitalRead(PIN_BTN_CW_RAPIDE) == LOW) {
        Moteur.setSpeed(800);
        while (digitalRead(PIN_BTN_CW_RAPIDE) == LOW) {
          Moteur.runSpeed();
        }
        lcd.setCursor(10, 2); lcd.print(Moteur.currentPosition()); lcd.print("    ");
      }
      // 4. Rotation Anti-Horaire Rapide
      else if (digitalRead(PIN_BTN_CCW_RAPIDE) == LOW) {
        Moteur.setSpeed(-800);
        while (digitalRead(PIN_BTN_CCW_RAPIDE) == LOW) {
          Moteur.runSpeed();
        }
        lcd.setCursor(10, 2); lcd.print(Moteur.currentPosition()); lcd.print("    ");
      }

      // --- SAUVEGARDE DE LA POSITION PAR CLIC ---
      if (digitalRead(PIN_SW) == LOW && (millis() - dernierTempsClic > DELAI_DEBOND_CLIC)) {
        dernierTempsClic = millis();
        
        positionsVoies[voieSelectionnee] = Moteur.currentPosition();
        EEPROM.put(ADDR_VOIES_START + (voieSelectionnee * sizeof(long)), positionsVoies[voieSelectionnee]);
        
        lcd.setCursor(0, 3);
        lcd.print("Position MEMORISEE  ");
        delay(1500);
        AffichageInitial();
        digitalWrite(PIN_ENABLE, HIGH);
        
        etatCourant = VEILLE;
        derniereVoieAffichee = -1;
      }
      break;
  
  }
}
La routine qui permet de trouver le chemin le plus court utilise un déplacement relatif qui est l’écart entre la position à atteindre et la position médiane de 22000 : si l’écart est > 22000 on tourne dans le sens positif et s’il est inférieur on tourne dans le sens négatif. Et pareil avec des valeurs de déplacements négatives.
Capture d'écran 2026-07-05 175403.png

Un petit truc de programmation qui m’a piégé… ne jamais déclarer de variables dans un switch case. 😜
Ici, j’avais bêtement déclaré deux variables dans le case DEPLACEMENT
Le compilateur de l’IDE Arduino ne bronche pas car par rapport à un vrai compilateur C++ il est très (trop) permissif et ne signale que les erreurs bloquantes mais pas les avertissements. Et ces déclarations mal placées bloquent l’Arduino.
Bêtise1.jpg
Si on doit quand même utiliser des variables locales dans un bloc switch il faut encadrer tout le bloc "case" d’accolades. Ainsi tout ce qui est entre les accolades est local. C’est d’ailleurs une bonne pratique que de mettre systématiquement des accolades autour des blocs switch case.
Mais le mieux est de mettre toutes les déclarations dans l'entête ou dans le void Setup()
Bêtise2.jpg


Je ferai une vidéo complète pour expliquer le code en détails car il faudra forcément pouvoir l’adapter à la marge pour chaque besoin.


N’hésitez pas si vous avez des questions.


:a_suivre:
Vous n’avez pas les permissions nécessaires pour voir les fichiers joints à ce message.
Avatar du membre
France likiki
Site Admin
Site Admin
Messages : 16480
Enregistré le : 27 févr. 2021, 16:11
Prénom : Christian
LOCALISATION : Ile de France
Centres d'intérêts : Le train miniature (H0 3R), l'impression 3D
Echelle pratiquée : H0
Âge : 59
Pays :
France
France
juil. 2026 05 18:15

Re: Le pont tournant d'Olivier

Message par France likiki

:bienv: :bienv: :bienv:

Cela me permet de constater que je me suis bien fait chi... avec mon programme ascenseur. Il y avait plus simple. :728: :728: :728:
Si le Goëland se gratte le gland, il fera mauvais temps,
Si il se gratte le cul, il ne fera pas beau non plus.

Proverbe de là bas.

ImageImageImage

:tirechap:

Si vous souhaitez faire un don pour le forum , c'est ici.
Avatar du membre
France philippe017
Posteur insomniac
Posteur insomniac
Messages : 458
Enregistré le : 17 nov. 2025, 20:57
Prénom : Philippe
LOCALISATION : Gémozac
Centres d'intérêts : Modélisme ferroviaire, programmation décodeur , impression 3D j'ai des imprimantes en filament et des imprimantes en résine.
Echelle pratiquée : H0
Âge : 55
Pays :
France
France
juil. 2026 05 18:18

Re: Le pont tournant d'Olivier

Message par France philippe017

:bienv: :bienv: :bienv: :cooooool:
Cordialement,

Philippe
Avatar du membre
France TrainDoly
1200 posts et +
1200 posts et +
Messages : 1293
Enregistré le : 09 févr. 2025, 14:37
Prénom : Olivier
LOCALISATION : Expatrié en Alsace
Centres d'intérêts : Train HO & Impression résine & Fraiseuse CNC faite maison
Echelle pratiquée : H0
Pays :
France
France
juil. 2026 05 18:52

Re: Le pont tournant d'Olivier

Message par France TrainDoly

likiki a écrit : 05 juil. 2026, 18:15 :bienv: :bienv: :bienv:

Cela me permet de constater que je me suis bien fait chi... avec mon programme ascenseur. Il y avait plus simple. :728: :728: :728:
Oui, moi je ne me fais plus chier.
Quand j'ai un souci je téléphone chez Ramasoft Technologies, je prends rendez-vous sur Discord et j'ai ma réponse à coup-sûr! :mdr3:
Avatar du membre
France TrainDoly
1200 posts et +
1200 posts et +
Messages : 1293
Enregistré le : 09 févr. 2025, 14:37
Prénom : Olivier
LOCALISATION : Expatrié en Alsace
Centres d'intérêts : Train HO & Impression résine & Fraiseuse CNC faite maison
Echelle pratiquée : H0
Pays :
France
France
juil. 2026 05 18:56

Re: Le pont tournant d'Olivier

Message par France TrainDoly

Merci beaucoup Patrick pour cette étude!
Ton prototype donne envie!
Avatar du membre
France ramasoft
Chef es bogies
Chef es bogies
Messages : 5471
Enregistré le : 29 juil. 2024, 19:06
Prénom : Patrick
LOCALISATION : Indre - Levroux
Centres d'intérêts : Modélisme ferroviaire
Programmation C++, VB6 et assembleur
Électronique
Echelle pratiquée : H0
Âge : 68
Pays :
France
France
juil. 2026 05 18:58

Re: Le pont tournant d'Olivier

Message par France ramasoft

Bah oui, c'est fait pour :hhhaaaa:

Comme ça quand tu voudras t'y mettre tu auras tout ce qu'il faut.
Avatar du membre
France herwat
2000 posts et +
2000 posts et +
Messages : 6965
Enregistré le : 11 mars 2021, 16:14
Prénom : Hervé
LOCALISATION : Creuse
Centres d'intérêts : Modélisme ferroviaire
Impression 3D
Arduino
Echelle pratiquée : H0
Âge : 79
Pays :
France
France
juil. 2026 05 21:33

Re: Le pont tournant d'Olivier

Message par France herwat

:PetitPoucejpg: :PetitPoucejpg: :PetitPoucejpg:

C'est simple, efficace, bien expliqué . :bienv:
Avatar du membre
France ramasoft
Chef es bogies
Chef es bogies
Messages : 5471
Enregistré le : 29 juil. 2024, 19:06
Prénom : Patrick
LOCALISATION : Indre - Levroux
Centres d'intérêts : Modélisme ferroviaire
Programmation C++, VB6 et assembleur
Électronique
Echelle pratiquée : H0
Âge : 68
Pays :
France
France
juil. 2026 06 09:54

Re: Le pont tournant d'Olivier

Message par France ramasoft

Problème de recalage à la mise sous tension

Je me suis aperçu qu’à la mise sous tension, le moteur donnait parfois un à-coup avant de démarrer ce qui causait un petit décalage par rapport à la position enregistrée.
J’ai demandé à Claude et à Gemini et le phénomène est inhérent à la technologie des moteurs pas à pas surtout s’ils sont utilisés en micro stepping.

Ça s’appelle : L'alignement sur le "Pas Complet" (Phase de boot du driver)
Lorsqu’on coupe le courant (12V), le moteur se détend légèrement. Au démarrage, dès qu’on passe la broche PIN_ENABLE à LOW (pour activer le moteur), le TMC2209 alimente les bobines et force le moteur à se verrouiller sur la "phase électrique" active la plus proche. Si le moteur avait bougé d'une fraction de millimètre hors tension, il se "recale" violemment.
Et le fait d’être en 1/32e de pas accentue ce comportement à cause de la taille infime des micro-pas.
En mode "Pas Entier", le moteur NEMA 17 standard fait 200 pas par tour, soit un angle de 1,8∘ par pas. En configuration 1/32e de pas, chaque micro-pas ne représente plus qu'un angle minuscule de 0,056∘ (1,8/32).
Physiquement, un micro-pas n'est pas un "cran" mécanique dans le moteur, mais un équilibre magnétique subtil créé par le driver en dosant le courant dans les deux bobines.
Hors tension :
• La moindre friction mécanique dans les engrenages (rapport 16/110).
• Le poids du pont tournant ou des voies.
• La simple détente naturelle des aimants permanents du moteur (le couple de saillance ou detent torque).
Tout cela suffit à faire glisser l'axe du moteur de quelques fractions de degrés. À l'échelle d'un pas entier, ce glissement est invisible. Mais à l'échelle du 1/32e de pas, ce minuscule glissement représente facilement 4, 8 ou 12 micro-pas de décalage !

D’après les IA, il n’y a pas de solution logicielle.
C’est pour cela que mon pont Heljan utilise un moteur classique avec un masque optique à fente qui génère des impulsions qui tiennent lieu de pas et ainsi il garde sa position à la remise sous tension. Je pensais que c’était pour limiter les couts mais c’est aussi par nécessité technologique…

Donc la solution serait de garder le moteur sous tension 24/24 ce qui n’est pas possible (mais faisable car théoriquement le moteur en phase de maintien ne doit pas chauffer selon Gemini IA).
J’ai donc modifié le code pour :
• Garder le moteur en maintien tant que le pont est sous tension : PIN_ENABLE est passé en LOW au début du code et n’en bouge plus.
• Faire un recalage automatique à chaque mise sous tension. C’est ce que fait mon ascenseur et c’est pour cela qu’il n’y a aucun problème de précision d’arrêt.
• Positionner ensuite le pont sur la voie n°1


Il faudra mettre la voie d’entrée principale proche du point Zéro et arrêter le pont sur cette voie de manière à ce que la séquence d’initialisation au démarrage soit la plus courte possible.


La nouvelle version V1.21:
Pont_Tournant_V1_21.ino
Fonctions_Menus.ino
Fonctions_Deplacement.ino
Vous n’avez pas les permissions nécessaires pour voir les fichiers joints à ce message.
Avatar du membre
France TrainDoly
1200 posts et +
1200 posts et +
Messages : 1293
Enregistré le : 09 févr. 2025, 14:37
Prénom : Olivier
LOCALISATION : Expatrié en Alsace
Centres d'intérêts : Train HO & Impression résine & Fraiseuse CNC faite maison
Echelle pratiquée : H0
Pays :
France
France
juil. 2026 06 11:38

Re: Le pont tournant d'Olivier

Message par France TrainDoly

J'ai bien lu et relu mais je ne comprends toujours pas à quel moment ton système sert l'apéro?
Avatar du membre
France Amigorl
Posteur irrécupérable
Posteur irrécupérable
Messages : 830
Enregistré le : 02 févr. 2025, 20:15
Prénom : Rémy
LOCALISATION : Pau
Centres d'intérêts : Digitalisation matériel ancien, construction d'un réseau HO. Arduino, Dccex-Conmmand-Station, Rocrail, Digitalisation pont tournant Jouef, Decodeur accessoires DIY. 3D PLA et débutant SLA.
Echelle pratiquée : H0
Âge : 64
Pays :
France
France
juil. 2026 06 12:57

Re: Le pont tournant d'Olivier

Message par France Amigorl

:mdr3:
Avatar du membre
France ramasoft
Chef es bogies
Chef es bogies
Messages : 5471
Enregistré le : 29 juil. 2024, 19:06
Prénom : Patrick
LOCALISATION : Indre - Levroux
Centres d'intérêts : Modélisme ferroviaire
Programmation C++, VB6 et assembleur
Électronique
Echelle pratiquée : H0
Âge : 68
Pays :
France
France
juil. 2026 06 13:12

Re: Le pont tournant d'Olivier

Message par France ramasoft

:haha:
Avatar du membre
France likiki
Site Admin
Site Admin
Messages : 16480
Enregistré le : 27 févr. 2021, 16:11
Prénom : Christian
LOCALISATION : Ile de France
Centres d'intérêts : Le train miniature (H0 3R), l'impression 3D
Echelle pratiquée : H0
Âge : 59
Pays :
France
France
juil. 2026 06 14:38

Re: Le pont tournant d'Olivier

Message par France likiki

Il sert l'apéro quand il trouve le point 51. :mdr3: :mdr1:
Si le Goëland se gratte le gland, il fera mauvais temps,
Si il se gratte le cul, il ne fera pas beau non plus.

Proverbe de là bas.

ImageImageImage

:tirechap:

Si vous souhaitez faire un don pour le forum , c'est ici.
Avatar du membre
France C2Vues-BricoTrain
2000 posts et +
2000 posts et +
Messages : 9057
Enregistré le : 08 mars 2024, 11:45
Prénom : Fernando
LOCALISATION : Clamart
Centres d'intérêts : Modélisme Ferroviaire - Échelles N et HO
Images IA
Echelle pratiquée : N
Âge : 74
Pays :
France
France
juil. 2026 06 14:50

Re: Le pont tournant d'Olivier

Message par France C2Vues-BricoTrain

likiki a écrit : 06 juil. 2026, 14:38 Il sert l'apéro quand il trouve le point 51. :mdr3: :mdr1:
Et le 45 aussi :mdr1: :mdr3:
(Ce nombre est bien impair, no ?) :vert
Pour me retrouver sur ma chaîne :
https://youtube.com/@c2vues-bricotrain? ... VpM6qhmP2u

Quand trop gentil, tu seras,
Dans le côté sombre, tu l'auras !

Yoda
Maître Jedi dans Star Wars
Avatar du membre
France TrainDoly
1200 posts et +
1200 posts et +
Messages : 1293
Enregistré le : 09 févr. 2025, 14:37
Prénom : Olivier
LOCALISATION : Expatrié en Alsace
Centres d'intérêts : Train HO & Impression résine & Fraiseuse CNC faite maison
Echelle pratiquée : H0
Pays :
France
France
juil. 2026 06 15:36

Re: Le pont tournant d'Olivier

Message par France TrainDoly

C2Vues-BricoTrain a écrit : 06 juil. 2026, 14:50
likiki a écrit : 06 juil. 2026, 14:38 Il sert l'apéro quand il trouve le point 51. :mdr3: :mdr1:
Et le 45 aussi :mdr1: :mdr3:
(Ce nombre est bien impair, no ?) :vert
Nan mais t'es qui là pour dire ça? :vert
Avatar du membre
France C2Vues-BricoTrain
2000 posts et +
2000 posts et +
Messages : 9057
Enregistré le : 08 mars 2024, 11:45
Prénom : Fernando
LOCALISATION : Clamart
Centres d'intérêts : Modélisme Ferroviaire - Échelles N et HO
Images IA
Echelle pratiquée : N
Âge : 74
Pays :
France
France
juil. 2026 06 17:02

Re: Le pont tournant d'Olivier

Message par France C2Vues-BricoTrain

Téquila ? je sais pas ! :gratte: :mdr1:

Conjugaison 2.jpeg


À 2 lettres inversées près.jpeg


:hhhaaaa: :hhhaaaa: :hhhaaaa: :tire langue
Vous n’avez pas les permissions nécessaires pour voir les fichiers joints à ce message.
Pour me retrouver sur ma chaîne :
https://youtube.com/@c2vues-bricotrain? ... VpM6qhmP2u

Quand trop gentil, tu seras,
Dans le côté sombre, tu l'auras !

Yoda
Maître Jedi dans Star Wars
Avatar du membre
France patrice_b
2000 posts et +
2000 posts et +
Messages : 3632
Enregistré le : 31 janv. 2025, 14:45
Prénom : Patrice
LOCALISATION : Beziers Herault
Centres d'intérêts : Modelisme HO et HOM ou HOe
Echelle pratiquée : H0
Âge : 65
Pays :
France
France
juil. 2026 08 12:12

Re: Le pont tournant d'Olivier

Message par France patrice_b

Impressionnant d'efficacité, merci pour cet article Patrick.

:bienv: :bienv:
Cordialement,

Patrice
Avatar du membre
France patrice_b
2000 posts et +
2000 posts et +
Messages : 3632
Enregistré le : 31 janv. 2025, 14:45
Prénom : Patrice
LOCALISATION : Beziers Herault
Centres d'intérêts : Modelisme HO et HOM ou HOe
Echelle pratiquée : H0
Âge : 65
Pays :
France
France
juil. 2026 08 12:12

Re: Le pont tournant d'Olivier

Message par France patrice_b

:tire langue :hhhaaaa: :hhhaaaa:
Cordialement,

Patrice
Répondre

Retourner vers « Arduino et le train miniature »

Qui est en ligne

Utilisateurs parcourant ce forum : Aucun utilisateur enregistré et 1 invité