This shows you the differences between two versions of the page.
|
pm:prj2026:alexandru.jipa2803:valentina.ceban [2026/05/24 18:38] valentina.ceban |
pm:prj2026:alexandru.jipa2803:valentina.ceban [2026/05/24 20:26] (current) valentina.ceban [Concluzii] |
||
|---|---|---|---|
| Line 1: | Line 1: | ||
| - | ====== Simon Says - Joc de memorie cu LEDs, LCD si buzzer ====== | + | ====== Simon Says - Joc de memorie cu LEDs, LCD si Buzzer ====== |
| ===== Introducere ===== | ===== Introducere ===== | ||
| - | Proiectul reprezinta o implementare a jocului clasic **Simon Says** pe placa **ATmega328P Xplained Mini**. | + | Proiectul reprezinta o implementare extinsa a jocului clasic **Simon Says** pe placa **ATmega328P Xplained Mini**. |
| Jocul afiseaza secvente de lumini prin 4 LED-uri colorate, iar jucatorul trebuie sa reproduca | Jocul afiseaza secvente de lumini prin 4 LED-uri colorate, iar jucatorul trebuie sa reproduca | ||
| secventa in ordinea corecta, apasand butoanele corespunzatoare. Cu fiecare nivel reusit, secventa | secventa in ordinea corecta, apasand butoanele corespunzatoare. Cu fiecare nivel reusit, secventa | ||
| Line 9: | Line 9: | ||
| Proiectul integreaza: | Proiectul integreaza: | ||
| - | * **LCD 16x2 cu modul I2C** pentru afisarea scorului, nivelului, vitezei si mesajelor de stare | + | * **LCD 16x2 cu modul I2C** pentru afisarea scorului, nivelului, countdown-ului si mesajelor de stare |
| + | * **Animatii LCD** cu 8 caractere custom (inima, skull, trofeu, note muzicale, progress bar) | ||
| + | * **Selectare dificultate** la start — Easy / Medium / Hard, cu viteze si timpi diferiti | ||
| + | * **Dificultate adaptiva bidiectionala** — viteza creste la succese si scade la greseli | ||
| + | * **Countdown vizibil** pe LCD in timpul in care jucatorul replica secventa | ||
| + | * **Feedback sonor** prin buzzer activ, cu note diferite per culoare | ||
| + | * **Melodie "We Are the Champions"** la baterea recordului, cu LED-uri sincronizate | ||
| + | * **Celebrare record in timp real** — fara sa intrerupa jocul | ||
| * **EEPROM persistent** pentru salvarea celui mai bun scor intre resetari | * **EEPROM persistent** pentru salvarea celui mai bun scor intre resetari | ||
| - | * **Dificultate adaptiva** — viteza creste la succese si scade la greseli | ||
| - | * **Feedback sonor** prin buzzer pasiv, cu note diferite per culoare | ||
| - | * **Animatii** la pornire, victorie si game over | ||
| **Schema bloc:** | **Schema bloc:** | ||
| + | {{pm:prj2026:alexandru.jipa2803:schema_electrica_valentinaceban.png?300}} | ||
| Line 23: | Line 28: | ||
| ==== Logica jocului ==== | ==== Logica jocului ==== | ||
| - | - La pornire, LCD afiseaza "SIMON SAYS" si cel mai bun scor din EEPROM | + | - La pornire, LCD afiseaza animatie de intro cu scroll si cel mai bun scor din EEPROM |
| - | - LED-urile executa o animatie de bun-venit (se aprind pe rand) | + | - LED-urile executa o animatie de bun-venit (wave in ambele directii) |
| + | - Jucatorul selecteaza dificultatea: **Easy** (B1), **Medium** (B2), **Hard** (B3) | ||
| - La apasarea oricarui buton, jocul incepe | - La apasarea oricarui buton, jocul incepe | ||
| - | - Microcontrolerul genereaza un element aleator (0-3) si il adauga la secventa | + | - Microcontrolerul genereaza o secventa aleatoare si o afiseaza prin LED-uri cu sunet specific fiecarei culori |
| - | - LED-urile se aprind **pe rand**, in ordinea secventei, cu sunet specific fiecarei culori | + | - Jucatorul reproduce secventa; pe LCD se afiseaza countdown-ul de timp ramas |
| - | - Jucatorul reproduce secventa apasand butoanele in ordinea corecta | + | - La nivel corect: animatie inima care bate, scor actualizat, dificultate crescuta |
| - | - La raspuns corect: animatie de victorie, nivel urmator, viteza crescuta | + | - Daca scorul depaseste recordul: celebrare imediata cu trofeu pe LCD si melodie, jocul **continua** |
| - | - La raspuns gresit: sunet de eroare, toate LED-urile clipesc, dificultatea scade usor (dificultate adaptiva) | + | - La raspuns gresit sau timeout: skull animat, Game Over, scor afisat, selectie dificultate din nou |
| - | - Scorul cel mai bun este salvat in EEPROM si persista intre resetari | + | - Recordul persista in EEPROM intre resetari si deconectari |
| ==== Masina de stari ==== | ==== Masina de stari ==== | ||
| <code> | <code> | ||
| - | IDLE (ecran start) | + | IDLE (ecran intro + animatie) |
| - | │ apasare buton | + | │ apasare orice buton |
| ▼ | ▼ | ||
| - | SHOW_SEQUENCE (afisare secventa LED-uri) | + | SELECT_DIFFICULTY (Easy / Medium / Hard) |
| + | │ apasare B1 / B2 / B3 | ||
| + | ▼ | ||
| + | SHOW_SEQUENCE (afisare secventa LED-uri cu sunet) | ||
| │ secventa terminata | │ secventa terminata | ||
| ▼ | ▼ | ||
| - | WAIT_INPUT (asteapta input jucator) | + | WAIT_INPUT (asteapta input jucator, countdown vizibil pe LCD) |
| - | ├─ corect → CORRECT (animatie + nivel urmator) → SHOW_SEQUENCE | + | ├─ corect, scor > record → RECORD (trofeu + melodie) → SHOW_SEQUENCE |
| - | └─ gresit → GAME_OVER (animatie + salvare scor) → IDLE | + | ├─ corect, scor <= record → CORRECT (animatie inima) → SHOW_SEQUENCE |
| + | └─ gresit / timeout → GAME_OVER (skull animat + salvare scor) → SELECT_DIFFICULTY | ||
| </code> | </code> | ||
| + | |||
| ===== Hardware Design ===== | ===== Hardware Design ===== | ||
| Line 53: | Line 64: | ||
| ^ Componenta ^ Model/Valoare ^ Cantitate ^ Rol in proiect ^ | ^ Componenta ^ Model/Valoare ^ Cantitate ^ Rol in proiect ^ | ||
| | Microcontroller | ATmega328P Xplained Mini | 1 | Unitate centrala de control | | | Microcontroller | ATmega328P Xplained Mini | 1 | Unitate centrala de control | | ||
| - | | Display LCD | 16x2 cu modul I2C (PCF8574) | 1 | Afisare scor, nivel, mesaje | | + | | Display LCD | 16x2 cu modul I2C (PCF8574) | 1 | Afisare scor, nivel, countdown, animatii | |
| | LED rosu | 5mm, 2V, 20mA | 1 | Culoarea 1 a jocului | | | LED rosu | 5mm, 2V, 20mA | 1 | Culoarea 1 a jocului | | ||
| - | | LED albastru | 5mm, 3.2V, 20mA | 1 | Culoarea 2 a jocului | | + | | LED verde | 5mm, 2.1V, 20mA | 1 | Culoarea 2 a jocului | |
| - | | LED verde | 5mm, 2.1V, 20mA | 1 | Culoarea 3 a jocului | | + | | LED galben | 5mm, 2V, 20mA | 1 | Culoarea 3 a jocului | |
| - | | LED galben | 5mm, 2V, 20mA | 1 | Culoarea 4 a jocului | | + | | LED albastru | 5mm, 3.2V, 20mA | 1 | Culoarea 4 a jocului | |
| - | | Rezistenta | 220Ω | 4 | Protectie LED-uri | | + | | Rezistenta | 220Ω | 4 | Protectie LED-uri (limiteaza curentul la ~15mA) | |
| - | | Butoane tactile | 6x6mm | 4 | Input jucator | | + | | Butoane tactile | 6x6mm, 4 pini | 4 | Input jucator | |
| - | | Buzzer pasiv | 5V | 1 | Feedback sonor | | + | | Buzzer activ | 5V | 1 | Feedback sonor, note diferite per culoare | |
| - | | Breadboard | 830 puncte | 1 | Prototipare | | + | | Breadboard | 830 puncte | 1 | Prototipare circuit | |
| - | | Fire jumper | M-M | ~25 | Conexiuni | | + | | Fire jumper | M-M | ~25 | Conexiuni intre componente | |
| ==== Pinii folositi ==== | ==== Pinii folositi ==== | ||
| ^ Componenta ^ Pin ATmega ^ Pin Arduino ^ Motivatie alegere ^ | ^ Componenta ^ Pin ATmega ^ Pin Arduino ^ Motivatie alegere ^ | ||
| - | | LED rosu | PD4 | D4 | GPIO simplu, fara functii speciale | | + | | LED rosu | PD2 | D2 | GPIO simplu, fara functii speciale | |
| + | | LED verde | PD3 | D3 | GPIO simplu, fara functii speciale | | ||
| + | | LED galben | PD4 | D4 | GPIO simplu, fara functii speciale | | ||
| | LED albastru | PD5 | D5 | GPIO simplu, fara functii speciale | | | LED albastru | PD5 | D5 | GPIO simplu, fara functii speciale | | ||
| - | | LED verde | PD6 | D6 | GPIO simplu, fara functii speciale | | + | | Buton rosu | PD6 | D6 | GPIO cu pull-up intern disponibil | |
| - | | LED galben | PD7 | D7 | GPIO simplu, fara functii speciale | | + | | Buton verde | PD7 | D7 | GPIO cu pull-up intern disponibil | |
| - | | Buton rosu | PB0 | D8 | GPIO cu pull-up intern disponibil | | + | | Buton galben | PB0 | D8 | GPIO cu pull-up intern disponibil | |
| | Buton albastru | PB1 | D9 | GPIO cu pull-up intern disponibil | | | Buton albastru | PB1 | D9 | GPIO cu pull-up intern disponibil | | ||
| - | | Buton verde | PB2 | D10 | GPIO cu pull-up intern disponibil | | + | | Buzzer activ | PB2 | D10 | Compatibil cu functia tone() | |
| - | | Buton galben | PB3 | D11 | GPIO cu pull-up intern disponibil | | + | | LCD SDA | PC4 | A4 | Pin hardware TWI/I2C al ATmega328P | |
| - | | Buzzer | PB4 | D12 | Compatibil cu functia tone() | | + | | LCD SCL | PC5 | A5 | Pin hardware TWI/I2C al ATmega328P | |
| - | | LCD SDA | PC4 | A4 | Pin hardware TWI/I2C al ATmega | | + | |
| - | | LCD SCL | PC5 | A5 | Pin hardware TWI/I2C al ATmega | | + | |
| **Motivatia alegerii pinilor:** | **Motivatia alegerii pinilor:** | ||
| - | * PD0/PD1 au fost evitati — sunt RX/TX, folositi de comunicatia seriala USB cu mEDBG | + | * **PD0/PD1 au fost evitati** — sunt RX/TX, folositi de comunicatia seriala USB cu mEDBG; conectarea LED-urilor pe acesti pini blocheaza upload-ul de cod |
| - | * PC4/PC5 sunt pinii hardware TWI (I2C) ai ATmega328P, oferind comunicatie stabila cu LCD-ul | + | * **PC4/PC5** sunt pinii hardware TWI (I2C) ai ATmega328P, oferind comunicatie stabila cu LCD-ul prin protocol hardware, nu software bitbang |
| - | * PB4 (D12) este compatibil cu timer-ul folosit intern de functia tone() pentru generarea de frecvente audio | + | * **Butoanele folosesc INPUT_PULLUP intern** — elimina necesitatea rezistentelor externe de pull-down, simplificand circuitul si reducand numarul de componente |
| - | * LED-urile pe PD4-PD7 permit setarea simultana prin operatii pe registrul PORTD | + | |
| ==== Schema electrica ==== | ==== Schema electrica ==== | ||
| Line 89: | Line 99: | ||
| **LED-uri (x4, conectare identica):** | **LED-uri (x4, conectare identica):** | ||
| <code> | <code> | ||
| - | Pin GPIO (PD4-PD7) ──────── Anod LED (+, picior lung) | + | Pin GPIO (D2-D5) ── Rezistenta 220Ω ── Anod LED (+, picior lung) |
| - | Catod LED (-, picior scurt) ── Rezistenta 220Ω ── GND | + | Catod LED (-, picior scurt) ── GND |
| </code> | </code> | ||
| - | **Butoane (x4, cu pull-up intern):** | + | **Butoane (x4, cu pull-up intern, fara rezistente externe):** |
| <code> | <code> | ||
| - | Pin GPIO (PB0-PB3) ──────── Pin 1 buton | + | Pin GPIO (D6-D9) ── Pin 1 buton (o latura) |
| - | GND ─────────────────────── Pin 2 buton (opus) | + | GND ─────────────── Pin 2 buton (latura opusa, de cealalta parte a canalului central) |
| - | Pull-up intern activat in cod prin INPUT_PULLUP | + | Pull-up intern activat prin INPUT_PULLUP → citeste LOW la apasare, HIGH in repaus |
| - | Butonul citeste LOW cand e apasat, HIGH cand e liber | + | |
| </code> | </code> | ||
| - | **Buzzer pasiv:** | + | **Buzzer activ:** |
| <code> | <code> | ||
| - | PB4 (D12) ──────────────── Pin + buzzer | + | D10 ── Pin + buzzer (marcat cu +) |
| - | GND ─────────────────────── Pin - buzzer | + | GND ── Pin - buzzer (marcat cu -) |
| </code> | </code> | ||
| **LCD I2C:** | **LCD I2C:** | ||
| <code> | <code> | ||
| - | PC4 (A4/SDA) ───────────── SDA modul I2C | + | A4 (SDA/PC4) ── SDA modul I2C |
| - | PC5 (A5/SCL) ───────────── SCL modul I2C | + | A5 (SCL/PC5) ── SCL modul I2C |
| - | 5V ──────────────────────── VCC modul I2C | + | 5V ──────────── VCC modul I2C |
| - | GND ─────────────────────── GND modul I2C | + | GND ─────────── GND modul I2C |
| Potentiometru albastru pe modul = reglaj contrast | Potentiometru albastru pe modul = reglaj contrast | ||
| </code> | </code> | ||
| - | **Alimentare breadboard:** | + | {{pm:prj2026:alexandru.jipa2803:5211176995717324748.jpg?300}} |
| - | <code> | + | |
| - | 5V placa ──── linia rosie (+) breadboard | + | |
| - | GND placa ──── linia albastra (-) breadboard | + | |
| - | </code> | + | |
| - | {{pm:prj2026:alexandru.jipa2803:5211176995717324748.jpg?300}} | ||
| ===== Software Design ===== | ===== Software Design ===== | ||
| Line 128: | Line 132: | ||
| ==== Mediu de dezvoltare ==== | ==== Mediu de dezvoltare ==== | ||
| - | * **PlatformIO** + **framework Arduino** (MiniCore pentru ATmega328P) | + | * **Arduino IDE** cu framework Arduino |
| - | * **Arduino IDE** (alternativ, pentru upload rapid) | + | |
| * Limbaj: **C++** cu API Arduino | * Limbaj: **C++** cu API Arduino | ||
| + | * Upload prin USB (mEDBG integrat pe placa Xplained Mini) | ||
| + | * **Serial Monitor** la 9600 baud pentru debugging si logging | ||
| ==== Biblioteci folosite ==== | ==== Biblioteci folosite ==== | ||
| ^ Biblioteca ^ Sursa ^ Motivatie ^ | ^ Biblioteca ^ Sursa ^ Motivatie ^ | ||
| - | | Wire.h | Built-in Arduino | Comunicatie I2C hardware cu LCD — nu necesita instalare externa | | + | | Wire.h | Built-in Arduino | Comunicatie I2C hardware cu LCD | |
| - | | EEPROM.h | Built-in Arduino | Acces la memoria EEPROM a ATmega328P pentru persistenta high score | | + | | LiquidCrystal_I2C.h | Frank de Brabander | Abstractizare comenzi LCD: initializare, cursor, print, caractere custom | |
| - | | Arduino.h | Built-in | API de baza: pinMode, digitalWrite, tone, delay | | + | | EEPROM.h | Built-in Arduino | Acces la memoria EEPROM pentru persistenta high score | |
| - | + | ||
| - | **Motivatia alegerii Wire.h in loc de LiquidCrystal_I2C:** | + | |
| - | Libraria LiquidCrystal_I2C a prezentat probleme de compatibilitate cu toolchain-ul MiniCore | + | |
| - | pentru ATmega328P Xplained Mini. Solutia adoptata a fost implementarea directa a protocolului | + | |
| - | I2C pentru LCD folosind Wire.h (built-in), scriind manual functiile de initializare, trimitere | + | |
| - | nibble/byte, setare cursor si print. Aceasta abordare ofera control complet si elimina | + | |
| - | dependentele externe. | + | |
| ==== Elementul de noutate ==== | ==== Elementul de noutate ==== | ||
| - | * **Dificultate adaptiva bidiectionala**: viteza creste la succese si scade la greseli, mentinand jocul accesibil indiferent de nivelul jucatorului | + | * **Selectare dificultate la start**: jucatorul alege Easy / Medium / Hard inainte de fiecare joc, cu viteze si timpi de replica diferiti; selectia reapare dupa fiecare Game Over |
| - | * **Sistem de bonus**: 3 raspunsuri consecutive corecte declanseaza o crestere suplimentara a vitezei | + | * **Dificultate adaptiva bidiectionala**: viteza creste la succese (−20ms/nivel) si scade la greseli (+30ms), cu limite 150ms–600ms, mentinand jocul accesibil indiferent de nivel |
| - | * **Persistenta high score in EEPROM**: scorul maxim supravietuieste resetarilor si deconectarii de la alimentare | + | * **Celebrare record in timp real**: daca scorul depaseste recordul in timpul jocului (nu doar la Game Over), se afiseaza trofeu pe LCD, se canta melodia si se salveaza in EEPROM — jocul **continua** fara intrerupere |
| - | * **Animatii procedurale**: secventele de LED-uri la pornire, victorie si eroare sunt generate programatic, fara timere hardware blocante | + | * **Animatii LCD cu caractere custom**: 8 simboluri definite ca matrici 5x8 pixeli (inima, skull, trofeu, note muzicale, sageata, progress bar) folosite in animatii procedurale |
| + | * **Melodie la record**: primele 15 note din "We Are the Champions" cantate pe buzzer, cu LED-urile clipind sincronizat cu fiecare nota | ||
| + | * **Countdown vizibil**: in timpul replicii jucatorului, LCD afiseaza secundele ramase, actualizate in timp real | ||
| ==== Functionalitati din laborator utilizate ==== | ==== Functionalitati din laborator utilizate ==== | ||
| ^ Functionalitate ^ Utilizare in proiect ^ | ^ Functionalitate ^ Utilizare in proiect ^ | ||
| - | | GPIO output | Control LED-uri: PORTD pentru afisarea secventei | | + | | GPIO output | Control LED-uri pe D2-D5 pentru afisarea secventei si animatii | |
| - | | GPIO input cu pull-up | Citire butoane fara rezistente externe: INPUT_PULLUP pe PB0-PB3 | | + | | GPIO input cu pull-up | Citire butoane fara rezistente externe: INPUT_PULLUP pe D6-D9 | |
| - | | Timer (tone()) | Generare frecvente audio pentru buzzer pasiv — intern foloseste Timer2 | | + | | Timer (tone()) | Generare frecvente audio pentru buzzer — intern foloseste Timer2 | |
| | TWI/I2C (Wire.h) | Comunicatie cu modulul LCD prin 2 fire (SDA/SCL) | | | TWI/I2C (Wire.h) | Comunicatie cu modulul LCD prin 2 fire (SDA/SCL) | | ||
| - | | EEPROM | Stocare persistenta a high score-ului la adresa 0 | | + | | EEPROM | Stocare persistenta a high score-ului la adresa 0 cu EEPROM.put/get | |
| - | | Intreruperi soft (debounce) | Eliminarea zgomotului mecanic al butoanelor prin delay 50ms + asteptare release | | + | | Debounce software | Eliminarea bouncing-ului mecanic: asteptare release + delay 50ms | |
| - | ==== Scheletul proiectului ==== | + | ==== Scheletul proiectului si portiuni de cod relevante ==== |
| + | |||
| + | === Selectare dificultate === | ||
| + | |||
| + | La pornire si dupa fiecare Game Over, jucatorul selecteaza dificultatea prin primele 3 butoane. | ||
| + | LED-urile se aprind ca indicatori vizuali in timp ce asteapta selectia: | ||
| + | |||
| + | ^ Dificultate ^ Buton ^ Viteza initiala ^ Timp replica ^ | ||
| + | | Easy | B1 (rosu) | 600ms per LED | 12 secunde | | ||
| + | | Medium | B2 (verde) | 500ms per LED | 10 secunde | | ||
| + | | Hard | B3 (galben) | 300ms per LED | 7 secunde | | ||
| <code cpp> | <code cpp> | ||
| - | // Masina de stari implementata prin apeluri recursive de functii: | + | void selecteazaDificultate() { |
| + | lcd.write(CHAR_NOTE); lcd.print("E "); // Easy | ||
| + | lcd.write(CHAR_SAGEATA); lcd.print("M "); // Medium | ||
| + | lcd.write(CHAR_SKULL); lcd.print("H"); // Hard | ||
| - | asteaptaStart() | + | // Aprinde LED-urile ca indicatori vizuali |
| - | └─> animatieStart() + afisare LCD "SIMON SAYS + Best Score" | + | for (int i = 0; i < 3; i++) digitalWrite(ledPins[i], HIGH); |
| - | └─> asteapta apasare buton → incepeJoc() | + | |
| - | incepeJoc() | + | while (!ales) { |
| - | └─> reseteaza lungime=1, viteza=600ms, scor=0 | + | if (digitalRead(btnPins[0]) == LOW) { viteza=600; timpReplica=12000; ales=true; } |
| - | └─> genereazaSecventa() → arataSiAsteapta() | + | if (digitalRead(btnPins[1]) == LOW) { viteza=500; timpReplica=10000; ales=true; } |
| + | if (digitalRead(btnPins[2]) == LOW) { viteza=300; timpReplica=7000; ales=true; } | ||
| + | } | ||
| + | } | ||
| + | </code> | ||
| - | arataSiAsteapta() | + | === Dificultate adaptiva bidiectionala === |
| - | └─> afiseaza nivel + viteza pe LCD | + | |
| - | └─> aprinde LED-urile pe rand cu sunet (secventa) | + | |
| - | └─> pentru fiecare element din secventa: | + | |
| - | asteaptaButon() | + | |
| - | └─> corect: feedback LED+sunet, continua | + | |
| - | └─> gresit: gameOver() | + | |
| - | └─> dupa secventa completa: animatieVictorie() | + | |
| - | └─> dificultate adaptiva: scade viteza (creste dificultatea) | + | |
| - | └─> bonus daca 3 consecutive corecte | + | |
| - | └─> genereazaSecventa() + arataSiAsteapta() (nivel urmator) | + | |
| - | gameOver() | + | Dupa fiecare nivel, viteza si timpul de replica se ajusteaza automat. |
| - | └─> dificultate adaptiva: creste viteza (scade dificultatea) | + | Limitele explicite previn ca jocul sa devina imposibil sau prea lent: |
| - | └─> verifica si salveaza high score in EEPROM | + | |
| - | └─> afisare LCD "Game Over + Scor + Best" | + | <code cpp> |
| - | └─> asteaptaStart() | + | void ajusteazaDificultate(bool corect) { |
| + | if (corect) { | ||
| + | viteza = max(150, viteza - 20); // LED-urile se aprind mai repede | ||
| + | timpReplica = max(5000, timpReplica - 300); // mai putin timp de replica | ||
| + | } else { | ||
| + | viteza = min(600, viteza + 30); // LED-urile se aprind mai lent | ||
| + | timpReplica = min(12000, timpReplica + 500);// mai mult timp de replica | ||
| + | } | ||
| + | } | ||
| + | </code> | ||
| + | |||
| + | === Countdown vizibil pe LCD === | ||
| + | |||
| + | In bucla de asteptare a butoanelor, LCD-ul este actualizat continuu cu secundele ramase. | ||
| + | Functia returneaza −1 la timeout, tratata ca greseala: | ||
| + | |||
| + | <code cpp> | ||
| + | int waitForButton() { | ||
| + | unsigned long start = millis(); | ||
| + | while (millis() - start < (unsigned long)timpReplica) { | ||
| + | int secRamase = (timpReplica - (millis() - start)) / 1000; | ||
| + | lcd.setCursor(9, 1); | ||
| + | lcd.print("T:"); lcd.print(secRamase); lcd.print("s "); | ||
| + | |||
| + | for (int i = 0; i < 4; i++) { | ||
| + | if (digitalRead(btnPins[i]) == LOW) { | ||
| + | lightUp(i, 300); | ||
| + | while (digitalRead(btnPins[i]) == LOW); // asteapta release | ||
| + | delay(50); // debounce | ||
| + | return i; | ||
| + | } | ||
| + | } | ||
| + | } | ||
| + | return -1; // timeout | ||
| + | } | ||
| + | </code> | ||
| + | |||
| + | === Animatii LCD cu caractere custom === | ||
| + | |||
| + | LCD-ul 16x2 suporta pana la 8 caractere custom definite ca matrici de pixeli 5x8. | ||
| + | Proiectul defineste 8 simboluri inregistrate la initializare cu ''lcd.createChar()'': | ||
| + | |||
| + | ^ Index ^ Simbol ^ Utilizat in ^ | ||
| + | | 0 | Inima plina | Nivel corect, record | | ||
| + | | 1 | Inima goala | Animatie batai inima | | ||
| + | | 2 | Skull | Game Over | | ||
| + | | 3 | Note muzicale | Intro, selectie Easy | | ||
| + | | 4 | Sageata | Nivel curent, selectie Medium | | ||
| + | | 5 | Trofeu | Record, castig | | ||
| + | | 6 | Progres plin | Progress bar | | ||
| + | | 7 | Progres gol | Progress bar | | ||
| + | |||
| + | Exemplu definitie si animatie inima care bate: | ||
| + | |||
| + | <code cpp> | ||
| + | byte inima[] = {0b00000,0b01010,0b11111,0b11111,0b11111,0b01110,0b00100,0b00000}; | ||
| + | byte inimaGoala[] = {0b00000,0b01010,0b10101,0b10001,0b10001,0b01010,0b00100,0b00000}; | ||
| + | |||
| + | void animatieInima() { | ||
| + | for (int i = 0; i < 3; i++) { // 3 batai | ||
| + | lcd.setCursor(15, 0); | ||
| + | lcd.write(CHAR_INIMA); // inima plina | ||
| + | tone(buzzer, 800); delay(80); noTone(buzzer); | ||
| + | delay(100); | ||
| + | lcd.setCursor(15, 0); | ||
| + | lcd.write(CHAR_INIMA_GOALA); // inima goala | ||
| + | delay(200); | ||
| + | } | ||
| + | lcd.setCursor(15, 0); | ||
| + | lcd.write(CHAR_INIMA); | ||
| + | } | ||
| + | </code> | ||
| + | |||
| + | Progress bar pe randul 2 al LCD-ului, care arata progresul prin cele 20 de nivele: | ||
| + | |||
| + | <code cpp> | ||
| + | void afiseazaProgressBar() { | ||
| + | lcd.setCursor(0, 1); | ||
| + | int pasi = map(level, 1, MAXLEVEL, 0, 16); // 1-20 nivele → 0-16 caractere | ||
| + | for (int i = 0; i < 16; i++) { | ||
| + | if (i < pasi) lcd.write(CHAR_PROG_FULL); | ||
| + | else lcd.write(CHAR_PROG_EMPTY); | ||
| + | } | ||
| + | } | ||
| + | </code> | ||
| + | |||
| + | === Celebrare record in timp real === | ||
| + | |||
| + | Spre deosebire de implementarile clasice (care verifica recordul doar la Game Over), | ||
| + | aceasta implementare verifica dupa fiecare nivel corect. La depasirea recordului, | ||
| + | se afiseaza trofeu, se salveaza in EEPROM si se canta melodia — apoi jocul **continua**: | ||
| + | |||
| + | <code cpp> | ||
| + | // In loop(), dupa completarea unui nivel: | ||
| + | scor += level * 10; | ||
| + | ajusteazaDificultate(true); | ||
| + | |||
| + | if (scor > highScore) { | ||
| + | celebreazaRecord(); // trofeu + melodie + salveaza EEPROM | ||
| + | } else { | ||
| + | animatieInima(); // animatie normala nivel corect | ||
| + | } | ||
| + | |||
| + | level++; | ||
| + | if (level > MAXLEVEL) win(); | ||
| + | else playSequence(); // jocul continua normal | ||
| + | |||
| + | void celebreazaRecord() { | ||
| + | lcd.write(CHAR_TROPHY); lcd.print(" NOU RECORD! "); lcd.write(CHAR_TROPHY); | ||
| + | highScore = scor; | ||
| + | EEPROM.put(0, highScore); // persistenta intre resetari | ||
| + | cantaMelodie(); // We Are the Champions | ||
| + | } | ||
| + | </code> | ||
| + | |||
| + | === Melodie la record === | ||
| + | |||
| + | La baterea recordului, buzzerul canta primele 15 note din "We Are the Champions". | ||
| + | LED-urile clipesc sincronizat cu melodia (cate un LED diferit per nota, in cicluri de 4): | ||
| + | |||
| + | <code cpp> | ||
| + | const int melodieNote[] = { 392,392,349,392,0,523,494,440,392, | ||
| + | 659,659,659,587,523,392 }; | ||
| + | const int melodieDurata[] = { 200,200,200,400,200,400,200,200,600, | ||
| + | 200,200,200,200,200,600 }; | ||
| + | |||
| + | void cantaMelodie() { | ||
| + | for (int i = 0; i < melodieLen; i++) { | ||
| + | if (melodieNote[i] == 0) { noTone(buzzer); } // pauza intre note | ||
| + | else { | ||
| + | tone(buzzer, melodieNote[i]); | ||
| + | digitalWrite(ledPins[i % 4], HIGH); // LED sincronizat cu nota | ||
| + | } | ||
| + | delay(melodieDurata[i]); | ||
| + | noTone(buzzer); | ||
| + | digitalWrite(ledPins[i % 4], LOW); | ||
| + | delay(30); | ||
| + | } | ||
| + | } | ||
| + | </code> | ||
| + | |||
| + | === Persistenta EEPROM === | ||
| + | |||
| + | Scorul maxim este salvat la adresa 0 din EEPROM. Validarea la citire protejeaza | ||
| + | impotriva datelor corupte sau a primei porniri (cand EEPROM contine valori nedefinite): | ||
| + | |||
| + | <code cpp> | ||
| + | // La pornire — citire cu validare: | ||
| + | EEPROM.get(0, highScore); | ||
| + | if (highScore < 0 || highScore > 9999) highScore = 0; | ||
| + | |||
| + | // La baterea recordului — scriere imediata: | ||
| + | EEPROM.put(0, highScore); | ||
| + | </code> | ||
| + | |||
| + | === Debounce butoane === | ||
| + | |||
| + | Butoanele mecanice genereaza bouncing la apasare si eliberare. | ||
| + | Debounce-ul software combina doua tehnici complementare: | ||
| + | |||
| + | <code cpp> | ||
| + | if (digitalRead(btnPins[i]) == LOW) { | ||
| + | lightUp(i, 300); // feedback vizual imediat | ||
| + | while (digitalRead(btnPins[i]) == LOW); // asteapta release complet | ||
| + | delay(50); // elimina bouncing la eliberare | ||
| + | return i; | ||
| + | } | ||
| </code> | </code> | ||
| Line 201: | Line 373: | ||
| **1. Testare LED-uri individual:** | **1. Testare LED-uri individual:** | ||
| <code cpp> | <code cpp> | ||
| - | // Cod de test: fiecare buton aprinde LED-ul corespunzator | + | // Aprinde fiecare LED pe rand cu delay, verifica orientarea si conexiunile |
| - | if (digitalRead(butoane[i]) == LOW) digitalWrite(leduri[i], HIGH); | + | void loop() { |
| + | for (int i = 0; i < 4; i++) { | ||
| + | digitalWrite(ledPins[i], HIGH); delay(500); | ||
| + | digitalWrite(ledPins[i], LOW); delay(200); | ||
| + | } | ||
| + | } | ||
| </code> | </code> | ||
| - | **2. Validare prin Serial Monitor:** | + | **2. Testare butoane cu Serial Monitor:** |
| - | La fiecare eveniment (nivel, apasare buton, corect/gresit) se afiseaza mesaje pe Serial Monitor la 9600 baud: | + | <code cpp> |
| + | // Afiseaza butonul apasat — a identificat problema de inversare GND/semnal | ||
| + | void loop() { | ||
| + | for (int i = 0; i < 4; i++) { | ||
| + | if (digitalRead(btnPins[i]) == LOW) { | ||
| + | Serial.print("Buton "); Serial.print(i+1); Serial.println(" apasat!"); | ||
| + | delay(300); | ||
| + | } | ||
| + | } | ||
| + | } | ||
| + | </code> | ||
| + | |||
| + | **3. Validare prin Serial Monitor in joc:** | ||
| + | La fiecare eveniment se afiseaza mesaje la 9600 baud: | ||
| <code> | <code> | ||
| - | === Nivel: 3 | Viteza: 540ms === | + | === SIMON SAYS === |
| - | -> ROSU | + | Dificultate: MEDIUM |
| - | -> VERDE | + | ------------------ |
| - | -> ALBASTRU | + | NIVEL 3 | Viteza: 460ms |
| - | Randul tau! | + | Secventa: |
| - | Apasat: ROSU -> Corect! | + | 1. Rosu |
| - | Apasat: VERDE -> Corect! | + | 2. Verde |
| - | Apasat: ALBASTRU -> Corect! | + | 3. Albastru |
| - | BONUS VITEZA! 3 consecutive corecte! | + | Repeta secventa! |
| + | Apasat: Rosu -> Corect! (1/3) | ||
| + | Apasat: Verde -> Corect! (2/3) | ||
| + | Apasat: Albastru -> Corect! (3/3) | ||
| + | Nivel completat! Scor: 60 | ||
| + | *** NOU HIGH SCORE! *** | ||
| + | Viteza noua: 440 | Timp replica: 9400 | ||
| </code> | </code> | ||
| - | **3. Testare EEPROM:** | + | **4. Testare EEPROM:** |
| - | Dupa game over cu scor nou, s-a verificat ca la resetare hardware scorul persista (afisat pe LCD la pornire). | + | Dupa game over cu scor nou, s-a verificat ca la resetare hardware scorul persista |
| + | (citit cu EEPROM.get la pornire si afisat pe LCD). | ||
| ==== Optimizari realizate ==== | ==== Optimizari realizate ==== | ||
| - | * **Debounce software**: dupa detectarea apasarii, se asteapta 50ms si release-ul butonului, eliminand bouncing-ul mecanic | + | * **Debounce software**: asteptare release + delay 50ms, eliminand bouncing-ul mecanic fara timere hardware |
| - | * **randomSeed(analogRead(A3))**: pinul A3 (neconectat) citeste zgomot analogic, asigurand secvente cu adevarat aleatorii la fiecare pornire | + | * **randomSeed(analogRead(A0))**: pinul A0 neconectat citeste zgomot analogic, asigurand secvente cu adevarat aleatorii la fiecare pornire |
| - | * **Implementare I2C manuala**: elimina overhead-ul unei librarii externe, reducand dimensiunea codului compilat | + | * **INPUT_PULLUP**: elimina rezistentele externe de pull-down pentru butoane, simplificand circuitul |
| - | * **Dificultate adaptiva bidiectionala**: mentine jocul engageable indiferent de skill-ul jucatorului | + | * **Validare EEPROM la citire**: protejeaza impotriva valorilor corupte sau nedefinite la prima pornire |
| + | * **Celebrare record inline**: verificarea si salvarea recordului se face dupa fiecare nivel, nu doar la Game Over, fara a intrerupe fluxul jocului | ||
| ===== Rezultate Obtinute ===== | ===== Rezultate Obtinute ===== | ||
| - | Proiectul functioneaza conform specificatiilor: | + | Proiectul functioneaza conform specificatiilor extinse: |
| - | * Secventele sunt generate aleator si afisate corect pe LED-uri cu sunet | + | * Selectarea dificultaii este functionala cu feedback LED si LCD, reapare dupa fiecare Game Over |
| - | * Butoanele sunt detectate corect cu debounce | + | * Secventele sunt generate aleator si afisate corect pe LED-uri cu sunet specific per culoare |
| - | * LCD-ul afiseaza nivel, viteza curenta si scor in timp real | + | * Butoanele sunt detectate corect cu debounce, fara false pozitive sau omisiuni |
| - | * High score-ul persista in EEPROM intre resetari | + | * LCD afiseaza nivel, scor, countdown timp ramas si animatii in timp real |
| - | * Dificultatea se ajusteaza dinamic in functie de performanta jucatorului | + | * Dificultatea se ajusteaza automat dupa fiecare nivel in ambele directii |
| + | * Recordul se salveaza in EEPROM si persista intre resetari hardware si deconectari | ||
| + | * Celebrarea recordului se declanseaza imediat, fara sa intrerupa jocul | ||
| + | * Melodia "We Are the Champions" se canta corect cu LED-urile sincronizate pe note | ||
| + | * Progress bar-ul reflecta fidel progresul prin cele 20 de nivele | ||
| Line 242: | Line 445: | ||
| Proiectul Simon Says a fost implementat cu succes pe placa ATmega328P Xplained Mini, | Proiectul Simon Says a fost implementat cu succes pe placa ATmega328P Xplained Mini, | ||
| - | demonstrand utilizarea GPIO, I2C, EEPROM si timer-e in cadrul framework-ului Arduino. | + | depasind specificatiile de baza prin adaugarea mai multor functionalitati care imbunatatesc |
| + | experienta de joc si complexitatea tehnica. | ||
| Principalele provocari intampinate: | Principalele provocari intampinate: | ||
| Line 248: | Line 452: | ||
| * Conflicte de pini intre RX/TX si LED-uri — rezolvate prin relocarea LED-urilor pe PD4-PD7 | * Conflicte de pini intre RX/TX si LED-uri — rezolvate prin relocarea LED-urilor pe PD4-PD7 | ||
| * Calibrarea debounce-ului butoanelor pentru raspuns fluid | * Calibrarea debounce-ului butoanelor pentru raspuns fluid | ||
| + | * High score blocat — valoare corupta in EEPROM dintr-o sesiune anterioara; rezolvat prin validare la citire si reset manual | ||
| + | |||
| + | Lectii invatate: | ||
| + | * Documentarea pinilor inainte de cablare evita conflicte greu de depanat | ||
| + | * Testarea incrementala (LED, buton, LCD, buzzer separat) economiseste timp considerabil | ||
| + | * INPUT_PULLUP simplifica semnificativ circuitul butoanelor | ||
| + | * Verificarea recordului in timp real (nu doar la Game Over) ofera o experienta de joc mai fluida | ||
| Line 256: | Line 467: | ||
| * [[https://docs.arduino.cc/learn/built-in-libraries/eeprom/|Arduino EEPROM Library Documentation]] | * [[https://docs.arduino.cc/learn/built-in-libraries/eeprom/|Arduino EEPROM Library Documentation]] | ||
| * [[https://docs.arduino.cc/learn/communication/wire/|Arduino Wire Library (I2C)]] | * [[https://docs.arduino.cc/learn/communication/wire/|Arduino Wire Library (I2C)]] | ||
| + | * [[https://github.com/johnrickman/LiquidCrystal_I2C|LiquidCrystal_I2C Library - Frank de Brabander]] | ||
| * [[https://ocw.cs.pub.ro/courses/pm/tutorial/proiect|Ghid proiect PM - OCW UPB]] | * [[https://ocw.cs.pub.ro/courses/pm/tutorial/proiect|Ghid proiect PM - OCW UPB]] | ||