Kontrol Gerakan Dengan Arduino: Mengarahkan Slider Kamera
() translation by (you can also view the original English article)
Ketersediaan motor dan driver stepper murah dewasa ini memungkinkan banyak kesempatan untuk bereksperimen di luar proyek pemotongan dan pencetakan 2D / 3D yang lebih mahal dan rumit.
Untuk proyek ini, saya akan mengambil slider kamera OpenBuilds (lihat video dalam Membangun Slider Video Dasar Dengan Open Source CNC Parts) dan menguncinya. Saya juga akan membuat sistem mandiri untuk mengendalikan motor.
Tutorial ini secara khusus mencakup pembuatan perangkat keras, tetapi terutama membangun GUI LCD 16x2 yang belum sempurna menggunakan pustaka LiquidCrystal dan sistem menu sederhana untuk ditampilkan, diikuti oleh cara kerja driver A4988 dan cara mengendalikannya dengan Arduino.
Proyek ini berat di perulangan dan langkah-langkah, dan sementara keseluruhan proyek lebih menengah, saya sudah mencoba menjelaskannya sedemikian rupa sehingga pemula dapat berdiri dan berjalan relatif cepat.
Daftar perlengkapan
Komponen
- Arduino Uno
- LCD Keypad Shield atau LCD 16x2 terpisah dan tombol jika anda tahu cara membuatnya bekerja
- Pololu A4988 [Black Edition] Stepper Driver
- Aluminium self-stick heat sink kecil
- Breadboards, male dan female kabel jumper , dll
- 220-330 ohm resistor (1/4W mungkin akan melakukan), transistor NPN standar (saya menggunakan BC109)
- 3.5mm stereo TRS socket
- Kabel adaptor TRS 3,5 mm hingga 2,5 mm
- Kabel perpanjangan 3,5 mm yang diperlukan untuk panjang slider
- Catu daya jack 9V barrel jika anda ingin menjauhkan Arduino dari daya USB komputer
- 12V 2A power supply untuk menjalankan motor stepper
- NEMA 17 stepper motor
Part
- GT2 5mm-lebar, sabuk timing 2mm-pitch: menggandakan panjang slider plus kaki untuk keamanan (11 kaki untuk saya)
- Kit katrol idler yang mulus
- Belt-tension pegas torsi jika anda mengalami kesulitan mempertahankan ketegangan sabuk dalam jangka panjang
- 2x Belt crimp clamp (bisa diganti dengan zipties kecil)
- GT2 7mm-lebar, pulley aluminium 20-gigi yang sama ukuran sebagai poros motor
- 4x 30mm M3-0.5 Sekrup mesin cap-kepala
Alat-alat
- Komputer dengan Arduino IDE (saya menggunakan Win7, Arduino 1.0.5 r2)
- Besi solder dengan ujung pahat kecil, solder, dll
- 2.5mm kunci allen untuk sekrup M5
- 2mm kunci allen untuk sekrup M3
- 1.5mm kunci allen untuk mengatur sekrup di pulley GT2
- multimeter untuk troubleshooting dan penyesuaian saat ini
- Tang sempit untuk mengencangkan di ruang kecil
Ikhtisar Fungsional
Saya akan membahas menambahkan motor dan pulley ke slider, merangkai sabuk di sekitar dan mengencangkan semuanya. Ini adalah modifikasi yang sederhana.
Kemudian saya akan membahas cara menyusun kit Pololu A4988 Black Edition dan cara memasangnya di papan breadboard bersama dengan semua papan eksternal lainnya, serta plywood enclosure sederhana yang saya ketuk bersama dalam beberapa menit untuk daya 12V saya persediaan (tercantum di atas) untuk mencegah guncangan ketika terminal kabel terkena.



Menu memungkinkan input jarak untuk bepergian, waktu perjalanan, jumlah langkah untuk bepergian dan arah perjalanan. Di akhir setiap langkah, slider berhenti sementara kamera dipicu.
Memodifikasi Slider
Langkah 1: Merakit Motor
OpenBuilds V-Slot Actuator End Mount memiliki lubang NEMA 17 dimensi di dalamnya, sehingga empat sekrup kepala topi M3 30mm semuanya diperlukan untuk memasang motor ke sana.



Pastikan puli 20 gigi GT2 ada di dalam dudukan sebelum anda memasukkan poros motor, karena tunggangannya tidak cukup lebar untuk ditambahkan setelahnya. Setelah motor dikencangkan pada bagian bawah, kencangkan sekrup yang disetel dengan salah satunya terhadap bagian datar poros motor, memastikan bahwa gigi langsung sejajar dengan pusat dari seluruh unit ekstrusi.
Langkah 2: Kit Idler Pulley
Kit idler pulley bekerja sama seperti kit roda, dan slot ke Actuator Mount ujung yang berlawanan:



