Bangun Replika Turut Portal 2 Nyata dengan Arduino – Gerak 3 Sumbu & Audio
Komponen dan Perlengkapan
Alat dan Mesin yang Diperlukan
Tentang Proyek Ini
The story
Natal ini, saya memutuskan untuk mendesain dan membangun turut kerja Portal dari game Portal 2. Bagi saya, ini adalah latihan untuk memodelkan seluruh rangkaian di Fusion 360 terlebih dahulu sebelum membangun sesuatu. Desain ini menggunakan Arduino Nano, chip MP3 player, sensor jarak, servo, LED, dan bagian 3D print.
Tujuannya adalah untuk membuatnya bergerak di 3 "sumbu", dengan suara ucapan dari game dan LED untuk mensimulasikan peluru.
- Buka "sayap" jika ada seseorang di depan. Gunakan mekanisme crank dengan slider, cuma begitu saja.
- Jika orang masih ada setelah terbuka, sembur sampai mereka jatuh. LED dan suara mesin telanjur.
- Jika orang sudah tidak ada, jalankan rutinitas pencarian scan sedikit.
- Tutup dan tidur sampai orang lain datang.
- Gunakan suara dan suara turut Portal dari game.
Saya mengambil beberapa kebebasan dalam desain, mencoba membuatnya mirip dengan yang ada dalam game, tetapi fungsional dan dapat dicetak. Dengan beberapa sketsa dasar yang ditemukan online, saya mulai memodelkan dan merencanakan...
Suara disimpan pada kartu microSD, yang dapat diakses dari sisi belakang sehingga suara dapat diperbarui atau diubah nanti. Ini berada seiring dengan strip isian hitam, sehingga pada dasarnya tidak terlihat setelah terpasang. 18 ekspresi dan suara individual digunakan dalam putaran ini.
Sensor lidar (time-of-flight) ada pada chip dengan profil persegi panjang. Sensor ini hampir tidak terlihat dari depan setelah terpasang.
Step 1: Fusion 360 Modelling
Desain dimulai dari sketsa yang ditemukan online. Menggunakan gambar ini sebagai kanvas, saya mulai menggambar garis besar 3 tampilan. Lalu adalah masalah mengekstrusinya menjadi 3D, kemudian shelling bentuk umum dan membuat potongan. Semua elektronika dibangun di Fusion sebagai komponen yang disisipkan dan ditempatkan di tempat yang saya pikir masuk akal. Parameter penggerak adalah:
- Arduino Nano harus memiliki konektor yang dapat diakses untuk pembaruan setelah dirangkap sepenuhnya
- Kartu microSD harus dapat diakses dengan cara yang sama, dan idealnya tidak terlihat setelah terpasang
- Sensor time-of-flight juga harus tidak terlihat
- Konektor listrik 2.1mm untuk daya di belakang
- Bagian cetakan sebesar mungkin (tidak banyak potongan kecil)
- Cetak tanpa support
Setelah komponen (Nano, chip lain, servo) ditambahkan ke bentuk dasar, mereka dipindahkan dan ditempatkan sesuai kebutuhan, dan struktur pendukung dibangun untuk mendukung mereka di dalam shell.
Mekanisme pembukaan sayap adalah mekanisme crank dan slide. Mengapa? Karena saya ingin menggunakan mekanisme crank, itulah mengapa! Ini menambahkan beberapa komplikasi, tetapi juga memiliki manfaat; setelah geometri ditentukan, repeatabilitas operasional akan terjamin dan batas min dan max pada dasarnya terjamin.
Setelah seluruh model selesai dan saya yakin bahwa itu akan bekerja, dan dapat dibangun (dicetak) dan dirangkap, saya melanjutkan dan mencetak bagian-bagian serta membangun prototipe. Setelah itu berhasil, saya kembali ke model dan melakukan beberapa penyesuaian untuk memperbaiki penampilan dan kemampuan dirangkap (apakah itu kata?). Model ini adalah hasil dari perubahan tersebut.
Ini cukup melelahkan, karena sebenarnya tidak ada banyak bentuk kotak dalam hal ini, dan itu tertutup dengan erat, tanpa akses nyata untuk penyesuaian setelah dirangkap. Saya mempelajari cukup banyak dalam proyek ini, seperti menggunakan komponen yang tertanam dalam komponen lain. Ini membuat memanipulasi dan menjaga sub-assembly tetap terhubung untuk akses cepat. Pada akhirnya, itu layak untuk usahanya!
Step 2: 3D Printed Parts
Portal turret v4.zip diperbarui dan seharusnya berisi semua bagian cetak yang diperlukan untuk membangun turut terbaru.
--- "Rear test Leg" baru. Tidak benar seperti aslinya, tetapi ada di belakang, dan memungkinkan seseorang menggunakan konektor mini-USB lurus langsung ke atas. ---
Ini dicetak pada Prusa Mk2, menggunakan PLA untuk semua bagian. Orientasi cetakan seharusnya cukup jelas. Badan dicetak dalam posisi vertikal, tanpa support. Ada celah besar di sisi yang harus di-bridge, tetapi saya benar-benar tidak memiliki masalah besar dengan pengaturan cukup standar, selain buncitan. Buncitan di depan dan belakang dapat hampir dihilangkan dengan filamen yang baik dan beberapa pengaturan cetakan yang baik. Saya menemukan bahwa 0.2mm bekerja baik di slicer dan menghasilkan hasil yang bagus. Yang lebih besar dan pembukaan mulai muncul di badan di dekat strip isian.
Desain saluran dan ujung dibuat dengan chamfer 45 derajat, jadi elemen "tergantung di udara" akan minimal.
Saya hanya memiliki sedikit pembersihan untuk memasang rangkaian bersama. Strip isian yang masuk ke saluran sekarang cukup sederhana, dengan lebar yang dikurangi dan ketebalan yang konsisten. Saya pikir seseorang dapat menggunakan bahan tipis hitam dipotong menjadi strip daripada menggunakan bagian cetakan ini (dicetak di tepi).
Satu area yang memerlukan ketelitian adalah pin slider di frame pitch. Pin lurus (paku) di lubang bor yang dikejar dengan bor 1/8" dan sedikit pelumas akan sangat membantu.
Lens%2Bv3b.stl
Body-RHS%2Bv4.stl
Body-LHS%2Bw%2Bcomponents%2Bv4.stl
Wing%2BRH%2Bbottom%2Bv4.stl
Wing%2BRH%2Btop%2Bv4.stl
Wing%2BLH%2Bbottom%2Bv4.stl
Wing%2BLH%2Btop%2Bv4.stl
Pitch%2BFrame%2Bv4.stl
Leg%2B-%2BREAR%2Bv4.stl
Infill%2Bstrips%2Bv3.stl
Leg%2B-%2BREAR%2Btest.stl
Pitch%2BFrame%2B-%2Bmodified%2B1.stl
wings%2Bv6.zip
Arduino%2BBase%2Bw%2BRadio.stl
Portal%2BTurret%2Bv4.zip
Step 3: Components
V4 (Red turret) has smaller bridging, requiring v4 wings and Pitch frame.
The v6 wings are an option that allows different colour guns. Saya belum membuat satu seperti ini secara pribadi, tetapi seharusnya berfungsi baik.
Setelah model selesai, dan saya puas dengan prototipe saya, saya mencetak rev 2 dengan komponen plastik seperti yang ditunjukkan. Semua di sini adalah PLA, dengan Hitam dan warna desain (Biru dalam kasus ini), dan sedikit PLA translucent untuk "Lens" atau mata laser tengah.
Foto ini cukup menangkap komponen, dengan pengecualian kabel.
Step 4: Electronics
Pembangunan menggunakan komponen berikut:
- Arduino Nano (1)
- DFPlayer Mini MP3 player (or MP3-TF-16P) (1)
- VL53L0X Time-of-Flight Ranging Sensor (1)
- Generic SG90 micro servos (3)
- 5mm red LEDs (5)
- 220 Ohm resistors for LEDs (5)
- 1kOhm resistor (1)
- 4cm speaker, 4Ohm, 3 Watt (1)
- 2.1mm power connector (1)
- 3" long framing nails (4)
- M2 x 6 screws (8)
- M2.5 x 8 screws (2)
- M3 x 8 screws (4)
- M3 x 12 screws (4)
- M3 x 16 screws (2)
- shrink wrap
- small tie wraps
Semua komponen mudah didapat dari Arduino, Amazon atau Banggood dan sumber lainnya.
Baut diperoleh dari kit rangkaian. Ini adalah siksa untuk mendapatkannya selain itu...
Step 5: Mechanical Assembly
Sebagian besar assembly mekanis cukup wajar. Bagian cetakan dicetak dengan lubang lebih besar yang dibuat Gewinda jika diperlukan, jadi pemotongan cepat dengan baut sebelum assembly akhir akan membantu dan membuat assembly komponen yang dibaut cukup mudah.
Arduino dan chip MP3 klik ke dalam kerangka tanpa hardware. VL53LOX akan masuk ke cakaran depan tanpa perekat. Coba pasang terlebih dahulu, lalu lepaskan dan pasang setelah kabel dipasang.
Assembly slider menggunakan 4 paku framing sebagai rel slider. Mereka berdiameter sekitar 1/8", dengan kepala dipotong. Ini diambil dari strip paku framing DeWalt yang digunakan dengan mesin paku framing listrik mereka. Tidak perlu dikatakan, paku halus adalah harus.
Servo dipasang seperti yang ditunjukkan. Orientasi penting. Servo pitch dan pivot "dikonsentrasikan" saat dipasang ke dalam bagian-bagian mereka. Crank dipasang sehingga, saat dalam posisi terbuka, itu akan tertutup dengan berputar searah jarum jam, saat dilihat dari depan. Posisi terbuka adalah batang dan crank lurus dalam satu baris, dengan rotasi 10 derajat lagi hingga terkunci.
Assembly kaki adalah bagian termudah. Baut 2-2.5mm, dengan tutup kaki mencengkeram siku setiap kaki. Gunakan perekat yang tidak meluput di atas atas plate kaki. Dengan cara ini, body yang berputar tidak akan macet jika Anda menyesuaikan jangkauan rotasi.
Semua koneksi servo ke bagian cetakan dibuat menggunakan crank pendek putih yang datang dengan servo. Crank ini cukup ditekan ke dalam bagian cetakan. Saya mencoba mencetak lubang geser di bagian yang terhubung ke setiap servo, tetapi memiliki kesuksesan yang terbatas dan dapat diulang. Lebih mudah menggunakan crank yang datang dengan servo.
Assembly crank menggunakan baut 2.5mm yang lebih panjang. Batang crank tidak boleh ditekan di antara setengah crank. Faktanya, Anda bisa mencoba menggunakan baut yang lebih pendek tanpa bagian Crank2. Itu seharusnya juga bekerja (semoga tidak ada torsi yang signifikan jika sayap bergerak bebas).
Speaker ditangkap oleh mount servo (2 potongan) yang menangkap speaker. Speaker di antara "kaki" ini, dan diletakkan pada tempatnya dengan mengamankannya ke servo pitch. Servo ini kemudian terhubung ke assembly pitch (slider), diikuti oleh assembly crank dengan batang. Semua ini dirangkap sebelum dipasang di body LHS dengan 4 baut kecil.
Setelah inti utama dipasang, dengan Arduino dan MP3 player sementara ditempatkan, maka seruannya dimulai - kabel!
Step 6: Wiring
V5 - Radio option (Red turret photos). This includes an nRF24L01 radio chip. Completely changes the Arduino pin wiring to accommodate all the connections. Details to come...
Packaging akhir rapat, jadi menghabiskan waktu di sini untuk menentukan panjang kabel sangat layak. Sebagian besar kabel koneksi yang saya miliki akhirnya antara 3" - 4".
LED dihubungkan dengan resistor 220 Ohm secara langsung, diikuti oleh beberapa krimp, dan beberapa kabel diputar, lalu diubah tempat, setelah mereka telah diuji. Saya menggunakan kabel ringan di sini karena saya memiliki beberapa yang tersisa (kabel komunikasi tipe CAT5) dan tidak ingin kabel yang terlihat menjadi mengganggu.
Bagian mekanis di-mock fit di cakaran, lalu routing kabel ditentukan, lalu memotong dan mempersiapkan kabel adalah berikutnya.
Saya membuat konektor servo sehingga saya dapat memasukkan dan mengganti servo jika saya pernah mengocok sesuatu dan mencabut gear. Ini sangat membantu setelah mengocok selama prototipe pertama saya.
Setelah puas dengan sebagian besar kabel, LED disolder di akhir. Lalu saatnya untuk secara hati-hati memasukkan rangkaian yang terhubung ke setengah cakaran. Langkah terakhir adalah menyolder konektor daya ke kabel daya setelah semuanya di dalam.
-- Catatan penting: Pastikan kabel yang disimpan di belakang Nano tidak menekan tombol reset!! Ini akan jelas menyebabkan masalah dan mencegah unit bekerja dengan baik. --
Pada titik ini, semua kabel selesai, tetapi sebelum assembly akhir, penting untuk mengunggah kode ke Nano dan menyalakannya untuk memastikan LED, servo, dan MP3 player semua bekerja seperti yang dirancang. Setelah itu, saatnya untuk memasang bagian mekanis yang tersisa.
Step 7: Arduino Code
Updated code! Cleaned it up and made some tweaks.
File yang dilampirkan adalah apa yang saya buat untuk menggerakkan unit seperti yang ditampilkan dalam video. Saya akan terus menyesuaikan untuk mengubah karakter Turut dan cara perilakunya. Banyak opsi di sini.
Saya menyusun kode menggunakan subroutine yang saya panggil sesuai kebutuhan. Ini menjaga badan utama tetap bersih dan sangat membantu saat saya bermain-main dengan karakteristik yang berbeda. Ini membantu saya memanipulasi kode untuk perilaku yang berbeda.
Saya juga menggunakan banyak variabel di depan yang membantu saya menyesuaikan dan menyesuaikan posisi parkir dan jangkauan min dan max, misalnya.
Saya menggunakan library DFMiniMP3 dalam kode saya. Saya mencoba library lain, seperti DFRobot, tetapi mengalami masalah, jadi kembali ke yang ini. Ini berarti saya harus menjaga bagian 'static void' agar tetap fungsional. Ini tidak diperlukan untuk operasi, tetapi hei, saya bukan programmer ahli. Saya akan senang mendengar tentang library lain yang sama sederhana dan rapi seperti library VL53LOX. Beritahu saya jika Anda menemukan cara yang lebih baik untuk melakukannya!
Seperti yang diketahui, implementasi suara dilakukan dengan cara sederhana, dengan memiliki folder bernama "mp3" pada kartu SD, dengan nama file 0001.mp3, 0002.mp3, dll. Empat digit pertama harus dalam format ini, tetapi Anda dapat menambahkan sufiks apa pun setelah itu untuk membantu mengidentifikasi suara tertentu. Lihat https://www.dfrobot.com/blog-277.html untuk contoh. Saya telah menyertakan gambar nama file saya seperti yang digunakan pada folder. Angka sesuai dengan panggilan dalam kode.
File suara saya ambil dari halaman Wikipedia tentang suara Turut Portal. Kode memilih file suara secara acak (1 dari 2, atau 3 suara) agar hal tidak menjadi membosankan.
Step 8: Final Body Assembly
Bagian ini sedikit rumit karena strip isian hitam. Skala assembly akhir cukup kecil sehingga strip dan lubang penerima sangat kecil. Ini memerlukan pemotongan saluran dengan penunjuk atau alat cakar kecil lainnya untuk memastikan strip akan muat dengan sedikit rintangan sebelum mencoba memasang sisi lainnya.
Mengikat kabel bersama dengan rapi dan mengikat dengan kabel ikat sesuai kebutuhan akan membuat ini jauh lebih mudah.
Saya telah memasang beberapa ini sekarang dan merasa lebih mudah untuk memasang dua setengah bersama-sama terlebih dahulu, lalu memasukkan strip isian. Masukkan satu sisi ke dalam setengah dengan "rak" yang mencegah strip isian jatuh ke dalam, lalu cangkangkan sedikit dan tekan perlahan. Tidak terlalu buruk sekarang.
Ini adalah bagian yang lebih rumit. Mungkin suatu hari, saya akan mempertimbangkan ulang assembly ini, tetapi saya suka cara tampilannya setelah selesai, dan itu cukup kuat.
Step 9: Wing Assemblies
Sekarang badan sudah selesai, dengan LED sayap yang menyisir, saatnya untuk menyiapkan sayap dan memasang.
Sangat penting bahwa lubang slider dikejar dengan bor 1/8", lalu dibersihkan. Potong kepala paku menggunakan potong baut, klepat, bor tangan atau alat potong kaki favorit Anda. Pin slider (paku dipotong) dipasang ke dalam sayap dengan menekannya ke dalam setiap bagian sayap. Paku lurus, dibuang burr dan halus adalah kunci untuk membuat ini bekerja. Lubang dan slider sayap harus dilubangi pelumas dan diuji sebelum menghubungkan batang crank dan berjalan. Grafit kering, atau pelumas PLA yang cocok direkomendasikan. Saya menemukan bahwa tabung kecil pelumas pribadi bekerja sangat bagus, dan murah. Ini sangat licin. Ini juga memerlukan beberapa penjelasan ketika pasangan atau orang tua Anda masuk dan bertanya apa persis yang Anda butuhkan itu di bangku kerja!!
Mulailah dengan menentukan bagian sayap mana yang mana, dan coba slide bagian itu terlebih dahulu. Lalu pasang setengah atas dan bawah bersama-sama setelah pin dipasang, terapkan beberapa pelumas (batok kapas bekerja baik untuk ini) dan pastikan sayap meluncur dengan baik. Ini bisa rumit, tetapi tanpa memastikan sayap meluncur dengan mudah, tanpa macet, Anda akan mengalami waktu yang menyedihkan. Percayalah...
Setelah sayap siap, hanya masalah meluncurkannya ke tempat, menempatkan batang penghubung di atas lubang di sayap dan memasang dengan satu baut. Lalu, LED dimasukkan ke lubang pistol, kabel disematkan di sebelah sayap dan Anda siap! Anda juga dapat menggunakan lem panas untuk mengunci mereka pada tempat setelah semuanya diuji.
Step 10: Scare and Shock Your Friends!!
Catatan kecil terakhir pada desain ini adalah bahwa stop kontak miring adalah ide bagus, karena tidak mengganggu kaki belakang saat berputar. Kaki belakang yang direvisi (v3) telah diperpanjang untuk memberi sedikit lebih banyak ruang.
Setelah dibangun dan dihubungkan (5V atau cocok untuk Nano), itu akan duduk dengan tenang sampai seseorang terdeteksi dalam jarak yang diprogram, lalu hidup dan membunuh siapa pun yang memasuki domainnya!!
Beritahu saya jika Anda membuat satu (atau lebih) dan jika Anda menemukan fitur atau konsep baru!
Kode
- Portal turret w Radio (RED)
Portal turret w Radio (RED)C/C++
Code used for turret. This includes code that will enable control by the Master Turret Control, featured on another page.
Different flavours require tweaking.
// /* Portal Turret - Option With Radio!! * Chris Nowak - Feb 2019 * https://www.instructables.com/member/ChrisN219/ * https://create.arduino.cc/projecthub/Novachris/talking-portal-2-turret-gun-637bf3 * * This code includes all the features needed to perform the Cara Mia Opera, * Chat Time and Manual mode, as controlled by the Maseter Turret Control (MTC). * The MTC is a separate controller featured on another build. The turret * will operate autonomously using this code without the MTC. * * The code is built to work with three turrets, but each will operate independently. * Lots of debugging code left in. Use or clean up as desired. It's mostly debugged, * but isn't perfect. Given the price you paid, I figure that's ok! ;) * ================= RED ==================== */ #include <Wire.h> #include <VL53L0X.h> #include "Arduino.h" #include <SoftwareSerial.h> #include <Servo.h> #include <DFMiniMp3.h> #include <RF24.h> #include <RF24Network.h> #include <SPI.h> class Mp3Notify { public: static void OnError(uint8_t errorCode) { // see DfMp3_Error for code meaning Serial.println(); Serial.print("Com Error "); Serial.println(errorCode); } static void OnPlayFinished(uint8_t globalTrack) { Serial.println(); Serial.print("Play finished for #"); Serial.println(globalTrack); } static void OnCardOnline(uint8_t code) { Serial.println(); Serial.print("Card online "); Serial.println(code); } static void OnCardInserted(uint8_t code) { Serial.println(); Serial.print("Card inserted "); Serial.println(code); } static void OnCardRemoved(uint8_t code) { Serial.println(); Serial.print("Card removed "); Serial.println(code); } }; Servo servo_pivot; Servo servo_wings; Servo servo_pitch; VL53L0X sensor; // Setup for soundcard SoftwareSerial secondarySerial(4, 2); // RX, TX DFMiniMp3<SoftwareSerial, Mp3Notify> mp3(secondarySerial); // Setup for radio RF24 radio(10, 9); // nRF24L01 (CE,CSN) RF24Network network(radio); // Include the radio in the network const uint64_t this_node = 01; // This turret - Red - in Octal format ( 04,031, etc) const uint64_t WHT = 02; // Turret 02 - White const uint64_t BLU = 03; // Turret 03 - Blue const uint64_t MTC = 00; // Master Turret Control unsigned long previousMillis1 = 0; unsigned long previousMillis2 = 0; unsigned long previousMillis3 = 0; byte LED_LH_up = A1; byte LED_LH_down = A2; byte LED_CENTRE = A3; byte LED_RH_up = 7; byte LED_RH_down = 8; byte PIVOT = 6; //white byte WINGS = 3; //yellow byte PITCH = 5; //blue byte parkPIVOT = 96; //Smaller number rotates CW as viewed from the top byte posPIVOT = 96; byte maxPIVOT = 116; byte minPIVOT = 76; byte WINGclose = 165; byte WINGopen = 10; byte WINGpos = 160; byte parkPITCH = 86; byte posPITCH = 86; byte maxPITCH = 96; byte minPITCH = 75; byte pos = 90; byte pulse = 20; byte pitchCW = 1; byte pivotCW = 1; byte randomWake; byte randomOpen; byte randomFalse; byte randomStart; byte randomDisengage; int triggerDistance = 300; byte buttonStatus; byte busyRed = 1; byte restModeWhite; byte goState; int x; int y; byte pb1State; // Fire byte pb2State; // say random comment byte pb3State; byte randomPic; // Payload from Master Turret Control (MTC) // int payload [] = {0, 1, 2 , 3 , 4 , 5 }; // int payload [] = {x, y, pb1State, pb2State, pb3State, goState}; int payload [6]; /* This is the "conversation" map for "Chat Time". * 0-99 = Red Turret sayings * 100-199 = White Turret sayings * 200-299 = Blue Turret sayings * Files on all SD cards are saved as 0-100. * Add 100 to the file number here for White, 200 for Blue. * File 204 would be BLUE turret, file number 0004. */ int chatSayings [] = { 0, // Start pause on i = 0, followed by "rounds"... 204, 164, 25, // 1 205, 127, 76, // 2 208, 162, 65, // 3 143, 230, 23, // 4 130, 41, 225, // 5 153, 31, 133, // 6 234, 49, 155, // 7 229, 175, 74, // 8 231, 58, 226, // 9 161, 223, 59, // 10 227, 68, 236, // 11 136, 50, 224, // 12 34, 160, 78, // 13 222, 42 // End }; /* This is the RED and WHITE turret timing map. They share sayings. * These timings correspond to the time needed * to play the individual sayings, in milliseconds. * Change the sayings above as desired, but do not change these timings. * For example, i = 2 will take 0.8 seconds (NormalTimings[2]) * to play the chatSayings[2] file. */ int NormalTimings [] = { 1000, // Start pause on i = 0, followed by "rounds"... 2600, 800, 2800, 900, 1700, 1600, 1300, 2500, 1400, 1900, // 1 - 10 1600, 2300, 800, 3000, 300, 100, 200, 0, 0, 300, // 11 - 20 298000, 1300, 2600, 1300, 1400, 2100, 1900, 1600, 800, 1700, // 21 - 30 1100, 1000, 1000, 2100, 1500, 1300, 1100, 800, 1200, 1000, // 31 - 40 2200, 1700, 1300, 1400, 1500, 1000, 2000, 500, 2700, 9000, // 41 - 50 1100, 1200, 900, 2400, 1200, 1100, 2100, 2000, 2500, 1700, // 51 - 60 1100, 1000, 1100, 500, 1900, 0, 1300, 2100, 1700, 900, // 61 - 70 1100, 800, 1100, 1700, 1100, 1100, 1500, 1500, 500, 900, // 71 - 80 2100 // 81 }; /* This is the BLUE turret timing map. * These timings correspond to the time needed * to play the individual sayings, in seconds. * For example, i = 2 will take 0.9 seconds (DefectiveTimings[2]) * to play the chatSayings [2] file. */ int DefectiveTimings [] = { 1000, // Start pause on i = 0, followed by "rounds"... 1700, 900, 2000, 600, 1100, 1800, 1900, 3000, 1500, 800, // 1 - 10 2100, 800, 1900, 900, 3200, 2700, 0, 0, 0, 2000, // 11 - 20 4400, 800, 3200, 900, 1400, 2000, 2100, 1200, 1300, 1000, // 21 - 30 1100, 1400, 2100, 1000, 1600, 1000, 1200 // 31 - 40 }; /////////////////////////////////////////////////////////////////// //======================= SETUP =================================// /////////////////////////////////////////////////////////////////// void setup(){ secondarySerial.begin(9600);// Serial.begin(9600);// mp3.begin(); mp3.setVolume(22); Wire.begin(); SPI.begin(); // Radio setup radio.begin(); radio.setPALevel(RF24_PA_LOW); // set radio to low power. All near each other radio.setDataRate(RF24_2MBPS); // I find this works best with multiple radios network.begin(70, this_node); //(channel, node address) sensor_read(); pinMode(LED_LH_up, OUTPUT); pinMode(LED_LH_down, OUTPUT); pinMode(LED_CENTRE, OUTPUT); pinMode(LED_RH_up, OUTPUT); pinMode(LED_RH_down, OUTPUT); digitalWrite(LED_CENTRE, HIGH); activate_servos(); servo_wings.write(WINGopen); // open wings servo_pivot.write(parkPIVOT); // park pivot servo_pitch.write(parkPITCH); // park pitch randomWake = random(1, 3); mp3.playMp3FolderTrack(1); // play waking up comment delay(2000); servo_wings.write(WINGclose); // close wings delay(1500); digitalWrite(LED_CENTRE, LOW); turn_off_servos(); busyRed = 0; } /////////////////////////////////////////////////////////////////// //======================= MAIN LOOP =============================// /////////////////////////////////////////////////////////////////// void loop(){ while (sensor.readRangeSingleMillimeters()>triggerDistance) { //nothing in front of sensor, do nothing ReadNet(); WriteNet(); output_sensor(); if (payload[5] == 1) { // Conditions used with MTC. Turret will work automatically without MTC delay(500); WriteNet(); Cara_Mia(); // Opera time!! } else if (payload[5] == 2) { delay(500); WriteNet(); Chatty_time(); // Chatty time!!} ReadNet(); } else if (payload[5] == 3) { delay(500); WriteNet(); ManualControl (); // Manual Control } } if (sensor.readRangeSingleMillimeters()<triggerDistance){ //something in front of sensor delay (200); //wait before checking again to see if someone still there... if (sensor.readRangeSingleMillimeters()<triggerDistance){ //someone there (not false trigger) busyRed = 1; ReadNet(); WriteNet(); output_sensor(); activate_servos(); // wake up servos activate(); // open wings and say "I see you" comment if (sensor.readRangeSingleMillimeters()>triggerDistance){ // opened up and person gone falseActivate(); //say "where'd you go?" comment and close up delay (2000); scanArea(); //perform scan of area } else { // someone definitely there - open fire!! engage(); delay (2400); for (int j=0; j <= 2; j++){ if (sensor.readRangeSingleMillimeters()<triggerDistance){ // as long as someone there... for (int i=0; i <= 25; i++){ // fire for a short period fire();} // shoot them... output_sensor(); } } } output_sensor(); disengage(); // no more target, stop firing and prepare to close } else { output_sensor(); delay(350); } } } /////////////////////////////////////////////////////////////////// //======================= SUBROUTINES ===========================// /////////////////////////////////////////////////////////////////// //===== read the VLX53 sensor =====// void sensor_read(){ sensor.init(); sensor.setTimeout(100); // lower the return signal rate limit (default is 0.25 MCPS) sensor.setSignalRateLimit(0.25); // started with 0.1 // increase laser pulse periods (defaults are 14 and 10 PCLKs) sensor.setVcselPulsePeriod(VL53L0X::VcselPeriodPreRange, 14); //started with 18 sensor.setVcselPulsePeriod(VL53L0X::VcselPeriodFinalRange, 10); //started with 14 } //===== monitor output for debugging =====// void output_sensor(){ // Option out or delete if not interested. Serial.println(); Serial.print ("-RED- "); Serial.print("Distance "); Serial.print(sensor.readRangeSingleMillimeters()); Serial.print ("\t"); if (sensor.timeoutOccurred()) { Serial.print(" TIMEOUT"); } Serial.print(F("Pivot< ")); Serial.print (posPIVOT); Serial.print (" "); Serial.print(F("Pitch< ")); Serial.print (posPITCH); Serial.print (" "); Serial.print(F("Wings< ")); Serial.print (WINGpos); Serial.print (" "); Serial.print(F("busyRed: "));Serial.print (busyRed); Serial.print (" "); Serial.print(F("x :"));Serial.print (payload[0]); Serial.print ("\t"); Serial.print(F("y :"));Serial.print (payload[1]); Serial.print ("\t"); Serial.print(F("pb1 :"));Serial.print (payload[2]); Serial.print (" "); Serial.print(F("pb2 :"));Serial.print (payload[3]); Serial.print (" "); Serial.print(F("pb3 :"));Serial.print (payload[4]); Serial.print (" "); Serial.print(F("goState: ")); Serial.print(payload[5]);Serial.print (" "); } //===== update servo positions =====// void increment_servos(){ if (pitchCW == 0) posPITCH = posPITCH - 3; // the higher the number, the jerkier the motion if (pitchCW == 1) posPITCH = posPITCH + 3; if (pivotCW == 0) posPIVOT = posPIVOT - 3; if (pivotCW == 1) posPIVOT = posPIVOT + 3; if (posPIVOT >= maxPIVOT) pivotCW = 0; if (posPIVOT <= minPIVOT) pivotCW = 1; if (posPITCH >= maxPITCH) pitchCW = 0; if (posPITCH <= minPITCH) pitchCW = 1; } /////////////////////////////////////////////////////////////////// //======================= ACTIVATE ==============================// /////////////////////////////////////////////////////////////////// void activate(){ // open up wings and say something busyRed = 1; ReadNet(); WriteNet(); output_sensor(); digitalWrite(LED_CENTRE, HIGH); //LED eye on randomOpen = random(3, 6);// pick random opening comment mp3.playMp3FolderTrack(randomOpen); // play random "I see you" comment servo_wings.write(WINGopen); // open wings output_sensor(); delay (3400); } /////////////////////////////////////////////////////////////////// //====================FALSE ACTIVATE ============================// /////////////////////////////////////////////////////////////////// void falseActivate(){ // busyRed = 1; ReadNet(); WriteNet(); output_sensor(); randomFalse = random(6, 9);// pick random opening comment mp3.playMp3FolderTrack(randomFalse); // play random "where'd you go?" comment delay (1800); } /////////////////////////////////////////////////////////////////// //======================= SCAN AREA =============================// /////////////////////////////////////////////////////////////////// void scanArea(){ // continue scanning for a bit after falseActivate busyRed = 1; ReadNet(); WriteNet(); output_sensor(); mp3.playMp3FolderTrack(2); // "searching..." servo_pitch.write(parkPITCH); delay(1600); servo_pitch.detach(); servo_wings.detach(); mp3.playMp3FolderTrack(21); for (int i=0; i <= 220; i++){ //scan for a little bit... output_sensor(); if (pivotCW == 0) posPIVOT = posPIVOT - 1; // increment one step CW if CW = 0 if (pivotCW == 1) posPIVOT = posPIVOT + 1; // otherwise go one step other direction if (posPIVOT >= maxPIVOT) pivotCW = 0; // if max rotation clockwise, switch to counterclockwise if (posPIVOT <= minPIVOT) pivotCW = 1; // if min rotation counterclockwise,switch to clockwise servo_pivot.write(posPIVOT); if (sensor.readRangeSingleMillimeters()<triggerDistance/2){ //Somebody stepped in front i = 350; engage(); delay (2500); activate_servos(); for (int i=0; i <= 25; i++){ // fire for a short period fire(); // shoot them... } } } activate_servos(); // fully scanned with no one passing... servo_pivot.write(parkPIVOT); //close up. output_sensor(); delay(1200); } /////////////////////////////////////////////////////////////////// //======================= ENGAGE ================================// /////////////////////////////////////////////////////////////////// void engage(){ //smartass comment before firing busyRed = 1; ReadNet(); WriteNet(); output_sensor(); randomStart = random(9, 12);// pick random smartass comment before firing mp3.playMp3FolderTrack(randomStart); output_sensor(); } /////////////////////////////////////////////////////////////////// //======================= FIRE ==================================// /////////////////////////////////////////////////////////////////// void fire(){ // fire sound and turret movement ReadNet(); WriteNet(); output_sensor(); mp3.playMp3FolderTrack(15); servo_pivot.write(posPIVOT); servo_pitch.write(posPITCH); digitalWrite(LED_CENTRE, HIGH); digitalWrite(LED_LH_up, LOW); digitalWrite(LED_LH_down, HIGH); digitalWrite(LED_RH_up, LOW); digitalWrite(LED_RH_down, HIGH); delay(pulse*2); servo_pivot.write(posPIVOT); servo_pitch.write(posPITCH); digitalWrite(LED_LH_up, HIGH); digitalWrite(LED_LH_down, LOW); digitalWrite(LED_RH_up, HIGH); digitalWrite(LED_RH_down, LOW); delay(pulse); digitalWrite(LED_LH_up, LOW); digitalWrite(LED_RH_up, LOW); increment_servos(); // update servo position } /////////////////////////////////////////////////////////////////// //======================= DISENGAGE =============================// /////////////////////////////////////////////////////////////////// void disengage(){ //re-align wings (pitch) and pivot home before closing wings busyRed = 1; ReadNet(); WriteNet(); output_sensor(); delay(2000); randomDisengage = random(12, 15); mp3.playMp3FolderTrack(randomDisengage); // "Goodbye" comment servo_pivot.write(parkPIVOT); servo_pitch.write(parkPITCH); digitalWrite(LED_LH_up, LOW); digitalWrite(LED_LH_down, LOW); digitalWrite(LED_RH_up, LOW); digitalWrite(LED_RH_down, LOW); // turn off firing LEDs delay (2000); servo_wings.write(WINGclose); delay (1500); digitalWrite(LED_CENTRE, LOW); // turn off central LED delay (100); output_sensor(); turn_off_servos(); busyRed = 0; } void turn_off_servos(){ // detach servos so they're not engaged servo_pivot.detach(); servo_pitch.detach(); servo_wings.detach(); } void activate_servos(){ // activate servos for movement servo_pivot.attach(PIVOT); servo_pitch.attach(PITCH); servo_wings.attach(WINGS); } /////////////////////////////////////////////////////////////////// //======================= CARA MIA OPERA ========================// /////////////////////////////////////////////////////////////////// void Cara_Mia(){ busyRed = 1; WriteNet(); digitalWrite(LED_CENTRE, HIGH); int randomWingPos; activate_servos(); unsigned long endTimer[] = {8200, 40000, 100000}; // unsigned long startTimer; for (int i=0; i <= 2; i++) { startTimer = millis(); mp3.playMp3FolderTrack(100 + i); while (millis() < (startTimer + endTimer[i])){ ReadNet(); WriteNet(); if (payload[4] == 0) { disengage(); busyRed = 0; WriteNet(); return;} // debugging print below. Delete or comment out as desired. Serial.print(F("millis() ")); Serial.print (millis()); Serial.print (" "); Serial.print(F("startTimer ")); Serial.print (startTimer); Serial.print (" "); Serial.print(F("endTimer ")); Serial.print (endTimer[i]); Serial.print (" "); Serial.print(F("End Time ")); Serial.print (endTimer[i] + startTimer); Serial.print (" "); Serial.print(F("previousMillis1 ")); Serial.print (previousMillis1); Serial.print (" "); Serial.println(); if ((millis() - previousMillis1) >= (850 + (i * 100))) { // 1050 randomWingPos = random(10 + (i*20), (60 + (i*20))); servo_wings.write(randomWingPos); previousMillis1 = millis(); } } } disengage(); busyRed = 0; goState = 0; mp3.stop(); ReadNet(); delay(1000); } /////////////////////////////////////////////////////////////////// //======================= CHATTY TIME ===========================// /////////////////////////////////////////////////////////////////// void Chatty_time(){ busyRed = 1; WriteNet(); int i = 0; int talk; int saying; int timeadder = 750; int talkTime = NormalTimings[i]; int randomPivotPos; activate_servos(); servo_wings.write(WINGopen); digitalWrite(LED_CENTRE, HIGH); do { ReadNet(); WriteNet(); //output_sensor(); // used for debugging... if (i >= 43) { // end of sequence busyRed = 0; WriteNet(); disengage(); return; } unsigned long currentMillis = millis(); // grab current time if ((unsigned long)(currentMillis - previousMillis3) >= talkTime) { if (chatSayings[i] < 100) { // RED Turret talking talk = chatSayings[i]; saying = chatSayings[i]; talkTime = (NormalTimings[saying] + timeadder); } else if ((chatSayings[i] > 99) && (chatSayings[i] < 200)) { // WHITE turret talking talk = 0; saying = chatSayings[i] - 100; talkTime = (NormalTimings[saying] + timeadder); } else { // BLUE turret talking talk = 0; saying = chatSayings[i] - 200; // sound file # of BLUE talkTime = (DefectiveTimings[saying] + timeadder); // Time for that saying } if (talk == 0) { digitalWrite(LED_CENTRE, LOW); } else { digitalWrite(LED_CENTRE, HIGH); mp3.playMp3FolderTrack(talk); } randomPivotPos = random(minPIVOT, maxPIVOT); servo_pivot.write(randomPivotPos); Serial.println(); Serial.print(F("i: ")); Serial.print (i); Serial.print ("\t"); Serial.print(F("chatSayings[i] ")); Serial.print (chatSayings[i]); Serial.print ("\t"); Serial.print(F("Saying ")); Serial.print (saying); Serial.print ("\t"); Serial.print(F("talk ")); Serial.print (talk); Serial.print ("\t"); Serial.print(F("chat time ")); Serial.print (talkTime); Serial.print ("\t"); Serial.print(F("busyRed: "));Serial.print (busyRed); Serial.print (" "); previousMillis3 = millis(); i++; } } while (payload[4] == 1); busyRed = 0; WriteNet(); digitalWrite(LED_CENTRE, LOW); disengage(); } /////////////////////////////////////////////////////////////////// //======================= MANUAL CONTROL =======================// /////////////////////////////////////////////////////////////////// void ManualControl(){ int servoWings; int servoPitch; int servoPivot; activate_servos(); servo_wings.write(WINGopen); digitalWrite(LED_CENTRE, HIGH); ReadNet(); do { output_sensor(); ReadNet(); servoPivot = map(payload[0], 1023, 0, minPIVOT, maxPIVOT); servoPitch = map(payload[1], 1023, 0, minPITCH, maxPITCH); servo_pivot.write(servoPivot); servo_pitch.write(servoPitch); unsigned long currentMillis = millis(); // grab current time if (payload[3] == 0) { if ((unsigned long)(currentMillis - previousMillis1) >= 2500) { randomPic = random(1, 20); mp3.playMp3FolderTrack(randomPic); previousMillis1 = millis(); } } if (payload[2] == 0){ fire(); } } while (payload[5] == 3); disengage(); busyRed = 0; WriteNet(); digitalWrite(LED_CENTRE, LOW); } /////////////////////////////////////////////////////////////////// //========================= RECEIVING ===========================// /////////////////////////////////////////////////////////////////// void ReadNet(){ network.update(); if ( network.available() ) { RF24NetworkHeader header; network.peek(header); network.read(header, &payload, sizeof(payload));} // Read the package } /////////////////////////////////////////////////////////////////// //========================= SENDING =============================// /////////////////////////////////////////////////////////////////// void WriteNet(){ network.update(); RF24NetworkHeader header4(MTC); bool ok4 = network.write(header4, &busyRed, sizeof(busyRed)); /* if (ok4) { // used for debugging... Serial.print("MTC ok4 ");} else { Serial.print("------- ");} */ }
Bagian dan Enclosure Kustom
Lens%2Bv3b.stl
Body-RHS%2Bv4.stl
Body-LHS%2Bw%2Bcomponents%2Bv4.stl
Wing%2BRH%2Bbottom%2Bv4.stl
Wing%2BRH%2Btop%2Bv4.stl
Wing%2BLH%2Bbottom%2Bv4.stl
Wing%2BLH%2Btop%2Bv4.stl
Pitch%2BFrame%2Bv4.stl
Leg%2B-%2BREAR%2Bv4.stl
Infill%2Bstrips%2Bv3.stl
Leg%2B-%2BREAR%2Btest.stl
Pitch%2BFrame%2B-%2Bmodified%2B1.stl
Arduino%2BBase%2Bw%2BRadio.stl
Skema
Skema yang saya buat untuk membangun turut.