Un moteur pas-à-pas (stepper motor) tourne par pas angulaires discrets — typiquement 200 pas par tour (1,8° par pas) — sous la commande séquentielle de plusieurs bobines. La position du rotor est donc connue par construction sans capteur de retour, ce qui fait du pas-à-pas l’actionneur de prédilection pour le positionnement précis multi-tours (imprimantes 3D, CNC, scanners). Cette fiche couvre le pilotage via deux drivers emblématiques : 28BYJ-48 + ULN2003 pour la version pédagogique, et NEMA17 + A4988 pour le pas-à-pas industriel.

À quoi ça sert ?

Cas typiques :

  • Positionnement angulaire précis (sans capteur, à partir d’un home connu).
  • Mouvement linéaire par vis — axes d’imprimante 3D, CNC, plotter, télescope.
  • Rotation à très basse vitesse sans à-coups (où un moteur CC tressauterait).
  • Vannes motorisées multitours, treuils, mécanismes d’horloge.

Comparaison rapide :

ActionneurPositionCouplePrécision
Servo standard0-180°faiblemoyenne
Moteur CCaucunbonnul (sans encodeur)
Moteur pas-à-paspar pasmoyenexcellente

Procédure pas à pas

Quatre étapes : choisir le couple moteur + driver, câbler, installer la bibliothèque, écrire le code.

1. Choisir le moteur et le driver

Version pédagogique : 28BYJ-48 + ULN2003

  • Moteur : 28BYJ-48, 5 V, 4 fils + 1 commun (mode unipolaire), réducteur 1:64 intégré (donc 2048 ou 4096 pas par tour à l’arbre sortant).
  • Driver : ULN2003 (transistors Darlington), montage carte LED de 4 LEDs qui clignotent dans l’ordre des phases — très pédagogique.
  • Fourni dans la plupart des kits Arduino.

Version industrielle : NEMA17 + A4988 ou DRV8825

  • Moteur : NEMA17, bipolaire, 4 fils, 200 pas/tour, 0,8 à 1,7 A par bobine.
  • Driver : A4988 ou DRV8825, microstepping jusqu’à 1/16 ou 1/32, contrôle par 2 broches seulement (STEP, DIR).
  • Utilisé dans les imprimantes 3D et CNC.

Pour un premier essai pédagogique, le 28BYJ-48 + ULN2003 est imbattable. Pour un projet sérieux à puissance, on bascule sur NEMA17 + A4988.

2. Câbler

Pour 28BYJ-48 + ULN2003 : le moteur a un connecteur Dupont 5 broches qui s’enfiche directement sur le module ULN2003. Côté Arduino :

ULN2003Arduino Uno
IN1D8
IN2D9
IN3D10
IN4D11
+ (alim)+5 V (ou alim externe 5-12 V)
− (GND)GND

Pour NEMA17 + A4988 :

A4988Arduino Uno
STEPD2
DIRD3
SLEEP ↔ RESETreliés ensemble (pour activer le driver)
ENABLEGND (toujours actif) ou broche Arduino pour activation
VDD (logique)+5 V Arduino
GND (logique)GND Arduino
VMOT (puissance)alimentation 12-24 V externe
GND (puissance)GND alimentation + GND Arduino (commun)
1A, 1B, 2A, 2B4 fils du moteur (2 par bobine)

Important sur A4988 : un condensateur 100 µF entre VMOT et GND est obligatoire pour absorber les pics de courant. Son absence détruit le driver.

Branchement d'un 28BYJ-48 via un module ULN2003 : IN1-IN4 → D8-D11, alimentation + et −, le moteur s'enfiche sur le connecteur 5 broches du module|560

3. Installer la bibliothèque

Deux options :

  • Stepper.h — livrée avec l’IDE, simple, suffisante pour 28BYJ-48 + ULN2003.
  • AccelStepper — à installer via le gestionnaire, gère les rampes d’accélération/décélération et le multi-stepper coordonné. Indispensable dès qu’on veut un mouvement lisse, ou qu’on a 2+ moteurs synchronisés (CNC).

