Circuito per slitta motorizzata con Arduino

Realizzazione di un circuito per una “slitta” motorizzata con Arduino.
Finalmente un pò di libertà per smanettare ancora una volta con Arduino.
E’ passato moltissimo tempo dall’ultimo articolo, tempo durante il quale purtroppo, sono stato assente dal sito e dal canale YouTube. Ciò nonostante, devo dire, siete stati in tanti a scrivere e a commentare (anche privatamente) per dei consigli o anche solo per dei ringraziamenti. Quindi prima di cominciare questo nuovo post, ringrazio tutti voi per aver continuato a leggere i miei seppur vecchi articoli, e per avermi dimostrato che se non altro sono serviti a qualcuno.
Grazie davvero!

Tra gli articoli più gettonati, ho notato che a suscitare molto interesse, sono stati quelli relativi al controllo di motori passo passo, come ad esempio:

Pilotare un motore passo passo con Arduino e il driver A4988
Controllo di due motori passo passo con Arduino e un Joystick

Prendendo spunto dagli ultimi commenti, ho pensato di realizzare qualcosa che possa, se non soddisfare, quanto meno avvicinarsi, all’esigenza di Walter.

L’idea generale è quella di una slitta che si muove autonomamente avanti e in dietro su un binario. Il tutto immaginario perchè mi sono limitato soltanto alla realizzazione del circuito e del codice. Senza slitta e senza binario per intenderci 😀

All’accensione del sistema, la slitta deve essere in grado di resettare il suo stato e posizionarsi all’inizio del binario.
Questa fase l’ho chiamata “Homing”.
Al termine della fase di homing, la slitta rimane in attesa della pressione di un pulsante, il bottone “Work”, per iniziare il suo movimento.
Per determinare i fine corsa del binario, ho utilizzato due “Limit Switch” o interruttori di fine corsa (come preferisci).
Durante la fase di lavoro sarà possibile scegliere il livello di “risoluzione” del motore. Per risoluzione intendo lo scostamento in termini di step del rotore che grazie al pulsante “Risoluzione” tramite il driver A4988, potrà essere definito in: uno step, 1/2, 1/4, 1/8 e 1/16 di step.
Se durante l’esecuzione dovessero esserci problemi, ho previsto un pulsante di “Reset” che interrompe l’esecuzione del movimento e ripristina lo stato della slitta, che quindi si riposiziona in modalità iniziale.

Scenario

In uno scenario di utilizzo tipo, le cose andrebbero in questo modo:
– accendo il dispositivo;
– aspetto che termini la fase di Homing;
– avvio il movimento con il pulsante Work;
– tramite il pulsante Risoluzione scelgo quella che fa al caso mio (di default è 1 STEP);
– individuata la risoluzione ideale premo il pulsante Reset per riposizionare la slitta;
– a questo punto sono pronto per ripremere il pulsante Work ed eseguire il movimento alla risoluzione scelta.

Lo schema

Circuito per slitta motorizzata con Arduino

Circuito per slitta motorizzata con Arduino

Come avrai potuto notare dallo schema dei collegamenti, in questa occasione ho deciso di utilizzare un’unica fonte di alimentazione. Uso un alimentatore a 12 volt che alimenta sia Arduino, sia la VMOT del driver A4988 e di conseguenza il motore. Per l’alimentazione logica del driver, uso i 5 volt forniti da Arduino.
La capacità del condensatore elettrolitico posto in parallelo all’alimentazione motore è di 100uF, e come riportato dal sito del produttore deve essere almeno di 47uF.

Warning: This carrier board uses low-ESR ceramic capacitors, which makes it susceptible to destructive LC voltage spikes, especially when using power leads longer than a few inches. Under the right conditions, these spikes can exceed the 35 V maximum voltage rating for the A4988 and permanently damage the board, even when the motor supply voltage is as low as 12 V. One way to protect the driver from such spikes is to put a large (at least 47 µF) electrolytic capacitor across motor power (VMOT) and ground somewhere close to the board.

