Gestione stepper NEMA17 con A4988: uso della libreria Bounce

Buongiorno.

Come detto in altro thread sto cercando di pilotare una piattaforma girevole che nasce con un motore in CC per la sua rotazione.

Sto cercando di pilotarla con uno stepper e ho cominciato con un driver ULN2003 che pilota un motore 28BYJ-48 ma non funziona bene perchè il lasco dell’albero vanifica la precisione che dovrebbe avere uno stepper, evidentemente è di scarsa qualità ma non ne ho trovati in rete che siano dichiarati buoni, quindi sono passato a un NEMA17 pilotandolo con un driver A4988.

Dapprima ho scritto una sketch con la libreria “stepper” poi l’ho modificato utilizzando la libreria ”accelstepper” e utilizzando il disabling se il motore è fermo per vedere se riesco a raffreddarlo.

Il disabling funziona ma non ne sono sicuro faccia quello che pensavo facesse.

Poi sono passato ad aggiungere la libreria “Bounce2” ma se utilizzo le sue funzioni per far muovere il motore non su muove un bel niente.

Qui lo sketch funzionante senza le funzioni di debouncing

 /* Example sketch to control a NEMA17 stepper motor with 
  A4988 driver board, AccelStepper, Bounce2 and Arduino UNO
  More info: https://www.makerguides.com */

#include "AccelStepper.h"
#include "Bounce2.h"

// Assegnazione pin motore:
#define stepPin 7
#define dirPin 8
#define enablePin 9

// Assegnazione pin bottoni:
const int buttonPin2 = 2;  // nome pulsante e pin associato
const int buttonPin4 = 4;  // nome pulsante e pin associato
const int buttonPin5 = 5;  // nome pulsante e pin associato
const int buttonPin6 = 6;  // nome pulsante e pin associato

// Inizializzazione posizione e bottoni
int posizione    = 1;
int buttonState1 = 0;
int buttonState2 = 0;
int buttonState3 = 0;
int buttonState4 = 0;

int enablePinstatus = 0;

int stepperspeed=196;

// Millisecondi per il deounce
  const int debounceMs = 10;

//Stato (Enable/Disable)
boolean abilitato;

const int gradi180   = 8015;                   // numero di step per una rotazione di 180°
//const int gradi12    = gradi180 / 180 * 15;  // numero di step per una rotazione di 12° 
const int gradi12    = 560;                    // numero di step per una rotazione di 12°
const int gradi168   = gradi180 - gradi12;     // numero di step per una rotazione di 168°

// Define the AccelStepper interface type; 4 wire motor in half step mode:
#define motorInterfaceType 1

// Create a new instance of the AccelStepper class:
AccelStepper stepper = AccelStepper(motorInterfaceType, stepPin, dirPin);

// DEBOUNCE-INIT
// Creazione istanze dei pulsanti
Bounce dbbutton1 = Bounce();
Bounce dbbutton2 = Bounce();
Bounce dbbutton3 = Bounce();
Bounce dbbutton4 = Bounce();
// DEBOUNCE-END

void setup() 
{
  // DEBOUNCE-INIT
  // Inizializzazione stato enable/disable (LOW/HIGH)
  abilitato = false;

//Definizione modalità dei pin
  pinMode(buttonPin2, INPUT);
  pinMode(buttonPin4, INPUT);
  pinMode(buttonPin5, INPUT);
  pinMode(buttonPin6, INPUT);
  pinMode(enablePin, OUTPUT);

  //Impostazione pullUp dei i pin dei bottoni
  digitalWrite(buttonPin2, HIGH);
  digitalWrite(buttonPin4, HIGH);
  digitalWrite(buttonPin5, HIGH);
  digitalWrite(buttonPin6, HIGH);

  //setta stato del pin enable
  //digitalWrite(enablePin, !abilitato)

  // Set the maximum steps per second:
  stepper.setMaxSpeed(200);

  // Inizializzazione bottoni
  dbbutton1.attach(buttonPin2);
  dbbutton1.interval(debounceMs);

  dbbutton2.attach(buttonPin4);
  dbbutton2.interval(debounceMs);

  dbbutton3.attach(buttonPin5);
  dbbutton3.interval(debounceMs);

  dbbutton4.attach(buttonPin6);
  dbbutton4.interval(debounceMs);
  // DEBOUNCE-END
  
    //inizializzo la comunicazione seriale cosi da mostrare i valori nel Monitor Seriale
  Serial.begin(9600);
}