Pour un premier essai, Stepper.h suffit.

4. Écrire le code (28BYJ-48 + Stepper.h)

#include <Stepper.h>
 
const int PAS_PAR_TOUR = 2048;  // 28BYJ-48 avec réducteur 1:64
Stepper monMoteur(PAS_PAR_TOUR, 8, 10, 9, 11);  // ordre des broches IN1, IN3, IN2, IN4
 
void setup() {
  monMoteur.setSpeed(10);  // 10 tr/min
  Serial.begin(115200);
}
 
void loop() {
  Serial.println("Sens horaire");
  monMoteur.step(PAS_PAR_TOUR);  // un tour complet
  delay(1000);
 
  Serial.println("Sens anti-horaire");
  monMoteur.step(-PAS_PAR_TOUR); // un tour dans l'autre sens
  delay(1000);
}

Téléverser. Le moteur tourne d’un tour, fait une pause, repart en sens inverse. L’ordre des broches (8, 10, 9, 11) correspond au séquencement des phases : ne pas inverser au hasard, sinon le moteur vibre sans tourner.

Exemple — Mouvement par bouton de pas

Cas complet : un bouton fait avancer le moteur d’un quart de tour à chaque appui.

Câblage : 28BYJ-48 + ULN2003 comme au schéma de l’étape 2 + bouton entre D2 et GND (INPUT_PULLUP, pas de résistance externe).

#include <Stepper.h>
 
const int PAS_PAR_TOUR = 2048;
const int PAS_INCREMENT = PAS_PAR_TOUR / 4;  // quart de tour
Stepper monMoteur(PAS_PAR_TOUR, 8, 10, 9, 11);
 
const int BOUTON = 2;
bool dernierEtat = HIGH, etatStable = HIGH;
unsigned long dernierAntirebond = 0;
 
void setup() {
  monMoteur.setSpeed(15);
  pinMode(BOUTON, INPUT_PULLUP);
}
 
void loop() {
  bool lect = digitalRead(BOUTON);
  if (lect != dernierEtat) {
    dernierAntirebond = millis();
    dernierEtat = lect;
  }
  if (millis() - dernierAntirebond > 30 && lect != etatStable) {
    etatStable = lect;
    if (etatStable == LOW) monMoteur.step(PAS_INCREMENT);  // front descendant : un pas par appui
  }
}

Comment lire ce code

L’anti-rebond non bloquant suit le même motif que pour le moteur CC : dernierEtat repère l’instant où la lecture brute change (et relance le chronomètre), etatStable retient l’état confirmé après 30 ms. Le pas n’est déclenché que sur un front descendant (INPUT_PULLUP → appui = LOW). Attention : monMoteur.step() est bloquant. Pendant le quart de tour, la loop() est gelée et le bouton n’est pas lu, donc un appui pendant le mouvement est ignoré (voir Pièges). Pour réagir en continu, il faut une bibliothèque non bloquante comme AccelStepper.

Chaque appui fait avancer d’un quart de tour. Pratique pour des projets de positionnement séquentiel (distributeur, sélecteur multi-positions).

Pièges

Ordre des phases incorrect. Avec Stepper(steps, A, B, C, D), l’ordre des 4 broches détermine le sens de séquencement des bobines. Si l’ordre est faux, le moteur vibre sans tourner. Solution : essayer les permutations courantes, ou se référer à la datasheet du driver (pour ULN2003 + 28BYJ-48, l’ordre (8, 10, 9, 11) est le bon en standard).

Courant insuffisant à l’alimentation. Un 28BYJ-48 tire ~200-300 mA en pic, un NEMA17 1-1,7 A par bobine. Alimenter par le +5 V Arduino fait redémarrer la carte. Toujours alimentation séparée pour le moteur (au moins pour NEMA17), GND commun.

A4988 sans condensateur sur VMOT. Destruction quasi-immédiate du driver à l’allumage par pic de tension inductif. Condensateur 100 µF minimum entre VMOT et GND, près du driver.

