Rörelsekontroll med Arduino Motoriserar en kamerabildare

Tillgången till billiga stegmotorer och förare ger dessa dagar gott om möjligheter att experimentera utanför de dyrare och komplicerade 2D / 3D-skärnings- och tryckprojekten. 

För det här projektet tar jag OpenBuilds-kamerareglaget (se byggbilden i Bygg en grundläggande videoreglage med Open Source CNC-delar) och motorisera den. Jag ska också skapa ett fristående system för styrning av motorn.

Denna handledning täcker specifikt att sätta ihop hårdvaran, men bygger främst en rudimentär 16x2 LCD-GUI med LiquidCrystal-biblioteket och ett enkelt menysystem för att det ska visas, följt av A4988-stegdrivarens funktion och hur man styr det med Arduino. 

Projektet är tungt i loopar och steg, och överallt är projektet mer mellanliggande. Jag har försökt att förklara det på ett sådant sätt att nybörjare kan komma igång relativt snabbt.

Utrustningslista

Komponenter

  • Arduino Uno
  • LCD knappsatsskärm eller separat 16x2 LCD och knappar om du vet hur man gör det
  • Pololu A4988 [Black Edition] Stepper Driver
  • Små aluminium självhäftande kylfläns
  • Brödbrädor, manliga och kvinnliga trummor, etc.
  • 220-330 ohm motstånd (1 / 4W kommer förmodligen göra), standard NPN transistor (jag använde en BC109)
  • 3,5 mm stereo TRS-uttag
  • 3,5 mm till 2,5 mm stereo TRS-adapterkabel
  • 3,5 mm förlängningskabel som behövs för glidlängden
  • 9V fatuttag om du vill ta bort Arduino från datorns USB-ström
  • 12V 2A strömförsörjning för att köra stegmotorn
  • NEMA 17 stegmotor

Delar

  • GT2 5mm brett, 2mm-kuggrem: Dubbel gliderlängd plus en fot för säkerhet (11 fot för mig)
  • Smidig tomgångsskiva
  • Bältesspänningsfjäder om du har svårt att bibehålla bältesspänningen över en lång sikt
  • 2x Beltkrympklämma (kan ersättas med små dragkedjor)
  • GT2 7mm bred, 20-tandig aluminiumskiva med samma borrstorlek som motoraxel
  • 4x 30mm M3-0.5 cap-head maskinskruvar

Verktyg

  • Dator med Arduino IDE (jag använder Win7, Arduino 1.0.5 r2)
  • Lödstryk med liten mejselspets, lödd, etc.
  • 2,5 mm insexnyckel för M5-skruvar
  • 2 mm insexnyckel för M3-skruvar
  • 1,5 mm insexnyckel för skruvar i GT2 remskiva
  • multimeter för felsökning och aktuell justering
  • Smala tänger för åtdragning i små utrymmen

Funktionsöversikt

Jag täcker med att lägga till motor och remskivor till reglaget, stränga bältet runt och fästa det hela. Det är en enkel ändring. 

Sedan täcker jag hur man sätter ihop en Pololu A4988 Black Edition-sats och hur man spänner upp den på ett brödbräda tillsammans med alla andra externa brädor, liksom en enkel plywoodhölje, jag slog ihop om några minuter för min 12V-effekt matning (listad ovan) för att förhindra stötar då ledningsklämmorna utsätts.

Sidledes så det är tillräckligt stort för att stiftnumren fortfarande är synliga!

Menyn tillåter ingång av avstånd att resa, dags att resa, Antal steg att resa in och färdriktning. I slutet av varje steg pausar reglaget medan kameran utlöses.

Ändra reglaget

Steg 1: Motormontering

OpenBuilds V-Slot Actuator End Mount har NEMA 17-dimensionshål i den, så fyra skruvhuvud 30 mm M3 är alla som krävs för att montera motorn på den.

OpenBuilds V-Slot Aktuator End Mount

Se till att 20-tums GT2-remskivan är inuti fästet innan du sätter in motoraxeln, eftersom fästet inte är tillräckligt stort för att lägga på det efteråt. När motorn är skruvad i botten, dra åt skruvarna med en av dem mot den platta delen av motoraxeln, så att tänderna är direkt i linje med centrum av hela extruderingsenheten.