void loop() 
{
  //DEBOUNCE-INIT
  dbbutton1.update();
  dbbutton2.update();
  dbbutton3.update();
  dbbutton3.update();
  //DEBOUNCE-END

  // Set the current position to 0:
  stepper.setCurrentPosition(0);

  buttonState1 = 0;
  buttonState2 = 0;
  buttonState3 = 0;
  buttonState4 = 0;

  // Leggi stato del bottone 1
  buttonState1 = digitalRead(buttonPin2);
  //DEBOUNCE-INIT
  //buttonState1 = dbbutton1.read();
  //DEBOUNCE-END

    /*Invio i dati letti al Monitor seriale, cosi da poterli vedere a video
       Serial.print("buttonState1 = ");
       Serial.print(buttonState1);
       Serial.print("  Posizione iniziale= ");
       Serial.println(posizione);
       delay(5000);
    */

 // Controlla se il bottone 1 è stato premuto (valore HIGH)
 if (buttonState1 == HIGH) 
  {
    abilitato=false;
    digitalWrite(enablePin,abilitato);

    /*Invio i dati letti al Monitor seriale, cosi da poterli vedere a video
       Serial.print("buttonState1 = ");
       Serial.print(buttonState1);
       Serial.print("  Posizione iniziale= ");
       Serial.println(posizione);
       delay(5000);
    */
    
    if (posizione == 1) 
    {
      //Aziona il motore per 180° senso antiorario
      stepper.setCurrentPosition(0);
           
      while (stepper.currentPosition() <= gradi180)
        {
          /*Invio i dati letti al Monitor seriale, cosi da poterli vedere a video
             Serial.print("buttonState1 = ");
             Serial.print(buttonState1);
             Serial.print("  Posizione  corrente= ");
             Serial.println(stepper.currentPosition());
          */

          stepper.setSpeed(stepperspeed);
          stepper.runSpeed();
        } 

        /*Invio i dati letti al Monitor seriale, cosi da poterli vedere a video
          Serial.print("buttonState1 = ");
          Serial.print(buttonState1);
          Serial.print("  Posizione iniziale= ");
          Serial.println(posizione);
          delay(5000);
        */
    } 
        else 
    {
       //Aziona il motore per 15° senso orario
      stepper.setCurrentPosition(0);
           
      while (stepper.currentPosition() >= -gradi12)
        {
          //Invio i dati letti al Monitor seriale, cosi da poterli vedere a video
          // Serial.print("buttonState1 = HIGH");
          // Serial.print("  Posizione  corrente= ");
          // Serial.println(stepper.currentPosition());

          stepper.setSpeed(-stepperspeed);
          stepper.runSpeed();
        }

      posizione = 1;
      //Invio i dati letti al Monitor seriale, cosi da poterli vedere a video
      //Serial.print("buttonState1 = HIGH");
      //Serial.print("  Posizione finale= ");
      //Serial.println(posizione);
      //delay(5000); 
    }

    abilitato=true;
    digitalWrite(enablePin,abilitato);
  }

  buttonState1 = 0;

  // Leggi stato del bottone 2
    buttonState2 = digitalRead(buttonPin4);
  //DEBOUNCE-INIT
  //buttonState2 = dbbutton2.read();
  //DEBOUNCE-END
 
 // Controlla se il bottone 2 è stato premuto (valore HIGH)
 if (buttonState2 == HIGH) 
  {
    abilitato=false;
    digitalWrite(enablePin,abilitato);

    //Invio i dati letti al Monitor seriale, cosi da poterli vedere a video
    //Serial.print("buttonState2 = HIGH");
    //Serial.print("  Posizione iniziale = ");
    //Serial.println(posizione);
    //delay(5000);

    if (posizione == 1) 
    {
      //Aziona il motore per 15° senso antiorario
      stepper.setCurrentPosition(0);
           
      while (stepper.currentPosition() <= gradi12)
        {
          //Invio i dati letti al Monitor seriale, cosi da poterli vedere a video
          // Serial.print("buttonState1 = HIGH");
          // Serial.print("  Posizione  corrente= ");
          // Serial.println(stepper.currentPosition());

          stepper.setSpeed(stepperspeed);
          stepper.runSpeed();
        } 

      posizione = 2;  

      //Invio i dati letti al Monitor seriale, cosi da poterli vedere a video
      //Serial.print("buttonState2 = HIGH");
      //Serial.print("  Posizione finale = ");
      //Serial.println(posizione);
    } 
        else 
    {
      //Invio i dati letti al Monitor seriale, cosi da poterli vedere a video
      //Serial.print("buttonState2 = HIGH");
      //Serial.print("  Posizione iniziale = ");
      //Serial.println(posizione);

       //Aziona il motore per 168° senso antiorario
      stepper.setCurrentPosition(0);
           
      while (stepper.currentPosition() <= gradi168)
        {
          //Invio i dati letti al Monitor seriale, cosi da poterli vedere a video
          // Serial.print("buttonState1 = HIGH");
          // Serial.print("  Posizione  corrente= ");
          // Serial.println(stepper.currentPosition());

          stepper.setSpeed(stepperspeed);
          stepper.runSpeed();
        }

      posizione = 1; 

      //Invio i dati letti al Monitor seriale, cosi da poterli vedere a video
      //Serial.print("buttonState2 = HIGH");
      //Serial.print("  Posizione finale = ");
      //Serial.println(posizione);
    }

    abilitato=true;
    digitalWrite(enablePin,abilitato);
  }

buttonState2 = 0;

  // Leggi stato del bottone 3
  // buttonState3 = digitalRead(buttonPin5);
  //DEBOUNCE-INIT
     buttonState3 = dbbutton3.read();
  //DEBOUNCE-END

  // Controlla se il bottone 3 è stato premuto (valore HIGH)
  // allora ruota in senso antiorario
  if (buttonState3 == HIGH) 
   {
    abilitato=false;
    digitalWrite(enablePin,abilitato);

    stepper.setCurrentPosition(0);
           
    while (buttonState3==HIGH)
      {
        /*Invio i dati letti al Monitor seriale, cosi da poterli vedere a video
        Serial.print("buttonState3 = ");
        Serial.print(buttonState3);
        Serial.print("  Posizione  corrente= ");
        Serial.println(stepper.currentPosition());
        */
        stepper.setSpeed(stepperspeed);
        stepper.runSpeed();
               
        buttonState3 = digitalRead(buttonPin5);
        //DEBOUNCE-INIT
        //buttonState3 = dbbutton3.read();
        //DEBOUNCE-END
      }
               
      /*        
      Serial.print("  Fuori loop ");
      Serial.println(stepper.currentPosition());
      Serial.print(" buttonState3: ");
      Serial.println(buttonState3);
      delay(1000);
      */
      
      abilitato=true;
      digitalWrite(enablePin,abilitato);
   }      
 
  // Leggi stato del bottone 4
  buttonState4 = digitalRead(buttonPin6);
  //DEBOUNCE-INIT
  //buttonState4 = dbbutton4.read();
  //DEBOUNCE-END

  // Controlla se il bottone 4 è stato premuto (valore HIGH)
  // allora ruota in senso antiorario
  if (buttonState4 == HIGH) 
   {
    abilitato=false;
    digitalWrite(enablePin,abilitato);
    
    stepper.setCurrentPosition(0);
           
      while (buttonState4==HIGH)
        {
          /*Invio i dati letti al Monitor seriale, cosi da poterli vedere a video
          Serial.print("buttonState4 = ");
          Serial.print(buttonState4);
          Serial.print("  Posizione  corrente= ");
          Serial.println(stepper.currentPosition());
          */
          stepper.setSpeed(-stepperspeed);
          stepper.runSpeed();

          buttonState4 = digitalRead(buttonPin6);
          //DEBOUNCE-INIT
          //buttonState4 = dbbutton4.read();
          //DEBOUNCE-END
        }
            
      /*        
      Serial.print("  Fuori loop ");
      Serial.println(stepper.currentPosition());
      Serial.print(" buttonState4: ");
      Serial.println(buttonState4);
      delay(1000);
      */
      
      abilitato=true;
      digitalWrite(enablePin,abilitato);
   }    
}

