Gestion d’un pont tournant : Arduino, Écran TFT et Moteur Pas à Pas v1
Description :
Ce projet consiste en la création d’un système de pilotage automatisé pour un pont tournant ou une coulisse de triage destiné au modélisme ferroviaire. L’objectif principal est de permettre le déplacement précis d’une locomotive vers différentes voies de garage via une interface numérique intuitive.
Le cœur du système repose sur une carte Arduino qui orchestre trois fonctions majeures :
- Le Contrôle de Mouvement : Un moteur pas à pas (piloté par un driver de type TB6600) assure le déplacement de la plateforme. La précision du moteur permet d’aligner les rails avec une tolérance minimale, garantissant un passage fluide du train.
- L’Interface Utilisateur (HMI) : Un écran couleur TFT ST7789 affiche en temps réel la position actuelle et la voie cible. La sélection s’effectue à l’aide d’un encodeur rotatif, offrant une navigation fluide dans les menus.
- La Gestion du Point Zéro (Homing) : Le système intègre une procédure d’initialisation automatique. Au démarrage, le moteur cherche un capteur de fin de course pour définir sa position de référence, assurant ainsi que les coordonnées enregistrées dans le code correspondent parfaitement à la réalité physique du réseau.
Fonctionnement simplifié :
- Initialisation : Le pont se déplace seul vers sa position d’origine (Home).
- Sélection : L’utilisateur tourne l’encodeur pour choisir le numéro de la voie souhaitée sur l’écran.
- Exécution : Une pression sur le bouton valide le choix, et le moteur déplace le pont vers la coordonnée exacte enregistrée pour cette voie.
C’est une solution robuste qui remplace les systèmes mécaniques manuels par une électronique de précision, idéale pour moderniser un réseau ferroviaire miniature.
Prérequis :
- 1 x Carte Arduino Nano
- 1 x Moteur pas à pas 12V (ex. NEMA 17)
- 1 × Driver TB6600
- 1 x Écran LCD TFT 2.4 pouces, avec Module combiné d’encodeur rotatif EC11 et Interface SPI ST7789
- 1 x Speed Sensor Module LM393
- Fils de connexion
- 1 x Breadboard
Version IDE :
- Arduino IDE 2.3.5
Bibliothèque :
- Adafruit_GFX.h (version: 1.12.0 par Adafruit)
- Adafruit_ST7789.h (version: 1.11.0 par Adafruit)
- Adafruit BusIO (version: 1.7.14 par Adafruit)
STL :
Vidéo de démonstration :
Schéma de câblage :


