⚙️ Contrôle Proportionnel de 2 Moteurs Pas à Pas avec Arduino/ESP32 : Maîtrisez les E/S et les Interruptions

💡 Note du Maker : Les anciens microcontrôleurs (comme le ST6225) utilisaient une manipulation complexe de registres. Aujourd'hui, avec Arduino ou ESP32, nous pouvons accomplir les mêmes tâches (E/S parallèles, interruptions) avec un code plus propre, plus lisible et des bibliothèques puissantes. Ce guide modernise ce concept classique pour vos projets de modélisme, CNC ou robotique.
Schéma de contrôle de deux moteurs pas à pas avec Arduino et potentiomètres
Figure 1 : Architecture moderne de contrôle proportionnel de moteurs pas à pas.

Pourquoi ce Projet est Essentiel pour les Makers ?

Contrôler la vitesse et la direction de deux moteurs pas à pas de manière indépendante et proportionnelle (par exemple, via deux potentiomètres) est une compétence fondamentale. Ce projet vous apprend à :

  • Configurer les Entrées/Sorties (E/S) parallèles de manière moderne.
  • Utiliser les interruptions matérielles pour une réponse immédiate (bouton d'arrêt d'urgence ou inversion).
  • Gérer le mouvement de plusieurs moteurs sans bloquer le processeur (programmation non-bloquante).

📦 1. Liste des Composants (BOM)

  • Microcontrôleur : Arduino Uno ou ESP32 DevKit
  • Moteurs : 2x Moteurs pas à pas (ex: 28BYJ-48 avec driver ULN2003, ou NEMA 17 avec A4988)
  • Contrôle proportionnel : 2x Potentiomètres 10kΩ
  • Interruption : 2x Boutons-poussoirs (Pull-up interne activé)
  • Divers : Breadboard, câbles jumper, alimentation externe 5V/12V (selon le moteur)

🔌 2. Théorie Moderne des E/S Parallèles

Contrairement aux anciens microcontrôleurs où il fallait manipuler manuellement les registres de direction (DDR) et de données (DR), Arduino et ESP32 simplifient cela via des fonctions dédiées :

  • pinMode(pin, OUTPUT) : Configure la broche en sortie (équivalent moderne du registre DDR).
  • digitalWrite(pin, HIGH/LOW) : Envoie une commande (équivalent du registre DR en écriture).
  • digitalRead(pin) : Lit l'état d'un capteur (équivalent du registre DR en lecture).

Pour les moteurs pas à pas, nous utilisons 4 broches de sortie par moteur pour contrôler les bobines en séquence (mode "Wave Drive" ou "Full Step").

💻 3. Le Code Arduino Complet (Optimisé et Commenté)

Ce code utilise une approche non-bloquante (sans delay()) pour garantir que les interruptions (boutons) soient détectées instantanément, même pendant que les moteurs tournent.