Le istruzioni esecutive per il debouncing sono commentate tra “//DEBOUNCE-INIT” E “DEBOUNCE-END”, basta scommentare la linea e commentare quella funzionante tipo

           buttonState4 = digitalRead(buttonPin6);
          //DEBOUNCE-INIT
          //buttonState4 = dbbutton4.read();
          //DEBOUNCE-END

diventa

          //buttonState4 = digitalRead(buttonPin6);
          //DEBOUNCE-INIT
          buttonState4 = dbbutton4.read();
          //DEBOUNCE-END

Avete qualche suggerimento?

Grazie.

Massimo.

Secondo me hai fatto casino tra input e input-pullup

Riguardaci bene a questa cosa

Ho copiato un esempio perchè non è che ci capisco granchè di pull-up e pull-down quindi non saprei proprio che cosa correggere.

Grazie.

Esempio di libreria?

Usi Bounce2 ma poi usi digitalRead invece di syntax di libreria.
E se hai pull-up, devi cercare if (dbbutton3.read() == LOW).
Oppure anche qui, usi syntax di libreria.

L’esempio viene da qui

Nello sketch vedi digitalRead perchè con questi funziona, commentate vedi le funzioni analoghe della librerie, usando le quali non funziona.

Riguardo il discorso pull-up non sono in grado di capire, come ho giá confessato.

