Startracker con arduino

Ciao a tutti ragazzi,
sto realizzando un piccolo programma per controllare con arduino uno startracker.
Devo far eseguire ad un motore stepper una rotazione al giorno.

Dati:

  • Motore Nema17 1.8°/step (360°/1.8°/step = 200step)
  • Driver Microstep 32 (drv8825)
  • Riduttore epicicloidale 100:1
  • Durata di un giorno (23h 56' 4") 86'164'000 ms

Calcoli:

  • Numero di microstep per eseguire 1 rotazione dell'albero in uscita dal riduttore:
    200step/giro * 32 microstep * 100 di riduzione = 640000 microstep/giro
  • Numero di secondi per ogni step:
    86'164'000 / 640000 = 134,6 ms/step arrotondato a 135 ms/step

Ora il mio dubbio è su quale sia il miglior modo per controllare il motore stepper con quel delay per i microstep? è troppo piccolo come intervallo tra ogni microstep?
al momento questo è il mio sketch:

#define stepPin 9
#define dirPin 8
#define enablePin 10
void setup()
{
 pinMode(enablePin, OUTPUT);
 digitalWrite(enablePin, HIGH);
 pinMode(stepPin, OUTPUT);
 pinMode(dirPin, OUTPUT);
}
void loop()
{
     int i;
     digitalWrite(enablePin, LOW);
     delayMicroseconds(2);
     digitalWrite(dirPin, HIGH);
     for(i=0; i<=1000000; i++)
     {
         digitalWrite(stepPin, LOW);
         delayMicroseconds(2);
         digitalWrite(stepPin, HIGH);
         delay(135);
     }
}

Come alimentazione per arduino pensavo a 6v (4+4 batterie duracell in parallelo per avere un maggior durata) oppure ad un power bank per cellulare quindi 5v (ne ho uno da 15'000mAh) mentre per il motore pensavo a 12v (8+8 batterie duracell in parallelo).
Visto che ho pochissimi step al secondo posso permettermi di avere anche un basso voltaggio per il motore?magari un altro power bank, quindi sempre 5v?
Se come intervallo è fattibile, si riuscirebbe secondo voi a realizzarlo con arduino micro o nano?
Altro dubbio, avendo bisogno di un intevallo così piccolo e preciso devo considerare anche un errore dovuto alla lettura dello schetch di arduino?
Per esempio che arduino per leggere ed eseguire le righe del loop for impiega un tempo n e quindi nel deley devo indicare delay(135-n).

Ho allegato anche un'immagine di come dovrebbe venire una volta montato il motore al supporto ed i PDF da cui sono partito per il cablaggio e lo sketch.
Posso anche alimentare una ventola da 5cm a 5v usanto i 5v di arduino? Andrebbe fissata ai 4 fori che potete vedere nel pezzo rosso nell'immagine.

Grazie in anticipo per il vostro aiuto. :grinning:

Edit:
Ho aggiunto anche il pdf con le specifiche del motore.
Grazie Guglielmo, scusa per il mio errore :smiley:

PololuPres_V3.pdf (738 KB)

StepperTester.pdf (17 KB)

17HS19-1684S-PG100.pdf (143 KB)

>lakko: ti ricordo che in conformità al regolamento, punto 7, devi editare il tuo post (quindi NON scrivendo un nuovo post, ma utilizzando il bottone More -> Modify che si trova in basso a destra del tuo post) e racchiudere il codice all'interno dei tag CODE (... sono quelli che in edit inserisce il bottone con icona fatta così: </>, tutto a sinistra).

Grazie, :slight_smile:

Guglielmo

lakko:
avendo bisogno di un intevallo così piccolo e preciso devo considerare anche un errore dovuto alla lettura dello schetch di arduino?

Assolutamente si:

  1. la lettura di micros è affetta da un jitter di qualche µs (ma non dovrebbe essere rilevante nel lungo periodo)
  2. i tempi riportati da micros (o millis) sono invece affetti da un'intrinseca imprecisione dell'oscillatore (e dall'eventule uso/disabilitazione degli interrupt), per cui si parla di parecchi minuti di errore al giorno.
  3. anche se i punti sopra non ci fossero, sarebbe impossibile calcolare la durata del codice in modo da realizzare tempi di esecuzione perfettamente deterministici (cosa che si potrebbe invece fare, anche se con fatica, in assembly).

Vedo due soluzioni: usare un ATMEGA328 standalone con oscillatore quarzato (o una board con oscillatore al quarzo invece che risuonatore ceramico), oppure generare un segnale campione con periodo 134.6ms tramite apposito hardware esterno (magari termostatato). Un oscillatore 1MHz preciso, seguito da un divisore per 134600 dovrebbe bastare.

Ciao e grazie per la risposta,
per quanto riguarda le alimentazioni cosa ne pensate?

lakko:
Come alimentazione per arduino pensavo a 6v (4+4 batterie duracell in parallelo per avere un maggior durata) oppure ad un power bank per cellulare quindi 5v (ne ho uno da 15'000mAh) mentre per il motore pensavo a 12v (8+8 batterie duracell in parallelo).

Grazie in anticipo

Claudio_FF:
Assolutamente si:

  1. la lettura di micros è affetta da un jitter di qualche µs (ma non dovrebbe essere rilevante nel lungo periodo)
  2. i tempi riportati da micros (o millis) sono affetti da un'intrinseca imprecisione dell'oscillatore (e dall'eventule uso/disabilitazione degli interrupt), per cui si parla di parecchi minuti di errore al giorno.
  3. anche se i punti sopra non ci fossero, sarebbe impossibile calcolare la durata del codice in modo da realizzare tempi di esecuzione perfettamente deterministici (cosa che si potrebbe invece fare in assembly).

Vedo due soluzioni: usare un ATMEGA328 standalone con oscillatore quarzato (o una board con oscillatore al quarzo invece che risuonatore ceramico), oppure generare un segnale campione con periodo 135µs tramite apposito hardware esterno (magari termostatato). Un oscillatore 1MHz preciso, seguito da un divisore per 135 dovrebbe bastare.

Forse viene a costare meno uno shield Ethernet e prendere l'ora da Internet ogni 30 minuti, in mezzo vai di millis, tanto l'errore Non sarà cumulativo

Forse viene a costare meno uno shield Ethernet e prendere l'ora da Internet ogni 30 minuti, in mezzo vai di millis, tanto l'errore Non sarà cumulativo

costa ancora meno un rtc ds3231 e non hai bisogno di internet.. fai la correzione dell'errore forse di 2 secondi al mese scollegando solo l'rtc e collegandolo ad un'altra board per settarlo... :smiley:

Ma lui deve contare 134.6ms, non regolare l'ora, se il conteggio avviene con micros() l'errore si accumula comunque.

Vorrei provare a vedere (anche se con l'errore) come viene una foto e per quanto tempo posso tenere aperto l'otturatore prima che compaiano le strisce delle stelle nelle foto.
mi rimane il discorso dell'alimentazione per lo stepper. può funzionare con due battery pack AA da 8 pile ciascuno in parallelo?

Grazie :grinning:

Claudio_FF:
Ma lui deve contare 134.6ms, non regolare l'ora, se il conteggio avviene con micros() l'errore si accumula comunque.

ma se ogni n minuti ricalcolo il puntamento l'errore si accumula solo tra un puntamento e l'altro

può funzionare con due battery pack AA da 8 pile ciascuno in parallelo?

Si, 12V sono il minimo per quell'accoppiata , Stepper + DRV8825. Solo che non ti durano più di un paio d'ore
Forse con una batteria al piombo, ricaricabile, è meglio

Antoniosanantonio:
ma se ogni n minuti ricalcolo il puntamento l'errore si accumula solo tra un puntamento e l'altro

Con l'ora da internet, ma anche da un rtc, non ci fai nulla per il semplice motivo che è impossibile sincronizzare il conteggio interno con quello esterno in modo preciso come serve per questa applicazione.
La soluzione è usare un timer, preferibilmente a 16 bit, in free run, così da non essere influenzato da eventuali eventi bloccanti, e usare questo come clock per ottenere il desiderato step rate in modo preciso e stabile, l'errore dovuto al risonatore può essere facilmente misurato e compensato, oppure si usa una board Arduino compatibile dotata di quarzo.

Ho fatto un paio di conti veloci, settando il prescaler del timer 1 a CLK/64 e imponendo il conteggio a 33650 si ottengono 134.6 ms esatti come richiesto, basta collegare il pin di uscita, OCA o OCB a scelta, all'ingresso STEP del driver ed ecco ottenuto il movimento con la desiderata temporizzazione.

Soluzione super semplice, senza "impazzire" con i registri dei Timer, basta usare la libreria TimerOne e impostare il periodo a 134600 us per ottenere il risultato desiderato.

Codice minimo:

#include <TimerOne.h>
#define M1 9

void setup() 
{
  pinMode(13, OUTPUT);    
  
  Timer1.initialize(134600);  // setta il Timer 1 per ciclo a 134600 us 
  Timer1.pwm(M1, 128);     // attiva il pin 9 per il pwm, setta duty al 12.5%
  digitalWrite(13, HIGH);     // accende il led 
 }

void loop()
{
 
}

Dato che avevo qualche dubbio se la libreria TimerOne è in grado di gestire temporizzazioni di questo tipo ecco la verifica strumentale, impulsi con periodo di 134.6 ms.

astrobeed:
Soluzione super semplice, senza "impazzire" con i registri dei Timer, basta usare la libreria TimerOne e impostare il periodo a 134600 us per ottenere il risultato desiderato.

Codice minimo:

#include <TimerOne.h>

#define M1 9

void setup()
{
 pinMode(13, OUTPUT);    
 
 Timer1.initialize(134600);  // setta il Timer 1 per ciclo a 134600 us
 Timer1.pwm(M1, 128);     // attiva il pin 9 per il pwm, setta duty al 12.5%
 digitalWrite(13, HIGH);     // accende il led
}

void loop()
{

}




Dato che avevo qualche dubbio se la libreria TimerOne è in grado di gestire temporizzazioni di questo tipo ecco la verifica strumentale, impulsi con periodo di 134.6 ms.

![pulse.jpg|800x480](upload://7L1sEzyNgYbbSd5taANvpX9SJna.jpeg)

Intanto ti ringrazio per il tempo che hai dedicato a trovare una soluzione :smiley:
Se ho capito bene ogni 134.6ms al pin9 c'è un impulso giusto?

Esatto, hai un impulso sul pin 9 ogni 134.6 ms, puoi modificare la sua durata agendo sul valore del dutycycle, 1023 = 100 %, 0 = 0%.
Puoi fermare la generazione degli impulsi con "timer1.disablePwm(M1)", la fai ripartire con "Timer1.pwm(M1, 128)".

Ma quindi il primo puntamento lo fai a mano?

astrobeed:
Esatto, hai un impulso sul pin 9 ogni 134.6 ms, puoi modificare la sua durata agendo sul valore del dutycycle, 1023 = 100 %, 0 = 0%.
Puoi fermare la generazione degli impulsi con "timer1.disablePwm(M1)", la fai ripartire con "Timer1.pwm(M1, 128)".

Perciò se ho capito il codice diventa così giusto?

#include <TimerOne.h>
#define M1 9
#define dirPin 8
#define enablePin 10
void setup()
{
 Timer1.initialize(134600);

 //pinMode(stepPin, OUTPUT);
 pinMode(enablePin, OUTPUT);
 digitalWrite(enablePin, HIGH);
 pinMode(dirPin, OUTPUT);
}
void loop()
{
     //int i;
     digitalWrite(enablePin, LOW);
     delayMicroseconds(2);
     digitalWrite(dirPin, HIGH);
     //for(i=0; i<=1000000; i++)
     //{
     //    digitalWrite(stepPin, LOW);
     //    delayMicroseconds(2);
     //   digitalWrite(stepPin, HIGH);
     //      delay(135);
     //}
     Timer1.pwm(M1, 128)
}

prendere l'ora da Internet ogni 30 minuti

io ho risposto pensando a questa affermazione...

astrobeed:
Soluzione super semplice, senza "impazzire" con i registri dei Timer, basta usare la libreria TimerOne e impostare il periodo a 134600 us per ottenere il risultato desiderato

Non ho modo di fare una verifica strumentale su una frequenza così bassa, ma secondo me anche usare il "normale" micros() nel modo corretto dovrebbe dare lo stesso risultato (solo con un leggero jitter di qualche µs sui fronti, più che altro sarei curioso di vedere il confronto):

void setup(){
    pinMode(13, OUTPUT);
}

uint32_t t = micros();

void loop(){
    if(micros() - t >= 134600){
        t += 134600;
        digitalWrite(13, 1);
        delay(67);
        digitalWrite(13, 0);
    }
}

Claudio_FF:
Non ho modo di fare una verifica strumentale su una frequenza così bassa, ma secondo me anche usare il "normale" micros() nel modo corretto dovrebbe dare lo stesso risultato

micros() ha un jitter tipico di compreso tra 0 e 4 us, purtroppo è un errore incrementale, nel tempo aumenta e dopo pochi minuti 'errore nel movimento, dello stepper, diventa più che sensibile, ovvero strisce sulle stelle da fotografare.
Tieni presente che anche la micros() soffre dello stesso problema della millis(), che a sua volta si basa sulla micros(), periodicamente viene fermata dalle altre routine che girano in background su Arduino, da non scordare mai che sotto il cofano dello sketch c'è molto di più rispetto alle nostre righe di codice.
Il solo modo per ottenere temporizzazione precise è usare direttamente i timer, millis e micros vanno bene per temporizzazioni senza pretese di grande precisione, sopratutto senza pretese di ripetibilità dei risultati.