Differences

This shows you the differences between two versions of the page.

Link to this comparison view

pm:prj2023:adarmaz:tiltthemaze [2023/05/05 09:05]
adumitrescu2708
pm:prj2023:adarmaz:tiltthemaze [2023/05/30 02:58] (current)
adumitrescu2708 [Implementare]
Line 1: Line 1:
 ====== Tilt the maze - Dumitrescu Alexandra 333CA ====== ====== Tilt the maze - Dumitrescu Alexandra 333CA ======
 +===== Demo =====
 +{{url>​https://​drive.google.com/​file/​d/​1b7s5bXKdAkVjzZ-PGNajw2d06Bly8HcU/​preview}}
 ===== Introducere ===== ===== Introducere =====
 Proiectul constă într-un labirint ce poate fi rotit de jucător pe 2 axe OX și OY printr-un joystick. Motivația proiectului meu a constat în dorința automatizării unui joc din copilărie. Labirintul dispune 5 găuri, iar jucătorului îi este oferită o singură bilă magnetică. Jocul propune următoarele 2 moduri: Proiectul constă într-un labirint ce poate fi rotit de jucător pe 2 axe OX și OY printr-un joystick. Motivația proiectului meu a constat în dorința automatizării unui joc din copilărie. Labirintul dispune 5 găuri, iar jucătorului îi este oferită o singură bilă magnetică. Jocul propune următoarele 2 moduri:
Line 26: Line 28:
   - [[pm:​lab:​lab3-2023| Laborator 6.]] I2C pentru ecranul LCD   - [[pm:​lab:​lab3-2023| Laborator 6.]] I2C pentru ecranul LCD
   - [[pm:​lab:​lab3-2023| Laborator 3.]] PWM pentru servomotoare   - [[pm:​lab:​lab3-2023| Laborator 3.]] PWM pentru servomotoare
-<html><​a ​class="media mediafile mf_pdf"​ href="?do=export_pdf"​>Export to PDF</​a></​html>+=== Schema circuitului === 
 +{{:​pm:​prj2023:​adarmaz:​untitled_sketch_2_bb.png?​600|}} 
 +===== Software Design ===== 
 +==== Descriere generală ==== 
 +Pentru realizarea proiectului am utilizat Arduino IDE, în care am importat următoarele biblioteci 
 +  - **SoftwareSerial.h** - pentru comunicarea pe interfața serială între cele 2 Arduino-uri ((Un Arduino se ocupă de cele 2 servomotoare și de joystick, iar al doilea de senzori, LCD și leduri)) \\ 
 +  - **Servo.h** - pentru a deplasa cele două servomotoare ((Un servo acționează planul pe axa OX, iar al doilea pe OZ)) \\  
 +  - **LiquidCrystal_I2C.h** - pentru a scrie rezultatele obținute pe ecranul LCD cu modulul I2C incorporat \\ 
 +În cadrul proiectului,​ am utilizat și operații pe regiștri pentru \\  
 +  - **Întreruperi hardware** - pentru a detecta când este apăsat butonul joystickului am utilizat o întrerupere externă pe pinul PD2 
 +  - **Timer** - pentru a afișa durata jocului am setat Timerul 1 în modul CTC să genereze o întrerupere cu frecvența 1 Hz 
 +==== Implementare ==== 
 +**Arduino Uno 1. - Controller Servo & Joystick** - Primul Arduino are următoarele roluri \\ 
 + - primește date de la joystick\\ 
 + - încearcă să reducă pe cât posibil zgomotul posibil provocat de jucător pentru a asigura o deplasare lină a labirintului\\ 
 + - să mapeze datele de intrare din intervalul [0, 1024) în intervalul [25°, 45°)\\ 
 + - să asigure viteza între rotații să fie corespunzător de mică, tot pentru a asigura o deplasare lină a labirintului 
 + - să trimită la începutul jocului prin interfața serială Arduino-ului 2. informații primite de la joystick privind începerea jocului și alegerea game modului //Save the Ball// sau //Send the Ball//.\\  
 + - în cazul în care a fost selectat modul de joc //Save the ball//, Arduino-ul generează random la fiecare 10s o poziție pe OX și pe OY în care să se deplaseze labirintul.\\ 
 + Putem să le luăm pe rănd, în ordinea cronologică începerii jocului \\ \\ 
 + ​- ​ Prima dată, am setat un **JOYSTICK_DEADZONE** pentru a asigura că nu există niciun fel de zgomot în momentul în care labirintul stagnează în **SERVO_DEFAULT_POSITION** (35°), practic atunci când jucătorul nu mișcă joystick-ul sau îl mișcă suficient de puțin, labirintul trebuie să sea pe loc pentru a reduce zgomotele. Dacă datele depășesc **JOYSTICK_DEADZONE**,​ atunci vom mapa datele primite pe OX și OY în intervalul [25°, 45°). \\ 
 +<spoiler Implementarea deadzone-ului și poziționarea servomotoarelor.> 
 +<code C>  
 +int joystick_deadzone = 200; 
 +(...) 
 +void loop() { 
 +  int ox = analogRead(A3);​ 
 +  int oy = analogRead(A2);​ 
 +    
 +   /* check and adjust deadzone */ 
 +  if (abs(ox - 512) < joystick_deadzone) { 
 +    ox = 512; 
 +  } 
 + 
 +  if (abs(oy - 512) < joystick_deadzone) { 
 +    oy = 512; 
 +  } 
 +   
 +   int servo_ox_pos = map(ox, 0, 1023, 25, 45); 
 +   int servo_oy_pos = map(oy, 0, 1023, 25, 45); 
 +
 +</​code>​ 
 +</​spoiler>​ 
 + - Variabila **IS_START_GAME** detectează dacă jocul început sau nu. Pentru a monitoriza acest aspect, contorizăm folosind întreruperea hardware externă generată de apăsarea butonului joystickului,​ de câte ori a fost apăsat butonul. Prima apăsare anunță începerea jocului, iar a doua selectarea modului de joc. Deoarece pot apărea probleme la apăsare continuă a butonului, în sensul că întreruperea externă se poate activa de mai multe ori pentru o singură apăsare, am implementat o soluție în care se asigură că butonul este apăsat, apoi eliberat și abia apoi o altă apăsare este validată. //Jocul începe o dată ce s-au trimit cele 2 apăsări de buton, iar Arduino 2. trimite înapoi modul de joc selectat// \\ 
 +<spoiler Întreruperea externă pentru detectarea apăsării butonului și încheierea conexiunii.>​ 
 +<code C>  
 +void loop() {   
 +  if(mySerial.available() >1) { 
 +      byte finished ​mySerial.read();​ 
 +      is_start_game = true;  
 +      chosen_game_mode = finished; 
 +  } 
 +
 +(...) 
 +ISR(INT0_vect) { 
 +  int value = PIND & (1 << JOYSTICK_BUTTON);​ 
 +  if(is_button_pressed == true && old_value != 0) { 
 +    is_button_pressed_second = true; 
 +  } 
 +  if(value == 0) { 
 +    is_button_pressed = true;   
 +  } 
 +  old_value = value; 
 +
 +</​code>​ 
 +</​spoiler>​ 
 + - Pentru a nu aglomera comunicarea pe interfața serială (deoarece ar putea interveni desincronizări în primirea/​trimiterea datelor în Receiver / Transmitter sau datele ar putea fi primite cu o întârziere care nu este necesară), am propus o soluție în care pe interfață se comunică doar la începutul jocului căte un **singur byte la un moment dat, în care biții codifică starea actuală a joystickului**. Nu ne interesează decăt up/​low/​pressed pentru că interfața de pe LCD permite alegerea game modului prin swipe up/low. **Între cele Arduinouri se stabilește un protocol de comunicație. Ambele părți trebuie să cunoască decizia de game mode luat și, implicit încheierea conexiunii. Din punctul alegerii jocului, Arduino-urile au taskuri separate specializate pe modul de joc. **\\ 
 +^ button[0:​7] ​ ^ Semnification ^ 
 +| button[0] ​   | Button Pressed |  
 +| button[1] ​   | Up             |  
 +| button[2] ​   | Low            | 
 +| button[3] ​   | 2nd Button Pressed| ​ \\ 
 +{{:​pm:​prj2023:​adarmaz:​protocol2.png?500| Protocolul de comunicație între Arduino-uri}}\\ 
 + - O dată ce game modeul a fost setat se așteaptă un byte înapoi de sincronizare între cele 2 Arduino-uri,​ care semnifică game modeul selectat, după care începe jocul, pornind servomotoarele și dăndu-le ca direcție datele mapate [25°, 45°) de la joystick. Pentru a asigura o viteză acceptabilă astfel încât să nu se miște brusc/​dezordonat labirintul am lasat un delay pentru fiecare unghi în care se deplasează servomotorul.\\ \\ 
 + 
 +**Arduino Uno 2. - Controller Senzori & LCD & Led** - Al doilea Arduino are următoarele roluri \\ 
 +- Primind datele de pe interfața serială de la Arduino 1. setează logica celor 2 moduri de joc. \\ 
 +- Primul mod, //Save the Ball// așteaptă ca pentru fiecare gaură să detecteze senzorul magnetic bila magnetică. Dacă bila a fost detectată, se oprește și afișează timpul jocului. \\ 
 +- Al doilea mod, //Send The Ball//, generează random o gaură, implicit pornește LED-ul găurii respective, pentru a anunța jucătorul unde trebuie să trimită mingea, și așteaptă ca senzorii să detecteze fie dacă mingea a picat în gura corectă, fie nu. Va afișa rezultatul **FAIL/​PASS** și durata jocului. 
 +<spoiler Parsarea datelor primite prin protocolul de comunicare de Arduino2.>​ 
 +<code C> 
 +void loop() { 
 +if(mySerial.available() >2) { 
 +    /* extract info from protocol */ 
 +    byte received = mySerial.read();​ 
 +    byte button_pressed = (received & 0x01); 
 +    byte upper_cursor = (received ​>> 1) & 0x01; 
 +    byte lower_cursor = (received >> 2) & 0x01; 
 +    byte second_button_pressed = (received >> 3) & 0x01; 
 +  
 +    if(button_pressed && !start_game) { 
 +      start_game = true; 
 +      set_LCD_start_game();​ 
 +    } 
 + 
 +    if(start_game && !set_game_mode && (upper_cursor != 0 || lower_cursor != 0)) { 
 +      /* choose game mode */ 
 +      last_game_picked = upper_cursor != 0 ? 1 : 2;  
 +      byte cursor_LCD = upper_cursor != 0 ? 1 : 0; 
 +      if(cursor_LCD != previous_cursor_state) { 
 +        previous_cursor_state = cursor_LCD;​ 
 +        parse_cursor(cursor_LCD);​ 
 +      } 
 +    } 
 +  } 
 +</code> 
 +</​spoiler>​ 
 +==== Rezultate obținute ==== 
 +Consider că partea care mi-provocat cele mai multe probleme a fost fizica și dinamica jocului. Au apărut dificultăți atât în montarea platformei și a senzorilor și a ledurilor, cât și în fizica deplasării labirintului (viteză prea mare, mișcare bruscă etc.). În final, am reușit să implementez ce mi-am propus. Multe probleme au apărut și din cauza bibliotecilor folosite, primordial SoftwareSerial.h,​ care mi-a limitat posibilitatea lucrului cu regiștrii. ​ \\ 
 +{{:​pm:​prj2023:​adarmaz:​thumbnail_img_3947.jpg?​400|}} \\ 
 +{{:​pm:​prj2023:​adarmaz:​thumbnail_img_3951.jpg?​400|}} \\ 
 +{{:​pm:​prj2023:​adarmaz:​thumbnail_img_3944.jpg?​400|}} \\ 
 +{{:​pm:​prj2023:​adarmaz:​thumbnail_img_3955.jpg?​400|}} 
 +---- 
 +==== Concluzii ==== 
 +Mă bucur că am reușit să duc la bun final proiectul pe care mi l-am propus, contrar numeroaselor probleme care s-au ivit. Țin să menționez că deși am reușit să implementez tot ce mi-am propus există câteva lipsuri ale proiectului,​ pe care cu siguranță le voi adăuga în viitor și care ar face experiența jocului mai bună. O problemă de luat în calcul este că atunci când doresc resetarea jocului, trebuie re-rulat codul, deoarece, la cum a fost gândită soluția inițial, conexiunea pe interfața serială între cele două Arduino-uri se finalizează o dată cu începerea jocului. Overall, mi s-a părut fun și mă consider mulțumită de proiectul obținut, mai ales că este primul proiect la care lucrez. \\ 
 +Am învățat ca folosirea bibliotecilor (mai ales Software Serial) poate provoca numeroase probleme, precum limitarea flexibilității și a lucrului cu regiștrii. 
 +===== Download ===== 
 +<note important>**Codul sursă pentru cele două Arduino-uri poate fi găsit la următorul repository de Github  
 +[[https://​github.com/​adumitrescu2708/​TiltTheMaze]].**</note> 
 +===== Bibliografie ===== 
 +Pentru realizarea proiectului am folosit următoarele tool-uri, pentru schema bloc [[https://​miro.com/​index/​|Miro]],​ pentru schema circuitului electric [[https://​fritzing.org/​|Fritzing]],​ iar pentru implementare am urmărit laboratoarele și m-am folosit de articolele \\ 
 +- [[https://​diyodemag.com/​projects/​arduino_uno_servo-operated_ball_maze_marble_mayh|https://​diyodemag.com/​projects/​arduino_uno_servo-operated_ball_maze_marble_mayh]] \\ 
 +- [[https://​www.hackster.io/​michael-engel/​automatic-marble-labyrinth-solver-d54ad3|https://​www.hackster.io/​michael-engel/​automatic-marble-labyrinth-solver-d54ad3]] \\ 
 +- [[https://​ocw.cs.pub.ro/​courses/​_media/​pm/​atmel-7810-automotive-microcontrollers-atmega328p_datasheet.pdf|https://​ocw.cs.pub.ro/​courses/​_media/​pm/​atmel-7810-automotive-microcontrollers-atmega328p_datasheet.pdf]] \\ 
 +- [[https://​forum.arduino.cc/​t/​joystick-huge-outer-deadzone/​939904|https://​forum.arduino.cc/​t/​joystick-huge-outer-deadzone/​939904]] \\
pm/prj2023/adarmaz/tiltthemaze.1683266716.txt.gz · Last modified: 2023/05/05 09:05 by adumitrescu2708
CC Attribution-Share Alike 3.0 Unported
www.chimeric.de Valid CSS Driven by DokuWiki do yourself a favour and use a real browser - get firefox!! Recent changes RSS feed Valid XHTML 1.0