This shows you the differences between two versions of the page.
pm:prj2023:dene:karaoke-player [2023/05/30 11:56] daria.florea [Hardware Design] |
pm:prj2023:dene:karaoke-player [2023/05/30 12:22] (current) daria.florea [Bibliografie/Resurse] |
||
---|---|---|---|
Line 48: | Line 48: | ||
===== Software Design ===== | ===== Software Design ===== | ||
+ | **Biblioteci folosite** | ||
- | <note tip> | + | * TMRpcm.h - redarea melodiilor în format wav de pe cardul SD pe difuzor |
- | Descrierea codului aplicaţiei (firmware): | + | * SD.h - citirea datelor de pe cardul microSD |
- | * mediu de dezvoltare (if any) (e.g. AVR Studio, CodeVisionAVR) | + | * LiquidCrystal_I2C.h - afișarea mesajelor pe ecranul LCD 16x2 I2C |
- | * librării şi surse 3rd-party (e.g. Procyon AVRlib) | + | |
- | * algoritmi şi structuri pe care plănuiţi să le implementaţi | + | |
- | * (etapa 3) surse şi funcţii implementate | + | |
- | </note> | + | |
- | ===== Rezultate Obţinute ===== | + | În funcția **loop** se verifică la fiecare moment de timp dacă s-a înregistrat o apăsare a unuia dintre butoane. Pentru a nu înregistra mai multe apăsări decât avem, am folosit funcția millis din laborator pentru a contoriza numărul de milisecunde dintre două apăsări de buton succesive. Dacă butonul de start/stop este apăsat, se pornește prima melodie, altfel melodia este oprită și se așteaptă o nouă apăsare a butonului. Butonul de next face tranziția între melodii. Pe cardul microSD avem stocate 3 melodii: Photograph, Tattoo și Someone like you. |
- | <note tip> | + | **Afișarea versurilor** se face tot prin intermediul funcției millis, pe care o folosim pentru a contoriza perioada de timp dintre două versuri. Din cauza memoriei puține disponibile pe plăcuța Arduino Uno, se pot reda un număr mic de versuri pentru fiecare melodie. |
- | Care au fost rezultatele obţinute în urma realizării proiectului vostru. | + | |
- | </note> | + | |
- | ===== Concluzii ===== | + | **Schimbarea melodiei** se poate face și prin monitorul Serial care poate fi controlat pe laptop sau telefon datorită modulului Bluetooth. Se pot schimba atât melodiile, cât și alte caracteristici ale melodiei (calitate, volum etc.) sau chiar culoarea LED-ului RGB care redă lumina camerei. |
- | ===== Download ===== | + | Funcțiile **qtyLED0, qtyLED1** ș.a.m.d. sunt folosite pentru a reda culori în funcție de amplitudinea sunetului melodiei redate în acel moment. |
- | <note warning> | + | Mai jos las atașat codul acestui proiect |
- | O arhivă (sau mai multe dacă este cazul) cu fişierele obţinute în urma realizării proiectului: surse, scheme, etc. Un fişier README, un ChangeLog, un script de compilare şi copiere automată pe uC crează întotdeauna o impresie bună ;-). | + | |
- | Fişierele se încarcă pe wiki folosind facilitatea **Add Images or other files**. Namespace-ul în care se încarcă fişierele este de tipul **:pm:prj20??:c?** sau **:pm:prj20??:c?:nume_student** (dacă este cazul). **Exemplu:** Dumitru Alin, 331CC -> **:pm:prj2009:cc:dumitru_alin**. | + | <code> |
- | </note> | + | #include <SD.h> |
+ | #define SD_ChipSelectPin 10 | ||
+ | #include <TMRpcm.h> // Library to play wav file | ||
+ | #include <SPI.h> | ||
+ | #include <LiquidCrystal_I2C.h> | ||
- | ===== Jurnal ===== | + | #define LED_ON HIGH |
+ | #define LED_OFF LOW | ||
- | <note tip> | + | // RGB LEDs |
- | Puteți avea și o secțiune de jurnal în care să poată urmări asistentul de proiect progresul proiectului. | + | const int LED_R = 8; |
- | </note> | + | const int LED_G = 7; |
+ | const int LED_B = 6; | ||
+ | |||
+ | const int nextButtonPin = 2; // the number of the pushbutton pin | ||
+ | int nextButtonState = 0; // variable for reading the pushbutton status | ||
+ | int nextButtonTime, nextButtonLastTime = 0; // Interrupts | ||
+ | |||
+ | const int startButtonPin = 3; // the number of the pushbutton pin | ||
+ | int startButtonState = 0; // variable for reading the pushbutton status | ||
+ | int startButtonTime, startButtonLastTime = 0; // Interrupts | ||
+ | |||
+ | // Object for playing songs | ||
+ | TMRpcm tmrpcm; | ||
+ | |||
+ | // Couting periods for displaying lyrics | ||
+ | unsigned long current_time = 0, last_time = 0; | ||
+ | |||
+ | int current_song = 0; | ||
+ | int current_verse = 0; | ||
+ | int count = 0; | ||
+ | |||
+ | // Variables for LEDs | ||
+ | int waveform = 0; | ||
+ | int qtyLED = 0; | ||
+ | int timeLED = 25; | ||
+ | |||
+ | // Set the LCD address to 0x27 for a 16 chars and 2 line display | ||
+ | LiquidCrystal_I2C lcd(0x27, 16, 2); | ||
+ | |||
+ | void setup() { | ||
+ | // cli(); | ||
+ | tmrpcm.speakerPin = 9; | ||
+ | |||
+ | Serial.begin(9600); | ||
+ | |||
+ | // Buttons | ||
+ | // initialize the pushbutton pin as an input: | ||
+ | pinMode(nextButtonPin, INPUT); | ||
+ | pinMode(startButtonPin, INPUT); | ||
+ | |||
+ | // Song LEDs | ||
+ | pinMode(17, OUTPUT); | ||
+ | pinMode(16, OUTPUT); | ||
+ | pinMode(15, OUTPUT); | ||
+ | pinMode(14, OUTPUT); | ||
+ | |||
+ | // RGB LEDs | ||
+ | pinMode(LED_R, OUTPUT); | ||
+ | pinMode(LED_G, OUTPUT); | ||
+ | pinMode(LED_B, OUTPUT); | ||
+ | |||
+ | digitalWrite(LED_R, LED_OFF); | ||
+ | digitalWrite(LED_G, LED_OFF); | ||
+ | digitalWrite(LED_B, LED_OFF); | ||
+ | |||
+ | // Check SD card | ||
+ | if (!SD.begin(SD_ChipSelectPin)) | ||
+ | { | ||
+ | Serial.println("SD fail"); | ||
+ | return; | ||
+ | } | ||
+ | else | ||
+ | Serial.println("SD ok"); | ||
+ | |||
+ | // Set volume speaker | ||
+ | tmrpcm.volume(2); | ||
+ | |||
+ | // initialize the LCD | ||
+ | lcd.begin(); | ||
+ | |||
+ | // Turn on the blacklight and print a message. | ||
+ | lcd.backlight(); | ||
+ | lcd.print("Karaoke Player"); | ||
+ | } | ||
+ | |||
+ | void loop() | ||
+ | { | ||
+ | // Photograph lyrics | ||
+ | String song1[] = { | ||
+ | "Loving can hurt", | ||
+ | "loving can hurt", | ||
+ | "sometimes" | ||
+ | }; | ||
+ | |||
+ | int song1_length = 3; | ||
+ | |||
+ | // Tattoo Lyrics | ||
+ | String song2[] = { | ||
+ | "I don't wanna go", | ||
+ | "But baby, we both", | ||
+ | "know" | ||
+ | }; | ||
+ | int song2_length = 3; | ||
+ | |||
+ | // Someone Like You Lyrics | ||
+ | String song3[] = { | ||
+ | "I heard that", | ||
+ | "you're settled", | ||
+ | "down" | ||
+ | }; | ||
+ | int song3_length = 3; | ||
+ | |||
+ | // read the state of the pushbuttons values: | ||
+ | nextButtonState = digitalRead(nextButtonPin); | ||
+ | nextButtonTime = millis(); | ||
+ | |||
+ | // check if the pushbutton is pressed. If it is, the buttonState is HIGH: | ||
+ | if (nextButtonState == HIGH && nextButtonTime - nextButtonLastTime > 200) { | ||
+ | // Next song | ||
+ | nextButtonLastTime = nextButtonTime; | ||
+ | |||
+ | current_song++; | ||
+ | if (current_song > 3) current_song = 1; | ||
+ | |||
+ | switch (current_song) { | ||
+ | case 1: tmrpcm.play("sng1.wav"); lcd.clear(); lcd.print("Photograph"); current_verse = 0; count = 0; break; | ||
+ | case 2: tmrpcm.play("sng2.wav"); lcd.clear(); lcd.print("Tattoo"); current_verse = 0; count = 0; break; | ||
+ | case 3: tmrpcm.play("sng3.wav"); lcd.clear(); lcd.print("Someone like you"); current_verse = 0; count = 0; break; | ||
+ | } | ||
+ | } | ||
+ | |||
+ | startButtonState = digitalRead(startButtonPin); | ||
+ | startButtonTime = millis(); | ||
+ | |||
+ | if (startButtonState == HIGH && startButtonTime - startButtonLastTime > 200) { | ||
+ | // Next song | ||
+ | startButtonLastTime = startButtonTime; | ||
+ | |||
+ | if (current_song != 0) | ||
+ | { | ||
+ | current_song = 0; | ||
+ | tmrpcm.stopPlayback(); | ||
+ | lcd.clear(); | ||
+ | lcd.print("Play a song!"); | ||
+ | lcd.setCursor(0, 1); | ||
+ | lcd.print("Press start"); | ||
+ | } | ||
+ | else { | ||
+ | tmrpcm.play("sng1.wav"); | ||
+ | lcd.clear(); | ||
+ | lcd.print("Photograph"); | ||
+ | current_verse = 0; | ||
+ | count = 0; | ||
+ | current_song = 1; | ||
+ | } | ||
+ | } | ||
+ | |||
+ | current_time = millis(); | ||
+ | |||
+ | // 5 seconds after last period count | ||
+ | if (current_time - last_time > 5000) | ||
+ | { | ||
+ | last_time = current_time; | ||
+ | count ++; | ||
+ | if (current_song == 1 && count > 5) | ||
+ | { | ||
+ | if (current_verse < song1_length) | ||
+ | { | ||
+ | lcd.clear(); | ||
+ | lcd.print(song1[current_verse]); | ||
+ | current_verse++; | ||
+ | } | ||
+ | } | ||
+ | |||
+ | if (current_song == 2 && count > 4) | ||
+ | { | ||
+ | if (current_verse < song2_length) | ||
+ | { | ||
+ | lcd.clear(); | ||
+ | lcd.print(song2[current_verse]); | ||
+ | current_verse++; | ||
+ | } | ||
+ | } | ||
+ | |||
+ | if (current_song == 3 && count > 3) | ||
+ | { | ||
+ | if (current_verse < song3_length) | ||
+ | { | ||
+ | lcd.clear(); | ||
+ | lcd.print(song3[current_verse]); | ||
+ | current_verse++; | ||
+ | } | ||
+ | } | ||
+ | } | ||
+ | |||
+ | waveform = OCR1A; //read TIMER1 value, represent for wave form music | ||
+ | waveform = waveform - 256; // middle point of volume(2) is 256 | ||
+ | qtyLED = waveform / 16; //q'ty LED need to be | ||
+ | |||
+ | if (qtyLED <= 0) | ||
+ | { | ||
+ | qtyLED0(); | ||
+ | delay(timeLED); | ||
+ | } | ||
+ | |||
+ | if (qtyLED == 1) { | ||
+ | qtyLED0(); | ||
+ | delay(timeLED); | ||
+ | qtyLED1(); | ||
+ | delay(timeLED); | ||
+ | } | ||
+ | |||
+ | if (qtyLED == 2) { | ||
+ | qtyLED0(); | ||
+ | delay(timeLED); | ||
+ | qtyLED1(); | ||
+ | delay(timeLED); | ||
+ | qtyLED2(); | ||
+ | delay(timeLED); | ||
+ | } | ||
+ | |||
+ | if (qtyLED == 3) { | ||
+ | qtyLED0(); | ||
+ | delay(timeLED); | ||
+ | qtyLED1(); | ||
+ | delay(timeLED); | ||
+ | qtyLED2(); | ||
+ | delay(timeLED); | ||
+ | qtyLED3(); | ||
+ | delay(timeLED); | ||
+ | } | ||
+ | |||
+ | if (qtyLED == 4) { | ||
+ | qtyLED0(); | ||
+ | delay(timeLED); | ||
+ | qtyLED1(); | ||
+ | delay(timeLED); | ||
+ | qtyLED2(); | ||
+ | delay(timeLED); | ||
+ | qtyLED3(); | ||
+ | delay(timeLED); | ||
+ | qtyLED4(); | ||
+ | delay(timeLED); | ||
+ | } | ||
+ | |||
+ | if (Serial.available()) { | ||
+ | switch (Serial.read()) { | ||
+ | case 'd': tmrpcm.play("sng1.wav"); lcd.clear(); lcd.print("Photograph"); current_song = 1; current_verse = 0; count = 0; break; | ||
+ | case 'b': tmrpcm.play("sng2.wav"); lcd.clear(); lcd.print("Tattoo"); current_song = 2; current_verse = 0; count = 0; break; | ||
+ | case 'r': tmrpcm.play("sng3.wav"); lcd.clear(); lcd.print("Someone like you"); current_song = 3; current_verse = 0; count = 0; break; | ||
+ | case 'p': tmrpcm.pause(); break; | ||
+ | case '?': if (tmrpcm.isPlaying()) { | ||
+ | Serial.println("A wav file is being played"); | ||
+ | } break; | ||
+ | |||
+ | case 'S': tmrpcm.stopPlayback(); break; | ||
+ | case '=': tmrpcm.volume(1); break; | ||
+ | case '-': tmrpcm.volume(0); break; | ||
+ | case '0': tmrpcm.quality(0); break; | ||
+ | case '1': digitalWrite(LED_R, LED_ON); digitalWrite(LED_G, LED_OFF); digitalWrite(LED_B, LED_OFF); break; // red | ||
+ | case '2': digitalWrite(LED_R, LED_OFF); digitalWrite(LED_G, LED_ON); digitalWrite(LED_B, LED_OFF); break; // green | ||
+ | case '3': digitalWrite(LED_R, LED_ON); digitalWrite(LED_G, LED_OFF); digitalWrite(LED_B, LED_ON); break; // purple | ||
+ | case '4': digitalWrite(LED_R, LED_OFF); digitalWrite(LED_G, LED_OFF); digitalWrite(LED_B, LED_ON); break; // blue | ||
+ | default: break; | ||
+ | } | ||
+ | } | ||
+ | } | ||
+ | |||
+ | void qtyLED1() { | ||
+ | digitalWrite(17, 0); | ||
+ | digitalWrite(16, 0); | ||
+ | digitalWrite(15, 0); | ||
+ | digitalWrite(14, 1); | ||
+ | } | ||
+ | |||
+ | void qtyLED2() { | ||
+ | digitalWrite(17, 0); | ||
+ | digitalWrite(16, 0); | ||
+ | digitalWrite(15, 1); | ||
+ | digitalWrite(14, 1); | ||
+ | } | ||
+ | |||
+ | void qtyLED3() { | ||
+ | digitalWrite(17, 0); | ||
+ | digitalWrite(16, 1); | ||
+ | digitalWrite(15, 1); | ||
+ | digitalWrite(14, 1); | ||
+ | } | ||
+ | |||
+ | void qtyLED4() { | ||
+ | digitalWrite(17, 1); | ||
+ | digitalWrite(16, 1); | ||
+ | digitalWrite(15, 1); | ||
+ | digitalWrite(14, 1); | ||
+ | } | ||
+ | |||
+ | void qtyLED0() { | ||
+ | digitalWrite(17, 0); | ||
+ | digitalWrite(16, 0); | ||
+ | digitalWrite(15, 0); | ||
+ | digitalWrite(14, 0); | ||
+ | } | ||
+ | </code> | ||
+ | |||
+ | ===== Rezultate Obţinute ===== | ||
+ | |||
+ | Am obținut un proiect funcțional care are capacitatea de a reda 3 melodii prin intermediul celor 2 butoane (start/stop și next), care aprinde/stinge 4 LED-uri în funcție de amplitudinea sunetului la un moment dat, care afișează melodia/versurile melodiei pe un ecran LCD 16x2 și care aprinde un LED RGB în funcție de comenzile primite de pe telefon, datorită modului Bluetooth. | ||
+ | |||
+ | ===== Concluzii ===== | ||
+ | |||
+ | Am obținut în urma acestui proiect un Karaoke player funcțional, care conține și alte funcționalități, pe lângă cele principale. Este important de menționat că încă se pot face modificări semnificative la proiect, în special pe partea de afișare a versurilor melodiilor. | ||
===== Bibliografie/Resurse ===== | ===== Bibliografie/Resurse ===== | ||
- | <note> | + | **Resurse Software** |
- | Listă cu documente, datasheet-uri, resurse Internet folosite, eventual grupate pe **Resurse Software** şi **Resurse Hardware**. | + | * Laboratoare PM |
- | </note> | + | * LED-uri în funcție de amplitudinea sunetului - https://www.hackster.io/whitebank/arduino-music-player-with-leds-tutorial-cc6114 |
+ | * Redare melodii wav - https://www.instructables.com/Playing-Wave-file-using-arduino/ | ||
+ | |||
+ | **Resurse Hardware** | ||
+ | * Conectare difuzor la Arduino UNO - https://www.hackster.io/whitebank/arduino-music-player-with-leds-tutorial-cc6114 | ||
+ | * Laboratoare PM | ||
<html><a class="media mediafile mf_pdf" href="?do=export_pdf">Export to PDF</a></html> | <html><a class="media mediafile mf_pdf" href="?do=export_pdf">Export to PDF</a></html> |