Réglage du courant A4988 incorrect. Le potentiomètre du driver règle le courant limité par bobine. Trop bas → couple trop faible, perte de pas. Trop haut → driver et moteur surchauffent. Calibrer en mesurant Vref au multimètre selon la formule de la fiche du driver (Vref = 8 × R_SENSE × I_MAX, typiquement Vref ≈ 0,8 V pour 1 A sur A4988 avec R = 0,1 Ω).

Perte de pas sous charge. Si on lui demande d’aller trop vite ou si la charge est trop importante, le moteur saute des pas : la position calculée par le programme ne correspond plus à la position réelle. Symptôme : décalage cumulatif au cours du temps. Le couple d’un pas-à-pas chute quand la fréquence de pas monte, l’inductance des bobines empêchant le courant de s’établir avant le pas suivant. Premier réflexe, donc : ralentir, et monter en vitesse par une rampe d’accélération plutôt que de démarrer à pleine cadence (AccelStepper). Ensuite seulement, chercher du couple : vérifier le réglage du courant (piège ci-dessus), prendre un moteur ou un driver mieux dimensionné, ou décharger l’axe mécaniquement (réduction, contrepoids, ressort de rappel). Le microstepping n’est pas un remède au manque de couple. Il le diminue, voir Cas particulier ci-dessous. Et si le couple manque encore une fois ces leviers épuisés, le problème n’est plus électronique : le calcul du couple nécessaire, du rapport de réduction et de l’inertie de la charge n’est pas traité ici, il relève de la mécanique et des cours des collègues.

Stockage de chaleur sur ULN2003. Sous charge prolongée, le module ULN2003 chauffe (transistors Darlington, dissipation passive). Le 28BYJ-48 n’a pas de mode libre : il consomme en permanence ses ~150 mA même à l’arrêt. Pour réduire la dissipation à l’arrêt, couper l’alimentation par MOSFET ou utiliser un driver à mode sleep.

Bibliothèque Stepper.h bloquante. monMoteur.step(N) est bloquant : pendant la commande, rien d’autre ne s’exécute. Pour des mouvements lisses + parallélisme, basculer sur AccelStepper (non bloquante avec .run() à appeler dans loop()).

Sens A4988 fonction de DIR au moment du STEP. Changer DIR pendant un STEP actif peut produire des pas erratiques. Bonne pratique : laisser DIR stable au moins 1 µs avant STEP, ce que toutes les bibliothèques sérieuses font.

Cas particulier — Microstepping et lissage

Les drivers A4988 / DRV8825 / TMC2209 supportent le microstepping : au lieu d’exécuter un pas physique complet, ils interpolent la commande des bobines pour produire des positions intermédiaires (1/2, 1/4, 1/8, 1/16, 1/32 de pas).

Avantages : mouvement nettement plus lisse, bruit réduit, précision angulaire améliorée.

Inconvénients : couple effectif réduit aux positions intermédiaires (le moteur passe plus de temps à tenir une position non-stable). Un microstepping plus fin ne compense donc jamais une perte de pas sous charge, il l’aggrave. Et la coordination du driver pour ramener au pas entier nécessite des bibliothèques avancées.

Sur les imprimantes 3D modernes, drivers TMC2209 en microstepping 1/256, quasi-silencieux.

Raccrochage projet

  • Étape 2 de la phase de preuve de concept — validation isolée du moteur + driver : tour complet dans les deux sens, vitesse maximale sans perte de pas.
  • Étape 3 de la phase de preuve de concept — intégration dans une chaîne de mouvement précis (axe X, distributeur multi-positions, etc.).
  • Étape 4 de la phase de concept — arbitrage entre pas-à-pas et alternatives (servo, moteur CC + encodeur) selon le besoin de précision + couple + budget.

Le pas-à-pas est l’actionneur typique des projets école qui visent un mouvement répétable précis sans capteur de retour — courbure d’apprentissage modérée, résultats spectaculaires en démonstration.

Voir aussi