Steg 2: Idler Remskiva

Idler remskivan går ihop precis som ett hjulkit och slitsar i motsatt ände Actuator Mount:

Rullarskivan

Steg 3: Belting Up

Mata bältet genom centrum av V-spåret i linje med remskivorna, se till att tänderna är vända uppåt. 

Sedan mata upp det och över de två remskivorna och sätt tillbaka det i mitten till Dolly-byggplattan.

Tänderna griper ihop varandra.

Här sitter du ena sidan genom bältesspalten och klämmer fast, eller slipsar den, använd sedan den för att dra åt hela bältet genom hela systemet innan du ansluter den andra sidan. Inte för tätt för att motorn ska vrida, men inte tillräckligt för att hoppa över tänderna på drivhjulet!

Montering av elektroniken

Steg 1: Montera Stepper Driver

Pololu A4988 Black Edition Stepper Motor Driver (tekniskt A4988 bärplatta - A4988 är själva chipet) kommer typiskt i kitform, vilket helt enkelt innebär att huvudet måste lödas på. Eftersom det här är en kraftkomponent, trots att den inte kör enheten med maximal kapacitet, är det en bra idé att lägga till en kylfläns för att förbättra dess livslängd.
Bryt huvudraden i halva för att ha två rader av åtta. Slå in dem i de pläterade genom hålen i brädet, och släck det försiktigt in i brödbrädet. Löd stiften på plats medan brödbrädet håller allt gott och vinkelrätt.

Att sätta ihop Stepper Driver

När det här är klart, skära av hörnet på en liten självhäftande kylfläns med hjälp av en hacksåg eller rullsåg (försiktigt, i en klämma!) För att montera till A4988 IC.

Skär av hörnet av en liten självhäftande kylfläns

Steg 2: Breadboard-Montera komponenterna

Nu måste allting monteras på brödbrädor så att det kan anslutas till en fungerande cicuit. Jag använder separata brädor för varje del för tydlighetens skull i bilderna, men gärna passa allt i ett enda kort om du önskar.
LCD-knappsatsskärmen kan inte monteras på ett bräda tack vare Arduinos märkliga val att följa en designfel i stället för att uppfylla standarder. Detta kommer att hållas åtskilt, men skrapa det till en bit av trä eller något för att skydda stiften är kanske inte en dålig idé.

Kamerautlösningskretsen är på sitt mest enkla sätt består av ett motstånd, en transistor och en 2,5 mm TRS sub-mini-kontakt. Jag har lagt till en LED som blinkar när utlösningsstiftspikarna är höga och en 3,5 mm TRS mini-jack för att möjliggöra flexibilitet. 

Om du köper komponenter för den här byggnaden, skulle en 3,5 mm uttag avsedd för 0,1 "hällbrädor vara en bra idé, men min är från den avtagna högen så jag har lödd en kontakt till den istället.
Lägg ut allt, redo att dra upp det hela.

Steg 3: Ansluta allt tillsammans

Tiden att ta tag i alla jumperkablarna. Att ha nog att hålla saker färgkodade kommer att göra livet enklare vid felsökning. Se kretsschemat överst om följande beskrivning förvirrar dig när som helst.

Ansluta allt tillsammans

Tryck först på LCD-skärmen. Ta 10 kvinnliga hoppare och anslut dem till följande sköldstift: digitala stiften 4-9, nätspärrarna återställs (om du vill använda LCD-återställningsknappen), 5V och en av GNDs. 

Om du har man-till-man-hoppare kan du lämna den där. I annat fall ska man ansluta hanehoppare till andra ände av kvinnorna - för att ansluta dem till motsvarande Arduino-huvuduttag. Om du har en LCD-knappsatsskärm som har passande kvinnliga rubriker installerade ovanpå, kan du hoppa över det här steget eftersom din skärm inte blockerar någonting.
Därefter Pololu A4988 styrelsen. Detta behöver åtta hoppare på ena sidan, jag har använt svart och rött för logik / motorkraft vid smala änden och röd / grön / blå / gul i mitten fyra för att matcha stegmotorns servoledningar. 

