La disponibilità di motori stepper e driver economici oggi offre ampie possibilità di sperimentare al di fuori dei più costosi e complicati progetti di taglio e stampa 2D / 3D.
Per questo progetto, prenderò il dispositivo di scorrimento della telecamera di OpenBuilds (fare riferimento al video di costruzione nella Creazione di un dispositivo di scorrimento video di base con parti CNC open source) e motorizzarlo. Creerò anche un sistema autonomo per il controllo del motore.
Questo tutorial copre specificamente la messa a punto dell'hardware, ma principalmente la creazione di una GUI 16x2 LCD rudimentale utilizzando la libreria LiquidCrystal e un semplice sistema di menu per visualizzarlo, seguito dal funzionamento del driver stepper A4988 e da come controllarlo con Arduino.
Questo progetto è pesante su loop e passaggi, e mentre nel complesso il progetto è più intermedio, ho cercato di spiegarlo in modo tale che i principianti possano essere operativi abbastanza velocemente.
Coprirò aggiungendo il motore e le pulegge al cursore, avvolgendo la cintura intorno e fissandola tutta. È una semplice modifica.
Poi spiegherò come assemblare un kit Pololu A4988 Black Edition e come cablarlo su una breadboard insieme a tutte le altre schede esterne, oltre a un semplice involucro in compensato che ho messo insieme in pochi minuti per la mia alimentazione a 12V alimentazione (elencata sopra) al fine di evitare urti in quanto i terminali di cablaggio sono esposti.
Il menu consente l'inserimento di distanza da percorrere, tempo di viaggiare, numero di passaggi per viaggiare e senso di marcia. Alla fine di ogni fase, il cursore si interrompe mentre la fotocamera viene attivata.
L'attacco dell'attuatore a V-Slot di OpenBuilds presenta fori di dimensioni NEMA 17, quindi quattro viti a testa cilindrica M3 da 30 mm sono tutto ciò che è necessario per montare il motore su di esso.
Assicurarsi che la puleggia GT2 a 20 denti si trovi all'interno del supporto prima di inserire l'albero del motore, poiché il supporto non è sufficientemente largo da poterlo successivamente aggiungere. Una volta avvitato il motore sul fondo, stringere le viti di fermo con una di esse contro la parte piatta dell'albero motore, assicurandosi che i denti siano direttamente allineati al centro dell'intera unità di estrusione.
Il kit puleggia folle va insieme come un kit di ruote e fessure nell'estremità opposta Attuatore:
Alimentare la cinghia attraverso il centro della V-slot in linea con le pulegge, assicurandosi che i denti siano rivolti verso l'alto.
Quindi alimentalo e sopra le due pulegge e riportalo al centro della piastra di costruzione del carrello.
Qui si avvolge un lato attraverso la fessura e il morsetto della cintura, oppure lo si annoda, quindi lo si usa per stringere l'intera cintura attraverso l'intero sistema prima di collegare l'altro lato. Non troppo stretto perché il motore ruoti, ma non abbastanza largo da saltare i denti sulla puleggia motrice!
Il driver del motore passo-passo Pololu A4988 Black Edition (tecnicamente la scheda carrier A4988 - l'A4988 è il chip stesso) in genere viene fornito in kit, il che significa semplicemente che le intestazioni devono essere saldate. Poiché si tratta di un componente di alimentazione, anche se non guida l'unità alla massima capacità, è consigliabile aggiungere un dissipatore di calore per migliorare la sua durata.
Rompere la riga di intestazione a metà per avere due righe di otto. Inserire questi nei fori passanti placcati nella scheda, quindi inserirli con cura nella breadboard. Saldare i perni in posizione mentre la breadboard mantiene tutto bello e perpendicolare.
Una volta che questo è completo, tagliare l'angolo di un piccolo dissipatore di autoadesivo usando un seghetto o un foglio di carta (con attenzione, in un morsetto!) Per montarlo sull'IC A4988.
Ora tutto deve essere montato su breadboard in modo che possa essere collegato insieme in un circuito funzionante. Sto usando schede separate per ogni parte per motivi di chiarezza nelle immagini, ma sentitevi liberi di adattare tutto in una singola scheda se lo desiderate.
Lo schermo della tastiera LCD non può essere montato su una scheda, grazie alla strana scelta di Arduino di aderire a un difetto di progettazione piuttosto che soddisfare gli standard. Questo sarà tenuto separato, anche se avvitarlo su un pezzo di legno o qualcosa per proteggere gli spilli potrebbe non essere una cattiva idea.
Il circuito di scatto della telecamera, nella sua forma più semplice, è costituito da un resistore, un transistor e un sub-mini plug TRS da 2,5 mm. Ho aggiunto un LED che lampeggerà quando il puntale del grilletto è alto e un mini jack TRS da 3,5 mm per consentire flessibilità.
Se stai acquistando componenti per questa build, sarebbe una buona idea un socket da 3,5 mm progettato per tavole da 0,1 ", ma il mio proviene dal mucchio scavato, quindi ho saldato un connettore ad esso.
Metti tutto fuori, pronto per collegare tutto.
È ora di prendere tutti i cavi jumper. Avere abbastanza cose per mantenere i colori codificati renderà la vita più facile durante la risoluzione dei problemi. Fare riferimento allo schema circuitale in alto se la seguente descrizione ti confonde in qualsiasi momento.
Per prima cosa cablate il display LCD. Afferra 10 ponticelli femminili e collegali ai seguenti pin di schermatura: pin digitali 4-9, reset dei pin del bus di alimentazione (se si desidera utilizzare il pulsante di reset LCD), 5 V e uno dei GND.
Se hai jumper da femmina a femmina, puoi lasciarlo lì. Altrimenti, collega i ponticelli maschi - all'altra estremità delle femmine - per inserirli nelle corrispondenti prese di testa Arduino. Se disponi di uno schermo per tastiera LCD con sopra intestazioni femmina passanti installate sulla parte superiore, puoi saltare questo passaggio poiché lo scudo non blocca nulla.
Successivamente, la scheda Pololu A4988. Ciò richiede otto ponticelli su un lato, ho usato il nero e il rosso per la logica / la potenza del motore all'estremità di easch e il rosso / verde / blu / giallo al centro quattro per abbinarli ai servi del motore del motore passo-passo.
Il pin di alimentazione logica va a 3.3V sull'arduino, poiché l'LCD in alto utilizza il pin 5V. I cavi di alimentazione del motore vanno al tuo alimentatore 12V. Dall'altra parte, vicino al chip A4988, sto usando il blu e l'arancione rispettivamente per STP e DIR in contrasto con i colori relativamente uniformi ovunque. Vanno ai pin 11 e 12 di Arduino, a meno che non si modifichi il codice. Quindi abbreviare RST e SLP insieme per mantenere attiva la scheda; Ho usato il cavo bianco qui.
Finalmente cablare il circuito dell'interruttore del grilletto della fotocamera. Qui i fili neri sono messi a terra - il filo della riga A ad Arduino, il filo della fila C allo zoccolo da 3,5 mm. Il giallo va al pin 13 di Arduino (quindi c'è un indicatore LED sulla scheda e all'interruttore!), E il filo rosso va sull'altro lato dello zoccolo da 3,5 mm (o cavo da 2,5 mm se stai andando quella rotta).
Inserire il motore passo-passo nei fili colorati secondo lo schema della scheda A4988 e la scheda tecnica dello stepper. Per me, era così:
Attenzione: ricorda che i fili che forniscono energia al motore probabilmente tireranno 1-2A alla tensione scelta, quindi assicurati che i cavi utilizzati siano classificati per questo. Il chip A4988 e la scheda intorno potrebbero surriscaldarsi! Il potenziometro integrato nella scheda fornisce la limitazione di corrente per proteggere sia l'A4988 che il motore, quindi assicurati di impostarlo in modo appropriato prima dell'uso utilizzando un multimetro.
Una volta assemblati i componenti, è possibile passare alla codifica. Scarica il file zip incluso in questo tutorial o controlla questo repository GitHub se preferisci. Descriverò come metterlo insieme in modo da poter capire il flusso generale del programma e come i moduli lavorano insieme.
L'unico elemento necessario per questo era la biblioteca di scrittura LCD LiquidCrystal.h
. Questo dà accesso a lcd.xxxx ()
funzioni. C'è un pow ()
nel programma, e ho trovato che include la libreria C ++ math.h
non è necessario in quanto alcune delle sue funzioni più utili sono incluse nell'ambiente Arduino di serie, incluso pow ()
.
#includereLiquidCrystal lcd (8, 9, 4, 5, 6, 7); // imposta i pin dell'output LCD // definiscono i pin del driver stepper const int stp = 11; // non può usare il pin 10 con l'SS LCD perché è il controllo della retroilluminazione. // se si abbassa, la retroilluminazione si spegne! const int dir = 12; // define trigger pin const int trig = 13; // BUTTONS // definisce i valori dei pulsanti const int btnUp = 0; const int btnDn = 1; const int btnL = 2; const int btnR = 3; const int btnSel = 4; const int btnNone = 5; // definisce le variabili di lettura dei pulsanti int btnVal = 5; int adcIn = 0;
Ho impostato i pin dell'uscita LCD, i pin di uscita del driver stepper e il pin di uscita del trigger della fotocamera. Una volta configurata l'interfaccia hardware vera e propria, ho aggiunto delle variabili per gli eventi dei pulsanti seguiti dalla funzione di lettura dei pulsanti, che ho adattato dal wiki DFRobot sul loro schermo LCD identico. Si noti che SainSmart non fornisce documentazione.
Questo è super veloce. Inizializza il display LCD e i relativi pin di uscita, seguito da una schermata di benvenuto di base, quindi accedi alla schermata iniziale: opzione 1 del menu con valori azzerati.
void setup () lcd.begin (16, 2); // inizializza LCD lib a schermo intero lcd.setCursor (0,0); // imposta la posizione del cursore pinMode (stp, OUTPUT); // inizializza pin stepper pinMode (dir, OUTPUT); pinMode (trig, OUTPUT); // inizializza il trigger pin digitaleWrite (trig, LOW); // assicura che il trigger sia disattivato lcd.print ("Welcome to"); // schermata di benvenuto lcd.setCursor (0,1); lcd.print ("SliderCam v0.2!"); ritardo (1000); lcd.clear (); lcd.print (menuItemsTop [0]); ritardo (100); lcd.setCursor (0,1); per (int i = 0; i < 4; i++) lcd.setCursor(i, 1); lcd.print(currentDistance[i]); lcd.setCursor(4,1); lcd.print("mm(max 1300)");
Il vantaggio qui in termini di codifica è che l'apparecchiatura non ha bisogno di fare nulla senza l'input dell'utente. Il che significa che la primissima cosa può essere semplicemente un eterno ciclo di sondaggi. Chiamando il readLcdButtons ()
la funzione continua fino a quando il suo valore cambia non influisce negativamente sulle prestazioni del programma e non è necessario preoccuparsi di lasciare i pin di interruzione disponibili.
void loop () do btnVal = readLcdButtons (); // legge continuamente i pulsanti ... while (btnVal == 5); // ... finché qualcosa non viene premuto
// declare button poll function int readLcdButtons () delay (90); // ritardo di rimbalzo, sintonizzato sperimentalmente. il ritardo va bene dato che il programma non dovrebbe fare nient'altro // a questo punto adcIn = analogRead (0); // legge il valore dal pin A0 / * valori di soglia confermati dalla sperimentazione con lo schizzo di calibrazione dei pulsanti restituendo i seguenti valori di lettura ADC: a destra: 0 su: 143 giù: 328 a sinistra: 504 seleziona: 741 * / if (adcIn> 1000) return btnNon ; se (adcIn < 50) return btnR; if (adcIn < 250) return btnUp; if (adcIn < 450) return btnDn; if (adcIn < 650) return btnL; if (adcIn < 850) return btnSel; return btnNone; //if it can't detect anything, return no button pressed
ReadLcdButtons ()
ha un ritardo di 90 ms per antirimbalzo i pulsanti. In realtà, questo non è un rimbalzo, in quanto non riprende la misurazione ADC dopo un certo periodo di tempo, ma esegue il polling dei pulsanti in modo non frequente per registrare raramente più di un clic.
Raggiunge la stessa cosa da una pratica visione UX. È più di un sondare i pulsanti ogni 90 ms anziché costantemente, ed è per questo che uso di ritardo()
generalmente non è considerato una buona pratica per gli scopi di debouncing, ma ha risolto il problema (solo le estremità dei menu erano accessibili).
Una volta che l'unità può reagire all'input, deve esserci un modo per visualizzare tali reazioni.
Dopo aver provato gli aggiornamenti al volo, ho determinato che un costante aggiornamento dello schermo come un vero sistema operativo era più facile da gestire nei miei tentativi di una struttura modulare aggiornabile. Fare ciò è semplice come svuotare lo schermo, quindi ricostruire in base ai parametri correnti conosciuti.
Sembra complicato, ma in pratica rende la vita molto più facile. Rimuove un gran numero di comandi LCD da qualsiasi altra parte del programma e crea una zona agnostica di tipo variabile che è minimamente influenzata dagli aggiornamenti del programma esterni ad esso.
L'attuale parte rinfrescante si è evoluta in quattro fasi distinte:
Ripristina parametri ...
// STAMPA NUOVI VALORI DI SCHERMO btnVal = btnNone; lcd.clear ();
... stampa la linea superiore ...
lcd.setCursor (0, 0); lcd.print (menuItemsTop [currentMenuItem]); // stampa la voce di menu di livello superiore
... stampa la linea di fondo, che spiegherò un po 'più tardi ...
lcd.setCursor (0,1); switch (currentMenuItem) case 0: for (int i = 0; i < 4; i++) lcd.setCursor(i, 1); lcd.print(currentDistance[i]); break; case 1: for (int i = 0; i < 6; i++) lcd.setCursor(i, 1); lcd.print(currentDuration[i]); break; case 2: for (int i = 0; i < 4; i++) lcd.setCursor(i, 1); lcd.print(currentSteps[i]); break; case 3: if (travelDir == 0) lcd.print("From Motor"); else lcd.print("To Motor"); break; case 4: lcd.print("Stop!"); break; //end switch
... e aggiungi comandi specifici per lo schermo sopra a quelli già stampati.
if (currentMenuItem == 0) lcd.setCursor (4,1); lcd.print ("mm (max 1300)"); // inserisce la corsa massima del carrello sul cursore usato if (currentMenuItem == 1) lcd.setCursor (6,1); lcd.print ( "s (3600 / hr)"); if (currentMenuLevel == 1) lcd.setCursor (currentCursorPos, 1); lcd.blink (); else lcd.noBlink ();
Naturalmente, quella sezione di refresh dello schermo esatto non scrive se stessa, e dobbiamo sapere il menu che sta scrivendo sullo schermo prima che possa essere completato. Le intestazioni principali sono semplici, poiché in realtà non cambiano in base all'input dell'utente. Ciò significa che può essere semplicemente una stringa di array, tecnicamente un array di puntatori char o una matrice di matrici:
// MENU GUI // definisce stringhe di voci di menu di primo livello per char di navigazione numerica * menuItemsTop [] = "Distanza 01>", "< 02 Duration >","< 03 Steps > ","< 04 Direction >","< 05 Go!"; int currentMenuLevel = 0; //top menu or submenu int currentMenuItem = 0; //x-axis position of menu selection int currentCursorPos = 0; //current lcd cursor position int currentDistance[4] = 0, 0, 0, 0; int currentDuration[6] = 0, 0, 0, 0, 0, 0; int currentSteps[4] = 0, 0, 0, 1;
Questo significa che questo menuItemsTop
la matrice può essere spostata semplicemente modificando il numero all'interno delle parentesi quadre al tempo di aggiornamento dello schermo. Il che accade, dato che tutto è a zero indice, per tracciare identicamente il numero intero currentMenuItem
.
Manipolazione currentMenuItem
sugli eventi pulsante ci consente una navigazione monodimensionale, quindi quando lo vedete menuItemsTop [currentMenuItem]
è ovviamente voce del menu corrente.
if (currentMenuLevel == 0) switch (btnVal) case btnL: if (currentMenuItem == 0) break; // non può andare a sinistra da qui altro currentMenuItem--; rompere; case btnR: if (currentMenuItem == 4) break; // non può andare direttamente da qui else currentMenuItem ++; rompere; case btnSel: currentMenuLevel ++; if (currentCursorPos> 3 && (currentMenuItem == 0 || currentMenuItem == 2)) currentCursorPos = 3; // non andare fuori dalla fine dei numeri per i numeri a 4 cifre se (currentCursorPos> 0 && (currentMenuItem> 2)) currentCursorPos = 0; // imposta il cursore lampeggiante a sinistra per le opzioni basate sul testo se (currentMenuItem == 4) motion = 1; controllo del movimento(); rompere; // fine del passaggio // fine del livello 0
Quindi puoi muoverti a destra e a sinistra, e andare in un menu, o nel caso di Partire! quindi il controllo del movimento è attivato. Questo è tutto ciò che è richiesto qui.
Il sistema dei sottomenu ha fatto un po 'di più, grazie alla sua complessità interna. Le prime tre voci, Distanza, Durata e passi, tecnicamente consistono in un sotto-sotto-menu, ognuno dei quali consente la navigazione del valore a più cifre così come ogni singolo carattere.
Questo è coperto rendendo ogni voce del sottomenu un sistema di intestazione commutato a sé stante. Anche se questo è stato un lungo cammino, è un metodo semplice e coerente per consentire tale navigazione di basso livello. Dal momento che ho appena capito il Distanza sottomenu e quindi copiato per gli altri sottomenu, ecco uno sguardo a quello.
else // cioè "else if currentMenuLevel = 1" if (currentMenuItem == 0) // 01 DISTANCE switch (btnVal) case btnUp: currentChar = currentDistance [currentCursorPos]; adjustDigit (currentChar, 1); currentDistance [currentCursorPos] = currentChar; rompere; case btnDn: currentChar = currentDistance [currentCursorPos]; adjustDigit (currentChar, 0); currentDistance [currentCursorPos] = currentChar; rompere; case btnL: if (currentCursorPos == 0) break; // non può andare a sinistra da qui altro currentCursorPos--; rompere; case btnR: if (currentCursorPos == 3) break; // non può andare a sinistra da qui altro currentCursorPos ++; rompere; case btnSel: parseArrayDistance (); currentMenuLevel--; // end switch // end DISTANCE
La sinistra e la destra sono essenzialmente le stesse del menu di livello superiore, semplicemente spostandosi avanti e indietro lungo il numero nello stesso modo, avendo il numero in realtà è un insieme di cifre in un array int e la posizione corrente memorizzata in un int chiamato currentCursorPos
che consente di lampeggiare come mostrato nel modulo di aggiornamento dello schermo sopra.
La stampa di questi array lungo la riga inferiore del display LCD indica i cicli for nella sezione di aggiornamento dello schermo; io
a partire dal 0 a 3, Colonna LCD
a partire dal 0 a 3, currentDistance []
a partire dal 0 a 3.
int adjustDigit (int x, int dir) // digit adjust function if (dir == 0 && x> 0) x--; // sottrai dalla cifra su btnDn se (dir == 1 && x < 9) x++; // add to digit on btnUp lcd.setCursor(currentCursorPos, 1); lcd.print(x); currentChar = x; return currentChar; //return new digit
Aumentando e diminuendo il numero si ottiene memorizzando la cifra corrente nella variabile currentChar
, che viene poi passato al adjustDigit ()
funzione insieme a un valore booleano che indica la direzione; per aumentare o diminuire il motore.
Questo semplicemente regola la cifra in base al valore booleano e salva il risultato, in cui il flusso ritorna al loop principale, dove il valore currentChar viene salvato nella posizione corretta dell'originale currentDistance []
array e la nuova cifra regolata viene stampata all'aggiornamento dello schermo.
Quando Select viene colpito da uno dei sottomenu dell'array numerico, attiva la relativa funzione di analisi, in questo caso parseArrayDistance ()
. È necessario analizzare la matrice, utilizzata per visualizzare e modificare comodamente, in un numero intero utile per i calcoli del movimento effettivo. Ho scelto di farlo ora piuttosto che su Partire! per far sentire UX scattante.
int adjustDigit (int x, int dir) // digit adjust function if (dir == 0 && x> 0) x--; // sottrai dalla cifra su btnDn se (dir == 1 && x < 9) x++; // add to digit on btnUp lcd.setCursor(currentCursorPos, 1); lcd.print(x); currentChar = x; return currentChar; //return new digit
Ho scoperto questa funzione dall'unico commento di passaggio che ho trovato dopo aver estenuato Google alla ricerca di funzioni standard da array a int, arrivando vuoto, e liberandomi del pasticcio delle funzioni da matrice a codice a int che erano una soluzione inefficace. Sembra abbastanza breve e leggero dato che è letteralmente basato sul fondamento della matematica decimale, ma se conosci un metodo migliore, sono tutto orecchie.
Tutti i valori sono impostati e tu colpisci Partire! Cosa succede dopo? È necessario calcolare esattamente cosa devono fare i numeri dati per eseguire la mozione finale. Questa parte è funzionale, ma un lavoro in corso; Sento che ci devono essere più opzioni per diversi tipi di movimento.
int motionControl () totalMotorSteps = currentDistanceInt * 5; // calcola i passi totali (0,2 mm = ingranaggio da 20 denti su cinghia da 2 mm, 40 mm per giro, 200 passi per giro, ergo 1/5 mm per passo) pulseDelay = (1000L * (currentDurationInt - (currentStepsInt * shutterDuration)))) / totalMotorSteps; // la durata della pausa in ms tra gli impulsi STP e il driver del motore intervalDistance = totalMotorSteps / currentStepsInt;
Quello che sta succedendo in questa funzione è abbastanza chiaro dai commenti, penso. Ho messo un shutterDuration
di 2 secondi nel software, principalmente per mantenere i test abbastanza rapidi. Se stai scattando di notte, a valori ISO più bassi, potrebbe essere necessario un tempo maggiore di 25-35 secondi, a seconda della velocità esatta dell'otturatore.
Il pulseDelay
è moltiplicato per 1000
alla fine per convertire da secondi a millisecondi, ovviamente. Il L
convertire la costante int in una lunga è più che mi sto comportando in modo errato di quanto non sia strettamente necessario. Poiché si tratta di uno schizzo relativamente piccolo, non sono eccessivamente preoccupato per l'utilizzo della memoria variabile.
Questi calcoli presuppongono che il ciclo stesso richieda un tempo trascurabile per essere eseguito rispetto al pulseDelay
tempo, che, una volta estratto il pulsante di polling, sembra essere vero.
// una volta per esecuzione generale se (travelDir == 0) digitalWrite (dir, LOW); else if (travelDir == 1) digitalWrite (dir, HIGH); //Serial.begin(9600); //Serial.println(pulseDelay); // step loop do digitalWrite (stp, HIGH); // ritardo del ritardo del motore antincendio (pulseDelay); digitalWrite (stp, LOW); // reset driver // btnVal = readLcdButtons (); // controlla che non ci siano interruzioni: questo richiede troppo tempo e rallenta notevolmente il motore; usa reset per stop! currentStep ++; // alla fine di ogni passaggio if (currentStep% intervalDistance == 0) // se il numero corrente di passi del motore è divisibile per il numero di passi motore in un passo della telecamera, spara la camera digitalWrite (trig, HIGH); // attiva il tempo di scatto della fotocamera (80); digitalWrite (trig, LOW); // resetta il ritardo del grilletto ((shutterDuration * 1000) -80); // il ritardo deve essere modificato in timer in modo che il pulsante di arresto possa essere interrogato mentre (currentStep < totalMotorSteps); //end motion control
Infine, nota il currentSteps
valore impostato a 1. Non ho creato una funzione di controllo degli errori per questo, ma il semplice buon senso dice dimensione del passo
diventa infinito se currentStepsInt == 0
, quindi è meglio tenerlo in uno se si desidera un movimento continuo. Ho già aggiunto una voce di miglioramento per questo.
Per qualcosa in esecuzione sul codice scritto più o meno da zero in due giorni e risolto da altri due, funziona come un sogno! La prova è nel budino, però. In realtà ottiene filmato timelapse utile, e l'unità di controllo funziona davvero bene sul campo?
Nei miei test, la risposta sembra essere un sì sincero. Di seguito è riportato un timelapse di due ore, di 650 frame, il primo test. Il dispositivo di scorrimento ha anche completato un test di 9 ore su 720 frame in modo impeccabile, ma sfortunatamente la batteria della fotocamera non ha funzionato molto bene dopo 2 ore in ... che non ho trovato fino al voto di 8,5 ore, naturalmente.
Se imposto il tempo e i passaggi in modo appropriato, il movimento può essere continuo per movimenti lenti del carrello nel video di azione dal vivo, anche se le estremità a scatti devono essere modificate o accelerate.
Il suono potrebbe essere un problema a meno che il tuo stepper non sia molto silenzioso, ma per aggiungere valore di produzione alle registrazioni automatiche, è un'opzione.
Come con qualsiasi cosa, ci sono possibili miglioramenti da apportare. Ho elencato questi in cima al .io no
file, anche se ammettatamente senza particolare attenzione per la fattibilità, né ordinato da alcun tipo di importanza.
Alcuni di questi ho pensato di risolvere prima di rilasciare questo tutorial con v0.2, ma sento che essi stessi sono un'esperienza di apprendimento da considerare in termini di smantellamento mentale dell'usabilità di un programma.
MIGLIORAMENTI E CONSIDERAZIONI VERSO V1.0: 1) Efficienza del codice di risposta del pulsante di sottomenu per le prime tre intestazioni di menu 2) Uso del tempo di posa lampadina come opzione di menu extra, passato a shutterDuration int 3) durata dell'otturatore dovrebbe essere pausa temporizzata, non ritardo ( ) - impossibile eseguire il polling del pulsante stop! 4) Utilizzare le funzioni della libreria EEPROM per risparmiare quantità, può quindi semplificare la sezione di controllo del movimento e utilizzare Ripristina come "stop" 5) Rimuovere il commutatore dal sottomenu "Vai", sostituirlo con una logica più appropriata 6) Sarebbe meglio fare i passi della telecamera piuttosto che il viaggio totale? "durata" è più di 15 secondi o 2 minuti di 30 minuti o 4 ore? 7) Qualche intro che sarebbe meglio come #define o inte meglio di booleano? Difficilmente si sta andando contro i limiti dello spazio SRAM a 8kB. 8) Interpolazione / attenuazione per le curve di accelerazione, in particolare per l'uso del video 9) Controllo degli errori per la dimensione del passo zero, o semplicemente aggiungere uno a intervalDistance se il valore è zero prima dei calcoli- l'altra estremità di Distance è ancora 1 step 10) Sub-16ms in ritardo () s essere meglio di delayMicroseconds ()? Quanto interrompono i tempi di interruzione? 11) Uso del sonno su A4988 per ridurre il consumo energetico sul campo? 12) Controllo degli errori per currentDurationInt <= currentStepsInt*shutterDuration, allowing no time for movement or even negative pulseDelay! */
Questi sono solo i miglioramenti che ho pensato fino ad ora, nel tentativo di guidare la base di codice da una v0.2 rudimentale ma funzionale verso una release v1.0 più ottimizzata e più capace. Potresti notare di più. Sentiti libero di lasciarli nei commenti qui sotto o su GitHub.
Se hai seguito dall'inizio alla fine, inclusa la porzione Tutzioni fotografiche + della build, ora sei l'orgoglioso proprietario di uno slider motorizzato di alta qualità in grado di produrre filmati timelapse e movimenti di carrello sottili. Se stai usando il codice per qualche altro progetto, mi piacerebbe vederlo.
In questo tutorial, ho esaminato diverse forme di controllo del flusso basato su loop, creando una GUI rudimentale e aggiornando un LCD in base all'input dell'utente. Ho anche guardato simultaneamente il controllo di più dispositivi meccanici esterni tramite schede breakout.
Avete visto il flusso e la facilità della programmazione del codice modulare