Moteur pas à pas qui se déplace sans solicitation

Je travaille sur un projet réunissant un moteur pas à pas Nema 17 piloté par un A4988, une ESP32 AZDelivery NodeMCU32, et un anneau de 24 LED. J'ai un souci de mouvement involontaire.

La situation est la suivante : lors de la calibration, le moteur tourne dans le sens antihoraire jusqu’à la détection par un fin de course, puis il poursuit sa course de 850 pas à partir de cette détection, ce qui définit son point zéro. Suite à cette prise de référence, le moteur se positionne directement en position 1 (1200 pas), sans qu'aucun ordre n'ait été donné.

Son cycle normal est le suivant : Déplacement 1 (1120 pas), Déplacement 2 (2500 pas), Déplacement 3 (4000 pas), suivi d'un dernier déplacement de retour au point zéro. Le problème est que le moteur se positionne systématiquement à 25 % après la prise de référence ou après le retour à cette référence.

J'ai questionné l'IA et testé plusieurs configurations, mais rien n'y fait. À part que mes cheveux tombent, en plus de mes bras, je n'arrive pas à comprendre ce qui ne fonctionne pas dans mon code. Une bonne âme serait-elle disposée à aider un pauvre débutant comme moi ?

Je vous joins le code ci-dessous. Juste une petite précision : j'utilise le logiciel Arduino sur un Mac avec macOS 10.12.6, ce qui explique certaines bibliothèques qui datent un peu, mais je ne crois pas que cela ait une influence sur le problème que je rencontre.

Je serais ravi de vous lire, dans l'espoir que tous mes cheveux ne tombent pas complètement… :wink:

#include <WiFi.h>
#include <WebServer.h>
#include <AccelStepper.h>
#include <FastLED.h>

// Informations de connexion WiFi
const char* ssid = "xxxxxxxxxx"; // Remplacez par votre SSID
const char* password = "xxxxxxxxxx"; // Remplacez par votre mot de passe

// Définition des broches pour le moteur pas à pas, le bouton poussoir, le capteur de point zéro, et l'A4988 enable pin
#define dirPin 12
#define stepPin 13
#define buttonPin 14
#define enablePin 27 // Pin d'activation du moteur
#define zeroPointPin 26 // Broche pour le capteur de point zéro

// Définition des broches et paramètres pour les LEDs
#define LED_PIN 33
#define NUM_LEDS 24
CRGB leds[NUM_LEDS];

// Définir les broches tactiles
#define touchPin1 02 // Utiliser la broche tactile T3 pour commande moteur (GPIO 2)
#define touchPin2 15 // Utiliser la broche tactile T9 pour commande couleurs (GPIO 15)

// Créer une instance de serveur web
WebServer server(80);

// Créer une instance de la classe AccelStepper
AccelStepper stepper(AccelStepper::DRIVER, stepPin, dirPin);

// Variables pour les positions du moteur
int positions[3] = {1200, 2500, 4000}; // Trois positions intermédiaires
int buttonPressCount = 0; // Compteur d'appuis du bouton
bool motorActive = false; // Indique si le moteur est actif
bool calibrationComplete = false; // Indique si la calibration est terminée

// Seuil de détection tactile
int touchThreshold = 45; // Augmenter cette valeur pour réduire la sensibilité

// Variables pour le délai anti-rebond
unsigned long lastTouchTime = 0;
unsigned long debounceDelay = 200;  // Délai anti-rebond de 200ms

// Fonction de détection du point zéro
bool isZeroPoint() {
  return digitalRead(zeroPointPin) == HIGH; // Ajustez selon votre capteur
}