Sui limit switch e sui pulsanti di comando, ho collegato delle resistenze da 12 Ohm anche se sarebbe meglio qualcosa di più sostanzioso, tipo 10K, in modo da limitare il passaggio di corrente quando premuti e quindi posti a massa. Serve per salvaguardare i pin di Arduino. Purtroppo a casa avevo solo queste.
Il led di stato è collegato sul pin 13 di Arduino. Questo pin presenta già un led on board sulla scheda quindi puoi anche evitare di montarne uno esterno.
Eventualmente, anche in questo caso occorre mettere una resistenza.
Per l’eventuale led esterno, una resistenza da 220 Ohm andrà bene.

In merito al Driver A4988

Ricorda che è possibile regolare la corrente di uscita del driver A4988 tramite il trimmer onboard.
Guarda qui sotto il video del produttore per capire come fare.
Durante i miei test non ho fatto nessuna regolazione, sarà fortuna o sarà un caso, ma né il modulo né il motore sembrano risentirne.

Il codice

Come potrai notare, la velocità del motore durante le fasi di homing e di lavoro sono cablate nel codice. Se non soddisfano le tue esigenze, puoi modificarle da “SPEED_WORKING” e “SPEED_HOMING”.
Tra il movimento di andata ed il movimento di ritorno della fase di lavoro e della fase di homing, ci sono delle pause. Di default sono 4 secondi per la fase di lavoro e 2 secondi per la fase di homing. Anche queste possono essere modificate agendo sui valori delle costanti DELAY_WORK e DELAY_HOMING.

Come sempre, il codice è ampiamente commentato ma per qualsiasi dubbio o chiarimento, puoi fare riferimento alla sezione commenti a fondo pagina.

Ricordati di includere le due librerie: AccelStepper e Bounce2.
Se non le hai già, basta andare in gestione librerie dall’IDE di Arduino ed installarle.

//Inclusione delle librerie
#include <AccelStepper.h>
#include <Bounce2.h>

//Definizione costanti relative ai PIN
const int PIN_SW_LIMIT_END = 2;
const int PIN_SW_WORK = 3;
const int PIN_SW_RESET = 4;
const int PIN_SW_RISOLUZIONE = 5;

const int PIN_DR_DIR = 6;
const int PIN_DR_STEP = 7;
const int PIN_MS_3 = 8;
const int PIN_MS_2 = 9;
const int PIN_MS_1 = 10;
const int PIN_DR_ENABLE = 11;

const int PIN_SW_LIMIT_START = 12;

const int PIN_LED_STATO = 13;

/*
  Tabella di riferimento relativa alla risoluzione del motore prelevata dal sito del produttore
  https://www.pololu.com/product/1182
  +---+------+------+------+----------------------+
  | # | MS1  | MS2  | MS3  | Microstep resolution |
  +---+------+------+------+----------------------+
  | 0 | Low  | Low  | Low  | Full step            |
  | 1 | High | Low  | Low  | Half step            |
  | 2 | Low  | High | Low  | Quarter step         |
  | 3 | High | High | Low  | Eighth step          |
  | 4 | High | High | High | Sixteenth step       |
  +---+------+------+------+----------------------+
*/

//Creiamo un array bidimensionale per contenere i valori della tabella della risoluzione.
const int RISOLUZIONE[5][3] = {
  {0, 0, 0},
  {1, 0, 0},
  {0, 1, 0},
  {1, 1, 0},
  {1, 1, 1}
};

const unsigned long DELAY_WORK = 4000;
const unsigned long DELAY_HOMING = 2000;

//Imposta velocità del motore durante la fase di lavoro
const double SPEED_WORKING = 20.0;

//Imposta velocità del motore durante la fase di reset della posizione
const double SPEED_HOMING = 40.0;

//il delay in ms di debounce applicato agli switch
const unsigned long DEBOUNCE_DELAY = 10;

boolean homing, working;

//risoluzione iniziale del motore
int selRisoluzione = 0;

