Differences

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

Link to this comparison view

pm:prj2023:adarmaz:tiltthemaze [2023/05/26 20:14]
adumitrescu2708 [Implementare]
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
 +=== Schema circuitului ===
 +{{:​pm:​prj2023:​adarmaz:​untitled_sketch_2_bb.png?​600|}}
 ===== Software Design ===== ===== Software Design =====
 ==== Descriere generală ==== ==== Descriere generală ====
Line 44: Line 48:
  - î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.\\  - î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 \\ \\  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°).\\ + ​- ​ 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°). \\ 
- - Variabila **IS_START_GAME** detectează dacă jocul a î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. \\ +<spoiler Implementarea deadzone-ului și poziționarea servomotoarelor.>​ 
- - 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 \\+<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 a î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:​7] ​ ^ Semnification ^
 | button[0] ​   | Button Pressed |  | button[0] ​   | Button Pressed | 
Line 52: Line 100:
 | button[2] ​   | Low            | | button[2] ​   | Low            |
 | button[3] ​   | 2nd Button Pressed| ​ \\ | 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.\\ \\  - 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.\\ \\
  
Line 58: Line 107:
 - 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. \\ - 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. - 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-a 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.1685121284.txt.gz · Last modified: 2023/05/26 20:14 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