⚙️ Contrôle Proportionnel de 2 Moteurs Pas à Pas avec Arduino/ESP32 : Maîtrisez les E/S et les Interruptions
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_runningetm2_runningsont déclarées commevolatile. 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 boucleloop()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 !