/* * Contrôle Proportionnel de 2 Moteurs Pas à Pas avec Interruptions * TechFix Hub - Modernisation des concepts d'E/S parallèles * Matériel : Arduino Uno / ESP32 + 2x Steppers (ULN2003) + 2x Potentiomètres + 2x Boutons */ // --- Définition des Broches (E/S Parallèles) --- // Moteur 1 (Bobines IN1 à IN4) const int m1_pins[] = {2, 3, 4, 5}; // Moteur 2 (Bobines IN1 à IN4) const int m2_pins[] = {6, 7, 8, 9}; // Entrées analogiques pour contrôle proportionnel (Vitesse) const int pot1_Pin = A0; const int pot2_Pin = A1; // Entrées numériques pour Interruptions (Arrêt d'urgence / Inversion) const int btn1_Pin = 10; // Utilise l'interruption 0 sur Arduino (Pin 2) -> Adaptez selon votre carte const int btn2_Pin = 11; // Utilise l'interruption 1 sur Arduino (Pin 3) // --- Variables d'état --- volatile bool m1_running = true; // 'volatile' est OBLIGATOIRE pour les variables modifiées dans une interruption volatile bool m2_running = true; int speed1 = 0; int speed2 = 0; // Séquence de pas pour moteur pas à pas unipolaire (Half-step pour plus de fluidité) const int step_sequence[8][4] = { {1, 0, 0, 0}, {1, 1, 0, 0}, {0, 1, 0, 0}, {0, 1, 1, 0}, {0, 0, 1, 0}, {0, 0, 1, 1}, {0, 0, 0, 1}, {1, 0, 0, 1} }; int current_step1 = 0; int current_step2 = 0; unsigned long last_step_time1 = 0; unsigned long last_step_time2 = 0; // --- Routine d'Interruption (ISR) --- // S'exécute instantanément quand le bouton est pressé, interrompant la boucle principale void ISR_Motor1() { m1_running = !m1_running; // Inverse l'état (Marche/Arrêt) } void ISR_Motor2() { m2_running = !m2_running; } void setup() { Serial.begin(115200); // Configuration des E/S en Sortie for (int i = 0; i < 4; i++) { pinMode(m1_pins[i], OUTPUT); pinMode(m2_pins[i], OUTPUT); } // Configuration des E/S en Entrée avec Pull-up interne (plus besoin de résistance externe !) pinMode(btn1_Pin, INPUT_PULLUP); pinMode(btn2_Pin, INPUT_PULLUP); // Configuration des Interruptions Matérielles (Déclenchement sur front descendant : FALLING) // Note: Sur Arduino Uno, utilisez les pins 2 et 3 pour les interruptions 0 et 1. // Adaptez les numéros d'interruption si vous utilisez un ESP32 (qui supporte toutes les pins GPIO). attachInterrupt(digitalPinToInterrupt(2), ISR_Motor1, FALLING); attachInterrupt(digitalPinToInterrupt(3), ISR_Motor2, FALLING); Serial.println("Système prêt. Utilisez les potentiomètres pour la vitesse, les boutons pour Marche/Arrêt."); } void loop() { // 1. Lecture proportionnelle des potentiomètres (0-1023) // On mappe la valeur pour obtenir un délai entre les pas (plus le délai est petit, plus c'est rapide) int raw1 = analogRead(pot1_Pin); int raw2 = analogRead(pot2_Pin); // Délai minimal de 2ms, maximal de 50ms entre chaque pas int delay1 = map(raw1, 0, 1023, 2, 50); int delay2 = map(raw2, 0, 1023, 2, 50); unsigned long current_time = millis(); // 2. Gestion Moteur 1 (Non-bloquante) if (m1_running && (current_time - last_step_time1 >= delay1)) { stepMotor(m1_pins, current_step1); current_step1 = (current_step1 + 1) % 8; // Passe au pas suivant (0 à 7) last_step_time1 = current_time; } // 3. Gestion Moteur 2 (Non-bloquante) if (m2_running && (current_time - last_step_time2 >= delay2)) { stepMotor(m2_pins, current_step2); current_step2 = (current_step2 + 1) % 8; last_step_time2 = current_time; } } // Fonction utilitaire pour envoyer la séquence aux broches void stepMotor(const int pins[], int step) { for (int i = 0; i < 4; i++) { digitalWrite(pins[i], step_sequence[step][i]); } }

⚡ 4. Comprendre les Interruptions (Le Remplaçant Moderne des Vecteurs)

Dans l'ancien système (ST6225), vous deviez programmer manuellement les adresses mémoire des vecteurs d'interruption (ex: FF6h). Avec Arduino/ESP32, la fonction attachInterrupt() gère cela automatiquement.

  • FALLING : Déclenche l'interruption quand le bouton passe de HIGH (relâché, grâce au Pull-up) à LOW (appuyé).
  • volatile : Les variables m1_running et m2_running sont déclarées comme volatile. Cela indique au compilateur de ne pas optimiser cette variable, car elle peut être modifiée à tout moment par l'interruption, en dehors de la boucle loop() principale.

🛠️ 5. Dépannage et Bonnes Pratiques

  • Les moteurs vibrent mais ne tournent pas : L'alimentation est insuffisante. Les moteurs pas à pas nécessitent beaucoup de courant au démarrage. Utilisez une alimentation externe dédiée, ne tirez pas le courant directement de la broche 5V de l'Arduino.
  • L'interruption ne se déclenche pas : Vérifiez que vous utilisez les bonnes broches. Sur Arduino Uno, seules les broches 2 et 3 supportent les interruptions matérielles. Sur ESP32, presque toutes les broches GPIO sont compatibles.
  • Mouvement saccadé : Passez en mode "Half-step" (8 étapes par cycle au lieu de 4) comme dans le code fourni, cela double la résolution et rend le mouvement beaucoup plus fluide.

Conclusion

Maîtriser les entrées/sorties parallèles et les interruptions est la pierre angulaire de l'électronique embarquée. En passant des anciens registres matériels aux fonctions modernes d'Arduino/ESP32, vous gagnez en productivité tout en conservant un contrôle absolu sur votre matériel. Ce projet de contrôle proportionnel est une base parfaite pour des applications plus complexes comme les traceurs (plotters), les bras robotiques ou les systèmes CNC DIY.

Prochaine étape : Consultez notre guide sur l'ajout d'un contrôle WiFi via ESP32 pour piloter ces moteurs depuis votre smartphone !

Bottom Ad [Post Page]