Langkah 3: Belting Up
Porsi sabuk melalui pusat slot V sejajar dengan pulleys, memastikan gigi menghadap ke atas
Kemudian beri porsi di atas dan di atas dua katrol dan bawa kembali ke tengah ke pelat bangun pengangkut.



Di sini anda membungkus satu sisi melalui slot sabuk dan klem, atau mengikatnya dengan zip, kemudian menggunakannya untuk mengencangkan seluruh sabuk melalui seluruh sistem sebelum menghubungkan sisi yang lain. Tidak terlalu ketat bagi motor untuk berbelok, tetapi tidak cukup longgar untuk melewati gigi pada puli drive!
Merakit Elektronik
Langkah 1: Merakit Driver Stepper
Pololu A4988 Black Edition Stepper Driver Motor (secara teknis A4988 carrier board-A4988 adalah chip itu sendiri) biasanya datang dalam bentuk kit, yang berarti bahwa header harus disolder. Karena ini adalah komponen daya, meskipun tidak menggerakkan unit pada kapasitas maksimumnya, ada baiknya untuk menambahkan heatsink untuk membantu meningkatkan masa pakainya.
Pecahkan baris header menjadi dua untuk memiliki dua baris delapan. Masukkan ini ke dalam lubang berlubang di papan, dan kemudian masukkan ini dengan hati-hati ke papan breadboard. Solder pin di tempatnya sementara papan breadboard memegang segala yang bagus dan tegak lurus.



Setelah ini selesai, potong sudut heatsink self-stick kecil menggunakan gergaji besi atau scrollsaw (hati-hati, di jepitan!) Untuk me-mount ke IC A4988.



Langkah 2: Breadboard-Mount Komponen
Sekarang semuanya harus di-mount ke breadboards sehingga dapat dihubungkan bersama menjadi cicuit yang berfungsi. Saya menggunakan papan terpisah untuk setiap bagian demi kejelasan gambar, tetapi merasa bebas untuk memasukkan semuanya ke dalam satu papan jika anda mau.
Perisai papan tombol LCD tidak dapat dipasang ke papan, berkat pilihan aneh Arduino untuk mematuhi cacat desain daripada memenuhi standar. Ini akan disimpan terpisah, meskipun mengacaukannya ke sepotong kayu atau sesuatu untuk melindungi pin mungkin bukan ide yang buruk.



Rangkaian pemicu kamera pada yang paling sederhana terdiri dari resistor, transistor dan sub-mini plug 2.5mm TRS. Saya telah menambahkan LED yang akan berkedip ketika pin pemicu berduri tinggi, dan jack mini TRS 3,5mm untuk memungkinkan fleksibilitas.
Jika anda membeli komponen untuk membangun ini, soket 3,5 mm yang dirancang untuk papan pitch 0,1 "akan menjadi ide yang bagus, tetapi saya adalah dari tumpukan yang mengumpulkan jadi saya sudah menyolder konektor ke itu sebagai gantinya.
Letakkan semuanya, siap untuk mentransfer semuanya.
Langkah 3: Pengkabelan Semuanya Bersama
Saatnya untuk mengambil semua kabel jumper. Memiliki cukup untuk menjaga kode warna akan membuat hidup lebih mudah ketika mengatasi masalah. Lihat diagram sirkuit di bagian atas jika uraian berikut membingungkan anda di titik mana pun.



Pertama pasang LCD. Ambil 10 jumper female dan hubungkan ke pin perisai berikut: pin digital 4-9, pin ulang daya bus (jika anda ingin menggunakan tombol reset LCD), 5V & salah satu GND.
Jika anda memiliki jumper female-ke-male, anda dapat meninggalkannya di sana. Jika tidak, hubungkan jumper male- ke ujung yang lain dari female- untuk menghubungkan mereka ke soket header Arduino yang sesuai. Jika anda memiliki perisai papan tombol LCD yang telah melewati header perempuan yang dipasang di atas, anda dapat melewati langkah ini karena perisai anda tidak memblokir apa pun.
Selanjutnya, papan Pololu A4988. Ini membutuhkan delapan jumper di satu sisi, saya telah menggunakan hitam dan merah untuk logika / kekuatan motor di ujung easch, dan merah / hijau / biru / kuning di tengah empat untuk menyesuaikan dengan lead servo motor stepper.
Pin daya logika masuk ke 3.3V pada arduino, karena LCD di atas menggunakan pin 5V. Daya motor mengarah ke catu daya 12V Anda. Di sisi lain, dekat chip A4988, saya menggunakan biru dan oranye untuk STP dan DIR masing-masing untuk kontras dengan warna yang relatif seragam di tempat lain. Mereka pergi ke Arduino pin 11 dan 12 masing-masing, kecuali anda memodifikasi kode. Kemudian RST dan SLP pendek bersama untuk menjaga agar papan tetap aktif; Saya telah menggunakan memimpin putih di sini.