Den logiska kraftpinnen går till 3,3 V på arduino, eftersom LCD-skärmen ovan använder 5V-stiftet. Motorns strömledare går till din 12V nätaggregat. På andra sidan, nära A4988-chipet, använder jag blå och orange för STP och DIR för att kontrastera med de relativt likformiga färgerna överallt. De går till Arduino-tapparna 11 respektive 12, såvida du inte ändrar koden. Sedan kort RST och SLP tillsammans för att hålla styrelsen aktiverad; Jag har använt den vita ledningen här.

När du är klar ska det se ut så här.

Sluta slutligen ledningsströmbrytarkretsen. Här är de svarta trådarna jordade - A-ledningstråden till Arduino, C-ledningen till 3,5 mm-uttaget. Den gula går till Arduino pin 13 (så det finns en LED-indikator på brädet och på strömbrytaren!), Och den röda ledningen går till andra sidan 3,5 mm uttaget (eller 2,5 mm stickkontakt om du går den vägen).
Sätt i stegmotorn i de färgade trådarna enligt A4988 styrdiagram och din stegers datablad. För mig var det så här:

I likhet med knappläsaren ingår testrotationsskissen i dragkedjan upptill.

Varning: kom ihåg att ledningarna som ger ström till motorn troligen kommer att dra 1-2A vid din valda spänning, så se till att de använda ledningarna är klassade för det. A4988-spånet och styrelsen runt det kan bli varmt! Potentiometern inbyggd i brädet ger nuvarande begränsning för att skydda både A4988 och motorn, så se till att du ställer in den på lämpligt sätt innan du använder en multimeter.

Ställa in programmet

När komponenterna har monterats kan du flytta till kodningen. Ladda ner zip som ingår i den här handledningen, eller kolla på detta GitHub respository om du föredrar. Jag beskriver hur jag sammanfattar det så att du kan förstå det allmänna programflödet och hur modulerna fungerar tillsammans.

Steg 1: Inkluderar och grundläggande definitioner

Det enda som var nödvändigt för detta var LCD-skrivbiblioteket LiquidCrystal.h. Detta ger tillgång till lcd.xxxx () funktioner. Det finns en pow () i programmet, och jag hittade det inklusive C ++-biblioteket math.h är inte nödvändigt eftersom några av dess mest användbara funktioner ingår i Arduino-miljön, inklusive pow ().


#inkludera  LiquidCrystal lcd (8, 9, 4, 5, 6, 7); // Set LCD-utgångsstift // Definiera Stepper-drivstiften Const Int Stp = 11; // kan inte använda pin 10 med SS LCD-skärmen eftersom det är bakgrundsbelysningen. // om den går låg, släcker bakgrundsbelysningen! const int dir = 12; // definiera triggerstift const int trig = 13; // KNAPPAR // definiera knappvärden const int btnUp = 0; const int btnDn = 1; const int btnL = 2; const int btnR = 3; const int btnSel = 4; const int btnNone = 5; // definiera knappläsningsvariabler int btnVal = 5; int adcIn = 0;

Jag ställer in LCD-utgångsstiften, stiftdrivrutinsutgångsstiften och kameratriggerutgångsstiftet. När det faktiska hårddiskgränssnittet har ställts in, lade jag till variabler för knapphändelser följt av knappläsningsfunktionen, som jag anpassade från DFRobot-wikien på deras identiska LCD-knappsatsskärm. Notera att SainSmart ger ingen dokumentation.

Steg 2: Setup () Loop

Detta är super straightfoward. Initialisera LCD-skärmen och relevanta utmatningsstiften, följt av en grundläggande välkomstskärm, och släpp sedan till startskärmen: menyalternativ 1 med värden nollställda.

tomrumsinställning () lcd.begin (16, 2); // initialisera LCD lib helskärm lcd.setCursor (0,0); // ställa markörposition pinMode (stp, OUTPUT); // initiera stegstiften pinMode (dir, OUTPUT); pinMode (trig, OUTPUT); // initiera utlösningsstift digitalWrite (trig, LOW); // Se till att utlösaren är avstängd lcd.print ("Welcome to"); // välkomstskärm lcd.setCursor (0,1); lcd.print ("SliderCam v0.2!"); fördröjning (1000); lcd.clear (); lcd.print (menuItemsTop [0]); fördröjning (100); lcd.setCursor (0,1); för (int i = 0; i < 4; i++)  lcd.setCursor(i, 1); lcd.print(currentDistance[i]);  lcd.setCursor(4,1); lcd.print("mm(max 1300)"); 