//istanzia il motore
AccelStepper motore(AccelStepper::DRIVER, PIN_DR_STEP, PIN_DR_DIR);

//istanzia i bottoni
Bounce btnSwResolution = Bounce();  //Scelta risoluzione step
Bounce btnSwReset = Bounce();       //Reset operazioni
Bounce btnSwWork = Bounce();        //Avvia esecuzione
Bounce btnSwLimitHome = Bounce();   //Limite iniziale
Bounce btnSwLimitEnd = Bounce();    //Limite fine corsa

void setup() {
  //definizione delle modalità dei pin
  pinMode(PIN_LED_STATO, OUTPUT);

  pinMode(PIN_DR_ENABLE, OUTPUT);
  pinMode(PIN_MS_1, OUTPUT);
  pinMode(PIN_MS_2, OUTPUT);
  pinMode(PIN_MS_3, OUTPUT);

  pinMode(PIN_SW_RISOLUZIONE, INPUT_PULLUP);
  pinMode(PIN_SW_RESET, INPUT_PULLUP);
  pinMode(PIN_SW_WORK, INPUT_PULLUP);

  pinMode(PIN_SW_LIMIT_START, INPUT_PULLUP);
  pinMode(PIN_SW_LIMIT_END, INPUT_PULLUP);

  //associamo i pin degli switch alle istanze dei bottoni (Boune)
  btnSwResolution.attach(PIN_SW_RISOLUZIONE);
  btnSwResolution.interval(DEBOUNCE_DELAY);

  btnSwReset.attach(PIN_SW_RESET);
  btnSwReset.interval(DEBOUNCE_DELAY);

  btnSwWork.attach(PIN_SW_WORK);
  btnSwWork.interval(DEBOUNCE_DELAY);

  btnSwLimitHome.attach(PIN_SW_LIMIT_START);
  btnSwLimitHome.interval(DEBOUNCE_DELAY);

  btnSwLimitEnd.attach(PIN_SW_LIMIT_END);
  btnSwLimitEnd.interval(DEBOUNCE_DELAY);

  delay(2000);

  //Esegui reset posizione del motore.
  resetHomePosition();

}

void loop() {

  //se non siamo in fase di reset della posizione
  if (!homing ) {

    //Tieni d'occhio gli switch "Work", "Reset" e "Risoluzione"
    handleSwResetAndResolution();
    handleSwWork();

    //Se siamo in fase di lavoro (working)
    if (working) {

      //Vai avanti fin quando non viene premuto lo switch di fine corsa
      while (digitalRead(PIN_SW_LIMIT_END) && working) {
        handleSwResetAndResolution();                 //Durante il while, controlla sempre se vengono premuti i tasti "Reset" o "Risoluzione"
        motore.move(100);
        motore.run();
      }

      motore.setCurrentPosition(0);
      //Raggiunta la posizione finale aspetta DELAY_WORK secondi
      delay(DELAY_WORK);

      //Torna in dietro fin quando non viene premuto lo switch di inizio corsa
      while (digitalRead(PIN_SW_LIMIT_START) && working) {
        handleSwResetAndResolution();                 //Durante il while, controlla sempre se vengono premuti i tasti "Reset" o "Risoluzione"
        motore.move(-100);
        motore.run();
      }

      motore.setCurrentPosition(0);

      //Al termine del lavoro, disabilita il driver e rimettiti in attesa
      working = false;
      setPinEnable(working);
    }

  }

}

//Funzione di comodo per accorpare la chiamata delle due funzioni contenute in essa
void handleSwResetAndResolution() {
  handleSwReset();
  handleSwResolution();
}