Code :
#include <Adafruit_GFX.h> // Librairie de base pour les graphiques
#include <Adafruit_ST7789.h> // Librairie spécifique pour l'écran ST7789
// Définition des broches pour l'écran TFT
#define TFT_CS 8
#define TFT_DC 10
#define TFT_RST 9
// Initialisation de l'objet tft
Adafruit_ST7789 tft = Adafruit_ST7789(TFT_CS, TFT_DC, TFT_RST);
// Définition des broches pour le moteur pas à pas (A4988 ou DRV8825)
#define stepPinM1 5 // Signal de pas
#define dirPinM1 6 // Signal de direction
#define enabledmotorM1 7 // Activation/Désactivation du moteur
#define InitPosition 4 // Capteur de fin de course (Homing)
#define BtInitPosition A3 // Bouton pour relancer l'initialisation
// Tableau des positions (en pas) pour chaque voie de train
long PositionVoies[] = { 0, 400, 800, 1200, 1600, 2000, 2400, 2800, 3200, 3600, 4000, 4400, 4800, 5200, 5600, -400, -800 };
long Position; // Position actuelle du moteur
// Définition des broches de l'encodeur rotatif et boutons
const int S1 = 2; // Signal A de l'encodeur (Interruption)
const int S2 = 3; // Signal B de l'encodeur
int PUSH = A2; // Bouton poussoir de l'encodeur pour valider
// Variables de gestion des voies
int nbPositionVoies = (sizeof(PositionVoies) / sizeof(PositionVoies[0])) - 1;
volatile int SelectVoie = 0; // Modifié par l'interruption de l'encodeur
int copieSelectVoie;
int dernierSelectVoie = 0;
int VoieActuel;
// Variables de temps et vitesse pour le mouvement sans blocage delay()
unsigned long currentTime;
unsigned long previousTimeM1;
int EtatstepPinM1;
int motorspeedInit = 1000; // Vitesse plus rapide pour l'initialisation (microsecondes)
int motorspeed = 5000; // Vitesse de croisière
int CorrectionInit = 445; // Décalage après avoir touché le capteur de fin de course
int Etape = 0; // Variable d'état pour la machine à états (switch case)
void setup() {
Serial.begin(9600);
// Configuration des entrées/sorties
pinMode(S1, INPUT_PULLUP);
pinMode(S2, INPUT_PULLUP);
// Attache une interruption sur S1 pour détecter la rotation de l'encodeur
attachInterrupt(digitalPinToInterrupt(S1), gestionEncodeur, FALLING);
pinMode(stepPinM1, OUTPUT);
pinMode(dirPinM1, OUTPUT);
pinMode(enabledmotorM1, OUTPUT);
digitalWrite(enabledmotorM1, HIGH); // Moteur désactivé par défaut
pinMode(InitPosition, INPUT_PULLUP);
pinMode(PUSH, INPUT);
pinMode(BtInitPosition, INPUT);
// Initialisation de l'écran TFT (240x320 pixels)
tft.init(240, 320);
tft.setRotation(2); // Orientation de l'affichage
tft.invertDisplay(false);
tft.fillScreen(ST77XX_BLACK);
// Texte de bienvenue
tft.setTextColor(ST77XX_GREEN, ST77XX_BLACK);
tft.setTextSize(2);
tft.setCursor(20, 5);
tft.print("projetsduino.com");
}
void loop() {
switch (Etape) {
case 0: // ÉTAPE : Initialisation (Homing)
tft.setTextSize(2);
tft.setCursor(30, 150);
tft.print("Initialisation");
tft.setCursor(60, 170);
tft.print("en cour...");
moteurInit(); // Cherche le capteur de fin de course
Position = 0; // Reset de la position interne
delay(1000);
moteur(CorrectionInit, 1); // Se place sur le point zéro réel
tft.setCursor(60, 170);
tft.print(" Terminer ");
delay(2000);
// Effacement zone texte
tft.setCursor(30, 150); tft.print(" ");
tft.setCursor(60, 170); tft.print(" ");
Position = 0; // Reset de la position interne
Etape++;
break;
case 1: // ÉTAPE : Affichage du menu principal
tft.setTextSize(2);
tft.setCursor(60, 60);
tft.print("Voie actuel");
tft.setTextSize(3);
tft.setCursor(90, 100);
tft.print(" 0 ");
tft.setTextSize(2);
tft.setCursor(20, 170);
tft.print("Selectionner une");
tft.setCursor(30, 190);
tft.print("voie de train :");
tft.setTextSize(3);
tft.setCursor(90, 230);
tft.print(" 0 ");
Etape++;
break;
case 2: // ÉTAPE : Attente de sélection utilisateur
// Si l'encodeur a tourné
if (SelectVoie != dernierSelectVoie) {
noInterrupts(); // Protection des variables volatiles
if (SelectVoie < 0) SelectVoie = 0;
if (SelectVoie > nbPositionVoies) SelectVoie = nbPositionVoies;
copieSelectVoie = SelectVoie;
dernierSelectVoie = copieSelectVoie;
// Mise à jour de l'affichage de la voie sélectionnée
tft.setTextSize(3);
if (copieSelectVoie < 10) {
tft.setCursor(90, 230);
tft.print(" "); tft.print(copieSelectVoie); tft.print(" ");
} else {
tft.setCursor(100, 230);
tft.print(copieSelectVoie);
}
interrupts();
}
// Si on appuie sur le bouton de validation
if (digitalRead(PUSH) == LOW) {
Etape = 3;
VoieActuel = copieSelectVoie;
}
// Si on appuie sur le bouton de reset position
if (digitalRead(BtInitPosition) == LOW) {
Etape = 6;
}
break;
case 3: // ÉTAPE : Mise à jour affichage avant mouvement
tft.setTextSize(3);
tft.setCursor(90, 100);
tft.print(" . "); // Indicateur de mouvement
tft.setTextSize(2);
tft.setCursor(20, 170);
tft.print(" Deplacement sur");
tft.setCursor(30, 190);
tft.print(" la voie : ");
Etape = 4;
break;
case 4: // ÉTAPE : Mouvement du moteur vers la voie choisie
moteur(PositionVoies[VoieActuel], 1);
Etape = 5;
break;
case 5: // ÉTAPE : Fin de mouvement, mise à jour affichage "Voie Actuelle"
tft.setTextSize(3);
if (VoieActuel < 10) {
tft.setCursor(90, 100);
tft.print(" "); tft.print(VoieActuel); tft.print(" ");
} else {
tft.setCursor(100, 100);
tft.print(VoieActuel);
}
Etape = 2; // Retour à l'écoute de l'encodeur
break;
case 6: // ÉTAPE : Reset complet
tft.fillScreen(ST77XX_BLACK);
Etape = 0;
break;
}
}
// Fonction de déplacement du moteur vers une position cible
void moteur(int PosDmd, int cpt) {
int sens;
// Détermination du sens de rotation
if (PosDmd > Position) {
digitalWrite(dirPinM1, LOW);
sens = 1;
} else {
digitalWrite(dirPinM1, HIGH);
sens = -1;
}
digitalWrite(enabledmotorM1, LOW); // Active le moteur
// Boucle de génération des pas (non-bloquante pour les interruptions)
while (PosDmd != Position) {
currentTime = micros();
if (currentTime - previousTimeM1 > motorspeed) {
if (sens == 1) {
Position++;
} else {
Position--;
}
previousTimeM1 = currentTime;
EtatstepPinM1 = !EtatstepPinM1; // Alterne l'état du pin pour créer un pulse
digitalWrite(stepPinM1, EtatstepPinM1);
}
}
digitalWrite(enabledmotorM1, HIGH); // Désactive le moteur (économise l'énergie/chauffe)
}
// Fonction de recherche de la position initiale (Homing)
void moteurInit() {
digitalWrite(enabledmotorM1, LOW);
digitalWrite(dirPinM1, LOW); // Direction vers le capteur
// Tourne jusqu'à ce que le capteur soit activé (LOW)
while (digitalRead(InitPosition) == LOW) {
currentTime = micros();
if (currentTime - previousTimeM1 > motorspeedInit) {
previousTimeM1 = currentTime;
EtatstepPinM1 = !EtatstepPinM1;
digitalWrite(stepPinM1, EtatstepPinM1);
}
}
digitalWrite(enabledmotorM1, HIGH);
}
// Fonction appelée automatiquement lors d'une rotation de l'encodeur
void gestionEncodeur() {
static unsigned long derniereInterruption = 0;
unsigned long tempsActuel = millis();
// Anti-rebond (debounce) logiciel de 2ms
if (tempsActuel - derniereInterruption > 2) {
if (digitalRead(S2) == HIGH) {
SelectVoie--;
} else {
SelectVoie++;
}
}
derniereInterruption = tempsActuel;
}