Grazie anche a te.

È evidente che hai male interpretato l'uso della libreria

Noi non abbiamo l’hardware ma ti consiglio di concentrarti solo sulla libreria bounce

E di studiare i suoi esempi

Solo dopo averli capiti integro i due programmi

Semmai ho mal copiato perchè non ho studiato la libreria, ho solo copiato pari pari le inizializzazioni e i vari settaggi sostituendo i nomi dello sketch d’esempio con quelli che avevo usato nella versione precedente del mio sketch.

Riguardo ai settaggi di pull-up, nello sketch d’esempio i 4 bottoni che uso vengono settati con un “digitalWrite (bottonen, HIGH)”, non so se ti dice qualcosa.

Quello che dovevo dire lo ho detto

Solo una segnalazione di servizio:

Ho fermato mio fratello e lo ho “bannato’ per qualche giorno: cominciava a diventare " nervoso”

Forse dovresti ragionare come funziona un bottone.
In caso di un bottone collegato a GND con input pullup, bottone viene rilevato quando stato e' LOW.

Grazie :slightly_smiling_face:

Guglielmo

Buongiorno, rieccomi con i miei problemi software.

Lo sketch che sto cercando di far funzionare con la libreria Bounce2 l’avevo copiato da un esempio che, ho capito dopo, era fatto per una configurazione con i pin pull-up.

Non ci avevo fatto caso perchè il “pinMode” era “INPUT” e non “INPUT_PULLUP” comunque ho risistemato il cablaggio e riprovato.

Sfortunatamente lo sketch sembra funzionare a caso ma non vi sto a tediare con il malfunzionamento principale ma vi faccio solo due esempi:

  1. nonostante abbia configurato
    “const int gradi180 = 1600; // numero di step per una rotazione di 180°”
    già due volte, dopo compilazioni per mettere dei “print” per dei test, il motore invece che fare un giro ne faceva mezzo per poi tornare, dopo altre compilazioni, a comportarsi correttamente.
    In buona sostanza il microstepping passava da da 1/16 a 1/8 e viceversa.
    Ora non so se il fatto che io abiliti il driver quando faccio una rotazione e lo disabiliti a rotazione avvenuta, possa portare a questo comportmento.

  2. Qui sotto un pezzo dello sketch