//Esegui reset della posizione del motore
void resetHomePosition() {
  homing = true;
  working = false;

  //abilitiamo il pin del driver e accendiamo il led di stato
  setPinEnable(true);

  //impostiamo velocita e accelerazione di reset
  motore.setMaxSpeed(SPEED_HOMING);
  motore.setSpeed(SPEED_HOMING);
  motore.setAcceleration(SPEED_HOMING * 3);

  //Durante la fase di reset facciamo girare il motore in full step
  setPinResolution(0, 0, 0);

  while (digitalRead(PIN_SW_LIMIT_END)) {
    motore.move(100);
    motore.run();
  }

  motore.setCurrentPosition(0);

  delay(DELAY_HOMING);

  while (digitalRead(PIN_SW_LIMIT_START)) {
    motore.move(-100);
    motore.run();
  }

  motore.setCurrentPosition(0);

  //Terminata la fase di reset, reimpostiamo la risoluzione del motore in base al valore di "selRisoluzione"
  setPinResolution(
    RISOLUZIONE[selRisoluzione][0],
    RISOLUZIONE[selRisoluzione][2],
    RISOLUZIONE[selRisoluzione][3]
  );

  //reimpostiamo velocita e accelerazione di lavoro
  motore.setMaxSpeed(SPEED_WORKING);
  motore.setSpeed(SPEED_WORKING);
  motore.setAcceleration(SPEED_WORKING * 3);

  //definisci stato delle variabili
  homing = false;
  working = false;

  //Disabilita il driver e spegni il led
  setPinEnable(false);
}

//Tieni d'occhio il bottone "Risoluzione"
void handleSwResolution() {

  btnSwResolution.update();

  /*
    Ad ogni pressione del tasto risoluzione, incrementa di uno il valore di "selRisoluzione" fin quando non raggiunge la lunghezza
    dell'array "RISOLUZIONE".
    In questo modo possiamo cambiare risoluzione in termini di step del motore.
  */
  if (btnSwResolution.fell()) {

    if (selRisoluzione < ((sizeof(RISOLUZIONE) / sizeof(RISOLUZIONE[0])) - 1)) {
      selRisoluzione++;
    } else {
      selRisoluzione = 0;
    }

    //Setta i valori contenuti nell'array "RISOLUZIONE" determinati dal numero presente in "selRisoluzione" sui pin MS1, MS2, MS3
    setPinResolution(
      RISOLUZIONE[selRisoluzione][0],
      RISOLUZIONE[selRisoluzione][2],
      RISOLUZIONE[selRisoluzione][3]
    );
  }

}

//Tieni d'occhio il bottone di "Reset"
void handleSwReset() {
  btnSwReset.update();
  if (btnSwReset.fell()) {
    delay(DELAY_HOMING);
    resetHomePosition();            //Richiama resetHomePosition() per resettare la posizione del motore.
  }
}

//Tieni d'occhio il bottone "Work"
void handleSwWork() {
  btnSwWork.update();
  if (btnSwWork.fell()) {
    working = !working;
    setPinEnable(working);
  }
}

//Imposta lo stato dei pin "PIN_DR_ENABLE" e "PIN_LED_STATO" in base al parametro "working";
void setPinEnable(boolean working) {
  digitalWrite(PIN_DR_ENABLE, !working);
  digitalWrite(PIN_LED_STATO, working);
}

//Imposta lo stato dei pin "PIN_MS_1", "PIN_MS_2" e "PIN_MS_3" in base ai parametri "MS1", "MS2" e "MS3"
void setPinResolution(int MS1, int MS2, int MS3) {
  digitalWrite(PIN_MS_1, MS1);
  digitalWrite(PIN_MS_2, MS2);
  digitalWrite(PIN_MS_3, MS3);
}

Il video

Come sempre

  • Importa le librerie necessarie;
  • Assicurati che tutti i collegamenti siano corretti;
  • Ricordati di impostare la porta COM del tuo Arduino;
  • Utilizza le tensioni corrette;
  • Ricorda che io non mi assumo nessuna responsabilità per eventuali danni o disastri che causi 😀

Spero che questo post ti sia stato utile! Per qualsiasi domanda o chiarimento, fai riferimento alla sezione commenti.
Sono ben accetti, birre, caffè e donazioni 😉

Trovi su Amazon





Approfondimenti