Steg 3: Övervakningsknappar

Fördelen här kodande är att utrustningen inte behöver göra någonting alls utan användarinmatning. Det betyder att det allra första kan helt enkelt vara en evig knappundersökningsling. Ringer till readLcdButtons () funktionen om och om tills dess värdeförändringar inte påverkar programets prestanda negativt, och du behöver inte oroa dig för att du lämnar avbrytsnålar.

tomrumsling () do btnVal = readLcdButtons (); // kontinuerligt läsa knapparna ... medan (btnVal == 5); // ... tills någonting trycks in
// förklara knappundersökningsfunktion int readLcdButtons () delay (90); // debounce delay, inställd experimentellt. fördröjning är bra eftersom programmet inte skulle göra något annat // på denna punkt ändå adcIn = analogRead (0); // läsvärde från tröskelvärdena A0 / * tröskelvärden bekräftas av experiment med knappkalibreringsketch som returnerar följande ADC-läsvärden: höger: 0 upp: 143 ner: 328 kvar: 504 välj: 741 * / om (adcIn> 1000) returnera btnNone ; om (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 () har en fördröjning på 90ms för att debounce knapparna. I själva verket är det inte en debounce, eftersom det inte tar upp ADC-mätningen efter en viss tid, men pollar knapparna knappast sällan för att sällan registrera mer än ett enda klick. 

Den uppnår samma sak från en praktisk UX-vy. Det är mer av en poll knappar varje 90ms snarare än ständigt, vilket är anledningen till användningen av fördröjning() anses i allmänhet inte som god praxis för avkallande ändamål, men det fixade problemet (endast var och en av menyerna var tillgängliga).

Steg 4: Skärmuppdatering

När enheten kan reagera på inmatning måste det finnas ett sätt att visa de här reaktionerna. 

Efter att ha försökt uppdateringar, fann jag att konsekvent skärmuppdatering som ett riktigt operativsystem var lättare att hantera i mina försök med en moduluppgraderbar struktur. Att göra detta är lika enkelt som att rensa skärmen och sedan bygga om baserat på kända nuvarande parametrar.
Detta låter komplicerat, men i praktiken gör livet mycket enklare. Det tar bort ett stort antal LCD-kommandon från någon annanstans i programmet och skapar en agnostisk zon med varierande typ som påverkas minimalt av programuppdateringar som är externa till det.
Den faktiska uppfriskande delen utvecklades till att bestå av fyra separata steg:
Återställ parametrar ...

// PRINT NYA SKÄRVÄRDER btnVal = btnNone; lcd.clear ();

... skriv ut topplinjen ...

lcd.setCursor (0, 0); lcd.print (menuItemsTop [currentMenuItem]); // Skriv ut toppnivå menyalternativ