// Fonction pour calibrer le point zéro
void calibrateZeroPoint() {
  Serial.println("Début de la calibration du point zéro.");
  motorActive = true; // Le moteur est actif
  digitalWrite(enablePin, LOW); // Activer le moteur

  stepper.setMaxSpeed(4000);
  stepper.setAcceleration(4000);
  stepper.setSpeed(-2000); // Déplacement vers le capteur

  // Faire tourner le moteur jusqu'à ce que le point zéro soit détecté
  while (!isZeroPoint()) {
    stepper.runSpeed();
    Serial.println("Moteur en mouvement...");
  }

  // Stopper le moteur dès que le point zéro est détecté
  stepper.stop(); // Assure l'arrêt immédiat du moteur
  delay(10); // Petite pause pour garantir l'arrêt complet

  Serial.println("Point zéro détecté, mouvement en arrière...");
 
  // Ajustement de la vitesse et de l'accélération pour le mouvement arrière
  stepper.setMaxSpeed(2000); // Réduire la vitesse pour plus de précision
  stepper.setAcceleration(1000); // Ajuster l'accélération
  stepper.move(-850); // Mouvement de 850 pas en arrière
  stepper.runToPosition(); // Exécuter jusqu'à la position cible
 
  stepper.setCurrentPosition(0); // Définir la position actuelle comme le point zéro
  Serial.println("Point zéro atteint et position calibrée.");
  calibrationComplete = true;

  motorActive = false;
  digitalWrite(enablePin, HIGH); // Désactiver le moteur
}

// Fonction pour l'effet chenillard
void showStartupSequence() {
  for (int i = 0; i < NUM_LEDS; i++) {
    leds[i] = CHSV(i * 255 / NUM_LEDS, 255, 255); // Couleurs arc-en-ciel
    FastLED.show();
    delay(50);
  }
  delay(500);
  FastLED.clear();
  FastLED.show();
}

// Fonction pour ajuster la luminosité des LEDs
void setLEDBrightness(uint8_t brightness) {
  for (int i = 0; i < NUM_LEDS; i++) {
    leds[i] = CRGB::White;
  }
  FastLED.setBrightness(brightness);
  FastLED.show();
}

// Définir les couleurs des LEDs
void setLEDColor(CRGB color) {
  fill_solid(leds, NUM_LEDS, color);
  FastLED.show();
}

// Éteindre les LEDs
void turnOffLEDs() {
  FastLED.clear();
  FastLED.show();
}

void setup() {
  Serial.begin(115200);

  pinMode(buttonPin, INPUT_PULLUP);
  pinMode(enablePin, OUTPUT);
  pinMode(zeroPointPin, INPUT_PULLUP);

  // Initialiser les LEDs
  FastLED.addLeds<NEOPIXEL, LED_PIN>(leds, NUM_LEDS);

  // Montrer l'effet chenillard au démarrage
  showStartupSequence();

  calibrateZeroPoint(); // Calibration au démarrage
 
  stepper.setMaxSpeed(4000);
  stepper.setAcceleration(10000);

  // Connexion au Wi-Fi
  WiFi.begin(ssid, password);
  while (WiFi.status() != WL_CONNECTED) {
    delay(1000);
    Serial.println("Connexion au WiFi en cours...");
  }
  Serial.println("Connecté au réseau WiFi.");

  server.on("/", handleRoot);
  server.on("/position1", handlePosition1);
  server.on("/position2", handlePosition2);
  server.on("/position3", handlePosition3);
  server.on("/return_to_zero", handleReturnToZero);
  server.on("/move_5000", handleMove5000); // Nouvelle route pour le mouvement de 5000 pas

  server.on("/led_blue", handleBlueLEDs); // Route pour définir les LEDs en bleu
  server.on("/led_pink", handlePinkLEDs); // Route pour définir les LEDs en rose
  server.on("/led_orange", handleOrangeLEDs); // Route pour définir les LEDs en orange
  server.on("/led_off", handleOffLEDs); // Route pour éteindre les LEDs

  server.begin();
  Serial.println("Serveur web démarré.");
}