void loop() 
{
  //!!!!!! l'update va eseguito PRIMA dei ogni lettura di un bottone
  //DEBOUNCE-INIT
  dbbutton1.update();
  dbbutton2.update();
  dbbutton3.update();
  dbbutton4.update();
  //DEBOUNCE-END

  // Set the current position to 0:
  stepper.setCurrentPosition(0);

  buttonState1 = 1;
  buttonState2 = 1;
  buttonState3 = 1;
  buttonState4 = 1;

    //
     Serial.print("buttonState1 fuori IF = ");
     Serial.println(buttonState1);
     delay(2000);
    //
  //-------------------------------------------------------------
  //  Leggi stato del bottone 1
  //-------------------------------------------------------------
  // Leggi stato del bottone 1
  //buttonState1 = digitalRead(buttonPin2);
  //DEBOUNCE-INIT
  buttonState1 = dbbutton1.read();
  //DEBOUNCE-END

 // Controlla se il bottone 1 è stato premuto (valore LOW)
 if (buttonState1 == LOW) 
  {
    abilitato=false;
    digitalWrite(enablePin,abilitato);
    
    if (posizione == 1) 
    ............................
    ............................
    ............................

Se le due istruzioni Serial.print sono attive lo sketch non entra nell’IF “if (buttonState1 == LOW) perché “buttonState1” rimane sempre 1 come inizializzato prima di “buttonState1 = dbbutton1.read();” (in pratica quest’ultima istruzione pare non funzionare), se le commento ci entra (senza toccare nient’altro ovviamente)

Se poi avrete pazienza vi racconterò anche l’altro difetto più grave.

Prima che chiamiate il 118 per un TSO lasciatemi spezzare una lancia in mio favore.

In cinquant’anni ho programmato in Fortan IV, Assembler, Cobol, MPL (all’Olivetti), Clipper 5 e ne ho viste di ogni e che ci sta tutto, quindi gradirei non essere mandato aff….lo troppo repentinamente.

Grazie.

Massimo.

Lascia stare il debounce software e fallo come s’ha da fare: hardware

Sono d’accordo con te, anche perchè, questi presunti problemi coi botton,i io non li ho riscontrati con le versioni precedenti nè con altri sketch per altri driver, però ormai mi ci ero messo e le cose incomprensibili mi hanno sempre disturbato.

Però HW non so come si fa.

Beh, Allora ispirati a quelli

Se tuo loop dura complessivamente 2001ms e 99.95% di questo tempo e' bloccato da delay(), quanto bene puo' funzionare?

Questa non l’ho mica capita tanto.

Il delay ferma lo sketch che poi riparte, forse il problema è che se premo il pulsante durante il delay non viene rilevato.

O il ragionamento non è questo?

Il delay impone alla MCU di rimanere in un loop senza fare niente per il tempo passato come parametro.
Di conseguenza gli algoritmi necessari per computare gli step dei motori passo passo non vengono eseguiti, come non viene eseguito tutto il resto.

Visto che sei agli inizi, il mio consiglio è di abbandonare più che puoi il concetto di delay nei tuoi algoritmi.

Il delay() è la droga del principiante: rassicurante e facile da usare, ma crea una dipendenza che impedisce al codice di crescere.
Quando lo usi devi essere cosciente che il micro non farà altro per il tempo impostato.

In line di massima devi sviluppare il tuo codice in modo che sia asincrono e non bloccante.

Ovviamente non viene rilevato. Poi loop e' cosi' veloce, che se premi un altra volta sempre casca in delay... Solo con fortuna viene rilevato.

#cotestatnt

Grazie per la risposta.

Uso il delay solo per necessità di debug e l’avevo messo per capire l’altro problema incomprensibile.

Lo sketch ha 4 IF uno dietro all’altro in serie.

Il primo fa ruotare il motore di 180° in senso antiorario o 12° in senso orario.
Il secondo fa ruotare il motore di 12° in senso antiorario o 168° in senso orario
Il terzo genera una rotazione continua in senso orario finchè il suo pulsante è premuto
Il quarto genera una rotazione continua in senso antiorario finchè il suo pulsante è premuto

Il problema è che il terzo e quarto IF funzionano benissimo mentre il primo e il secondo,
invece di essere eseguiti una volta alla pressione del loro bottone, vengono eseguiti due volte perchè, per motivi ignoti, lo stato del pulsante associato viene sentito HIGH anche se è rilasciato.

#Kmin

Ok, comunque ho abbandonato Bounce2 anche perché, come detto, non ho riscontrato problemi

Una domanda per tutti e due: se abbasso il pin SLEEP dell’A4988 ho letto che si disattiva completamente il driver (lo farei per far raffreddare il motore), è corretto?

Perché al primo tentativo non mi pare funzioni.

Grazie.

Massimo