... skriva ut bottenlinjen, som jag kommer att förklara lite senare ...

 lcd.setCursor (0,1); switch (currentMenuItem) fall 0: för (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

... och lägg till skärmspecifika kommandon överst på det som redan är skrivet ut.

om (currentMenuItem == 0) lcd.setCursor (4,1); lcd.print ("mm (max 1300)"); // Lägg in max vagnsresa på skjutreglaget som används om (currentMenuItem == 1) lcd.setCursor (6,1); lcd.print ( "s (3600 / h)");  om (currentMenuLevel == 1) lcd.setCursor (currentCursorPos, 1); lcd.blink ();  annat lcd.noBlink ();

Bygga en meny: Huvudrubriker

Naturligtvis skriver inte den exakta skärmuppdateringssektionen sig själv, och vi behöver veta vilken meny den skriver till skärmen innan den kan slutföras. Huvudrubrikerna är enkla, eftersom de inte ändras, beroende på användarinmatning. Det betyder att det helt enkelt kan vara en strängmatris - tekniskt en char pointer array eller en array av arrayer:

// MENU GUI // definiera menyn för strängar på toppnivå för numerisk navigering char * menuItemsTop [] = "01 Avstånd>", "< 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;

Detta innebär att detta menuItemsTop array kan navigeras helt enkelt genom att ändra numret inuti torget parentes vid skärmuppdateringstid. Det som bara händer, eftersom allt är nollindexerat, att spåra identiskt med heltalet currentMenuItem

manipulera currentMenuItem på knapphändelser tillåter oss endimensionell navigering, så när du ser menuItemsTop [currentMenuItem] det är självklart aktuell menyrubrik.

om (currentMenuLevel == 0) switch (btnVal) fall btnL: om (currentMenuItem == 0) brytas; // kan inte gå vänster från här annars currentMenuItem--; ha sönder;  fall btnR: om (currentMenuItem == 4) brytning; // kan inte gå direkt från här annars currentMenuItem ++; ha sönder;  fall btnSel: currentMenuLevel ++; om (currentCursorPos> 3 && (currentMenuItem == 0 || currentMenuItem == 2)) currentCursorPos = 3; // gå inte i slutet av siffrorna för de fyrsiffriga numren om (currentCursorPos> 0 && (currentMenuItem> 2)) currentCursorPos = 0; // Ange blinkande markör till vänster för textbaserade alternativ om (currentMenuItem == 4) motion = 1; rörelsekontroll(); ha sönder;  // slutet på strömbrytaren // slutet av nivå 0

Så du kan flytta åt vänster och höger och gå in i en meny eller i fallet med Gå! då är rörelsekontrollen aktiverad. Vilket är allt som krävs här.

Bygga en meny: Undermeny

Undermenyn systemet tog lite mer, tack vare sin interna komplexitet. De tre första inmatningarna, Distans, Varaktighet och Steg, Tekniskt består av en underundermeny, var och en tillåter navigering av det flersiffriga värdet såväl som varje enskilt tecken.

Detta är täckt genom att varje undermenyuppgift gör ett byt utgående system i sig. Medan det var långt ifrån, är det en enkel och konsekvent metod att tillåta sådan lågnivånavigering. Sedan jag verkligen bara räknat ut Distans undermenyn och sedan kopierade den över till de andra undermenyerna, här är en titt på den där.

annars // dvs "annat om currentMenuLevel = 1" om (currentMenuItem == 0) // 01 DISTANCE switch (btnVal) fall btnUp: currentChar = currentDistance [currentCursorPos]; adjustDigit (currentChar, 1); currentDistance [currentCursorPos] = currentChar; ha sönder;  fall btnDn: currentChar = currentDistance [currentCursorPos]; adjustDigit (currentChar, 0); currentDistance [currentCursorPos] = currentChar; ha sönder;  fall btnL: om (currentCursorPos == 0) brytning; // kan inte gå vänster från här annars currentCursorPos--; ha sönder;  fall btnR: if (currentCursorPos == 3) brytning; // kan inte gå vänster från här annars currentCursorPos ++; ha sönder;  fall btnSel: parseArrayDistance (); currentMenuLevel--;  // slutbrytare // slut DISTANCE

Vänster och höger är i stort sett samma som den översta menyn, helt enkelt rör sig fram och tillbaka längs numret på samma sätt, genom att ha siffra faktiskt vara en uppsättning siffror i en int array och den aktuella platsen lagrad i en int kallad currentCursorPos vilket möjliggör blinkning som ses i skärmuppdateringsmodulen ovan. 

Att skriva ut dessa arrays längs den nedre LCD-raden är vad för looparna var för i skärmuppdateringsavsnittet; jag från 0 till 3, LCD-kolonn från 0 till 3, currentDistance [] från 0 till 3.

int adjustDigit (int x, int dir) // sifferjustera funktion om (dir == 0 && x> 0) x--; // subtrahera från siffra på btnDn om (dir == 1 && x < 9) x++; // add to digit on btnUp lcd.setCursor(currentCursorPos, 1); lcd.print(x); currentChar = x; return currentChar; //return new digit 

Ökning och minskning av numret uppnås genom lagring av nuvarande siffra i variabeln currentChar, som sedan skickas till adjustDigit () funktion tillsammans med en booleskt värde indikeringsriktning; för att öka eller minska motorn. 

Detta justerar enkelt siffran enligt det booleska värdet och sparar resultatet, där flödet återgår till huvudslingan, där det aktuella värdet är sparat tillbaka till originalets korrekta läge currentDistance [] array och den nya justerade siffran skrivs ut på skärmuppdatering.

Parsing Display Array Values

När Select väljs från en av numreringsgruppsundermenyerna, utlöser den den relevanta parsningsfunktionen - i det här fallet parseArrayDistance (). Du måste analysera arrayen som används för att enkelt visa och redigera, till ett heltal som är användbart för aktuella rörelsesberäkningar. Jag valde att göra detta nu snarare än på Gå! för att hålla UX känslan snuskig.

int adjustDigit (int x, int dir) // sifferjustera funktion om (dir == 0 && x> 0) x--; // subtrahera från siffra på btnDn om (dir == 1 && x < 9) x++; // add to digit on btnUp lcd.setCursor(currentCursorPos, 1); lcd.print(x); currentChar = x; return currentChar; //return new digit 

Jag kom fram med den här funktionen från den användbara kommentaren som jag hittade efter att ha uttömt Google och letade efter standard array-to-int-funktioner, kom upp i tomt och blev av med roten på array-to-char-to-int-funktioner som var en ineffektiv lösning. Det verkar ganska kort och ljust med tanke på att det är bokstavligen baserat på grunden för decimalmatematik, men om du känner till en bättre metod, har jag alla öron.

Motion Control och Camera Utlösning

Alla värden är inställda och du träffar Gå! Vad händer sen? Du måste beräkna exakt vad de angivna siffrorna ska göra för att kunna köra den slutliga rörelsen. Denna del är funktionell, men ett pågående arbete; Jag känner att det måste finnas fler alternativ för olika typer av rörelse.

int motionControl () totalMotorSteps = currentDistanceInt * 5; // beräkna totala steg (0.2mm = 20-tand växel på 2mm stigbälte, 40mm per varv, 200 steg per varv, 1 / 5th mm per steg) pulseDelay = (1000L * (currentDurationInt - (currentStepsInt * shutterDuration))) / totalMotorSteps; // Hur lång tid pausar i ms mellan STP-pulserna till motordrivrutinen intervalDistance = totalMotorSteps / currentStepsInt;

Vad som händer i denna funktion är ganska tydligt ur kommentaren, tror jag. Jag har lagt en shutterDuration av 2 sekunder i programvaran, främst för att fortsätta testa ganska snabbt. Om du fotograferar på natten, vid lägre ISO-värden, kan det här vara mer som 25-35 sekunder beroende på din exakta slutartid.

De pulseDelay multipliceras med 1000 i slutet för att konvertera från sekunder till millisekunder, förstås. De L Att omvandla konstant int till en lång är mer att jag erar på sidan av försiktighet än vad som verkligen är strängt nödvändigt. Eftersom det är en relativt liten skiss, är jag inte alltför bekymrad över variabel minnesanvändning.

Dessa beräkningar förutsätter att slingan i sig kräver en försumbar tid att springa i jämförelse med pulseDelay tid, som, när jag tagit ut knappvalet, verkar vara sant.

// en gång per övergripande körning om (travelDir == 0) digitalWrite (dir, LOW); annars om (travelDir == 1) digitalWrite (dir, HIGH); //Serial.begin(9600); //Serial.println(pulseDelay); // steg loop gör digitalWrite (stp, HIGH); // fördröjningsfördröjning för brandmotorföraren (pulseDelay); digitalWrite (stp, LOW); // Återställ drivrutin // btnVal = readLcdButtons (); // kontrollera att det inte finns något stopp - det tar för lång tid och minskar avsevärt motorn, använd återställ för stopp! currentStep ++; // vid slutet av varje steg om (currentStep% intervalDistance == 0) // om det aktuella antalet motorsteg är delbart med antalet motorsteg i ett kamerasteg, skjut kamera digitalWrite (trig, HIGH); // utlösningsfördröjning för kamerautlösare (80); digitalWrite (trig, LOW); // Återställ utlösningsstiftfördröjning ((shutterDuration * 1000) -80); // Fördröjning behöver ändras till timer så att stoppknappen kan pollas medan (currentStep < totalMotorSteps);  //end motion control

Slutligen notera currentSteps värde satt till 1. Jag har inte skapat en felkontrollfunktion för detta, men enkel sunt förnuft säger steglängd blir oändlig om currentStepsInt == 0, så det är bäst att hålla det vid en om kontinuerlig rörelse är önskvärd. Jag har redan lagt till en förbättringspost för detta.

Körning av slutprodukten

För något som körs på kod skrivs mer eller mindre från början på två dagar och bugfixed över två mer fungerar det som en dröm! Beviset är dock i pudding. Får det faktiskt värdefullt timelapsfotografi, och fungerar kontrollenheten verkligen bra i fältet?

I mina test verkar svaret vara en helhjärtad ja. Nedan är en två timmars 650-timelapse-tid, det allra första testet. Slidaren utförde även ett 9-timmars 720-ramtest utan problem, men kamera batteriet gjorde det inte så bra efter 2 timmar ... vilket jag inte hittade fram till 8,5 timmars markering, naturligt.

Om jag ställer in tiden och stegen på lämpligt sätt kan rörelsen vara kontinuerlig för slow dolly-rörelser i live-actionvideo, men de ryckiga ändarna behöver redigeras eller hastighetsrampning. 

Ljud kan vara ett problem om inte din stepper är väldigt tyst, men för att lägga till produktionsvärde för självinspelningar är det ett alternativ.

förbättringar

Liksom med någonting kan det göras möjliga förbättringar. Jag har listat dessa på toppen av .ino fil, men visserligen utan särskild vård över genomförbarhet eller beställd av någon typ av betydelse. 

Några av dessa jag ansåg fixa innan jag släppte denna handledning med v0.2, men jag känner mig som om de i sig är en lärande erfarenhet att titta på när det gäller att mentalt avveckla användbarheten av ett program.

 Förbättringar och överväganden mot V1.0: 1) Effektivitet av undermenyknappens svarkod för de första tre menyrubrikerna 2) Användning av glödlampans slutartid som extra menyalternativ, skickad till slutareVärdighet int 3) Slutartid bör vara tidsinställd, inte fördröjning ) - kan inte stoppa stoppknappen! 4) Använd EEPROM-biblioteksfunktionerna för att spara kvantiteter, vilket kan förenkla rörelsekontrollsektionen och använd Återställ som "stopp" 5) Ta bort strömbrytaren från "Go" -menyn, ersätt med mer lämplig logikuppsättning 6) Skulle det vara bättre att klocka på kameran snarare än total resa? Varaktighet är mer som 15 sek eller 2 min än 30 min eller 4 timmar? 7) Vilka är det som är bättre som #definer eller ints bättre som booleska? Knappt springande mot gränserna för SRAM-utrymme vid 8 kB, dock. 8) Tweening / easing för accelerationskurvor, speciellt för videoanvändning 9) Felkontroll för nollstorlek eller helt enkelt lägg till en till intervalDistance om värdet är noll före beräkningar-annat än avståndet är fortfarande 1 steg 10) Skulle sub-16ms fördröjning () s vara bättre som fördröjningMicroseconds ()? Hur mycket avbryter koll på timing? 11) Användning av sömn på A4988 för att minska strömförbrukningen i fältet? 12) Felkontroll för currentDurationInt <= currentStepsInt*shutterDuration, allowing no time for movement or even negative pulseDelay! */

Det här är bara de förbättringar jag hittills har tänkt på, i ett försök att styra kodbasen från en rudimentär men funktionell v0.2 mot en mer optimerad och mer kapabel release v1.0. Du kanske märker mer. Gärna lämna dem i kommentarerna nedan eller på GitHub.

Avslutar

Om du har följt från början till slut, inklusive Photography Tuts + delen av byggnaden, är du nu den stolta ägaren till en högkvalitativ motoriserad kamerareglage som kan producera timelapsfotografi och subtila Dolly-dragningar. Om du använder koden för något annat projekt, skulle jag gärna se den.

I denna handledning har jag tittat på olika former av loopbaserad flödesstyrning, skapar en rudimentär GUI och uppdaterar en LCD-skärm baserat på användarinmatning. Jag tittade också på att samtidigt styra flera externa mekaniska enheter via brytbrädor. 

Du har sett flödet och användarvänligheten för programmering av modulär kod, samt att se idéer för hur du förbättrar kod som är funktionell men inte optimerad, både från UX och processorns effektivitetsstandard. Dessa verktyg bör tjäna dig bra för en rad olika kommunikations- och interaktionsbaserade projekt i framtiden.

Vänligen lämna några frågor eller kommentera kommentarerna nedan!