void loop() {
  server.handleClient();

  static int lastButtonState = HIGH;
  int buttonState = digitalRead(buttonPin);

  // Lire les valeurs tactiles
  int touchValue1 = touchRead(touchPin1);
  int touchValue2 = touchRead(touchPin2);

  // Moyennage des lectures tactiles
  static int touchSum1 = 0;
  static int touchSum2 = 0;
  static int touchCount = 0;
  const int numReadings = 10; // Nombre de lectures pour la moyenne

  touchSum1 += touchValue1;
  touchSum2 += touchValue2;
  touchCount++;

  if (touchCount >= numReadings) {
    int touchAverage1 = touchSum1 / numReadings;
    int touchAverage2 = touchSum2 / numReadings;
    touchSum1 = 0;
    touchSum2 = 0;
    touchCount = 0;

    // Vérifier si le fil de cuivre est touché pour le moteur
    if (touchAverage1 < touchThreshold && calibrationComplete) {
      delay(50);
      buttonPressCount = (buttonPressCount + 1) % 4;

      if (buttonPressCount == 1) {
        moveToPosition(positions[0]);
        setLEDBrightness(64); // 25% de luminosité
      } else if (buttonPressCount == 2) {
        moveToPosition(positions[1]);
        setLEDBrightness(128); // 50% de luminosité
      } else if (buttonPressCount == 3) {
        moveToPosition(positions[2]);
        setLEDBrightness(255); // 100% de luminosité
      } else {
        moveToPosition(0);
        FastLED.clear(); // Éteindre les LEDs
        FastLED.show();
      }
    }

    // Vérifier si le fil de cuivre est touché pour les LEDs
    if (touchAverage2 < touchThreshold) {
      unsigned long currentMillis = millis();
      if (currentMillis - lastTouchTime >= debounceDelay) {
        lastTouchTime = currentMillis; // Mettre à jour le temps du dernier contact
        static int ledState = 0;
        ledState = (ledState + 1) % 4; // Alterne entre 0, 1, 2, 3

        if (ledState == 0) {
          setLEDColor(CRGB::Blue);
        } else if (ledState == 1) {
          setLEDColor(CRGB::DeepPink);
        } else if (ledState == 2) {
          setLEDColor(CRGB::Orange);
        } else if (ledState == 3) {
          turnOffLEDs();
        }
      }
    }
  }

  if (buttonState == LOW && lastButtonState == HIGH) {
    buttonPressCount = (buttonPressCount + 1) % 4;
    if (buttonPressCount == 1) {
      moveToPosition(positions[0]);
      setLEDBrightness(64);
    } else if (buttonPressCount == 2) {
      moveToPosition(positions[1]);
      setLEDBrightness(128);
    } else if (buttonPressCount == 3) {
      moveToPosition(positions[2]);
      setLEDBrightness(255);
    } else {
      moveToPosition(0);
      FastLED.clear();
      FastLED.show();
    }
  }
  lastButtonState = buttonState;
}

void handleRoot() {
  String html = "\
  <html>\
  <head>\
  <title>Contrôle du moteur et des LEDs</title>\
  <style>\
  body { font-family: Arial, sans-serif; text-align: center; padding: 10px; background-color: #f0f0f0; }\
  h1 { color: #333; margin-bottom: 50px; }\
  .container { display: flex; justify-content: center; align-items: center; flex-wrap: wrap; }\
  .column { flex-basis: 48%; margin: 10px; }\
  .left, .right { flex-basis: 48%; }\
  button { width: 80%; padding: 40px; font-size: 48px; margin-bottom: 10px; border: none; border-radius: 10px; cursor: pointer; }\
  .left button { background-color: #4CAF50; color: white; }\
  .right button.blue { background-color: blue; color: white; }\
  .right button.pink { background-color: pink; color: white; }\
  .right button.orange { background-color: orange; color: white; }\
  .right button.off { background-color: grey; color: white; }\
  </style>\
  <script>\
  function sendRequest(url) {\
    var xhttp = new XMLHttpRequest();\
    xhttp.open('GET', url, true);\
    xhttp.send();\
  }\
  </script>\
  </head>\
  <body>\
  <div class=\"container\">\
  <div class=\"column left\">\
  <h1>Moteur</h1>\
  <button onclick=\"sendRequest('/position1')\">25%</button><br>\
  <button onclick=\"sendRequest('/position2')\">50%</button><br>\
  <button onclick=\"sendRequest('/position3')\">100%</button><br>\
  <button onclick=\"sendRequest('/return_to_zero')\">Off</button><br>\
  <button onclick=\"sendRequest('/move_5000')\">D&eacute;montage</button><br>\
  </div>\
  <div class=\"column right\">\
  <h1>LEDs</h1>\
  <button class=\"blue\" onclick=\"sendRequest('/led_blue')\">Bleu</button><br>\
  <button class=\"pink\" onclick=\"sendRequest('/led_pink')\">Rose</button><br>\
  <button class=\"orange\" onclick=\"sendRequest('/led_orange')\">Orange</button><br>\
  <button class=\"off\" onclick=\"sendRequest('/led_off')\">&#xC9;teindre</button><br>\
  </div>\
  </div>\
  </body>\
  </html>";

  server.send(200, "text/html", html);
}

