JeÅ›li już szukaÅ‚eÅ› w miarÄ™ taniego moduÅ‚u potrafiÄ…cego odtwarzać pliki dźwiÄ™kowe MP3 lub WAV, to zapewne musiaÅ‚eÅ› siÄ™ trochÄ™ zawieść. Owszem, sÄ… dostÄ™pne dość drogie shieldy do Arduino lub o wiele taÅ„sze moduÅ‚y, które niestety nie oferujÄ… zbyt wiele w zamian. Mnie osobiÅ›cie brakowaÅ‚o czegoÅ› poÅ›redniego - nie drogiego, ale również nie okrojonego do granic możliwoÅ›ci.
Przeglądając ebay, natrafiłem na bardzo ciekawy moduł MP3 bazujący na układzie YX5300 z komunikacją UART w cenie zaledwie 6$ z darmową wysyłką.
Pewnie zadajesz sobie pytanie, co może potrafić moduÅ‚ za 6 dolców? Wbrew pozorom - caÅ‚kiem sporo. UkÅ‚ad YX5300, który zostaÅ‚ zastosowany w tym module wyróżnia siÄ™ nastÄ™pujÄ…cymi cechami:
Pewnie zadajesz sobie pytanie, co może potrafić moduÅ‚ za 6 dolców? Wbrew pozorom - caÅ‚kiem sporo. UkÅ‚ad YX5300, który zostaÅ‚ zastosowany w tym module wyróżnia siÄ™ nastÄ™pujÄ…cymi cechami:
0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 |
0x7E | 0xFF | 0x06 | CMD | FBACK | DAT1 | DAT2 | SUM1 | SUM2 | 0xEF |
Najbardziej kłopotliwe okazuje się wyliczenie sumy kontrolnej SUM1 i SUM2, jednak jest możliwe pominięcie tego mechanizmu, pomijając je po prostu przy wysyłaniu (nie zmieniając wartości określającej długość polecenia)
Zatem możemy wysÅ‚ać tylko 8 bajtów :
0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 |
0x7E | 0xFF | 0x06 | CMD | FBACK | DAT1 | DAT2 | 0xEF |
ModuÅ‚ oferuje sporo ciekawych komend, za pomocÄ… których bÄ™dziemy mogli sterować ukÅ‚adem. DostÄ™pne sÄ… wszystkie podstawowe akcje jak pauza, play, stop, ustawienie gÅ‚oÅ›noÅ›ci. Dodatkowo jednak dostajemy sporo innych ciekawych poleceÅ„, które bez wÄ…tpienia przydadzÄ… siÄ™ w naszych projektach - tryb uÅ›pienia, tasowanie listy odtwarzania, zapÄ™tlanie utworu lub katalogu, czy sterowanie DAC. Nie wiem jak Wam, ale mnie ta "szeÅ›cio dolarowa" lista bardzo odpowiada:
CMD | DAT1 | DAT2 | Opis |
0x0C | 0x00 | 0x00 | Reset układu. Wymagana przerwa 500ms |
0x09 | 0x00 | 0x02 | Wybierz nośnik odtwarzania TF. Wymagana przerwa 200ms |
0x03 | 0x00 | 0x03 | Odtwarzaj 3 plik |
0x08 | 0x00 | 0x03 | Odtwarzaj w pętli 3 plik |
0x0F | 0x02 | 0x01 | Odtwarzaj 1 plik w 2 folderze |
0x22 | 0x1E | 0x04 | Odtwarzaj 4 plik z głośnością 30 = 1E |
0x17 | 0x02 | 0x00 | Odtwarzaj pÄ™tle dla utworów w 2 folderze |
0x18 | 0x00 | 0x00 | Przetasowanie utworów |
0x19 | 0x00 | 0x00 | Włącz pętle dla obecnie odtwarzanego utworu |
0x19 | 0x00 | 0x01 | Wyłącz pętle dla obecnie odtwarzanego utworu |
0x01 | 0x00 | 0x00 | NastÄ™pny utwór |
0x02 | 0x00 | 0x00 | Poprzedni utwór |
0x0E | 0x00 | 0x00 | Pauza |
0x0D | 0x00 | 0x00 | Wznów odtwarzanie |
0x16 | 0x00 | 0x00 | Zatrzymaj odtwarzanie |
0x04 | 0x00 | 0x00 | Vol+ |
0x05 | 0x00 | 0x00 | Vol- |
0x06 | 0x00 | 0x1E | Ustaw poziom głośności na 30 = 1E |
0x0A | 0x00 | 0x00 | Uśpienie układu (Sleep mode) |
0x0B | 0x00 | 0x00 | Budzenie układu (Wake up) |
0x1A | 0x00 | 0x00 | WÅ‚Ä…cz DAC |
0x1A | 0x00 | 0x01 | Wyłącz DAC |
Oprócz standardowych poleceÅ„ ukÅ‚ad zwraca również odpowiedzi na specjalne zapytania lub informuje nas o specyficznych zdarzeniach, jak wÅ‚ożenie karty pamiÄ™ci, zakoÅ„czeniu odtwarzania utworu i jego numerze itp:
CMD | Odpowiedź | Opis |
-- | 0x7E 0xFF 0x06 0x41 0x00 0x00 0x00 0xFE 0xBA 0xEF | Dane odebrane prawidłowo |
-- | 0x7E 0xFF 0x06 0x3A 0x00 0x00 0x02 0xFE 0xBF 0xEF | Włożono kartę pamięci TF |
-- | 0x7E 0xFF 0x06 0x3B 0x00 0x00 0x02 0xFE 0xBE 0xEF | Wyjęto nośnik pamięci TF |
-- | 0x7E 0xFF 0x06 0x40 0x00 0x00 0x06 0xFE 0xB5 0xEF | BÅ‚Ä…d - nie odnaleziono pliku |
-- | 0x7E 0xFF 0x06 0x3D 0x00 0x00 0x04 0xFE 0xBA 0xEF | Zakończono odtwarzać plik 4 |
0x42 | 0x7E 0xFF 0x06 0x42 0x00 0x02 0x00 0xFE 0xB6 0xE | Stan odtwarzacza - zatrzymany (stop) |
0x42 | 0x7E 0xFF 0x06 0x42 0x00 0x02 0x01 0xFE 0xB6 0xE | Stan odtwarzacza - odtwarza (play) |
0x42 | 0x7E 0xFF 0x06 0x42 0x00 0x02 0x02 0xFE 0xB6 0xE | Stan odtwarzacza - wstrzymany (pauza) |
0x48 | 0x7E 0xFF 0x06 0x48 0x00 0x00 0x07 0xFE 0xAC 0xEF | Znaleziono 7 plików |
0x4C | 0x7E 0xFF 0x06 0x4C 0x00 0x00 0x04 0xFE 0xAB 0xEF | Aktualnie odtwarzany 4 plik |
0x4F | 0x7E 0xFF 0x06 0x4F 0x00 0x00 0x02 0xFE 0xAA 0xEF | Znaleziono 2 foldery |
0x43 | 0x7E 0xFF 0x06 0x43 0x00 0x00 0x1E 0xFE 0x99 0xEF | Aktualny poziom głośności 30 = 1F |
Aby sprawnie odtwarzać pliki dźwiÄ™kowe z karty SD, zalecana jest struktura katalogów w formacie dwucyfrowym. Nawet jeÅ›li planujemy tylko jednÄ… playlistÄ™, warto trzymać jÄ… w folderze "01". Nazwy plików powinny mieć format jak poniżej, czyli kolejny numer trzycyfrowy i opcjonalna nazwa pliku. PrzestrzegajÄ…c tych zasad, w prosty sposób odtworzymy sprecyzowany przez nas plik MP3:
bash-4.2$ tree
.
├── 01
│ ├── 001-blue-piano.mp3
│ └── 002-gettin-ready.mp3
├── 02
│ ├── 001-ukulele-in-my-heart.mp3
│ ├── 002-inspiring-innovation.mp3
│ └── 003-thanks-for-the-memories.mp3
└── 03
├── 001-a-hero-is-born.mp3
└── 002-lullaby.mp3
Wysłanie poniższej komendy, spowoduje odtworzenie pliku 001-ukulele-in-my-heart.mp3 z folderu 02.
0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 |
0x7E | 0xFF | 0x06 | 0x0F | 0x00 | 0x02 | 0x01 | 0xEF |
Możliwe jest również wskazanie absolutnego numeru pliku do odtworzenia, niezależnie od folderu w którym siÄ™ znajduje. Należy jednak pamiÄ™tać, że moduÅ‚ nie sortuje zawartoÅ›ci katalogów, wiÄ™c jego struktura z punktu ukÅ‚adu wyglÄ…da tak:
bash-4.2$ tree -U
.
├── 01
│ ├── 001-blue-piano.mp3
│ └── 002-gettin-ready.mp3
├── 02
│ ├── 003-thanks-for-the-memories.mp3
│ ├── 002-inspiring-innovation.mp3
│ └── 001-ukulele-in-my-heart.mp3
└── 03
├── 002-lullaby.mp3
└── 001-a-hero-is-born.mp3
Jak widzimy, trzecim utworem absolutnym jest 003-thanks-for-the-memories.mp3 zamiast spodziewanego 001-ukulele-in-my-heart.mp3. Aby odtworzyć plik o absolutnym numerze od początku listy, należy wysłać polecenie:
0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 |
0x7E | 0xFF | 0x06 | 0x03 | 0x00 | 0x00 | 0x03 | 0xEF |
#include
SoftwareSerial mp3(5, 6);
static uint8_t cmdbuf[8] = {0};
void command(int8_t cmd, int16_t dat)
{
delay(20);
cmdbuf[0] = 0x7e; // bajt startu
cmdbuf[1] = 0xFF; // wersja
cmdbuf[2] = 0x06; // liczba bajtow polecenia
cmdbuf[3] = cmd; // polecenie
cmdbuf[4] = 0x00; // 0x00 = no feedback, 0x01 = feedback
cmdbuf[5] = (int8_t)(dat >> 8); // parametr DAT1
cmdbuf[6] = (int8_t)(dat); // parametr DAT2
cmdbuf[7] = 0xef; // bajt konczacy
for (uint8_t i = 0; i < 8; i++)
{
mp3.write(cmdbuf[i]);
}
}
void setup()
{
Serial.begin(9600);
mp3.begin(9600);
delay(500); // Czekamy 500ms na inicjalizacje
command(0x09, 0x0002); // Wybieramy karte SD jako zrodlo
delay(200); // Czekamu 200ms na inicjalizacje
command(0x06, 0x001E); // Ustaw glosnosc na 30
command(0x03, 0x0001); // Otwarzamy pierwszy utwor (kolejnosc nieposortowana)
}
void loop() { }
Odtworzenie pliku o absolutnym numerze wcale nie gwarantuje, że bÄ™dzie odtworzony ten, który oczekujemy na podstawie posortowanej listy. Dlatego najlepiej skorzystać z polecenia, podajÄ…c nazwÄ™ folderu i nazwÄ™ pliku w postaci poczÄ…tkowego numeru:
void setup()
{
Serial.begin(9600);
mp3.begin(9600);
delay(500); // Czekamy 500ms na inicjalizacje
command(0x09, 0x0002); // Wybieramy karte SD jako zrodlo
delay(200); // Czekamu 200ms na inicjalizacje
command(0x06, 0x001E); // Ustaw glosnosc na 30
command(0x0F, 0x0201); // Jesli chcemy miec pewnosc ze bedzie to 02/001?????.mp3
}
Powyższy fragment programu spowoduje, że zostanie odtworzony plik 001-ukulele-in-my-heart.mp3 w folderze 02/. Jednak będzie to odtworzenie tylko tego utworu. Aby zapętlić odtwarzanie całego folderu 02/ wystaczy wydać inne polecenie:
void setup()
{
Serial.begin(9600);
mp3.begin(9600);
delay(500); // Czekamy 500ms na inicjalizacje
command(0x09, 0x0002); // Wybieramy karte SD jako zrodlo
delay(200); // Czekamu 200ms na inicjalizacje
command(0x06, 0x001E); // Ustaw glosnosc na 30
command(0x17, 0x0200); // Odtwarzaj w petli folder 02/
}
ModuÅ‚ wysyÅ‚a również komunikaty i odpowiedzi na poszczególne polecenia i zdarzenia. Poniższy program wykrywa wÅ‚ożenie i wyciÄ…gniÄ™cie karty pamiÄ™ci. Dodatkowo zostanie odtworzony w pÄ™tli folder 02/ gdy karta zostanie wÅ‚ożona.
#include
SoftwareSerial mp3(5, 6);
static uint8_t cmdbuf[8] = {0};
static uint8_t ansbuf[10] = {0};
void command(int8_t cmd, int16_t dat)
{
delay(20);
cmdbuf[0] = 0x7e; // bajt startu
cmdbuf[1] = 0xFF; // wersja
cmdbuf[2] = 0x06; // liczba bajtow polecenia
cmdbuf[3] = cmd; // polecenie
cmdbuf[4] = 0x00; // 0x00 = no feedback, 0x01 = feedback
cmdbuf[5] = (int8_t)(dat >> 8); // parametr DAT1
cmdbuf[6] = (int8_t)(dat); // parametr DAT2
cmdbuf[7] = 0xef; // bajt konczacy
for (uint8_t i = 0; i < 8; i++)
{
mp3.write(cmdbuf[i]);
}
}
// Funkcja upiekszajaca hex
void byte2hex(uint8_t b)
{
Serial.print("0x");
if (b < 16) Serial.print("0");
Serial.print(b, HEX);
Serial.print(" ");
}
// Funkcja pobierania odpowiedzi
boolean answer(void)
{
uint8_t i = 0;
// Pobieramy tylko 10 bajtow
while(mp3.available() && (i < 10))
{
uint8_t b = mp3.read();
ansbuf[i] = b;
i++;
byte2hex(b);
}
Serial.println();
// Jesli poprawny format odpowiedzi
if ((ansbuf[0] == 0x7E) && (ansbuf[9] == 0xEF))
{
return true;
}
return false;
}
void setup()
{
Serial.begin(9600);
mp3.begin(9600);
delay(500); // Czekamy 500ms na inicjalizacje
command(0x09, 0x0002); // Wybieramy karte SD jako zrodlo
delay(200); // Czekamu 200ms na inicjalizacje
}
void loop()
{
if (mp3.available())
{
if (answer()) // Jesli jest poporawna odpowiedz
{
if (ansbuf[3] == 0x3A) // Jesli wlozono kart
{
Serial.println("Wlozono karte");
command(0x17, 0x0100); // Odtwarzaj w petli folder 01/
}
if (ansbuf[3] == 0x3B) // Jesli wyciagnieto kart
{
Serial.println("Wyciagnieto karte");
}
}
}
}
OczywiÅ›cie to tylko przykÅ‚ad, bowiem możemy jeszcze odpytać moduÅ‚ o ilość plików i folderów na karcie pamiÄ™ci lub sprawdzać, czy moduÅ‚ aktualnie odtwarza utwór oraz pobrać jego absolutny numer.
#include
SoftwareSerial mp3(5, 6);
static uint8_t cmdbuf[8] = {0};
static uint8_t ansbuf[10] = {0};
unsigned long int last = 0;
void command(int8_t cmd, int16_t dat)
{
delay(20);
cmdbuf[0] = 0x7e; // bajt startu
cmdbuf[1] = 0xFF; // wersja
cmdbuf[2] = 0x06; // liczba bajtow polecenia
cmdbuf[3] = cmd; // polecenie
cmdbuf[4] = 0x00; // 0x00 = no feedback, 0x01 = feedback
cmdbuf[5] = (int8_t)(dat >> 8); // parametr DAT1
cmdbuf[6] = (int8_t)(dat); // parametr DAT2
cmdbuf[7] = 0xef; // bajt konczacy
for (uint8_t i = 0; i < 8; i++)
{
mp3.write(cmdbuf[i]);
}
}
// Funkcja upiekszajaca hex
void byte2hex(uint8_t b)
{
Serial.print("0x");
if (b < 16) Serial.print("0");
Serial.print(b, HEX);
Serial.print(" ");
}
// Funkcja pobierania odpowiedzi
boolean answer(void)
{
uint8_t i = 0;
// Pobieramy tylko 10 bajtow
while(mp3.available() && (i < 10))
{
uint8_t b = mp3.read();
ansbuf[i] = b;
i++;
byte2hex(b);
}
Serial.println();
// Jesli poprawny format odpowiedzi
if ((ansbuf[0] == 0x7E) && (ansbuf[9] == 0xEF))
{
return true;
}
return false;
}
void setup()
{
Serial.begin(9600);
mp3.begin(9600);
delay(500); // Czekamy 500ms na inicjalizacje
command(0x09, 0x0002); // Wybieramy karte SD jako zrodlo
delay(200); // Czekamu 200ms na inicjalizacje
command(0x17, 0x0100); // Odtwarzaj w petli folder 01/
last = millis();
}
void loop()
{
if (mp3.available())
{
if (answer()) // Jesli jest poporawna odpowiedz
{
if (ansbuf[3] == 0x4C) // Aktualnie odtwarzany
{
Serial.print("Aktualnie odtwarzam plik: ");
Serial.println(ansbuf[6]);
}
}
}
if ((millis() - last) > 5000) // Pytamy co 5 seknud
{
last = millis();
command(0x4C, 0x0000); // Zapytaj o numer odtwarzanego pliku
}
}
Support sampling frequency (kHz):8 / 11.025 / 12 / 16 / 22.05 / 24 / 32 / 44.1 / 48
Support file format:MP3/WAV
Support Micro SD card, Micro SDHC Card
30 class adjustable volume
UART TTL serial control playback mode, baud rate is 9600bps
Power supply can be 3.2 ~ 5.2VDC
Size:43mm x 25mm