Akhirnya memasang rangkaian sakelar pemicu kamera. Di sini kabel-kabel hitam itu digaris bawahi - kabel baris A ke Arduino, kabel C baris ke soket 3,5 mm. Warnanya kuning ke Arduino pin 13 (jadi ada indikator LED di papan juga di saklar!), Dan kabel merah ke sisi lain soket 3,5mm (atau konektor 2.5mm jika Anda akan rute itu).
Pasang motor stepper ke dalam kabel berwarna sesuai dengan diagram papan A4988 dan datasheet stepper anda. Bagi saya, itu seperti ini:



Perhatian: ingat bahwa kabel yang menyediakan daya ke motor mungkin akan menarik 1-2A pada tegangan yang anda pilih, jadi pastikan kabel yang digunakan diberi nilai untuk itu. Chip A4988 dan papan di sekitarnya mungkin menjadi panas! Potensiometer yang terpasang di papan memberikan batasan saat ini untuk melindungi A4988 dan motor, jadi pastikan anda mengaturnya dengan tepat sebelum digunakan menggunakan multimeter.
Menyiapkan Program
Setelah komponen dirakit, Anda dapat pindah ke pengkodean. Unduh zip yang disertakan dengan tutorial ini, atau periksa respositori GitHub ini jika anda mau. Saya akan menjelaskan bagaimana saya menggabungkannya sehingga anda dapat memahami alur program umum dan bagaimana modul bekerja bersama.
Langkah 1: Termasuk dan Definisi Dasar
Satu-satunya termasuk yang diperlukan untuk ini adalah library menulis LCD LiquidCrystal.h.
Ini memberikan akses ke fungsi lcd.xxxx ()
. Ada pow ()
dalam program, dan saya menemukan bahwa termasuk matematika C ++ library.math.h
tidak diperlukan karena beberapa fungsi yang paling bermanfaat termasuk dalam lingkungan Arduino, termasuk pow ().
1 |
#include <LiquidCrystal.h> |
2 |
|
3 |
LiquidCrystal lcd(8, 9, 4, 5, 6, 7); //set LCD output pins |
4 |
|
5 |
//define stepper driver pins
|
6 |
const int stp = 11; //can't use pin 10 with the SS LCD as it's the backlight control. |
7 |
//if it goes low, backlight turns off!
|
8 |
const int dir = 12; |
9 |
|
10 |
//define trigger pin
|
11 |
const int trig = 13; |
12 |
|
13 |
//BUTTONS
|
14 |
//define button values
|
15 |
const int btnUp = 0; |
16 |
const int btnDn = 1; |
17 |
const int btnL = 2; |
18 |
const int btnR = 3; |
19 |
const int btnSel = 4; |
20 |
const int btnNone = 5; |
21 |
|
22 |
//define button-reading variables
|
23 |
int btnVal = 5; |
24 |
int adcIn = 0; |
Saya mengatur pin output LCD, pin output driver stepper, dan pin output pemicu kamera. Setelah antarmuka perangkat keras yang sebenarnya diatur, saya menambahkan variabel untuk acara tombol diikuti oleh fungsi membaca tombol, yang saya adaptasikan dari wiki DFRobot pada pelindung keypad LCD yang identik. Perhatikan bahwa SainSmart tidak menyediakan dokumentasi.
Langkah 2: Setup() Loop
Ini super straightfoward. Inisialasikan LCD dan pin output yang relevan, diikuti oleh layar selamat datang dasar, kemudian turun ke layar awal: opsi menu 1 dengan nilai-nilai memusatkan perhatian.
1 |
void setup() { |
2 |
lcd.begin(16, 2); // initialise LCD lib full-screen |
3 |
lcd.setCursor(0,0); // set cursor position |
4 |
|
5 |
pinMode(stp, OUTPUT); //initialise stepper pins |
6 |
pinMode(dir, OUTPUT); |
7 |
|
8 |
pinMode(trig, OUTPUT); //initialise trigger pin |
9 |
digitalWrite(trig, LOW); //ensure trigger is turned off |
10 |
|
11 |
lcd.print("Welcome to"); //welcome screen |
12 |
lcd.setCursor(0,1); |
13 |
lcd.print("SliderCam v0.2!"); |
14 |
delay(1000); |
15 |
lcd.clear(); |
16 |
lcd.print(menuItemsTop[0]); |
17 |
delay(100); |
18 |
lcd.setCursor(0,1); |
19 |
for (int i = 0; i < 4; i++) { |
20 |
lcd.setCursor(i, 1); |
21 |
lcd.print(currentDistance[i]); |
22 |
}
|
23 |
lcd.setCursor(4,1); |
24 |
lcd.print("mm(max 1300)"); |
25 |
}
|
Langkah 3: Monitor Tombol
Keuntungan di sini coding-bijaksana adalah bahwa peralatan tidak perlu melakukan apa-apa tanpa masukan pengguna. Yang berarti bahwa hal pertama hanya bisa menjadi lingkaran polling tombol abadi. Memanggil fungsi readLcdButtons ()
berulang-ulang sampai perubahan nilainya tidak berdampak negatif terhadap kinerja program, dan Anda tidak perlu khawatir meninggalkan pin interupsi yang tersedia.
1 |
void loop() { |
2 |
do { |
3 |
btnVal = readLcdButtons(); //continually read the buttons... |
4 |
}
|
5 |
while (btnVal==5); //...until something is pressed |
1 |
//declare button poll function
|
2 |
int readLcdButtons() { |
3 |
delay(90); //debounce delay, tuned experimentally. delay is fine as program shouldn't be doing anything else |
4 |
//at this point anyway
|
5 |
adcIn = analogRead(0); //read value from pin A0 |
6 |
|
7 |
/*threshold values confirmed by experimentation with button calibration sketch returning the following ADC read values:
|
8 |
right: 0
|
9 |
up: 143
|
10 |
down: 328
|
11 |
left: 504
|
12 |
select: 741
|
13 |
*/
|
14 |
|
15 |
if (adcIn > 1000) return btnNone; |
16 |
if (adcIn < 50) return btnR; |
17 |
if (adcIn < 250) return btnUp; |
18 |
if (adcIn < 450) return btnDn; |
19 |
if (adcIn < 650) return btnL; |
20 |
if (adcIn < 850) return btnSel; |
21 |
|
22 |
return btnNone; //if it can't detect anything, return no button pressed |
23 |
}
|
ReadLcdButtons ()
memiliki penundaan 90ms untuk debounce tombol. Pada kenyataannya, ini bukan debounce, karena tidak mengambil kembali pengukuran ADC setelah waktu yang ditentukan, tetapi lebih sering polling tombol-tombol yang jarang untuk mendaftar lebih dari satu klik.
Ini mencapai hal yang sama dari pandangan UX praktis. Ini lebih dari tombol jajak pendapat setiap 90ms daripada terus-menerus, yang mengapa penggunaan delay ()
umumnya tidak dianggap praktik yang baik untuk tujuan debouncing, tetapi memperbaiki masalah (hanya setiap akhir menu yang dapat diakses).
Langkah 4: Screen Refresh
Setelah unit dapat bereaksi terhadap input, perlu ada cara untuk menampilkan reaksi tersebut.
Setelah mencoba pembaruan on-the-fly, saya memutuskan bahwa penyegaran layar yang konsisten seperti OS nyata lebih mudah untuk dikelola dalam upaya saya pada struktur modular yang dapat di-upgrade. Melakukan hal ini sesederhana membersihkan layar, kemudian membangun kembali berdasarkan parameter yang diketahui saat ini.
Ini terdengar rumit, tetapi dalam praktiknya membuat hidup jauh lebih mudah. Ini menghapus sejumlah besar perintah LCD dari tempat lain dalam program, dan menciptakan zona tipe-agnostik variabel yang sangat dipengaruhi oleh pembaruan program di luarnya.
Bagian penyegaran yang sebenarnya berevolusi untuk terdiri dari empat langkah berbeda:
Setel ulang parameter ...
1 |
//PRINT NEW SCREEN VALUES
|
2 |
btnVal=btnNone; |
3 |
lcd.clear(); |
...cetak baris atas ...
1 |
lcd.setCursor(0, 0); |
2 |
lcd.print(menuItemsTop[currentMenuItem]); //print top level menu item |
cetak garis bawah, yang akan saya jelaskan nanti ...
1 |
lcd.setCursor(0,1); |
2 |
switch (currentMenuItem) { |
3 |
case 0: |
4 |
{
|
5 |
for (int i = 0; i < 4; i++) { |
6 |
lcd.setCursor(i, 1); |
7 |
lcd.print(currentDistance[i]); |
8 |
}
|
9 |
break; |
10 |
}
|
11 |
|
12 |
case 1: |
13 |
{
|
14 |
for (int i = 0; i < 6; i++) { |
15 |
lcd.setCursor(i, 1); |
16 |
lcd.print(currentDuration[i]); |
17 |
}
|
18 |
break; |
19 |
}
|
20 |
|
21 |
case 2: |
22 |
{
|
23 |
for (int i = 0; i < 4; i++) { |
24 |
lcd.setCursor(i, 1); |
25 |
lcd.print(currentSteps[i]); |
26 |
}
|
27 |
break; |
28 |
}
|
29 |
|
30 |
case 3: |
31 |
{
|
32 |
if (travelDir == 0) lcd.print("From Motor"); |
33 |
else lcd.print("To Motor"); |
34 |
break; |
35 |
}
|
36 |
|
37 |
case 4: |
38 |
{
|
39 |
lcd.print("Stop!"); |
40 |
break; |
41 |
}
|
42 |
} //end switch |
... dan tambahkan perintah khusus layar dari atas apa yang sudah dicetak.
1 |
if (currentMenuItem==0){ |
2 |
lcd.setCursor(4,1); |
3 |
lcd.print("mm(max 1300)"); //insert max carriage travel on slider used |
4 |
} |
5 |
if (currentMenuItem==1){ |
6 |
lcd.setCursor(6,1); |
7 |
lcd.print("s(3600/hr)"); |
8 |
} |
9 |
if (currentMenuLevel == 1) { |
10 |
lcd.setCursor(currentCursorPos, 1); |
11 |
lcd.blink(); |
12 |
} |
13 |
else lcd.noBlink(); |
Membangun Menu: Judul Utama
Tentu saja, bagian penyegaran layar yang tepat tidak menulis sendiri, dan kita perlu mengetahui menu yang ditulis ke layar sebelum dapat diselesaikan. Judul utamanya mudah, karena tidak benar-benar berubah tergantung pada input pengguna. Ini berarti dapat berupa string array - secara teknis array pointer char, atau array array:
1 |
//MENU GUI
|
2 |
//define top-level menu item strings for numerical navigation
|
3 |
char* menuItemsTop[] = { |
4 |
" 01 Distance >", "< 02 Duration >", "< 03 Steps > ", "< 04 Direction >", "< 05 Go!"}; |
5 |
|
6 |
int currentMenuLevel = 0; //top menu or submenu |
7 |
int currentMenuItem = 0; //x-axis position of menu selection |
8 |
int currentCursorPos = 0; //current lcd cursor position |
9 |
int currentDistance[4] = { |
10 |
0, 0, 0, 0}; |
11 |
int currentDuration[6] = { |
12 |
0, 0, 0, 0, 0, 0}; |
13 |
int currentSteps[4] = { |
14 |
0, 0, 0, 1}; |
Ini berarti bahwa array menuItemsTop
ini dapat dinavigasi hanya dengan mengubah nomor di dalam tanda kurung siku pada waktu penyegaran layar. Yang terjadi begitu saja, karena semuanya nol-indeks, untuk melacak identik dengan integer currentMenuItem
.
Memanipulasi currentMenuItem
pada event tombol memungkinkan kita navigasi satu dimensi, jadi ketika Anda melihat menuItemsTop [currentMenuItem]
itu jelas judul menu saat ini.
1 |
if (currentMenuLevel==0) { |
2 |
switch (btnVal){ |
3 |
case btnL: |
4 |
{
|
5 |
if (currentMenuItem == 0) break; //can't go left from here |
6 |
else currentMenuItem--; |
7 |
break; |
8 |
}
|
9 |
|
10 |
case btnR: |
11 |
{
|
12 |
if (currentMenuItem == 4) break; //can't go right from here |
13 |
else currentMenuItem++; |
14 |
break; |
15 |
}
|
16 |
|
17 |
case btnSel: |
18 |
{
|
19 |
currentMenuLevel++; |
20 |
if (currentCursorPos > 3 && (currentMenuItem == 0 || currentMenuItem == 2)) currentCursorPos = 3; //don't go off the end of the numbers for the 4-digit numbers |
21 |
if (currentCursorPos > 0 && (currentMenuItem > 2)) currentCursorPos = 0; // set blinking cursor to left for text-based options |
22 |
if (currentMenuItem == 4) { |
23 |
motion = 1; |
24 |
motionControl(); |
25 |
break; |
26 |
}
|
27 |
}
|
28 |
} //end of switch |
29 |
} //end of level 0 |
Jadi Anda dapat bergerak ke kiri dan kanan, dan masuk ke menu, atau dalam kasus Go! kemudian kontrol gerak diaktifkan. Itu saja yang dibutuhkan di sini.
Membangun Menu: Submenu
Sistem submenu mengambil sedikit lebih banyak pekerjaan, berkat kompleksitas internalnya. Tiga entri pertama, Jarak, Durasi dan Langkah-langkah, secara teknis terdiri dari sub-sub-menu, masing-masing memungkinkan navigasi nilai multi-digit serta setiap karakter individu.
Hal ini tercakup dengan membuat setiap entri submenu sebagai sistem pos yang ditukar dengan haknya sendiri. Meskipun ini masih sangat jauh, ini adalah metode yang sederhana dan konsisten untuk memungkinkan navigasi tingkat rendah seperti itu. Karena saya benar-benar baru mengetahui submenu Jarak dan menyalinnya untuk submenu lainnya, inilah pandangannya.
1 |
else { // i.e. "else if currentMenuLevel = 1" |
2 |
if (currentMenuItem == 0) { //01 DISTANCE |
3 |
|
4 |
switch (btnVal) { |
5 |
case btnUp: |
6 |
{
|
7 |
currentChar = currentDistance[currentCursorPos]; |
8 |
adjustDigit(currentChar, 1); |
9 |
currentDistance[currentCursorPos] = currentChar; |
10 |
break; |
11 |
}
|
12 |
|
13 |
case btnDn: |
14 |
{
|
15 |
currentChar = currentDistance[currentCursorPos]; |
16 |
adjustDigit(currentChar, 0); |
17 |
currentDistance[currentCursorPos] = currentChar; |
18 |
break; |
19 |
}
|
20 |
|
21 |
case btnL: |
22 |
{
|
23 |
if (currentCursorPos == 0) break; //can't go left from here |
24 |
else currentCursorPos--; |
25 |
break; |
26 |
}
|
27 |
|
28 |
case btnR: |
29 |
{
|
30 |
if (currentCursorPos == 3) break; //can't go left from here |
31 |
else currentCursorPos++; |
32 |
break; |
33 |
}
|
34 |
|
35 |
case btnSel: |
36 |
{
|
37 |
parseArrayDistance(); |
38 |
currentMenuLevel--; |
39 |
}
|
40 |
} //end switch |
41 |
} //end DISTANCE |
Kiri dan kanan pada dasarnya sama dengan menu tingkat atas, hanya bergerak maju mundur sepanjang nomor dengan cara yang sama, dengan memiliki nomor sebenarnya menjadi satu set angka dalam array int dan lokasi saat ini disimpan dalam int yang disebut currentCursorPos
yang memungkinkan berkedip seperti yang terlihat pada modul penyegaran layar di atas.
Mencetak array ini di sepanjang baris LCD bawah adalah untuk apa loop untuk di bagian penyegaran layar; i
dari 0 hingga 3, kolom LCD
dari 0 hingga 3, currentDistance []
dari 0 hingga 3.
1 |
int adjustDigit(int x, int dir){ //digit adjust function |
2 |
if (dir == 0 && x > 0) x--; //subtract from digit on btnDn |
3 |
if (dir == 1 && x < 9) x++; // add to digit on btnUp |
4 |
lcd.setCursor(currentCursorPos, 1); |
5 |
lcd.print(x); |
6 |
currentChar = x; |
7 |
return currentChar; //return new digit |
8 |
}
|
Peningkatan dan penurunan jumlah dicapai dengan menyimpan digit saat ini di variabel currentChar
, yang kemudian diteruskan ke fungsi adjustDigit ()
bersama dengan nilai boolean yang menunjukkan arah; untuk menambah atau mengurangi motor.
Ini hanya menyesuaikan digit sesuai dengan nilai boolean dan menyimpan hasilnya, di mana aliran kembali ke loop utama, di mana nilai currentChar disimpan kembali ke posisi yang benar dari array current.distance []
yang aktif dan digit yang baru disesuaikan dicetak di layar menyegarkan.
Parsing Nilai Tampilan Array
Ketika Pilih dipukul dari salah satu submenu nomor array, itu akan memicu fungsi parsing yang relevan- dalam hal ini parseArrayDistance ()
. Anda perlu menguraikan larik, digunakan untuk menampilkan dan mengedit dengan mudah, menjadi bilangan bulat yang berguna untuk perhitungan gerak yang sebenarnya. Saya memilih untuk melakukan ini sekarang daripada di Go! untuk membuat UX merasa cepat.
1 |
int adjustDigit(int x, int dir){ //digit adjust function |
2 |
if (dir == 0 && x > 0) x--; //subtract from digit on btnDn |
3 |
if (dir == 1 && x < 9) x++; // add to digit on btnUp |
4 |
lcd.setCursor(currentCursorPos, 1); |
5 |
lcd.print(x); |
6 |
currentChar = x; |
7 |
return currentChar; //return new digit |
8 |
}
|
Saya datang dengan fungsi ini dari satu komentar kelulusan berguna yang saya temukan setelah melelahkan Google mencari fungsi array-to-int standar, datang kosong, dan menyingkirkan kekacauan fungsi array-to-char-to-int yang solusi yang tidak efektif. Kelihatannya cukup pendek dan ringan mengingat itu secara harfiah didasarkan pada pondasi matematika desimal, tetapi jika anda tahu metode yang lebih baik, saya semua telinga.
Kontrol Gerakan dan Pemicu Kamera
Semua nilai ditetapkan dan Anda menekan Go! Apa yang terjadi selanjutnya? Anda perlu menghitung dengan tepat apa yang seharusnya diberikan oleh angka untuk melakukan gerakan terakhir. Bagian ini berfungsi, tetapi masih dalam proses; Saya merasa perlu ada lebih banyak pilihan untuk berbagai jenis gerakan.
1 |
int motionControl() { |
2 |
totalMotorSteps = currentDistanceInt * 5; //calculate total steps (0.2mm = 20-tooth gear on 2mm pitch belt; 40mm per rev, 200 steps per rev, ergo 1/5th mm per step) |
3 |
pulseDelay = (1000L * (currentDurationInt - (currentStepsInt * shutterDuration))) / totalMotorSteps; //how long to pause in ms between STP pulses to the motor driver |
4 |
intervalDistance = totalMotorSteps / currentStepsInt; |
Apa yang terjadi dalam fungsi ini cukup jelas dari komentar, saya kira. Saya telah memasang shutterDuration
2 detik dalam perangkat lunak, terutama untuk menjaga pengujian cukup cepat. Jika anda memotret di malam hari, pada ISO yang lebih rendah, ini mungkin perlu lebih seperti 25-35 detik, tergantung pada kecepatan rana anda.
PulseDelay
dikalikan dengan 1000
pada akhirnya untuk mengkonversi dari detik ke milidetik, tentu saja. L
untuk mengubah int konstan ke panjang lebih saya berdosa di sisi hati-hati daripada benar-benar diperlukan. Karena sketsa yang relatif kecil, saya tidak terlalu khawatir tentang penggunaan memori variabel.
Perhitungan ini mengasumsikan bahwa loop itu sendiri membutuhkan jumlah waktu yang dapat diabaikan untuk berjalan dibandingkan dengan waktu pulseDelay
, yang, setelah saya mengambil polling tombol, tampaknya benar.
1 |
//once per overall run
|
2 |
if (travelDir == 0) digitalWrite(dir, LOW); |
3 |
else if (travelDir == 1) digitalWrite(dir, HIGH); |
4 |
//Serial.begin(9600);
|
5 |
//Serial.println(pulseDelay);
|
6 |
|
7 |
//step loop
|
8 |
do { |
9 |
digitalWrite(stp, HIGH); //fire motor driver step |
10 |
delay(pulseDelay); |
11 |
digitalWrite(stp, LOW); //reset driver |
12 |
//btnVal = readLcdButtons(); //check there's no stoppage - this takes too long and significantly slows motor; use reset for stop!
|
13 |
currentStep++; |
14 |
|
15 |
//at end of each step
|
16 |
if (currentStep % intervalDistance == 0) { //if current number of motor steps is divisible by the number of motor steps in a camera step, fire the camera |
17 |
digitalWrite(trig, HIGH); //trigger camera shutter |
18 |
delay(80); |
19 |
digitalWrite(trig, LOW); //reset trigger pin |
20 |
delay((shutterDuration * 1000)-80); //delay needs changing to timer so stop button can be polled |
21 |
}
|
22 |
|
23 |
}
|
24 |
while (currentStep < totalMotorSteps); |
25 |
|
26 |
} //end motion control |
Terakhir, perhatikan nilai currentSteps
yang ditetapkan pada 1. Saya belum membuat fungsi pengecekan kesalahan untuk ini, tetapi akal sehat sederhana mengatakan stepSize
menjadi tidak terbatas jika currentStepsInt == 0
, jadi sebaiknya menyimpannya jika gerakan berkelanjutan diinginkan. Saya menambahkan entri perbaikan untuk ini sudah.
Menjalankan Produk Akhir
Untuk sesuatu yang berjalan pada kode yang ditulis kurang lebih dari awal dalam dua hari dan bug diperbaiki lebih dari dua, itu berfungsi seperti mimpi! Buktinya ada di pudding. Apakah itu benar-benar mendapatkan rekaman timelapse berharga, dan apakah unit kontrol benar-benar bekerja dengan baik di lapangan?
Dalam tes saya, jawabannya tampaknya merupakan jawaban yang tulus. Di bawah ini adalah timelapse berdurasi 650 jam, tes pertama. Slider juga menyelesaikan 9 jam 720 frame test dengan sempurna, tapi sayangnya baterai kamera tidak bekerja dengan baik setelah 2 jam ... yang tidak saya temukan sampai tanda 8.5 jam, secara alami.
Jika saya mengatur waktu dan langkah dengan tepat, gerakan dapat terus menerus untuk gerakan dolly yang lambat dalam video aksi langsung, meskipun ujung dendeng perlu diedit atau dipendekkan.
Suara mungkin menjadi masalah kecuali stepper anda sangat senyap, tetapi untuk menambahkan nilai produksi ke rekaman sendiri, itu adalah pilihan.
Perbaikan
Seperti apa pun, ada kemungkinan perbaikan yang harus dilakukan. Saya telah mencantumkan ini di bagian atas file .ino
, meskipun diakui tanpa kepedulian khusus atas kelayakan atau diperintahkan oleh jenis kepentingan apa pun.
Beberapa dari ini saya anggap memperbaiki sebelum merilis tutorial ini dengan v0.2, tapi saya merasa seperti mereka dalam diri mereka sendiri adalah pengalaman belajar untuk melihat dalam hal membongkar secara mental kegunaan suatu program.
1 |
IMPROVEMENTS AND CONSIDERATIONS TOWARDS V1.0: |
2 |
1) Efficiency of submenu button response code for first three menu headers |
3 |
2) Use of bulb shutter time as an extra menu option, passed to shutterDuration int |
4 |
3) shutter duration should be timed pause, not delay() - can't poll stop button! |
5 |
4) Use EEPROM library functions to save quantities, can thus simplify the motion control section and use Reset as "stop" |
6 |
5) Remove switch from "Go" submenu, replace with more appropriate logic statement |
7 |
6) Would it be better to time camera steps rather than total travel? "duration" being more like 15 sec or 2 min than 30 min or 4hrs? |
8 |
7) Any const ints that would be better as #define or ints better as boolean? Hardly running against the limits of SRAM space at 8kB, though. |
9 |
8) Tweening/easing for acceleration curves, particularly for video use |
10 |
9) Error check for zero step size, or simply add one to intervalDistance if value is zero before calculations- other end of Distance is still 1 step |
11 |
10) Would sub-16ms delay()s be better as delayMicroseconds()? How much do interrupts throw off timing? |
12 |
11) Use of sleep on A4988 to reduce power consumption in the field? |
13 |
12) Error check for currentDurationInt <= currentStepsInt*shutterDuration, allowing no time for movement or even negative pulseDelay! |
14 |
*/
|
Ini hanyalah perbaikan yang saya pikir sejauh ini, dalam upaya untuk membimbing basis kode dari v0.2 yang belum sempurna tetapi fungsional menuju rilis v1.0 yang lebih optimal dan lebih mampu. Anda mungkin memperhatikan lebih banyak. Jangan ragu untuk meninggalkan mereka di komentar di bawah atau di GitHub.
Membungkus
Jika Anda telah mengikuti dari awal hingga akhir, termasuk bagian Photography Tuts + dari build, anda sekarang adalah pemilik bangga dari slider kamera bermotor berkualitas tinggi yang dapat menghasilkan rekaman timelapse dan gerakan dolly halus. Jika anda menggunakan kode untuk beberapa proyek lain, saya ingin melihatnya.
Dalam tutorial ini, saya telah melihat berbagai bentuk kontrol aliran berbasis loop, menciptakan GUI yang belum sempurna dan memperbarui LCD berdasarkan input pengguna. Saya juga melihat secara bersamaan mengendalikan beberapa perangkat mekanis eksternal melalui papan breakout.
Anda telah melihat aliran dan kemudahan pemrograman kode modular, serta melihat ide untuk cara meningkatkan kode yang fungsional, tetapi tidak dioptimalkan, baik dari sudut pandang efisiensi UX dan prosesor. Alat-alat ini akan melayani anda dengan baik untuk berbagai proyek komunikasi dan interaksi di masa depan.
Silakan tinggalkan pertanyaan atau komentar di bawah ini!