void handlePosition1() {
  moveToPosition(positions[0]);
  server.send(200, "text/html", "Déplacé à la position 1");
}

void handlePosition2() {
  moveToPosition(positions[1]);
  server.send(200, "text/html", "Déplacé à la position 2");
}

void handlePosition3() {
  moveToPosition(positions[2]);
  server.send(200, "text/html", "Déplacé à la position 3");
}

void handleReturnToZero() {
  moveToPosition(0);
  server.send(200, "text/html", "Retour au point zéro");
}

void handleMove5000() {
  moveToPosition(5000);
  server.send(200, "text/html", "Déplacé de 5000 pas");
}

void handleBlueLEDs() {
  setLEDColor(CRGB::Blue);
  server.send(200, "text/html", "LEDs bleues activées");
}

void handlePinkLEDs() {
  setLEDColor(CRGB::DeepPink);
  server.send(200, "text/html", "LEDs roses activées");
}

void handleOrangeLEDs() {
  setLEDColor(CRGB::Orange);
  server.send(200, "text/html", "LEDs oranges activées");
}

void handleOffLEDs() {
  turnOffLEDs();
  server.send(200, "text/html", "LEDs éteintes");
}

void moveToPosition(int position) {
  if (!motorActive && calibrationComplete) {
    motorActive = true;
    digitalWrite(enablePin, LOW);
    stepper.moveTo(position);
    stepper.runToPosition();
    motorActive = false;
    digitalWrite(enablePin, HIGH);
  }
}

Si le capteur est sensible au bruit ou si le signal est instable, le moteur pourrait être amené à se repositionner sans commande explicite. Ajoutez un petit délai après la détection du point zéro pour vous assurer que le moteur est bien stabilisé avant de réinitialiser la position à zéro. Par exemple, après la détection du point zéro dans calibrateZeroPoint(), attendez quelques millisecondes pour éviter un rebond.

while (!isZeroPoint()) {
  stepper.runSpeed();
  Serial.println("Moteur en mouvement...");
}

delay(50);  // Ajoutez ce délai pour stabiliser le capteur

Merci aliarifat794 pour cette prompt réponse, j’apprécie votre intérêt.
Je ne suis pas convaincu que le capteur soit la cause de mon problème, étant donné qu'il envoie un signal 850 pas avant et que le déplacement prend environ 2 secondes, c'est à la fin de ces 2 secondes que la référence du point zéro se fait.

J'ai de la chance, je n'ais plus beaucoup à m'inquiéter de ça :wink:

Ce déplacement "parasite" ne peut être provoqué que par un moveToPosition(positions[0]);
Comme tu en a plusieurs, mets devant la première de ces lignes, un Serial.println("11111");, un Serial.println("22222"); devant la suivante et ainsi de suite, tu pourras, ainsi, remonter vers la source de ton souci, du moins je l'espère :wink:

A+
Cordialement
jpbbricole

Merci jpbbricole de ton intérêt à mon problème pas "tracto capillaire" mais presque...
C'est effectivement une bonne astuce Je tente...

