Membuat Boneka Interaktif Tema Squid Game dengan Arduino UNO – Proyek DIY
Komponen & Perlengkapan
RGB Diffused Common Cathode
Mata
×
2
SparkFun 7-Segment Serial Display - Red
Timer
×
1
Texas Instruments Shift Register- Parallel to Serial
Tampilan 7 segmen 4 digit
×
1
LCD Alphanumerik, 16 x 2
Tampilan menu
×
1
IR receiver (umum)
Untuk kontrol jarak jauh
×
1
Arduino UNO
Otak
×
1
SG90 Micro-servo motor
Putar kepala
×
1
PIR Motion Sensor (umum)
Deteksi gerakan
×
1
Ultrasonic Sensor - HC-SR04 (Generic)
Deteksi jarak untuk menang atau kalah
×
1
DFRobot DFPlayer - A Mini MP3 Player
Suara
×
1
Pushbutton Switch, Momentary
tombol menu dan tombol mulai
×
1
Alat & Mesin yang Diperlukan
3D Printer (umum)
Aplikasi & Layanan Online
Arduino IDE
Tentang Proyek Ini
Saya membuat boneka squid games yang fungsional sepenuhnya. Dia bermain permainan lampu merah-lampu hijau dengan Anda. Lengkap dengan kepala berputar, mata berwarna, dan dia bicara! Dia menggunakan deteksi ultrasonik dan gerakan untuk menentukan apakah Anda menang atau kalah. Tapi jangan khawatir, jika kalah dia hanya bertanya apakah Anda ingin bermain lagi.
Tonton video dan beritahu saya apa pendapat Anda.
Saya menggunakan setiap pin pada Arduino UNO! Yang belum pernah saya lakukan sebelumnya, jadi ini adalah pencapaian bagi saya. Proyek ini memakan waktu 3 minggu untuk dibuat dengan 1 minggu yang khusus untuk mencetak! Memakan waktu 6 hari untuk mencetak boneka ini. 1 minggu untuk perakitan dan minggu lain untuk mengedit video.
ELEGOO memberikan saya kit UNO gratis jika saya membuat video untuk mereka, jadi itulah mengapa saya membuat boneka ini. Pilihan adalah ini atau membuat escape room. Saya senang mereka memilih proyek ini. Saya harap orang-orang menikmatinya karena ini adalah proyek yang menyenangkan dan tampak sangat bagus serta menakutkan banyak orang. Tapi yang lebih penting, itu berfungsi.
Ini adalah semua bagian yang akan saya gunakan untuk build ini.
1. Mulai Mencetak
Pencetak akan memakan waktu lama. Memakan waktu 6 hari untuk mencetak seluruh boneka. Saya juga menggunakan filamen berwarna berbeda sehingga saya dapat mengurangi jumlah cat.
Saya remix model yang saya temukan di thingiverse.com, mengosongkan tengahnya, dan menambahkan lubang akses untuk elektronika. Saya juga memodifikasi pelat dada untuk Servo dan Ultra Sonic agar dapat dipasang.
2. Tidak ada yang suka melukai
Waktu untuk melukai. Saya menggunakan cat spray umum untuk ini. Saya melukai bagian dalam kepala boneka (dimasukkan mata) sehingga LED untuk mata tidak akan membuat seluruh wajah bersinar. Meskipun ini mungkin adalah efek yang Anda cari. Saya hanya ingin mata yang bersinar.
3. Magnet menarik tapi lem menempel
Salah satu cara untuk memasang semua anggota badan boneka adalah melelehkan magnet ke dalam plastik. Ini jika Anda ingin dapat melepasnya. Jika saya akan melakukan proyek ini lagi, saya mungkin hanya akan menempel semua anggota badannya. Menurut saya sekarang, tidak ada keuntungan besar menggunakan magnet selain dia dapat masuk ke kotak yang lebih kecil untuk penyimpanan jika Anda ingin. Satu hal yang tidak harus Anda pasang saat ini adalah kepala.
4. Mudah pada mata
Mulailah dengan langkah termudah, mata. Saya menggunakan LED tiga warna untuk mata. Seperti yang Anda ketahui, Anda dapat mencampur dan mencocokkan warna RGB untuk mendapatkan warna dasar apa pun yang Anda inginkan. Saya tetap pada warna primer dan sekunder sehingga saya tidak perlu mem-PWM sinyal. Tapi Anda dapat jika Anda mencarinya.
Pin terpanjang adalah ground, itu akan menjadi pin 2.
Hubungkan LED seperti yang digambarkan menggunakan resistor 220ohm untuk setiap kawat selain ground.
Untuk pemasangan, saya hanya menempel LED se dekat mungkin dengan pusat mata tetapi di sisi belakang. Pastikan kawat cukup panjang untuk melewati leher dan ke bagian bawah tubuhnya.
5. Menu LCD
Komponen termudah berikutnya adalah layar LCD 16x2. Anda harus menggunakan layar LCD dengan adapter I2C. Ini akan membuat hidup Anda jauh lebih mudah dan mengurangi jumlah IO dari 6 menjadi 2. Setelah terhubung, LCD harus menyala dengan "Welcome to the squid games!" di layar.
Untuk pemasangan, saya mencetak lingkaran tebal 1mm. Saya membuatnya tipis sehingga saya dapat melentukannya pada punggung boneka dengan heat gun. Ini jauh lebih mudah daripada mencari tahu kontur punggungnya (setidaknya untuk saya). Saya memasang insert berulir untuk layar dengan dadu di sisi belakang untuk mengamankan layar dan pemasang layar ke tubuh.
6. Hanya burung hantu yang memutar kepala 180 derajat
Servo sulit karena satu alasan utama, saya tidak menggunakan library servo. Saya tahu itu terdengar aneh tapi saya harus menggunakan timer1 untuk pembaruan tampilan 4 digit dan library servo juga menggunakan ini. Untungnya, servo adalah 0 derajat atau 180 derajat dan tidak ada di antaranya membuat ini jauh lebih mudah.
Timer1 diatur untuk interval .5ms, 2000hz. Periode servo adalah 20ms. Pada 0 derajat pin hanya perlu tinggi untuk 2 hitungan dan rendah selama sisa periode. Untuk 180 derajat pin perlu tinggi untuk 4 hitungan dan rendah selama sisa waktu.
Ada pemasang yang bagus di pelat dada untuk servo. Anda dapat mengobrakannya atau menempelkannya. Saya menggunakan epoxy untuk mengamankan servo ke pelat dada karena itu juga akan menambah kekuatan pada pelat dada dan semoga mencegahnya dari kerusakan.
7. Suara seperti kelelawar
Selanjutnya kita akan memasang modul jarak ultrasonik. Saya memperbarui ini setiap 250ms. Ini juga memiliki lokasi pemasangan yang bagus di pelat dada. Hanya ada 2 kawat untuk modul ini.
Saya menggunakan epoxy untuk memasang ultrasonik ke pelat dada.
8. Tanpa tali
Sensor IR untuk remote hanya diperlukan jika Anda ingin mengontrol permainan. Saya pikir ini akan menyenangkan tapi saya tidak benar-benar menggunakan mode ini, permainan otomatis sudah menyenangkan.
Saya memilih untuk memasang sensor IR di dalam klip di rambut boneka. Anda jelas dapat memilih untuk meletakkannya di tempat lain. Saya mencoba menyembunyikannya tapi mungkin ada tempat yang lebih baik karena IR tidak selalu melihat remote ketika dia memutar kelelahannya dan sensor berada di sisi lain.
9. Waktu untuk Waktu
Selanjutnya kita akan menyiapkan tampilan timer. Ini banyak pekerjaan untuk tampilan 4 digit. Saya akan menyertakan diagram koneksi dari ELEGOO. Permainan hanya hingga 5 menit jadi saya juga menghapus penggunaan digit paling signifikan. Tapi Anda dapat memutuskan untuk mempertahankannya jika Anda memiliki pin IO yang tersedia. Untuk memperbarui tampilan Anda harus mencicli LED sangat cepat karena Anda hanya dapat memiliki satu digit aktif pada satu waktu. Itu adalah alasan mereka terlihat berkedip ketika ditonton melalui kamera. Saya menggunakan refresh rate 2ms yang cukup cepat sehingga Anda tidak dapat melihat kedipan. Pada 5ms saya mulai dapat melihatnya berkedip ketika melihat tampilan di penglihatan samping Anda. Selain itu, Anda akan membutuhkan shift register 74HC595.
Pemasang tampilan tidak menyenangkan. Saya memutuskan bahwa terbaik adalah mengintegrasikan tampilan ke dalam pinggangnya. Boneka asli di Squid Games tentu saja tidak memiliki pinggang, tapi korban harus dilakukan untuk mendapatkan tampilan ini padanya. Jika Anda memilih rute ini juga, maskir persegi dengan ukuran yang sama dengan tampilan lalu potong dengan Dremel. Saya kemudian menggunakan epoxy putty untuk menambahkan transisi bertahap ke tampilan. Tapi ini tidak diperlukan, saya hanya pikir terlihat lebih baik dengan cara ini.
Saya memasang 74HC595 ke prototype shield, jika tidak Anda akan memiliki kawat yang kemana-mana. Solusi alternatif adalah menggunakan tampilan timer yang berbeda yang memiliki komunikasi yang lebih nyaman dengan lebih sedikit pin.
10. Saya lihat Anda bergerak
Detector gerakan adalah hal yang aneh. Benda ini menggunakan inframerah untuk mendeteksi gerakan. Salah satu hal yang saya pelajari adalah sensor ini membutuhkan waktu untuk memanaskan. Saat menyala itu membutuhkan 1 menit untuk memanaskan. Itulah mengapa ada waktu startup 1 menit untuk boneka. Ketidaknyamanan lain dengan modul ini adalah yang paling cepat dapat memperbarui deteksi gerakan adalah sekitar 5 detik. Ketidaknyamanan terakhir adalah seberapa sensitif sensor ini. Bahkan dengan sensitivitas diputar sepenuhnya ke bawah, itu masih dapat melihat gerakan terkecil dan kadang-kadang gerakan yang bahkan saya tidak tahu maksudnya. Untuk membantu mencegah "false positives" ini saya memasang sensor di dalam kotak pelindung kuda. Kotak tersebut memiliki lubang kecil (7mm) untuk detector gerakan melihat keluar. Sebagai bonus, ini mencegah Anda harus memasang sensor raksasa ini di luar boneka. Sensor gerakan hanya memiliki satu kawat biner untuk umpan balik, gerakan atau tidak.
Untuk memasang sensor, saya mencetak pelindung kuda dan menempelkannya di dalam boneka. Saya kemudian membuat lubang melalui tubuh. Saya menggunakan insert berulir di kotak pelindung untuk mengamankan sensor gerakan.
11. Jangan tekan tombol saya
Akhirnya, kita berada di tombol. Jika Anda memiliki pin IO tambahan, lebih mudah untuk menghubungkan masing-masing ke input digital. Tapi saya tidak memiliki kemewahan ini untuk UNO. Sebaliknya saya harus menggunakan input analog untuk membaca nilai resistor untuk menentukan tombol mana yang ditekan. Nilai yang saya gunakan adalah, 1K, 2K, dan 5K. Lalu saya memiliki resistor 220 Ohm untuk menarik input analog ke bawah. Jika tidak, itu akan melayang dan mendapatkan tekanan tombol acak.
Saya memasang tombol di plate pemasang yang sama dengan LCD. Ini tidak mudah tapi saya tidak memiliki cara yang lebih baik. Menyolder kawat pada tombol ini lalu membuatnya melewati lubang kecil yang dibuat di plastik akan menguji kesabaran Anda.
12. Apakah Anda mendengar saya sekarang?
Langkah terakhir dan mungkin yang paling penting adalah modul suara. Ini akan menggunakan port serial pada UNO jadi Anda harus menambahkan resistor 1K Ohm pada pin Tx dan Rx jika tidak, Anda akan terblokir dari pemrograman UNO setelah koneksi ini dibuat. Selain itu, Anda akan perlu menggunakan pin "busy" sehingga UNO tahu bahwa suara sudah memutar. Ini sangat penting jika Anda memiliki MP3 yang diputar berturut-turut.
Saya memasang modul pemutar MP3 di prototype shield. Shield ini membuat pemasangan komponen seperti ini sangat nyaman karena kemudian tinggal dihubungkan ke UNO. Modul ini membutuhkan speaker 8ohm dan memiliki output 3W. Speaker hanya ditempel di dasar boneka. Saya membuat lubang kecil di bawah speaker agar suara keluar lebih baik.
13. Pasang UNO
Pasang UNO pada platform dan colok prototype shield ke UNO. Pastikan Anda telah memberi label pada semua kawat, jika tidak Anda mungkin tidak tahu kemana mereka pergi saat ini. Dengan sedikit negosiasi, Anda dapat memasang UNO di dalam boneka dengan semua kawat terhubung.
Saya menggunakan insert berulir untuk memasang platform di dasar boneka.
14. Tes Perbaiki Tes
Ini saat Anda memakai topi debugging. Saya dapat memberitahu Anda bahwa perangkat lunak berfungsi di GitHub jadi setidaknya itu satu hal yang tidak perlu di-debug. Tapi silakan lakukan jika Anda ragu dan kirimkan pembaruan apa pun yang Anda temukan.
15. Ayo Bermain
Waktu untuk mencobanya dan bermain sebuah permainan. Inilah cara permainan diprogram.
Saat startup dia memutar kepala ke depan.
Sensor gerakan memakan waktu satu menit untuk menyala. Jadi ada timer saat memulai. Di pertengahan dia terkekeh dan memutar kelelahannya. Lalu mengumumkan ketika siap.
Bergantung pada apakah Anda mengatur permainan ke remote dia mengatakan hal yang berbeda. Dalam mode Auto dia meminta Anda untuk menekan tombol play. Dalam kasus saya, ini adalah tombol paling kanan. Dalam mode remote dia akan meminta Anda untuk menekan tombol power ketika Anda siap. Lalu tekan tombol play untuk beralih ke lampu merah atau lampu hijau.
Jadi ketika Anda siap, tekan tombol go dan dia akan memberi Anda 10 detik untuk masuk ke posisi. Biasanya orang lain di sekitar akan menekan tombol ini.
Lalu permainan dimulai. Dia akan mulai dengan Lampu Hijau. Untuk lampu hijau Anda harus masuk dalam 50cm untuk memicu kemenangan. Jika Anda dalam 100cm dia akan menunjukkan bahwa Anda semakin dekat. Lampu hijau hanya menggunakan sonar.
Untuk lampu merah sensor gerakan dan sensor jarak digunakan. Jika Anda bergerak cukup untuk sensor gerakan terpicu atau jika Anda bergerak lebih dari 10cm ke depan, Anda akan kalah permainan. Anda juga akan kalah permainan jika waktu habis. Dia akan mengingatkan Anda bahwa waktu hampir habis pada 5 detik terakhir.
Fitur keren terakhir adalah dia juga akan berbicara dengan suara Korea untuk lampu merah. Ini adalah fitur menu. Tekan tombol paling kiri untuk menyesuaikan item menu, dan tombol tengah untuk menyesuaikan opsi item.
16. Tonton Video
Video ini memakan waktu lama untuk diedit. Saya mungkin sudah 30 jam hanya untuk mengedit. Tapi itu menyenangkan untuk dibuat. Saya pikir hasilnya bagus dan lucu tapi saya ingin Anda melihatnya sendiri. Silakan beritahu saya apa pendapat Anda dan jika Anda memiliki pertanyaan.
https://youtu.be/jd_4a4x3uCw
Terima Kasih!
Kode
Sketsa Boneka Squid Game
Sketsa Boneka Squid GameC/C++
Ini akan mengontrol semua sensor dan logika permainan.
/// CodeMakesItGo Dec 2021 #include <DFPlayerMini_Fast.h> #include <FireTimer.h> #include <IRremote.h> #include <LiquidCrystal_I2C.h> #include <SoftwareSerial.h> #include <SR04.h> #include <Wire.h> /*-----( Analog Pins )-----*/ #define BUTTONS_IN A0 #define SONAR_TRIG_PIN A1 #define SONAR_ECHO_PIN A2 #define MOTION_IN A3 /*-----( Digital Pins )-----*/ #define LED_BLUE 13 #define LED_GREEN 12 #define LED_RED 11 #define SEGMENT_DATA 10 // DS #define SEGMENT_CLOCK 9 // SHCP #define SEGMENT_LATCH 8 // STCP #define SEGMENT_1_OUT 7 #define SEGMENT_2_OUT 6 #define SEGMENT_3_OUT 5 #define IR_DIGITAL_IN 4 // IR Remote #define SERVO_OUT 3 #define DFPLAYER_BUSY_IN 2 /*-----( Configuration )-----*/ #define TIMER_FREQUENCY 2000 #define TIMER_MATCH (int)(((16E+6) / (TIMER_FREQUENCY * 64.0)) - 1) #define TIMER_2MS ((TIMER_FREQUENCY / 1000) * 2) #define VOLUME 30 // 0-30 #define BETTER_HURRY_S 5 // play clip at 5 seconds left #define WIN_PROXIMITY_CM 50 // cm distance for winner #define CLOSE_PROXIMITY_CM 100 // cm distance for close to winning #define GREEN_LIGHT_MS 3000 // 3 seconds on for green light #define RED_LIGHT_MS 5000 // 5 seconds on for green light #define WAIT_FOR_STOP_MOTION_MS 5000 // 5 seconds to wait for motion detection to stop /*-----( Global Variables )-----*/ static unsigned int timer_1000ms = 0; static unsigned int timer_2ms = 0; static unsigned char digit = 0; // digit for 4 segment display static int countDown = 60; // Start 1 minute countdown on startup static const int sonarVariance = 10; // detect movement if greater than this static bool gameInPlay = false; static bool faceTree = false; static bool remotePlay = false; // 0 , 1, 2, 3, 4, 5, 6, 7, 8, 9, A, B, C, D, E, F, NULL const unsigned char numbers[] = {0x3f, 0x06, 0x5b, 0x4f, 0x66, 0x6d, 0x7d, 0x07, 0x7f, 0x6f, 0x77, 0x7c, 0x39, 0x5e, 0x79, 0x71, 0x00}; const char *MenuItems[] = {"Language", "Play Time", "Play Type"}; typedef enum { LANGUAGE, PLAYTIME, PLAYTYPE, MENUITEM_COUNT } MenuItemTypes; const char *Languages[] = {"English", "Korean"}; typedef enum { ENGLISH, KOREAN, LANUAGE_COUNT } LanguageTypes; static int language = 0; const char *PlayTime[] = {"300", "240", "180", "120", "60", "30", "15"}; typedef enum { PT300, PT240, PT180, PT120, PT60, PT30, PT15, PLAYTIME_COUNT } PlayTimeTypes; const int playTimes[] = {300, 240, 180, 120, 60, 30, 15}; static int playTime = 0; const char *PlayType[] = {"Auto", "Remote"}; typedef enum { AUTO, REMOTE, PLAYTYPE_COUNT } PlayTypeTypes; static int playType = 0; typedef enum { BLACK, RED, GREEN, BLUE, WHITE, YELLOW, PURPLE } EyeColors; EyeColors eyeColor = BLACK; typedef enum { WARMUP, WAIT, READY, GREENLIGHT, REDLIGHT, WIN, LOSE } GameStates; static GameStates gameState = WARMUP; /*-----( Class Objects )-----*/ FireTimer task_50ms; FireTimer task_250ms; DFPlayerMini_Fast dfPlayer; SR04 sonar = SR04(SONAR_ECHO_PIN, SONAR_TRIG_PIN); IRrecv irRecv(IR_DIGITAL_IN); decode_results irResults; LiquidCrystal_I2C lcdDisplay(0x27, 16, 2); // 16x2 LCD display /*-----( Functions )-----*/ void translateIR() // takes action based on IR code received { switch (irResults.value) { case 0xFFA25D: Serial.println("POWER"); if (gameState == WAIT) { gameInPlay = true; } break; case 0xFFE21D: Serial.println("FUNC/STOP"); break; case 0xFF629D: Serial.println("VOL+"); break; case 0xFF22DD: Serial.println("FAST BACK"); break; case 0xFF02FD: Serial.println("PAUSE"); remotePlay = !remotePlay; break; case 0xFFC23D: Serial.println("FAST FORWARD"); break; case 0xFFE01F: Serial.println("DOWN"); break; case 0xFFA857: Serial.println("VOL-"); break; case 0xFF906F: Serial.println("UP"); break; case 0xFF9867: Serial.println("EQ"); break; case 0xFFB04F: Serial.println("ST/REPT"); break; case 0xFF6897: Serial.println("0"); break; case 0xFF30CF: Serial.println("1"); break; case 0xFF18E7: Serial.println("2"); break; case 0xFF7A85: Serial.println("3"); break; case 0xFF10EF: Serial.println("4"); break; case 0xFF38C7: Serial.println("5"); break; case 0xFF5AA5: Serial.println("6"); break; case 0xFF42BD: Serial.println("7"); break; case 0xFF4AB5: Serial.println("8"); break; case 0xFF52AD: Serial.println("9"); break; case 0xFFFFFFFF: Serial.println(" REPEAT"); break; default: Serial.println(" other button "); } } bool isPlayingSound() { return (digitalRead(DFPLAYER_BUSY_IN) == LOW); } void updateTimeDisplay(unsigned char digit, unsigned char num) { digitalWrite(SEGMENT_LATCH, LOW); shiftOut(SEGMENT_DATA, SEGMENT_CLOCK, MSBFIRST, numbers[num]); // Active LOW digitalWrite(SEGMENT_1_OUT, digit == 1 ? LOW : HIGH); digitalWrite(SEGMENT_2_OUT, digit == 2 ? LOW : HIGH); digitalWrite(SEGMENT_3_OUT, digit == 3 ? LOW : HIGH); digitalWrite(SEGMENT_LATCH, HIGH); } void updateServoPosition() { static int servoPulseCount = 0; static bool lastPosition = false; // Only get new value at start of period if (servoPulseCount == 0) lastPosition = faceTree; if (!lastPosition) // 180 degrees { digitalWrite(SERVO_OUT, servoPulseCount < 5 ? HIGH : LOW); } else // 0 degrees { digitalWrite(SERVO_OUT, servoPulseCount < 1 ? HIGH : LOW); } servoPulseCount = (servoPulseCount + 1) % 40; // 20ms period } void updateMenuDisplay(const int button) { static int menuItem = 0; static int menuOption = 0; switch (button) { case 1: menuItem = (menuItem + 1) % MENUITEM_COUNT; if (menuItem == LANGUAGE) { menuOption = language; } else if (menuItem == PLAYTIME) { menuOption = playTime; } else if (menuItem == PLAYTYPE) { menuOption = playType; } else { menuOption = 0; } break; case 2: if (menuItem == LANGUAGE) { menuOption = (menuOption + 1) % LANUAGE_COUNT; language = menuOption; } else if (menuItem == PLAYTIME) { menuOption = (menuOption + 1) % PLAYTIME_COUNT; playTime = menuOption; } else if (menuItem == PLAYTYPE) { menuOption = (menuOption + 1) % PLAYTYPE_COUNT; playType = menuOption; } else { menuOption = 0; } break; case 3: if (gameState == WAIT) { gameInPlay = true; } if (gameState == GREENLIGHT || gameState == REDLIGHT) { gameInPlay = false; } default: break; } if (menuOption != -1) { lcdDisplay.clear(); lcdDisplay.setCursor(0, 0); lcdDisplay.print(MenuItems[menuItem]); lcdDisplay.setCursor(0, 1); if (menuItem == LANGUAGE) { lcdDisplay.print(Languages[menuOption]); } else if (menuItem == PLAYTIME) { lcdDisplay.print(PlayTime[menuOption]); } else if (menuItem == PLAYTYPE) { lcdDisplay.print(PlayType[menuOption]); } else { lcdDisplay.print("unknown option"); } } else { menuItem = 0; menuOption = 0; } } void handleButtons() { static int buttonPressed = 0; int value = analogRead(BUTTONS_IN); if (value < 600) // buttons released { if (buttonPressed != 0) updateMenuDisplay(buttonPressed); buttonPressed = 0; return; } else if (value < 700) { Serial.println("button 1"); buttonPressed = 1; } else if (value < 900) { Serial.println("button 2"); buttonPressed = 2; } else if (value < 1000) { Serial.println("button 3"); buttonPressed = 3; } else { Serial.println(value); buttonPressed = 0; } } static int lastSonarValue = 0; void handleSonar() { int value = sonar.Distance(); if (value > lastSonarValue + sonarVariance || value < lastSonarValue - sonarVariance) { Serial.println(value); lastSonarValue = value; } } static int lastMotion = 0; void handleMotion() { int value = digitalRead(MOTION_IN); if (value != lastMotion) { lastMotion = value; } if (lastMotion) Serial.println("Motion Detected"); } void handleLeds() { digitalWrite(LED_RED, eyeColor == RED || eyeColor == WHITE || eyeColor == PURPLE || eyeColor == YELLOW ? HIGH : LOW); digitalWrite(LED_GREEN, eyeColor == GREEN || eyeColor == WHITE || eyeColor == YELLOW ? HIGH : LOW); digitalWrite(LED_BLUE, eyeColor == BLUE || eyeColor == WHITE || eyeColor == PURPLE ? HIGH : LOW); } void handleRemote() { // have we received an IR signal? if (irRecv.decode(&irResults)) { translateIR(); irRecv.resume(); // receive the next value } } // Timer 1 ISR ISR(TIMER1_COMPA_vect) { // Allow this ISR to be interrupted sei(); updateServoPosition(); if (timer_1000ms++ == TIMER_FREQUENCY) { timer_1000ms = 0; countDown--; if (countDown < 0) { countDown = 0; } } if (timer_2ms++ == TIMER_2MS) { timer_2ms = 0; if (digit == 0) updateTimeDisplay(1, countDown % 10); if (digit == 1) updateTimeDisplay(2, (countDown / 10) % 10); if (digit == 2) updateTimeDisplay(3, (countDown / 100) % 10); if (digit == 3) updateTimeDisplay(4, 16); digit = ((digit + 1) % 4); } } void playGame() { static int sequence = 0; static long internalTimer = millis(); static bool closerClipPlayed = false; static bool hurryUpClipPlayed = false; static int captureDistance = 0; long currentTimer = internalTimer; if(isPlayingSound()) return; if (gameState == WARMUP) { // power up sound if (sequence == 0) { Serial.println("Warming Up"); dfPlayer.playFolder(1, 1); faceTree = false; eyeColor = YELLOW; sequence++; } // laugh at 30 else if (sequence == 1 && countDown <= 30) { Serial.println("Laughing"); dfPlayer.playFolder(1, 2); faceTree = true; sequence++; } else if (sequence == 2 && countDown <= 10) { Serial.println("Almost ready"); dfPlayer.playFolder(1, 3); sequence++; } else if (sequence == 3 && countDown == 0) { Serial.println("All ready, lets play"); dfPlayer.playFolder(1, 4); faceTree = false; sequence = 0; gameState = WAIT; gameInPlay = false; } } else if (gameState == WAIT) { currentTimer = millis(); if (gameInPlay) { gameState = READY; remotePlay = false; sequence = 0; } // Every 30 seconds else if (currentTimer - internalTimer > 30000 || sequence == 0) { internalTimer = millis(); if(playType == AUTO) { // press the go button when you are ready Serial.println("Press the go button when you are ready"); dfPlayer.playFolder(1, 5); } else { Serial.println("Press the power button on the remote when you are ready"); dfPlayer.playFolder(1, 6); } // eyes are blue eyeColor = BLUE; // facing players faceTree = false; gameInPlay = false; sequence++; } } else if (gameState == READY) { currentTimer = millis(); if (sequence == 0) { // get in position, game will start in 10 seconds Serial.println("Get in position."); dfPlayer.playFolder(1, 7); countDown = 10; // eyes are green eyeColor = WHITE; // facing players faceTree = false; sequence++; internalTimer = millis(); } else if (sequence == 1) { if (playType == REMOTE) { if (remotePlay) sequence++; } else sequence++; } else if (sequence == 2) { // at 0 seconds, here we go! if (countDown == 0) { countDown = playTimes[playTime]; Serial.print("play time set to "); Serial.println(countDown); Serial.println("Here we go!"); dfPlayer.playFolder(1, 8); gameState = GREENLIGHT; sequence = 0; } } } else if (gameState == GREENLIGHT) { currentTimer = millis(); if (sequence == 0) { // eyes are green eyeColor = GREEN; // play green light Serial.println("Green Light!"); dfPlayer.playFolder(1, 9); sequence++; } else if(sequence == 1) { // play motor sound dfPlayer.playFolder(1, 19); // facing tree faceTree = true; sequence++; internalTimer = millis(); } else if (sequence == 2) { // wait 3 seconds or until remote // switch to red light if (playType == AUTO && currentTimer - internalTimer > GREEN_LIGHT_MS) { sequence = 0; gameState = REDLIGHT; } else if (playType == REMOTE && remotePlay == false) { sequence = 0; gameState = REDLIGHT; } else { // look for winner button or distance if (gameInPlay == false || lastSonarValue < WIN_PROXIMITY_CM) { sequence = 0; gameState = WIN; } else if (countDown <= 0) { Serial.println("Out of Time"); dfPlayer.playFolder(1, 16); sequence = 0; gameState = LOSE; } // at 2 meters play "your getting closer" else if (lastSonarValue < CLOSE_PROXIMITY_CM && closerClipPlayed == false) { Serial.println("Getting closer!"); dfPlayer.playFolder(1, 11); closerClipPlayed = true; } // if less than 5 seconds play better hurry else if (countDown <= BETTER_HURRY_S && hurryUpClipPlayed == false) { Serial.println("Better Hurry"); dfPlayer.playFolder(1, 12); hurryUpClipPlayed = true; } } } } else if (gameState == REDLIGHT) { currentTimer = millis(); if (sequence == 0) { // eyes are red eyeColor = RED; Serial.println("Red Light!"); if(language == ENGLISH) { // play red light English dfPlayer.playFolder(1, 10); } else { // play red light Korean dfPlayer.playFolder(1, 18); } sequence++; } else if(sequence == 1) { // play motor sound dfPlayer.playFolder(1, 19); // facing players faceTree = false; // Save current distance captureDistance = lastSonarValue; sequence++; internalTimer = millis(); } else if (sequence == 2) { //wait for motion to settle if (lastMotion == 0 || (currentTimer - internalTimer) > WAIT_FOR_STOP_MOTION_MS) { internalTimer = millis(); sequence++; Serial.println("Done settling"); } Serial.println("Waiting to settle"); } else if (sequence == 3) { // back to green after 5 seconds if (playType == AUTO && currentTimer - internalTimer > RED_LIGHT_MS) { sequence = 0; gameState = GREENLIGHT; } else if (playType == REMOTE && remotePlay == true) { sequence = 0; gameState = GREENLIGHT; } else { // can't push the button while red light // detect movement // detect distance change if (gameInPlay == false || lastMotion == 1 || lastSonarValue < captureDistance) { Serial.println("Movement detected!"); dfPlayer.playFolder(1, 15); sequence = 0; gameState = LOSE; } if (countDown == 0) { Serial.println("Out of time"); dfPlayer.playFolder(1, 16); sequence = 0; gameState = LOSE; } } } } else if (gameState == WIN) { if (sequence == 0) { // play winner sound Serial.println("You Won!"); dfPlayer.playFolder(1, 13); // eyes are white eyeColor = WHITE; // facing players faceTree = false; sequence++; } else if (sequence == 1) { // wanna play again? Serial.println("Play Again?"); dfPlayer.playFolder(1, 17); gameInPlay = false; countDown = 0; // go to wait gameState = WAIT; sequence = 0; } } else if (gameState == LOSE) { if (sequence == 0) { // sorry better luck next time Serial.println("Sorry, you lost"); dfPlayer.playFolder(1, 14); // eyes are purple eyeColor = PURPLE; // face players faceTree = false; sequence++; } else if (sequence == 1) { // wanna play again? Serial.println("Play Again?"); dfPlayer.playFolder(1, 17); gameInPlay = false; countDown = 0; // go to wait gameState = WAIT; sequence = 0; } } else { //Shouldn't ever get here gameState = WARMUP; } } void loop() /*----( LOOP: RUNS CONSTANTLY )----*/ { if (task_50ms.fire()) { handleRemote(); handleButtons(); } if (task_250ms.fire()) { handleSonar(); handleMotion(); handleLeds(); playGame(); Serial.println(isPlayingSound()); } } // Setup Timer 1 for 2000Hz void setupTimer() { cli(); //stop interrupts TCCR1A = 0; // set entire TCCR1A register to 0 TCCR1B = 0; // same for TCCR1B TCNT1 = 0; //initialize counter value to 0 // set compare match register OCR1A = TIMER_MATCH; // = (16*10^6) / (2000*64) - 1 (must be <65536), 2000Hz // turn on CTC mode TCCR1B |= (1 << WGM12); // Set CS11 and CS10 bits for 64 prescaler TCCR1B |= (1 << CS11) | (1 << CS10); // enable timer compare interrupt TIMSK1 |= (1 << OCIE1A); sei(); //allow interrupts } void setup() { Serial.begin(9600); pinMode(MOTION_IN, INPUT); pinMode(BUTTONS_IN, INPUT); pinMode(DFPLAYER_BUSY_IN, INPUT); pinMode(SERVO_OUT, OUTPUT); pinMode(LED_RED, OUTPUT); pinMode(LED_GREEN, OUTPUT); pinMode(LED_BLUE, OUTPUT); pinMode(SEGMENT_LATCH, OUTPUT); pinMode(SEGMENT_CLOCK, OUTPUT); pinMode(SEGMENT_DATA, OUTPUT); pinMode(SEGMENT_1_OUT, OUTPUT); pinMode(SEGMENT_2_OUT, OUTPUT); pinMode(SEGMENT_3_OUT, OUTPUT); irRecv.enableIRIn(); // Start the receiver dfPlayer.begin(Serial); // Use the standard serial stream for DfPlayer dfPlayer.volume(VOLUME); // Set the DfPlay volume lcdDisplay.init(); // initialize the lcd lcdDisplay.backlight(); // Turn on backlight setupTimer(); // Start the high resolution timer ISR // Display welcome message lcdDisplay.setCursor(0, 0); lcdDisplay.print("Welcome to the"); lcdDisplay.setCursor(0, 1); lcdDisplay.print("Squid Games!"); // short delay to display welcome screen delay(1000); task_50ms.begin(50); // Start the 50ms timer task task_250ms.begin(250); // Start the 250ms timer task }
Project files
Semua file untuk build inihttps://github.com/CodeMakesItGo/SquidGamesDoll
Schematics
Ini adalah cara saya menghubungkan semua komponen ke UNO.
The project repo
Semua file untuk build ini disimpan di sini. https://github.com/CodeMakesItGo/SquidGamesDoll