Bonjour
Je possède un pont tournant ho jouef et une carte uno r3 puis je m’en servir
– quelle modification à apporter au schéma électrique
– ou poser le module LM 393
Merci
Cordialement
Bonjour,
Je souhaite motoriser un pont jouef avec un moteur pas à pas . J’ai découvert votre projet qui me semble très intéressant et relativement facile à réaliser compte tenu du schéma, de la vidéo ainsi que du programme Arduino associé. Je vais donc essayer de le mettre en pratique. J’ai juste une remarque à formuler quant au câblage du moteur pas à pas dont le code de couleur sur le schéma ne correspond pas à celui de la vidéo. J’aurais tendance à croire que le bon câblage est celui de la vidéo A+ bleu, A- rouge, B+ vert , B- noir , dites moi ce que vous pensez. En tout cas merci pour ce beau projetduino.
Bonjour,
Le mieux est de mesurer les fils avec un multimètre afin d’identifier les deux bobines du moteur pas à pas (PAP).
Une fois que vous les avez trouvées, il vous suffit de les connecter aux bornes A+/A- et B+/B-. Attention, car le code couleur peut varier d’un moteur à l’autre.
Si vous n’avez pas de multimètre, vous le remarquerez vite au démarrage : le moteur ne tournera pas ou risquera de vibrer fortement.
Cordialement.
PS : Pour identifier les bobines, réglez votre multimètre sur le mode continuité (le mode « bip ») ou sur la mesure de résistance.
Lorsque vous touchez deux fils appartenant à la même bobine, le multimètre émettra un son ou affichera une valeur de résistance faible (généralement entre 2 et 50 ohm).
Merci pour ces renseignements, j’ai pu mesurer la résistance des bobines et ainsi trouver les fils correspondants comme vous l’avez mentionné.