J'ai tenté... C'est bien à la position 1 que déplacement ce fait, mais je ne vois pas ce qui lui donne cet ordre de déplacement, après la calibration, ou après un retour au point zéro.

La librairie FastLED est énorme et franchement je ne me sens pas de la fouiller à fond mais, à mon sens, il est possible que les timings un peu pointus des smartLED entrent en conflit avec la gestion du moteur pas-à-pas.
Tu pourrais temporairement commenter les appels aux fonctions de FastLED pour voir si ton moteur tourne correctement dans ce cas.

Ca veut dire que cette condition:
if (buttonState == LOW && lastButtonState == HIGH)
est vraie?
Donc ton bouton est pressé ou en court circuit ou mal câblé, éventuellement fais en une photo.

Merci fdufnews, c'est également une bonne idée, mais je crois que la réponse de jpbbricole si dessous est la piste que je dois suivre... étant donné que j'ai ce retour dans le moniteur

22:51:08.408 -> Moteur en mouvement...
22:51:08.480 -> Point zéro détecté, mouvement en arrière...
22:51:10.428 -> Point zéro atteint et position calibrée.
22:51:11.566 -> Connexion au WiFi en cours...
22:51:11.566 -> Connecté au réseau WiFi.
22:51:11.566 -> Serveur web démarré.
22:51:11.601 -> Bouton pressé, compteur actuel : 1
22:51:12.279 -> Mouvement terminé à la position : 1200
Alors que je n'ai pas sollicité le bouton.

Oui je crois qu'il faut que je contrôle ce fâcheux bouton qui est vu dans le moniteur série comme pressé, Alors qu'il n'est pas sollicité
"22:51:11.601 -> Bouton pressé, compteur actuel : 1"

Attention, si c'est ce type de bouton:
image
ils se câblent "en diagonale"

Le câblage du bouton est bien en diagonale. J'ai tenté de désactivé ce bouton ,qui en plus est optionnel sur le circuit, mais il est toujours actif après la prise de référence ou le retour au point zéro, selon le moniteur série. En revanche je n'ai plus de retour au point zéro...

23:32:19.345 -> Moteur en mouvement...
23:32:19.382 -> Point zéro détecté, mouvement en arrière...
23:32:21.327 -> Point zéro atteint et position calibrée.
23:32:22.475 -> Connexion au WiFi en cours...
23:32:22.475 -> Connecté au réseau WiFi.
23:32:22.475 -> Serveur web démarré.
23:32:22.508 -> Bouton pressé, compteur actuel : 1
23:32:22.576 -> Mouvement vers la position : 2500
23:32:30.159 -> Mouvement vers la position : 4000
23:32:34.074 -> Bouton pressé, compteur actuel : 0
23:32:34.860 -> Bouton pressé, compteur actuel : 1
23:32:35.529 -> Bouton pressé, compteur actuel : 2
23:32:36.256 -> Bouton pressé, compteur actuel : 3
23:32:36.949 -> Bouton pressé, compteur actuel : 0
23:32:38.339 -> Bouton pressé, compteur actuel : 1
23:32:39.687 -> Bouton pressé, compteur actuel : 2
23:32:41.072 -> Bouton pressé, compteur actuel : 3
23:32:49.848 -> Mouvement vers la position : 1200
23:33:04.702 -> Mouvement vers la position : 2500
23:33:05.451 -> Mouvement vers la position : 4000

Bonjour,

A propos des boutons

Un détail, pour être précis et laisser ouvert "le champ des possibles", le câblage en diagonale pour ce genre de BP permet d'éviter toutes erreurs de câblage, mais il n'est pas impératif.

Vous pouvez aussi le cabler entre 1 et 2, ou 3 et 4.

C'est tout :roll_eyes:

Bonjour jef59,

Merci pour ces précisions :wink:

Bonsoir.
Une petite mesure a l ohmetre peut aussi aider a deceler le problème.

This topic was automatically closed 180 days after the last reply. New replies are no longer allowed.